This commit is contained in:
2026-04-13 13:06:31 +08:00
parent d613aa7d0d
commit 1deb5bba86
5 changed files with 177 additions and 73 deletions

View File

@@ -1150,6 +1150,9 @@ colTitle: 'Template title',
deleteLogSuccess: 'Deleted',
deleteLogFailed: 'Delete failed',
noFailureReason: 'No failure reason',
logDetailEditTitle: 'Edit delivery log',
logDetailPreviewTitle: 'Preview delivery log',
logLoadFailed: 'Failed to load logs',
deletedSuccess: 'Deleted',
mockPromotionSubject: 'Promotion for {journal}',
mockPromotionContent: '<p>Dear {name},</p><p>Check out our latest journal updates...</p>'
@@ -1163,8 +1166,8 @@ colTitle: 'Template title',
missingBoundRef: 'The bound reference no longer exists. Please cite again.',
notFoundById: 'No reference found for citation ID {id}',
selectRef: 'Select Reference',
reference: 'Reference',
originalOrder: 'Original order',
reference: 'Reference',
uncited: 'Uncited',
cancel: 'Cancel',
confirm: 'Confirm',

View File

@@ -1135,6 +1135,9 @@ const zh = {
deleteLogSuccess: '删除成功',
deleteLogFailed: '删除失败',
noFailureReason: '暂无失败原因',
logDetailEditTitle: '编辑发送记录',
logDetailPreviewTitle: '预览发送记录',
logLoadFailed: '加载日志失败',
deletedSuccess: '已删除',
mockPromotionSubject: '自动推广:{journal}',
mockPromotionContent: '<p>亲爱的 {name}</p><p>请查看我们最新的期刊更新...</p>'
@@ -1148,8 +1151,8 @@ const zh = {
missingBoundRef: '当前绑定的参考文献不存在,请重新引用',
notFoundById: '未查询到编号{id}相关参考文献',
selectRef: '选择参考文献',
reference: '参考文献',
originalOrder: '原排序',
reference: '参考文献',
uncited: '未引用',
cancel: '取消',
confirm: '确认',

View File

@@ -597,13 +597,7 @@
</el-table-column>
<el-table-column :label="$t('wordCite.originalOrder')" width="120" align="center">
<template slot-scope="scope">
<span>{{
scope.row.old_index != null && scope.row.old_index !== ''
? scope.row.old_index+1
: scope.row.old_index != null && scope.row.old_index !== ''
? scope.row.old_index+1
: '—'
}}</span>
<span>{{ refSelectorOrderIndexDisplay(scope.row) }}</span>
</template>
</el-table-column>
<el-table-column align="center" :width="'200'">
@@ -893,6 +887,8 @@ export default {
articleCiteIdOrder: [],
/** Edit Content 弹窗内当前 HTML与保存合并逻辑一致用于实时算出与稿面相同的 [n],避免仅依赖防抖后的 articleCiteIdOrder */
editModalDraftHtml: '',
/** 编辑器 getContent(raw) 快照draft 为空时 editModalBodyCiteOrder 用其与 extract 对齐稿面 */
editModalSyncedHtml: '',
/** 表格抽屉Title+Table+Note 合并稿,驱动 tableModalBodyCiteOrder与 editModalDraftHtml 对 Edit Content 一致) */
tableModalDraftHtml: '',
refSelectorVisible: false,
@@ -1049,16 +1045,22 @@ export default {
}
];
},
/** 弹窗内引用角标:按 Main_List + 当前草稿段合并后的全文首次出现顺序,与保存后一致 */
/** 弹窗内引用角标:与稿面同源 —— 始终用 extractAutociteOrderFromMainList(Main_List, am_id, 当前用于合并的 HTML) */
editModalBodyCiteOrder() {
if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) {
return this.articleCiteIdOrder;
}
const draft = this.editModalDraftHtml;
if (draft == null || draft === '') {
const d = this.editModalDraftHtml;
const draftTrim = d != null && String(d).trim() !== '' ? d : '';
const s = this.editModalSyncedHtml;
const syncedTrim = s != null && String(s).trim() !== '' ? s : '';
const saved = this.currentContent.content;
const savedTrim = saved != null && String(saved).trim() !== '' ? saved : '';
const htmlForExtract = draftTrim || syncedTrim || savedTrim;
if (!htmlForExtract) {
return this.articleCiteIdOrder;
}
const order = extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, draft);
const order = extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, htmlForExtract);
return order.length > 0 ? order : this.articleCiteIdOrder;
},
/**
@@ -1123,11 +1125,23 @@ export default {
},
watch: {
editVisible(val) {
if (!val) this.editModalDraftHtml = '';
if (!val) {
this.editModalDraftHtml = '';
this.editModalSyncedHtml = '';
}
},
/** 计算属性 editModalBodyCiteOrder 变化后重绘 TinyMCE 角标,与 body-cite-id-order 一致 */
editModalBodyCiteOrder: {
handler() {
if (!this.editVisible) return;
this.$nextTick(() => {
this.refreshEditModalAutociteDisplay();
});
}
},
threeVisible(val) {
if (!val) this.tableModalDraftHtml = '';
}
},
// 监听计算属性
// combinedValue(newVal, oldVal) {
// console.log('value1 或 value2 发生变化');
@@ -1519,8 +1533,8 @@ export default {
if (this.$refs.editPublicRefTableOnly) {
this.$refs.editPublicRefTableOnly.init();
}
if (this.editVisible && this.$refs.commonContent && this.$refs.commonContent.refreshAutociteDisplay) {
this.$refs.commonContent.refreshAutociteDisplay();
if (this.editVisible && this.$refs.commonContent) {
this.refreshEditModalAutociteDisplay();
}
if (this.addContentVisible && this.$refs.addContent && this.$refs.addContent.refreshAutociteDisplay) {
this.$refs.addContent.refreshAutociteDisplay();
@@ -1548,13 +1562,12 @@ export default {
applyRefOrderAfterFetchReferList() {
if (this.editVisible && this.currentContent && this.currentContent.am_id != null) {
let html = this.editModalDraftHtml;
if (html === '' || html == null) {
const t = this.$refs.commonContent && this.$refs.commonContent.$refs && this.$refs.commonContent.$refs.tinymceChild1;
if (t && t.editorInstance) {
html = t.editorInstance.getContent({ format: 'raw' }) || '';
} else {
html = (this.currentContent && this.currentContent.content) || '';
}
if (html === '' || html == null || !String(html).trim()) {
html = this.syncEditModalHtmlFromEditor();
if (html) this.editModalDraftHtml = html;
}
if (html === '' || html == null || !String(html).trim()) {
html = (this.currentContent && this.currentContent.content) || '';
}
this.flushReorderFromEditModal(html);
return;
@@ -1913,8 +1926,28 @@ export default {
this.articleCiteIdOrder = order;
},
onEditModalEditorInput(html) {
this.editModalDraftHtml = html || '';
this.flushReorderFromEditModal(html);
const h = html || '';
this.editModalDraftHtml = h;
this.editModalSyncedHtml = h;
this.flushReorderFromEditModal(h);
},
/** 从 Edit Content 内 TinyMCE 取 raw HTML 写入 editModalSyncedHtml供 extract 与 applyRefOrder 与稿面对齐 */
syncEditModalHtmlFromEditor() {
if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return '';
const cc = this.$refs.commonContent;
const inst = cc && cc.$refs && cc.$refs.tinymceChild1;
const ed = inst && inst.editorInstance;
if (!ed || typeof ed.getContent !== 'function') return '';
const raw = ed.getContent({ format: 'raw' }) || '';
this.editModalSyncedHtml = raw;
return raw;
},
refreshEditModalAutociteDisplay() {
if (!this.editVisible) return;
const cc = this.$refs.commonContent;
if (cc && typeof cc.refreshAutociteDisplay === 'function') {
cc.refreshAutociteDisplay();
}
},
/** 打开表格抽屉时用已有 lineStyle 字段拼合并稿,先写入 tableModalDraftHtml角标与 Edit Content 一样按全文算 */
seedTableModalDraftFromLineStyle() {
@@ -1977,6 +2010,28 @@ export default {
handleRefSelectionChange(rows) {
this.refSelectedRows = rows;
},
/**
* 选择文献弹窗「原排序」列:与表格 [n] 一致 —— 有 old_index 时显示 old_index+1否则显示 order_index已为 n
*/
refSelectorOrderIndexDisplay(row) {
if (!row) return '—';
const pick = (v) => {
if (v == null || v === '') return null;
const x = Number(v);
return Number.isNaN(x) ? null : x;
};
const oi =
row.old_index != null && row.old_index !== ''
? pick(row.old_index)
: pick(row.oldIndex);
if (oi != null) return oi + 1;
const ord =
row.order_index != null && row.order_index !== ''
? pick(row.order_index)
: pick(row.orderIndex);
if (ord != null) return ord;
return '—';
},
parseQuickPickNumbers(input) {
const s = String(input || '').trim();
if (!s) return [];
@@ -2074,15 +2129,13 @@ export default {
this.flushReorderFromTableModal();
}
if (this.editVisible && this.$refs.commonContent) {
const t = this.$refs.commonContent.$refs && this.$refs.commonContent.$refs.tinymceChild1;
if (t && t.editorInstance) {
const html = t.editorInstance.getContent({ format: 'raw' });
this.editModalDraftHtml = html || '';
const html = this.syncEditModalHtmlFromEditor();
if (html) {
this.editModalDraftHtml = html;
this.editModalSyncedHtml = html;
this.flushReorderFromEditModal(html);
}
if (typeof this.$refs.commonContent.refreshAutociteDisplay === 'function') {
this.$refs.commonContent.refreshAutociteDisplay();
}
this.refreshEditModalAutociteDisplay();
}
if (
this.addContentVisible &&
@@ -3244,13 +3297,15 @@ export default {
}
this.$nextTick(() => {
this.$nextTick(() => {
if (this.$refs.commonContent && this.$refs.commonContent.refreshAutociteDisplay) {
this.$refs.commonContent.refreshAutociteDisplay();
const raw = this.syncEditModalHtmlFromEditor();
if (raw) {
this.editModalDraftHtml = raw;
}
const draft = this.currentContent && this.currentContent.content;
if (draft != null) {
this.flushReorderFromEditModal(draft);
const html = raw || (this.currentContent && this.currentContent.content) || '';
if (html) {
this.flushReorderFromEditModal(html);
}
this.refreshEditModalAutociteDisplay();
});
});
}

View File

@@ -93,9 +93,22 @@ export default {
showRefButton: {
type: Boolean,
default: true
},
/**
* true仅「表格单元格编辑」场景角标/自动匹配 [n] 按 old_index+1或 order_index
* false与稿面一致始终用 bodyCiteIdOrder 的 citeMapEdit Content 等虽 type=table 放宽标签,也必须 false
*/
useTableLocalCitationIndex: {
type: Boolean,
default: false
}
},
computed: {
/** 是否启用表格局部序号(与全文 citeMap 互斥) */
tableLocalCiteActive() {
if (this.type !== 'table' || !this.useTableLocalCitationIndex) return false;
return Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0;
},
citeMap() {
const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : [];
const refIdSet = new Set(
@@ -136,33 +149,30 @@ export default {
return refs.some((r) => r && r.p_refer_id != null && String(r.p_refer_id).trim() !== '');
},
/**
* 表格编辑器专用:接口文献行的 old_index0 起)+1 = 单元格 [n] 对应的全局显示序号,再映射到 p_refer_id。
* old_index 时返回空对象,自动匹配回退为正文 citeMap。
* 表格编辑器专用:单元格 [n] 中的 n → p_refer_id。
* - 有 old_index0 起n = old_index + 1
* - 仅有 order_index 时:按接口约定已为与 [n] 一致的序号n = order_index不再 +1
*/
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);
const n = this._getTableBracketNoForRow(r);
if (n == null) return;
map[n] = String(r.p_refer_id);
});
return map;
},
/** p_refer_id → 表格角标序号old_index+1用于表格内渲染 [n] 与排序 */
/** p_refer_id → 表格角标数字 n [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;
const n = this._getTableBracketNoForRow(r);
if (n == null) return;
m[String(r.p_refer_id)] = n;
});
return m;
}
@@ -302,6 +312,27 @@ export default {
.map((s) => s.trim())
.filter(Boolean);
},
/**
* 与正文 [n] 一致的角标数字 n。old_index 为 0 起 → n=old_index+1无 old_index 时用 order_index已为 n.
*/
_getTableBracketNoForRow(r) {
if (!r) return null;
const pick = (v) => {
if (v == null || v === '') return null;
const x = Number(v);
return Number.isNaN(x) ? null : x;
};
const oi =
r.old_index != null && r.old_index !== ''
? pick(r.old_index)
: pick(r.oldIndex);
if (oi != null) return oi + 1;
const ord =
r.order_index != null && r.order_index !== ''
? pick(r.order_index)
: pick(r.orderIndex);
return ord;
},
/** 与 word.vue renderCiteLabels合并角标 id 按 citeMap 序号排序 */
sortAutociteIdsByCiteNumber(ids) {
const uniq = [...new Set(ids.map(String))];
@@ -347,10 +378,10 @@ export default {
});
return map;
},
/** 自动匹配 [n]:表格内用 old_index+1 映射mytable 之后段落仍用全局 citeMap */
/** 自动匹配 [n]:表格内用 old_index+1 或 order_index(=n)mytable 之后段落仍用全局 citeMap */
_getBracketNumToIdMaps() {
const globalMap = this.buildNumToRefIdMap();
if (this.type !== 'table') {
if (this.type !== 'table' || !this.useTableLocalCitationIndex) {
return { primary: globalMap, afterTable: globalMap };
}
const tm = this.tableBracketNumToRefIdMap || {};
@@ -375,17 +406,13 @@ export default {
});
},
_sortAutociteIdsForDisplay(ids) {
if (this.type === 'table' && Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0) {
if (this.tableLocalCiteActive) {
return this.sortAutociteIdsByTableBracketNumber(ids);
}
return this.sortAutociteIdsByCiteNumber(ids);
},
_sortIdsAfterBracketMatch(ids, tableOffset) {
if (
this.type === 'table' &&
tableOffset === 0 &&
Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0
) {
if (this.tableLocalCiteActive && tableOffset === 0) {
return this.sortAutociteIdsByTableBracketNumber(ids);
}
return this.sortAutociteIdsByCiteNumber(ids);
@@ -534,32 +561,40 @@ export default {
let replaced = 0;
let skippedSpecial = 0;
while ((m = re.exec(text)) !== null) {
const nums = this.parseBracketInnerToNumbers(m[1]);
// 规则:只要括号里包含 0 或 -1这一段不做解析替换但不影响其它正常 [1][2]
if (nums.some((n) => n === 0 || n === -1)) {
const rawNums = this.parseBracketInnerToNumbers(m[1]);
// 仅 [0]、[-1]、[0, -1] 等「全是 0/-1」不转换与正常号混写时去掉 0/-1 再匹配
const onlyZeroOrNegOne =
rawNums.length > 0 && rawNums.every((n) => n === 0 || n === -1);
if (onlyZeroOrNegOne) {
pieces.push({ type: 'text', s: text.slice(lastIndex, m.index) });
pieces.push({ type: 'text', s: m[0] });
lastIndex = m.index + m[0].length;
skippedSpecial++;
continue;
}
// 任一序号在参考文献中无对应含超出列表、tableOffset 后仍无效)则整段不转换,避免部分匹配
const nums = rawNums.filter((n) => n !== 0 && n !== -1);
const mapNo = (n) => (n > 0 && tableOffset > 0 ? n + tableOffset - 1 : n);
if (
!nums.length ||
nums.some((n) => {
const mappedNo = mapNo(n);
return !numToId[mappedNo];
})
) {
if (!nums.length) {
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 validNums = nums.filter((n) => numToId[mapNo(n)]);
const invalidNums = nums.filter((n) => !numToId[mapNo(n)]);
if (validNums.length === 0) {
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) => {
validNums.forEach((n) => {
const mappedNo = mapNo(n);
const id = numToId[mappedNo];
if (id && !seen.has(id)) {
@@ -571,6 +606,13 @@ export default {
if (ids.length > 0) {
const sorted = this._sortIdsAfterBracketMatch(ids, tableOffset);
pieces.push({ type: 'cite', ids: sorted });
if (invalidNums.length) {
const invSorted = [...new Set(invalidNums)].sort((a, b) => a - b);
const invLabel = this.formatCiteNumbers(invSorted);
if (invLabel) {
pieces.push({ type: 'text', s: ', [' + invLabel + ']' });
}
}
replaced++;
} else {
pieces.push({ type: 'text', s: m[0] });
@@ -598,7 +640,7 @@ export default {
// 提示:仅提示一次即可;这里在节点级别提示可能重复,放到宏任务末尾合并展示
clearTimeout(this._autoLinkSkipToastTimer);
this._autoLinkSkipToastTimer = setTimeout(() => {
this.$message.info(`Skipped ${skippedSpecial} bracket cite(s) containing 0/-1.`);
this.$message.info(`Skipped ${skippedSpecial} bracket cite(s) containing only 0 or -1.`);
}, 0);
}
return replaced;
@@ -622,7 +664,7 @@ export default {
const citeMap = this.citeMap || {};
const tableNums = this.tableRefIdToBracketNum || {};
const useTableBracketNums = this.type === 'table' && Object.keys(this.tableBracketNumToRefIdMap || {}).length > 0;
const useTableBracketNums = this.tableLocalCiteActive;
allAutocites.forEach((el) => {
ed.dom.setAttrib(el, 'contenteditable', 'false');

View File

@@ -2,6 +2,7 @@
<div>
<tinymce
type="table"
:use-table-local-citation-index="true"
:articleId="articleId"
ref="tinymceChild1"
:wordStyle="wordStyle"