From eff107aa15b86f1632d5886ed87d909427bd3826 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com> Date: Mon, 13 Apr 2026 16:42:26 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A1=A8=E6=A0=BC=E5=8F=82=E8=80=83=E6=96=87?= =?UTF-8?q?=E7=8C=AE=E7=9A=84=E9=A2=84=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/js/citeTablePreview.js | 169 ++++++++++++++++++ src/common/js/commonJS.js | 3 +- src/components/page/GenerateCharts.vue | 2 + .../page/components/Tinymce/index.vue | 9 +- .../page/components/table/DynamicTable.vue | 54 +++++- src/components/page/components/table/word.vue | 12 +- .../page/components/table/wordHtml.vue | 22 ++- .../components/table/wordHtmlTypesetting.vue | 24 ++- 8 files changed, 268 insertions(+), 27 deletions(-) create mode 100644 src/common/js/citeTablePreview.js diff --git a/src/common/js/citeTablePreview.js b/src/common/js/citeTablePreview.js new file mode 100644 index 0000000..a615e05 --- /dev/null +++ b/src/common/js/citeTablePreview.js @@ -0,0 +1,169 @@ +/** + * 表格弹窗预览:将单元格内空 mycite 转为可见 [n],与 word.vue renderCiteLabels 一致(纯函数,无 Vue 依赖) + */ + +export function formatCiteNumbers(nums) { + if (!nums || !nums.length) return ''; + const sorted = [...new Set(nums)].sort((a, b) => a - b); + const result = []; + let i = 0; + while (i < sorted.length) { + let j = i; + while (j < sorted.length - 1 && sorted[j + 1] === sorted[j] + 1) j++; + if (j - i >= 2) { + result.push(`${sorted[i]}–${sorted[j]}`); + } else { + for (let k = i; k <= j; k++) result.push(sorted[k]); + } + i = j + 1; + } + return result.join(', '); +} + +export function sortAutociteIdsByCiteNumber(ids, citeMap) { + const map = citeMap || {}; + const uniq = [...new Set(ids.map(String))]; + return uniq.sort((a, b) => { + const na = map[a]; + const nb = map[b]; + const ha = na != null && na !== ''; + const hb = nb != null && nb !== ''; + if (ha && hb) return Number(na) - Number(nb); + if (ha) return -1; + if (hb) return 1; + return String(a).localeCompare(String(b)); + }); +} + +/** + * @param {Array} refs - chanFerForm,项含 p_refer_id + * @param {Array} bodyCiteIdOrder - 正文首次出现顺序;空则按列表行序 1..n + */ +export function buildCiteMapFromRefs(refs, bodyCiteIdOrder) { + const refList = Array.isArray(refs) ? refs : []; + const refIdSet = new Set( + refList.map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')).filter(Boolean) + ); + + let orderedIds = []; + if (Array.isArray(bodyCiteIdOrder) && bodyCiteIdOrder.length > 0) { + orderedIds = bodyCiteIdOrder.map(String); + } + + if (orderedIds.length === 0) { + const map = {}; + refList.forEach((row, idx) => { + const key = row && row.p_refer_id != null ? String(row.p_refer_id) : ''; + if (key) map[key] = idx + 1; + }); + return map; + } + + const filtered = []; + orderedIds.forEach((id) => { + if (refIdSet.has(id) && !filtered.includes(id)) filtered.push(id); + }); + const map = {}; + filtered.forEach((id, idx) => { + map[id] = idx + 1; + }); + let next = filtered.length + 1; + refList.forEach((r) => { + const key = r && r.p_refer_id != null ? String(r.p_refer_id) : ''; + if (!key || map[key] != null) return; + map[key] = next++; + }); + return map; +} + +function escAttr(s) { + return String(s || '') + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/ 显示序号 + */ +export function renderCiteLabelsInHtml(html, refs, citeMap) { + if (!html || typeof html !== 'string') return html || ''; + if (!Array.isArray(refs) || refs.length === 0) return html; + + /** 与稿面一致:历史/表格 JSON 中可能为 */ + let h = html.replace(/<\/autocite>/gi, '').replace(/ { + const key = item && item.p_refer_id != null ? String(item.p_refer_id) : ''; + if (key) acc[key] = item; + return acc; + }, {}); + + let normalized = h.replace(/]*\/?>[\s\S]*?(?:<\/mycite>)?/gi, (fullTag) => { + const attrMatch = fullTag.match(/\bdata-id\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>]+))/i); + const dataId = (attrMatch && (attrMatch[1] || attrMatch[2] || attrMatch[3])) || ''; + if (!dataId) return ''; + return ``; + }); + const citeGroupRe = /(?:<\/mycite>\s*)+/gi; + normalized = normalized.replace(citeGroupRe, (groupMatch) => { + const ids = []; + const innerRe = /<\/mycite>/gi; + let m; + while ((m = innerRe.exec(groupMatch)) !== null) { + m[1].split(',').forEach((part) => { + const id = part.trim(); + if (id) ids.push(id); + }); + } + const sortedAll = sortAutociteIdsByCiteNumber(ids, citeMap); + const validIds = sortedAll.filter((id) => refMap[id]); + const sortedIds = validIds.length ? sortAutociteIdsByCiteNumber(validIds, citeMap) : []; + const map = citeMap || {}; + + const parts = sortedIds.map((id) => { + const ref = refMap[id]; + const no = ref ? map[id] : null; + const num = no != null && no !== '' ? String(no) : null; + return { id, ref, num }; + }); + + const numsForLabel = parts + .map((p) => p.num) + .filter((n) => n != null && n !== '') + .map(Number); + const label = numsForLabel.length > 0 ? formatCiteNumbers(numsForLabel) : ''; + if (!label) { + return ''; + } + const dataIdAttr = escAttr(sortedIds.join(',')); + return `[${label}]`; + }); + return normalized.replace(/]*data-id=""[^>]*>[\s\S]*?<\/mycite>/gi, ''); +} + +export function applyCiteLabelsToTableRows(rows, refs, citeMap) { + if (!Array.isArray(rows) || !rows.length) return rows; + if (!Array.isArray(refs) || refs.length === 0) return rows; + + return rows.map((row) => { + if (!Array.isArray(row)) return row; + const nextRow = row.map((cell) => { + if (!cell || typeof cell !== 'object') return cell; + const t = cell.text; + const lower = typeof t === 'string' ? t.toLowerCase() : ''; + if (!lower || (!lower.includes('mycite') && !lower.includes('autocite'))) return cell; + const next = renderCiteLabelsInHtml(t, refs, citeMap); + if (next === t) return cell; + return { ...cell, text: next }; + }); + // TableUtils.addRowIdToData 会给数组行对象挂 rowId,渲染 oddColor 依赖它,不能在 map 后丢失 + if (row.rowId != null) { + nextRow.rowId = row.rowId; + } + return nextRow; + }); +} diff --git a/src/common/js/commonJS.js b/src/common/js/commonJS.js index a4380f8..70ff034 100644 --- a/src/common/js/commonJS.js +++ b/src/common/js/commonJS.js @@ -1035,8 +1035,7 @@ str = str.replace(regex, function (match, content, offset, fullString) { const cells = row.querySelectorAll('th, td'); // 获取每个行中的单元格(包括 和 ) return await Promise.all( Array.from(cells).map(async (cell) => { - console.log("🚀 ~ parseTableToArray777 ~ cell:", cell); - + const text = await this.extractMathJaxLatex(cell); return { text, diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue index b10f107..7775199 100644 --- a/src/components/page/GenerateCharts.vue +++ b/src/components/page/GenerateCharts.vue @@ -57,6 +57,8 @@ table: 'api/Preaccept/getMainTables' }" :articleId="articleId" + :bodyCiteIdOrder="articleCiteIdOrder" + :chanFerForm="chanFerForm" :content="ManuscirptContent" ref="commonWordHtmlTypeSetting" @onDragStart="onDragStart" diff --git a/src/components/page/components/Tinymce/index.vue b/src/components/page/components/Tinymce/index.vue index 141dfa5..42a4b61 100644 --- a/src/components/page/components/Tinymce/index.vue +++ b/src/components/page/components/Tinymce/index.vue @@ -681,11 +681,12 @@ export default { const noCite = ref ? citeMap[String(id)] : null; const noTable = useTableBracketNums && tableNums[String(id)] != null ? tableNums[String(id)] : null; + // 单元格内 [n] 按 Original order(table 序号)匹配;角标展示与正文一致,优先用 citeMap 对应序号 const num = - noTable != null && noTable !== '' && !Number.isNaN(Number(noTable)) - ? String(noTable) - : noCite != null && noCite !== '' - ? String(noCite) + noCite != null && noCite !== '' && !Number.isNaN(Number(noCite)) + ? String(noCite) + : noTable != null && noTable !== '' && !Number.isNaN(Number(noTable)) + ? String(noTable) : null; return { id, ref, num }; }); diff --git a/src/components/page/components/table/DynamicTable.vue b/src/components/page/components/table/DynamicTable.vue index 5e06a47..e0c2897 100644 --- a/src/components/page/components/table/DynamicTable.vue +++ b/src/components/page/components/table/DynamicTable.vue @@ -58,9 +58,25 @@