From d613aa7d0d2cd77327ce3edfabad4de126f00813 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: Fri, 10 Apr 2026 15:53:15 +0800 Subject: [PATCH] tijiao --- src/api/index.js | 16 + src/components/common/langs/en.js | 6 +- src/components/common/langs/zh.js | 6 +- src/components/page/Complete_profile.vue | 22 +- src/components/page/GenerateCharts.vue | 90 ++++ src/components/page/PreIngestedEditor.vue | 10 +- src/components/page/comArtHtmlCreatNew.vue | 75 +++ .../page/components/Tinymce/index.vue | 194 +++++-- src/components/page/components/table/word.vue | 510 +++++++++++++++--- src/components/page/mailboxMouldDetail.vue | 6 +- 10 files changed, 796 insertions(+), 139 deletions(-) diff --git a/src/api/index.js b/src/api/index.js index 487fb66..a682184 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -102,6 +102,22 @@ export default { }) }, + /** + * POST x-www-form-urlencoded,数组字段序列化为 list[]=a&list[]=b(兼容 PHP 批量接口) + */ + postFormBracket(url, params) { + return new Promise((resolve, reject) => { + service + .post(url, qs.stringify(params, { arrayFormat: 'brackets' })) + .then((res) => { + resolve(res.data) + }) + .catch((err) => { + reject(err) + }) + }) + }, + /** * post方法,对应post请求 * @param {String} url [请求的url地址] diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 880c19d..3727c96 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1164,6 +1164,7 @@ colTitle: 'Template title', notFoundById: 'No reference found for citation ID {id}', selectRef: 'Select Reference', reference: 'Reference', + originalOrder: 'Original order', uncited: 'Uncited', cancel: 'Cancel', confirm: 'Confirm', @@ -1178,7 +1179,10 @@ colTitle: 'Template title', removeRefNeedClickCite: 'Click a citation in the text first, then choose Reference remove.', quickPickPlaceholder: 'Enter e.g. [5, 6, 10-15] to auto-select', quickPickApply: 'Link References', - quickPickClear: 'Clear selection' + quickPickClear: 'Clear selection', + currentCiteNo: 'Current', + locateInBody: 'Click to scroll to citation in text', + locateInRefHint: 'Highlight this entry in the list below' } diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index 5108e20..84d4c51 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1149,6 +1149,7 @@ const zh = { notFoundById: '未查询到编号{id}相关参考文献', selectRef: '选择参考文献', reference: '参考文献', + originalOrder: '原排序', uncited: '未引用', cancel: '取消', confirm: '确认', @@ -1163,7 +1164,10 @@ const zh = { removeRefNeedClickCite: '请先在正文中点击要删除的引用角标,再点「移除参考文献」。', quickPickPlaceholder: '输入如 [5, 6, 10-15] 自动勾选对应参考文献', quickPickApply: '链接参考文献', - quickPickClear: '清空勾选' + quickPickClear: '清空勾选', + currentCiteNo: '当前序号', + locateInBody: '点击定位正文角标', + locateInRefHint: '在下方列表中高亮该条' } diff --git a/src/components/page/Complete_profile.vue b/src/components/page/Complete_profile.vue index 774d765..b3e414a 100644 --- a/src/components/page/Complete_profile.vue +++ b/src/components/page/Complete_profile.vue @@ -136,16 +136,7 @@ -
- -
-

{{ this.$t('PreAccept.step2') }}

-

- Edit -

- -
-
+
@@ -170,7 +161,16 @@
- +
+ +
+

{{ this.$t('PreAccept.step2') }}

+

+ Edit +

+ +
+
diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue index 7792d6d..06d609e 100644 --- a/src/components/page/GenerateCharts.vue +++ b/src/components/page/GenerateCharts.vue @@ -595,6 +595,17 @@

+ + +
@@ -890,6 +901,8 @@ export default { refSelectorSource: 'commonContent', /** 表格抽屉内合并稿防抖:按全文首次出现顺序更新 articleCiteIdOrder,避免角标停留在列表序号 [38] */ _tableReorderTimer: null, + /** 最近一次 getReferList 返回的 p_refer_id 顺序;与当前 chanFerForm 不一致时回写 batchUpdateRefer */ + _lastReferListApiOrder: null, /** 打开选择器时若带 currentRefIds(编辑已有引用),排序用「传值计算的顺序」;关闭后清空 */ refSelectorContextIds: [], /** 选择参考文献弹窗:按 # 序号快速勾选,如 [5, 6, 10-15] */ @@ -1496,6 +1509,7 @@ export default { p_article_id: this.p_article_id }) .then((res) => { + this._lastReferListApiOrder = this.referIdOrderSnapshot(res.data && res.data.refers).slice(); this.chanFerForm = res.data.refers; this.chanFerFormRepeatList = Object.values(res.data.repeat || {}); for (let i = 0; i < this.chanFerForm.length; i++) { @@ -1522,6 +1536,8 @@ export default { } /** getReferList 会整表覆盖 chanFerForm,需再按正文首次出现顺序对齐,否则下方列表与 [n] 脱节 */ this.applyRefOrderAfterFetchReferList(); + /** 正文序与接口序不一致时回写(Main_List 未就绪时可能无变化,getDate 后会再比一次) */ + this.scheduleTryBatchSyncReferOrderIfOutOfSync(); }); }) .catch((err) => { @@ -1774,6 +1790,69 @@ export default { if (sig(next) === sig(refs)) return; this.chanFerForm = next; }, + /** + * 将当前 chanFerForm 全文同步到后端(顺序 + 各字段内容与 getReferList 行结构一致)。 + * 接口:api/References/batchUpdateRefer:p_article_id + list(JSON 字符串,文献对象数组,顺序即保存序) + */ + syncBatchReferenceOrderToServer() { + const pid = this.p_article_id; + if (pid == null || pid === '') return Promise.resolve(); + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + if (refs.length === 0) return Promise.resolve(); + let listJson; + try { + listJson = JSON.stringify(JSON.parse(JSON.stringify(refs))); + } catch (e) { + return Promise.resolve(); + } + return this.$api + .post('api/References/batchUpdateRefer', { + p_article_id: String(pid), + list: listJson + }) + .then((res) => { + const ok = res && (res.code === 0 || res.code === 1 || res.status === 1); + if (ok) { + this._lastReferListApiOrder = this.referIdOrderSnapshot(this.chanFerForm).slice(); + } + if (!ok && res && (res.msg || res.message)) { + console.warn('[batchUpdateRefer]', res.msg || res.message); + } + return res; + }) + .catch((err) => { + console.warn('[batchUpdateRefer] request failed', err); + }); + }, + /** 从 getReferList 原始 refers 提取 p_refer_id 顺序(字符串,便于比较) */ + referIdOrderSnapshot(refs) { + return (Array.isArray(refs) ? refs : []) + .map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')) + .filter(Boolean); + }, + /** 当前 chanFerForm 顺序与 _lastReferListApiOrder 不一致则调用 batchUpdateRefer */ + tryBatchSyncReferOrderIfOutOfSync() { + if (this._lastReferListApiOrder == null) return; + const track = this._lastReferListApiOrder.map(String); + const cur = this.referIdOrderSnapshot(this.chanFerForm); + if (cur.length === 0) return; + const same = track.length === cur.length && track.every((id, i) => id === cur[i]); + if (!same) { + this.syncBatchReferenceOrderToServer(); + } + }, + /** + * 等 applyRefOrder / syncRefOrder / 表格抽屉内 nextTick 跑完后再比对顺序。 + */ + scheduleTryBatchSyncReferOrderIfOutOfSync() { + this.$nextTick(() => { + this.$nextTick(() => { + this.$nextTick(() => { + this.tryBatchSyncReferOrderIfOutOfSync(); + }); + }); + }); + }, extractBracketCiteNumbersFromText(raw) { const out = []; if (!raw || typeof raw !== 'string') return out; @@ -2278,6 +2357,9 @@ export default { /** 以接口回写后的 Main_List 再对齐一次;稿面 word 会由 contentList 监听触发 syncRefOrder */ this.$nextTick(() => { this.reorderReferencesFromMainListBody(null, null); + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { loading.close(); @@ -3294,6 +3376,8 @@ export default { /** 正文加载后按 Main_List 扫出全文首次出现顺序,写入 articleCiteIdOrder,弹窗角标与稿面一致 */ this.$nextTick(() => { this.reorderReferencesFromMainListBody(null, null); + /** getReferList 早于 Main_List 时在此才完成正文序重排,与接口序不一致则回写 */ + this.scheduleTryBatchSyncReferOrderIfOutOfSync(); }); loading.close(); }); @@ -3519,6 +3603,9 @@ export default { if (w && typeof w.syncRefOrder === 'function') { w.syncRefOrder(); } + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { this.$message.error(res.msg); @@ -3571,6 +3658,9 @@ export default { if (w && typeof w.syncRefOrder === 'function') { w.syncRefOrder(); } + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { this.$message.error(res.msg); diff --git a/src/components/page/PreIngestedEditor.vue b/src/components/page/PreIngestedEditor.vue index 9153fbb..0c6ca2f 100644 --- a/src/components/page/PreIngestedEditor.vue +++ b/src/components/page/PreIngestedEditor.vue @@ -332,11 +332,11 @@ export default { // refName: 'setFiveRef', // rongCont: 'Modify the article body.' // }, - { - name: 'Text Proofread', - refName: 'setThreeRef', - rongCont: 'HTML layout.' - } + // { + // name: 'Text Proofread', + // refName: 'setThreeRef', + // rongCont: 'HTML layout.' + // } // { // name: 'Create Build', // refName: 'setSevenRef', diff --git a/src/components/page/comArtHtmlCreatNew.vue b/src/components/page/comArtHtmlCreatNew.vue index 0d923d8..67cf1b4 100644 --- a/src/components/page/comArtHtmlCreatNew.vue +++ b/src/components/page/comArtHtmlCreatNew.vue @@ -218,6 +218,8 @@ export default { refSelectorIsEdit: false, refSelectedRows: [], refSelectorSource: 'commonContent', + /** 最近一次 getReferList 返回的 p_refer_id 顺序;与当前 chanFerForm 不一致时回写 batchUpdateRefer */ + _lastReferListApiOrder: null, /** 打开选择器时若带 currentRefIds(编辑已有引用),排序用「传值计算的顺序」;关闭后清空 */ refSelectorContextIds: [], /** 选择参考文献弹窗:按 # 序号快速勾选,如 [5, 6, 10-15] */ @@ -448,6 +450,7 @@ export default { p_article_id: this.p_article_id }) .then((res) => { + this._lastReferListApiOrder = this.referIdOrderSnapshot(res.data && res.data.refers).slice(); this.chanFerForm = res.data.refers; this.chanFerFormRepeatList = Object.values(res.data.repeat || {}); for (let i = 0; i < this.chanFerForm.length; i++) { @@ -474,6 +477,7 @@ export default { } /** getReferList 会整表覆盖 chanFerForm,需再按正文首次出现顺序对齐,否则下方列表与 [n] 脱节 */ this.applyRefOrderAfterFetchReferList(); + this.scheduleTryBatchSyncReferOrderIfOutOfSync(); }); }) .catch((err) => { @@ -647,6 +651,64 @@ export default { if (sig(next) === sig(refs)) return; this.chanFerForm = next; }, + /** + * 将当前 chanFerForm 全文同步到后端(顺序 + 各字段)。 + * 接口:api/References/batchUpdateRefer:p_article_id + list(JSON 字符串,文献对象数组) + */ + syncBatchReferenceOrderToServer() { + const pid = this.p_article_id; + if (pid == null || pid === '') return Promise.resolve(); + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + if (refs.length === 0) return Promise.resolve(); + let listJson; + try { + listJson = JSON.stringify(JSON.parse(JSON.stringify(refs))); + } catch (e) { + return Promise.resolve(); + } + return this.$api + .post('api/References/batchUpdateRefer', { + p_article_id: String(pid), + list: listJson + }) + .then((res) => { + const ok = res && (res.code === 0 || res.code === 1 || res.status === 1); + if (ok) { + this._lastReferListApiOrder = this.referIdOrderSnapshot(this.chanFerForm).slice(); + } + if (!ok && res && (res.msg || res.message)) { + console.warn('[batchUpdateRefer]', res.msg || res.message); + } + return res; + }) + .catch((err) => { + console.warn('[batchUpdateRefer] request failed', err); + }); + }, + referIdOrderSnapshot(refs) { + return (Array.isArray(refs) ? refs : []) + .map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')) + .filter(Boolean); + }, + tryBatchSyncReferOrderIfOutOfSync() { + if (this._lastReferListApiOrder == null) return; + const track = this._lastReferListApiOrder.map(String); + const cur = this.referIdOrderSnapshot(this.chanFerForm); + if (cur.length === 0) return; + const same = track.length === cur.length && track.every((id, i) => id === cur[i]); + if (!same) { + this.syncBatchReferenceOrderToServer(); + } + }, + scheduleTryBatchSyncReferOrderIfOutOfSync() { + this.$nextTick(() => { + this.$nextTick(() => { + this.$nextTick(() => { + this.tryBatchSyncReferOrderIfOutOfSync(); + }); + }); + }); + }, /** 编辑弹窗内正文一变立即按全文合并稿重排参考文献(不再防抖,选中/输入后列表马上跟正文一致) */ scheduleReorderFromEditModal(html) { if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return; @@ -1066,6 +1128,9 @@ export default { /** 以接口回写后的 Main_List 再对齐一次;稿面 word 会由 contentList 监听触发 syncRefOrder */ this.$nextTick(() => { this.reorderReferencesFromMainListBody(null, null); + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { loading.close(); @@ -2078,6 +2143,10 @@ export default { if (this.$refs.catalogue) { await this.$refs.catalogue.getCatalogueList(); } + this.$nextTick(() => { + this.reorderReferencesFromMainListBody(null, null); + this.scheduleTryBatchSyncReferOrderIfOutOfSync(); + }); loading.close(); }); // }, 1000); @@ -2304,6 +2373,9 @@ export default { if (w && typeof w.syncRefOrder === 'function') { w.syncRefOrder(); } + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { this.$message.error(res.msg); @@ -2356,6 +2428,9 @@ export default { if (w && typeof w.syncRefOrder === 'function') { w.syncRefOrder(); } + this.$nextTick(() => { + this.syncBatchReferenceOrderToServer(); + }); }); } else { this.$message.error(res.msg); diff --git a/src/components/page/components/Tinymce/index.vue b/src/components/page/components/Tinymce/index.vue index d164e6e..855de9b 100644 --- a/src/components/page/components/Tinymce/index.vue +++ b/src/components/page/components/Tinymce/index.vue @@ -134,6 +134,37 @@ export default { hasReferencesForAutoLink() { const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; return refs.some((r) => r && r.p_refer_id != null && String(r.p_refer_id).trim() !== ''); + }, + /** + * 表格编辑器专用:接口文献行的 old_index(0 起)+1 = 单元格里 [n] 对应的全局显示序号,再映射到 p_refer_id。 + * 无 old_index 时返回空对象,自动匹配回退为正文 citeMap。 + */ + tableBracketNumToRefIdMap() { + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const map = {}; + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const oi = r.old_index != null && r.old_index !== '' ? r.old_index : r.oldIndex; + if (oi == null || oi === '') return; + const n = Number(oi); + if (Number.isNaN(n)) return; + map[n + 1] = String(r.p_refer_id); + }); + return map; + }, + /** p_refer_id → 表格角标序号(old_index+1),用于表格内渲染 [n] 与排序 */ + tableRefIdToBracketNum() { + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const m = {}; + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const oi = r.old_index != null && r.old_index !== '' ? r.old_index : r.oldIndex; + if (oi == null || oi === '') return; + const num = Number(oi); + if (Number.isNaN(num)) return; + m[String(r.p_refer_id)] = num + 1; + }); + return m; } }, data() { @@ -271,7 +302,7 @@ export default { .map((s) => s.trim()) .filter(Boolean); }, - /** 与 word.vue renderCiteLabels:tooltip 行按引用序号排序 */ + /** 与 word.vue renderCiteLabels:合并角标 id 按 citeMap 序号排序 */ sortAutociteIdsByCiteNumber(ids) { const uniq = [...new Set(ids.map(String))]; const map = this.citeMap || {}; @@ -316,10 +347,62 @@ export default { }); return map; }, - /** 解析 [1]、[1,2]、[1–4] 等括号内数字列表(不含方括号) */ + /** 自动匹配 [n]:表格内用 old_index+1 映射;mytable 之后段落仍用全局 citeMap */ + _getBracketNumToIdMaps() { + const globalMap = this.buildNumToRefIdMap(); + if (this.type !== 'table') { + return { primary: globalMap, afterTable: globalMap }; + } + const tm = this.tableBracketNumToRefIdMap || {}; + const has = Object.keys(tm).length > 0; + return { + primary: has ? tm : globalMap, + afterTable: globalMap + }; + }, + sortAutociteIdsByTableBracketNumber(ids) { + const uniq = [...new Set(ids.map(String))]; + const numById = this.tableRefIdToBracketNum || {}; + return uniq.sort((a, b) => { + const na = numById[a]; + const nb = numById[b]; + const ha = na != null && na !== '' && !Number.isNaN(Number(na)); + const hb = nb != null && nb !== '' && !Number.isNaN(Number(nb)); + if (ha && hb) return Number(na) - Number(nb); + if (ha) return -1; + if (hb) return 1; + return String(a).localeCompare(String(b)); + }); + }, + _sortAutociteIdsForDisplay(ids) { + if (this.type === 'table' && Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0) { + return this.sortAutociteIdsByTableBracketNumber(ids); + } + return this.sortAutociteIdsByCiteNumber(ids); + }, + _sortIdsAfterBracketMatch(ids, tableOffset) { + if ( + this.type === 'table' && + tableOffset === 0 && + Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0 + ) { + return this.sortAutociteIdsByTableBracketNumber(ids); + } + return this.sortAutociteIdsByCiteNumber(ids); + }, + /** 解析 [1]、[1,2]、[1–4]、[1, 2–3, 4] 等括号内数字列表(不含方括号) */ parseBracketInnerToNumbers(inner) { if (!inner || typeof inner !== 'string') return []; const t = inner.trim().replace(/,/g, ','); + // 先按逗号拆段,再对每段解析单号或区间;避免 parseInt('41-42') 只得到 41 + if (/[,,]/.test(t)) { + const parts = t.split(/[,,]/).map((s) => String(s).trim()).filter(Boolean); + const out = []; + parts.forEach((part) => { + out.push(...this.parseBracketInnerToNumbers(part)); + }); + return out; + } const range = t.match(/^(\d+)\s*[-–—]\s*(\d+)$/); if (range) { const a = Number(range[1]); @@ -331,12 +414,6 @@ export default { for (let i = lo; i <= hi; i++) out.push(i); return out; } - if (/[,,]/.test(t)) { - return t - .split(/[,,]/) - .map((x) => parseInt(String(x).trim(), 10)) - .filter((n) => !Number.isNaN(n)); - } const n = parseInt(t, 10); return Number.isNaN(n) ? [] : [n]; }, @@ -352,8 +429,8 @@ export default { this.$message.warning(this.$t('wordCite.noRefs')); return { replaced: 0 }; } - const numToId = this.buildNumToRefIdMap(); - if (Object.keys(numToId).length === 0) { + const maps = this._getBracketNumToIdMaps(); + if (Object.keys(maps.primary).length === 0) { this.$message.warning(this.$t('wordCite.noRefs')); return { replaced: 0 }; } @@ -362,7 +439,7 @@ export default { if (!body) return { replaced: 0 }; let replaced = 0; ed.undoManager.transact(() => { - replaced = this._replaceBracketCitesInDocOrder(body, doc, numToId, { tableOffset: 0 }); + replaced = this._replaceBracketCitesInDocOrder(body, doc, maps, { tableOffset: 0 }); }); this.renderAutociteInEditor(ed); ed.fire('change'); @@ -377,8 +454,8 @@ export default { return; } const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; - const numToId = this.buildNumToRefIdMap(); - if (refs.length && Object.keys(numToId).length) { + const maps = this._getBracketNumToIdMaps(); + if (refs.length && Object.keys(maps.primary).length) { this.$message.info(this.$t('wordCite.matchBracketRefsNone')); } }, @@ -418,9 +495,9 @@ export default { * 按文档顺序遍历,使「表格链接」后任意后续段落里的 [n] 都能用上该表的最大全局序号偏移。 * 旧实现只在同一父节点下、MYTABLE 与后续文本为兄弟时才生效,MYTABLE 在上一段、角标在下一段时会失效。 */ - _replaceBracketCitesInDocOrder(node, doc, numToId, state) { + _replaceBracketCitesInDocOrder(node, doc, maps, state) { if (node.nodeType === 3) { - return this._processTextNodeForBracketCites(node, doc, numToId, state.tableOffset); + return this._processTextNodeForBracketCites(node, doc, maps, state.tableOffset); } if (node.nodeType !== 1) return 0; const name = node.nodeName; @@ -431,7 +508,7 @@ export default { if (isMytable) { let total = 0; Array.from(node.childNodes).forEach((c) => { - total += this._replaceBracketCitesInDocOrder(c, doc, numToId, state); + total += this._replaceBracketCitesInDocOrder(c, doc, maps, state); }); const tid = (node.getAttribute && node.getAttribute('data-id')) || ''; const map = this.tableLinkCiteMaxMap || {}; @@ -443,11 +520,12 @@ export default { } let total = 0; Array.from(node.childNodes).forEach((c) => { - total += this._replaceBracketCitesInDocOrder(c, doc, numToId, state); + total += this._replaceBracketCitesInDocOrder(c, doc, maps, state); }); return total; }, - _processTextNodeForBracketCites(textNode, doc, numToId, tableOffset = 0) { + _processTextNodeForBracketCites(textNode, doc, maps, tableOffset = 0) { + const numToId = tableOffset > 0 ? maps.afterTable : maps.primary; const text = textNode.textContent; const re = /\[([\d\s,,\-–—]+)\]/g; let m; @@ -465,11 +543,24 @@ export default { skippedSpecial++; continue; } + // 任一序号在参考文献中无对应(含超出列表、tableOffset 后仍无效)则整段不转换,避免部分匹配 + const mapNo = (n) => (n > 0 && tableOffset > 0 ? n + tableOffset - 1 : n); + if ( + !nums.length || + nums.some((n) => { + const mappedNo = mapNo(n); + return !numToId[mappedNo]; + }) + ) { + pieces.push({ type: 'text', s: text.slice(lastIndex, m.index) }); + pieces.push({ type: 'text', s: m[0] }); + lastIndex = m.index + m[0].length; + continue; + } const ids = []; const seen = new Set(); nums.forEach((n) => { - // 表格链接后的局部序号从 tableMax+1 开始接续:1->max+1, 2->max+2 ... - const mappedNo = n > 0 && tableOffset > 0 ? n + tableOffset - 1 : n; + const mappedNo = mapNo(n); const id = numToId[mappedNo]; if (id && !seen.has(id)) { seen.add(id); @@ -478,7 +569,7 @@ export default { }); pieces.push({ type: 'text', s: text.slice(lastIndex, m.index) }); if (ids.length > 0) { - const sorted = this.sortAutociteIdsByCiteNumber(ids); + const sorted = this._sortIdsAfterBracketMatch(ids, tableOffset); pieces.push({ type: 'cite', ids: sorted }); replaced++; } else { @@ -517,8 +608,8 @@ export default { if (!body) return; const allAutocites = Array.from( ed.dom && typeof ed.dom.select === 'function' - ? ed.dom.select('mycite', body) - : body.querySelectorAll('mycite') + ? ed.dom.select('mycite,autocite', body) + : body.querySelectorAll('mycite, autocite') ); if (!allAutocites.length) return; @@ -530,23 +621,30 @@ export default { }); const citeMap = this.citeMap || {}; + const tableNums = this.tableRefIdToBracketNum || {}; + const useTableBracketNums = this.type === 'table' && Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0; allAutocites.forEach((el) => { ed.dom.setAttrib(el, 'contenteditable', 'false'); el.style.display = ''; - const sortedAll = this.sortAutociteIdsByCiteNumber( - this.parseAutociteDataIds(el.getAttribute('data-id')) - ); + const sortedAll = this._sortAutociteIdsForDisplay(this.parseAutociteDataIds(el.getAttribute('data-id'))); const validIds = sortedAll.filter((id) => refMap[String(id)]); if (validIds.length < sortedAll.length) { ed.dom.setAttrib(el, 'data-id', validIds.length ? validIds.join(',') : ''); } - const sortedIds = validIds.length ? this.sortAutociteIdsByCiteNumber(validIds) : []; + const sortedIds = validIds.length ? this._sortAutociteIdsForDisplay(validIds) : []; const parts = sortedIds.map((id) => { const ref = refMap[String(id)]; - const no = ref ? citeMap[String(id)] : null; - const num = no != null && no !== '' ? String(no) : null; + const noCite = ref ? citeMap[String(id)] : null; + const noTable = + useTableBracketNums && tableNums[String(id)] != null ? tableNums[String(id)] : null; + const num = + noTable != null && noTable !== '' && !Number.isNaN(Number(noTable)) + ? String(noTable) + : noCite != null && noCite !== '' + ? String(noCite) + : null; return { id, ref, num }; }); @@ -556,22 +654,6 @@ export default { .map(Number); const label = numsForLabel.length > 0 ? this.formatCiteNumbers(numsForLabel) : ''; - const lines = parts - .filter((p) => p.ref) - .map((p) => { - const ref = p.ref; - const content = - ref.refer_frag || - [ref.author, ref.title, ref.joura, ref.dateno].filter(Boolean).join(' ').trim() || - ''; - const doi = ref.doilink || ref.isbn || ref.doi || ''; - const numLabel = p.num; - if (numLabel) { - return `[${numLabel}] ${content}\nDOI: ${doi}`; - } - return `${content}\nDOI: ${doi}`; - }); - if (!label) { ed.dom.remove(el); return; @@ -579,8 +661,18 @@ export default { el.textContent = `[${label}]`; el.style.display = ''; - ed.dom.setAttrib(el, 'title', lines.join('\n')); + if (el.removeAttribute) el.removeAttribute('title'); ed.dom.setAttrib(el, 'data-cite-missing', null); + if (String(el.tagName || '').toLowerCase() === 'autocite') { + const doc = ed.getDoc(); + const nu = doc.createElement('mycite'); + nu.setAttribute('data-id', el.getAttribute('data-id') || ''); + nu.setAttribute('contenteditable', 'false'); + const st = el.getAttribute('style'); + if (st) nu.setAttribute('style', st); + nu.textContent = el.textContent; + el.parentNode.replaceChild(nu, el); + } }); this.padAutociteCaretPlaceholder(ed); }, @@ -589,7 +681,7 @@ export default { const doc = ed.getDoc(); const body = doc.body; if (!body) return; - body.querySelectorAll('mycite').forEach((el) => { + body.querySelectorAll('mycite, autocite').forEach((el) => { const next = el.nextSibling; if (next === null) { el.parentNode.appendChild(doc.createTextNode('\u200b')); @@ -658,8 +750,10 @@ export default { /** TinyMCE 会剔除「空」的行内标签;空 mycite 必须在入编辑器前占位,否则合并引用 [1–3] 等整段消失 */ normalizeAutociteHtmlForEditor(html) { if (!html || typeof html !== 'string') return html; + // 历史库/表格里可能为 ,与 mycite 统一,否则 renderAutociteInEditor 扫不到、无效 id 会残留 + let out = html.replace(/<\/autocite>/gi, '').replace(/]*)>[\s\S]*?<\/mycite>/gi, '​'); + out = out.replace(/]*)>[\s\S]*?<\/mycite>/gi, '​'); // 外侧:连续空格 /   合并为单个  ,避免「普通空格 +  」叠成大缝 out = out.replace(/(?:\s| | )+(?=)(?:\s| | )+/gi, ' '); @@ -1018,7 +1112,7 @@ export default { valid_elements: this.type == 'table' ? '*[*]' - : `img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,myfigure,mytable,wmath,mycite[data-id|contenteditable|title|data-cite-missing|style]${this.valid_elements}`, // 允许的标签和属性 + : `img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,myfigure,mytable,wmath,mycite[data-id|contenteditable|data-cite-missing|style]${this.valid_elements}`, // 允许的标签和属性(mycite 不使用 title 悬停) // valid_elements: '*[*]', // 允许所有 HTML 标签 noneditable_editable_class: 'MathJax', height: this.height, @@ -1205,7 +1299,7 @@ export default { }); ed.on('click', function (e) { - const autociteEl = e.target.closest('mycite'); + const autociteEl = e.target.closest('mycite') || e.target.closest('autocite'); if (autociteEl) { const dataIds = _this.parseAutociteDataIds(autociteEl.getAttribute('data-id')); _this._refBookmark = ed.selection.getBookmark(2); diff --git a/src/components/page/components/table/word.vue b/src/components/page/components/table/word.vue index cdd131f..0ece3eb 100644 --- a/src/components/page/components/table/word.vue +++ b/src/components/page/components/table/word.vue @@ -997,23 +997,9 @@
-