From 620a35f9588ed11c065ef767374d5455c9f8fdc0 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, 3 Apr 2026 09:05:56 +0800
Subject: [PATCH] =?UTF-8?q?=E6=8F=90=E4=BA=A4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/common/js/commonJS.js | 7 +-
src/components/common/langs/en.js | 7 +-
src/components/common/langs/zh.js | 7 +-
src/components/page/GenerateCharts.vue | 142 ++++++++--
.../page/components/Tinymce/index.vue | 218 ++++++++++++---
.../page/components/table/content.vue | 17 +-
src/components/page/components/table/word.vue | 252 ++++++++++++++++--
src/components/page/crawlTaskMonitor.vue | 45 +++-
.../page/editPublicRefTableOnly.vue | 20 +-
9 files changed, 609 insertions(+), 106 deletions(-)
diff --git a/src/common/js/commonJS.js b/src/common/js/commonJS.js
index 60bb1ab..53159c2 100644
--- a/src/common/js/commonJS.js
+++ b/src/common/js/commonJS.js
@@ -226,8 +226,7 @@ export default {
str = this.transformHtmlString(processedContent, 'table',{ keepBr: true })
- console.log("🚀 ~ extractContentWithoutOuterSpan888888 ~ str:", str);
-
+
// 创建一个临时的 DOM 元素来解析 HTML
const div = document.createElement('div');
@@ -917,9 +916,9 @@ str = str.replace(regex, function (match, content, offset, fullString) {
});
// 2. 删除所有不需要的标签 (除 `strong`, `em`, `sub`, `sup`, `b`, `i` 外的所有标签)
if (type == 'table') {
- inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|img|myfigure|mytable|myh3))[^>]+>/g, ''); // 删除不需要的标签
+ inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|img|myfigure|mytable|myh3|autocite))[^>]+>/g, ''); // 删除不需要的标签
} else {
- inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable|myh3))[^>]+>/g, ''); // 删除不需要的标签
+ inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable|myh3|autocite))[^>]+>/g, ''); // 删除不需要的标签
}
diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js
index 30e97d0..fd95bc2 100644
--- a/src/components/common/langs/en.js
+++ b/src/components/common/langs/en.js
@@ -1152,12 +1152,17 @@ colTitle: 'Template title',
placeholder: 'Please enter email content'
},
wordCite: {
+ noRefs: 'Reference list is not loaded yet. Please wait or refresh the page.',
notFoundById: 'No reference found for citation ID {id}',
selectRef: 'Select Reference',
reference: 'Reference',
cancel: 'Cancel',
confirm: 'Confirm',
- remove: 'Remove'
+ remove: 'Remove',
+ selected: 'Selected',
+ modifyRef: 'Edit citation',
+ removeRefTag: 'Remove citation',
+ citeUpdateFail: 'Could not update the citation in the text. Try again or use Edit.'
}
diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js
index a6d16ba..5cf7e8f 100644
--- a/src/components/common/langs/zh.js
+++ b/src/components/common/langs/zh.js
@@ -1137,12 +1137,17 @@ const zh = {
placeholder: '请输入邮件内容'
},
wordCite: {
+ noRefs: '参考文献列表尚未加载,请稍候再试或刷新页面',
notFoundById: '未查询到编号{id}相关参考文献',
selectRef: '选择参考文献',
reference: '参考文献',
cancel: '取消',
confirm: '确认',
- remove: '移除'
+ remove: '移除',
+ selected: '已选择',
+ modifyRef: '修改引用',
+ removeRefTag: '移除引用',
+ citeUpdateFail: '未能更新正文中的引用标签,请重试或进入编辑修改'
}
diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue
index 1e0601c..583a329 100644
--- a/src/components/page/GenerateCharts.vue
+++ b/src/components/page/GenerateCharts.vue
@@ -122,6 +122,7 @@
@onEditTitle="onEditTitle"
@onAddRow="onAddRow"
@changeComment="changeComment"
+ @openRefSelector="handleOpenRefSelectorFromManuscript"
style="width: calc(100%); height: calc(100%)"
:style="`100%`"
>
@@ -447,34 +448,46 @@
+
+ {{ $t('wordCite.selected') }}: [{{ refPreviewLabel }}]
+
-
-
+
+
- {{ scope.row.refer_frag || [scope.row.author, scope.row.title, scope.row.joura, scope.row.dateno].filter(Boolean).join(' ') || '-' }}
+ {{ scope.$index + 1 }}.
-
+
- {{ scope.row.doilink || scope.row.doi || scope.row.isbn || '-' }}
+
@@ -616,8 +629,8 @@ export default {
chanFerForm: [],
chanFerFormRepeatList: [],
refSelectorVisible: false,
- refSelectorCurrentRefId: null,
- refSelectedRow: null,
+ refSelectorIsEdit: false,
+ refSelectedRows: [],
refSelectorSource: 'commonContent'
};
},
@@ -628,6 +641,29 @@ export default {
editPublicRefTableOnly
},
computed: {
+ refPreviewLabel() {
+ if (!this.refSelectedRows.length) return '';
+ const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : [];
+ const nums = this.refSelectedRows.map((row) => {
+ const idx = refs.findIndex((r) => r.p_refer_id === row.p_refer_id);
+ return idx >= 0 ? idx + 1 : null;
+ }).filter((n) => n != null);
+ if (!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(', ');
+ },
catalogueContent() {
const base = Array.isArray(this.Main_List) ? this.Main_List : [];
if (!Array.isArray(this.chanFerForm) || this.chanFerForm.length === 0) return base;
@@ -721,41 +757,89 @@ 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.addContentVisible && this.$refs.addContent && this.$refs.addContent.refreshAutociteDisplay) {
+ this.$refs.addContent.refreshAutociteDisplay();
+ }
});
})
.catch((err) => {
console.log(err);
});
},
- handleOpenRefSelector(data) {
- this.refSelectorCurrentRefId = data && data.currentRefId ? data.currentRefId : null;
- this.refSelectedRow = null;
- if (this.editVisible) {
+ handleOpenRefSelectorFromManuscript(data) {
+ this.handleOpenRefSelector(data, 'manuscriptAutocite');
+ },
+ handleOpenRefSelector(data, sourceOverride) {
+ const currentIds = data && Array.isArray(data.currentRefIds) ? data.currentRefIds : [];
+ this.refSelectorIsEdit = currentIds.length > 0;
+ this.refSelectedRows = [];
+ if (sourceOverride === 'manuscriptAutocite' || (data && data.source === 'manuscript')) {
+ this.refSelectorSource = 'manuscriptAutocite';
+ } else if (this.editVisible) {
this.refSelectorSource = 'commonContent';
} else if (this.addContentVisible) {
this.refSelectorSource = 'addContent';
}
this.refSelectorVisible = true;
+ this.$nextTick(() => {
+ const table = this.$refs.refSelectorTable;
+ if (table) {
+ table.clearSelection();
+ if (currentIds.length > 0) {
+ const idSet = new Set(currentIds.map(String));
+ this.chanFerForm.forEach((row) => {
+ if (idSet.has(String(row.p_refer_id))) {
+ table.toggleRowSelection(row, true);
+ }
+ });
+ }
+ }
+ });
},
- handleRefCurrentChange(row) {
- this.refSelectedRow = row;
+ handleRefSelectionChange(rows) {
+ this.refSelectedRows = rows;
},
handleConfirmRefCite() {
- if (!this.refSelectedRow) return;
+ if (this.refSelectedRows.length === 0) return;
+ const ids = this.refSelectedRows.map((r) => r.p_refer_id);
+ if (this.refSelectorSource === 'manuscriptAutocite') {
+ const w = this.$refs.commonWord;
+ if (w && typeof w.applyManuscriptAutocite === 'function') {
+ w.applyManuscriptAutocite(ids);
+ }
+ this.refSelectorVisible = false;
+ this.refSelectedRows = [];
+ return;
+ }
const ref = this.$refs[this.refSelectorSource];
if (ref) {
- ref.insertAutocite(this.refSelectedRow.p_refer_id);
+ ref.insertAutocite(ids);
}
this.refSelectorVisible = false;
- this.refSelectedRow = null;
+ this.refSelectedRows = [];
},
handleRemoveRefCite() {
+ const idsToStrip = (this.refSelectedRows || []).map((r) => r.p_refer_id);
+ if (this.refSelectorSource === 'manuscriptAutocite') {
+ const w = this.$refs.commonWord;
+ if (w && typeof w.stripManuscriptAutociteIds === 'function') {
+ w.stripManuscriptAutociteIds(idsToStrip);
+ }
+ this.refSelectorVisible = false;
+ this.refSelectedRows = [];
+ return;
+ }
const ref = this.$refs[this.refSelectorSource];
- if (ref) {
+ if (ref && typeof ref.stripAutociteIds === 'function') {
+ ref.stripAutociteIds(idsToStrip);
+ } else if (ref) {
ref.removeAutocite();
}
this.refSelectorVisible = false;
- this.refSelectedRow = null;
+ this.refSelectedRows = [];
},
ChanFerMashUp(e) {
this.$api
@@ -1825,6 +1909,16 @@ export default {
this.editVisible = true;
this.currentId = dataId;
+ if (this.p_article_id) {
+ this.fetchReferList();
+ }
+ this.$nextTick(() => {
+ this.$nextTick(() => {
+ if (this.$refs.commonContent && this.$refs.commonContent.refreshAutociteDisplay) {
+ this.$refs.commonContent.refreshAutociteDisplay();
+ }
+ });
+ });
}
},
onAddContent(dataId) {
diff --git a/src/components/page/components/Tinymce/index.vue b/src/components/page/components/Tinymce/index.vue
index cfa1c18..185ded0 100644
--- a/src/components/page/components/Tinymce/index.vue
+++ b/src/components/page/components/Tinymce/index.vue
@@ -147,7 +147,7 @@ export default {
handler(val) {
if (!this.hasChange && this.hasInit) {
this.$nextTick(() => {
- window.tinymce.get(this.tinymceId).setContent(val);
+ this.handleSetContent(val);
});
}
},
@@ -155,9 +155,11 @@ export default {
},
chanFerForm: {
handler() {
- if (this.editorInstance) {
- this.renderAutociteInEditor(this.editorInstance);
- }
+ this.$nextTick(() => {
+ if (this.editorInstance) {
+ this.renderAutociteInEditor(this.editorInstance);
+ }
+ });
},
deep: true
}
@@ -183,10 +185,52 @@ export default {
this.destroyTinymce();
},
methods: {
+ /** 与正文一致:两项连续写作 1, 2;三项及以上连续写作 1-3 */
+ 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(', ');
+ },
+ parseAutociteDataIds(attr) {
+ if (!attr || typeof attr !== 'string') return [];
+ return attr
+ .split(',')
+ .map((s) => s.trim())
+ .filter(Boolean);
+ },
+ /** 与 word.vue renderCiteLabels:tooltip 行按引用序号排序 */
+ sortAutociteIdsByCiteNumber(ids) {
+ const uniq = [...new Set(ids.map(String))];
+ const map = this.citeMap || {};
+ 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));
+ });
+ },
renderAutociteInEditor(ed) {
const body = ed.getBody();
if (!body) return;
- const autocites = body.querySelectorAll('autocite');
+ const allAutocites = Array.from(body.querySelectorAll('autocite'));
+ if (!allAutocites.length) return;
+
const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : [];
const refMap = {};
refs.forEach((item) => {
@@ -194,41 +238,65 @@ export default {
if (key) refMap[key] = item;
});
- autocites.forEach((el) => {
+ allAutocites.forEach((el) => {
ed.dom.setAttrib(el, 'contenteditable', 'false');
- const dataId = el.getAttribute('data-id');
- const num = this.citeMap[String(dataId)];
- el.textContent = num != null ? `[${num}]` : '[?]';
+ el.style.display = '';
+ const ids = this.parseAutociteDataIds(el.getAttribute('data-id'));
+ const sortedIds = this.sortAutociteIdsByCiteNumber(ids);
+ const nums = sortedIds.map((id) => this.citeMap[String(id)]).filter((n) => n != null);
+ const label = nums.length > 0 ? this.formatCiteNumbers(nums) : '?';
- const ref = refMap[String(dataId)];
- if (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 tip = `[${num || '?'}] ${content}${doi ? '\nDOI: ' + doi : ''}`;
- ed.dom.setAttrib(el, 'title', tip);
- } else {
- ed.dom.setAttrib(el, 'title', this.$t('wordCite.notFoundById', { id: num != null ? num : dataId }));
+ const lines = sortedIds.map((id) => {
+ const no = this.citeMap[String(id)] != null ? this.citeMap[String(id)] : '?';
+ const ref = refMap[String(id)];
+ if (!ref) return this.$t('wordCite.notFoundById', { id: no === '?' ? id : no });
+ 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 || '[?]';
+ return `[${no}] ${content}\nDOI: ${doi}`;
+ });
+
+ el.textContent = `[${label}]`;
+ ed.dom.setAttrib(el, 'title', lines.join('\n'));
+ });
+ this.padAutociteCaretPlaceholder(ed);
+ },
+ /** 段尾不可编辑节点后浏览器/TinyMCE 容易把后续输入新开
,在引用后补零宽空格让光标留在同一段 */
+ padAutociteCaretPlaceholder(ed) {
+ const doc = ed.getDoc();
+ const body = doc.body;
+ if (!body) return;
+ body.querySelectorAll('autocite').forEach((el) => {
+ const next = el.nextSibling;
+ if (next === null) {
+ el.parentNode.appendChild(doc.createTextNode('\u200b'));
+ return;
+ }
+ if (next.nodeType === 3) {
+ if (next.textContent === '\u200b') return;
+ if (next.textContent === '') {
+ next.textContent = '\u200b';
+ }
}
});
},
- insertAutocite(refId) {
+ insertAutocite(refIds) {
const ed = this.editorInstance;
if (!ed) return;
+ const ids = (Array.isArray(refIds) ? refIds : [refIds]).map(String);
+ const dataId = ids.join(',');
+ const escaped = dataId.replace(/"/g, '"');
+
if (this._editingAutocite) {
- this._editingAutocite.setAttribute('data-id', refId);
- const num = this.citeMap[String(refId)];
- this._editingAutocite.textContent = num != null ? `[${num}]` : '[?]';
+ this._editingAutocite.setAttribute('data-id', dataId);
this._editingAutocite = null;
ed.fire('change');
} else {
if (this._refBookmark) {
ed.selection.moveToBookmark(this._refBookmark);
}
- const num = this.citeMap[String(refId)];
- const label = num != null ? `[${num}]` : '[?]';
- const html = `${label}`;
- ed.insertContent(html);
+ ed.insertContent(``);
}
+ this.renderAutociteInEditor(ed);
},
removeAutocite() {
const ed = this.editorInstance;
@@ -237,10 +305,45 @@ export default {
this._editingAutocite = null;
ed.fire('change');
},
+ /**
+ * 从当前编辑的 autocite 中去掉指定 p_refer_id;去掉后无 id 则删除整段标签。
+ * ids 为空:删除整段(与 removeAutocite 一致)。
+ */
+ stripAutociteIds(idsToRemove) {
+ const ed = this.editorInstance;
+ if (!ed || !this._editingAutocite) return;
+ const remove = new Set((idsToRemove || []).map((id) => String(id)));
+ const el = this._editingAutocite;
+ this._editingAutocite = null;
+ if (remove.size === 0) {
+ ed.dom.remove(el);
+ ed.fire('change');
+ return;
+ }
+ const parts = this.parseAutociteDataIds(el.getAttribute('data-id') || '');
+ const remaining = parts.filter((id) => !remove.has(String(id)));
+ if (remaining.length === 0) {
+ ed.dom.remove(el);
+ } else {
+ const sorted = this.sortAutociteIdsByCiteNumber(remaining);
+ el.setAttribute('data-id', sorted.join(','));
+ this.renderAutociteInEditor(ed);
+ }
+ ed.fire('change');
+ },
+ /** TinyMCE 会剔除「空」的行内标签;空 autocite 必须在入编辑器前占位,否则合并引用 [1–3] 等整段消失 */
+ normalizeAutociteHtmlForEditor(html) {
+ if (!html || typeof html !== 'string') return html;
+ let out = html.replace(/]*)>\s*<\/autocite>/gi, '');
+ // 与 getSafeContent 标签边界逻辑一致:autocite 左右水平空白改为 ,不换行符(避免吃掉段间 \n)
+ out = out.replace(/[^\S\r\n]+(?=)[^\S\r\n]+/gi, ' ');
+ return out;
+ },
handleSetContent(val) {
if (!this.editorInstance) return;
- let finalContent = val || '';
+ let finalContent = this.normalizeAutociteHtmlForEditor(val || '');
// 你的业务逻辑:自动包裹 标签
if (!finalContent.includes('wordTableHtml') && !finalContent.startsWith('
')) {
finalContent = '
' + finalContent + '
';
@@ -394,7 +497,10 @@ export default {
},
getDetail(val) {
if (this.hasInit == true) {
- this.$nextTick(() => window.tinymce.get(this.tinymceId).setContent(val));
+ this.$nextTick(() => {
+ const ed = window.tinymce.get(this.tinymceId);
+ if (ed) ed.setContent(this.normalizeAutociteHtmlForEditor(val || ''));
+ });
}
},
//将字符串添加到富文本编辑器中
@@ -505,7 +611,7 @@ export default {
return new Blob([u8arr], { type: mime });
},
formatHtml(val) {
- const rawValue = val || ''; // 处理 null
+ const rawValue = this.normalizeAutociteHtmlForEditor(val || ''); // 须先于 cleanEmptyTags,否则空 autocite 被删
const cleanEmptyTags = /<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g;
const replaceSpaces = /\s+(?=<)|(?<=>)\s+/g;
const removeBr = /
/gi; // 移除所有 br 标签
@@ -530,7 +636,7 @@ export default {
}
},
getSafeContent(val) {
- const rawValue = val || '';
+ const rawValue = this.normalizeAutociteHtmlForEditor(val || '');
const cleanEmptyTags = /<([a-zA-Z1-6]+)\b[^>]*><\/\1>/g;
const replaceSpaces = /\s+(?=<)|(?<=>)\s+/g;
@@ -572,7 +678,8 @@ export default {
..._this.tinymceOtherInit,
trim_span_elements: false, // 禁止修剪内联标签周围的空格
extended_valid_elements: 'blue[*],autocite[*]',
- custom_elements: 'blue,autocite',
+ /* ~ 前缀:按行内(类似 span)处理,否则默认当块级会拆段导致引用后强制换行 */
+ custom_elements: 'blue,~autocite',
valid_children: '+blue[#text|i|em|b|strong|span],+body[blue|autocite],+p[blue|autocite]',
inline: false, // 使用 iframe 模式
@@ -615,16 +722,19 @@ export default {
font-weight: bold !important;
}
+ /* inline 与 blue 引用一致,避免 inline-block 在行尾产生多余换行感 */
autocite {
- display: inline-block;
+ display: inline;
+ vertical-align: baseline;
color: rgb(0, 130, 170);
- font-weight: bold;
+ // font-weight: bold;
cursor: pointer;
padding: 0 2px;
border-radius: 3px;
background-color: rgba(0, 130, 170, 0.08);
user-select: all;
font-size: 12px;
+ white-space: nowrap;
}
autocite:hover {
background-color: rgba(0, 130, 170, 0.2);
@@ -649,7 +759,23 @@ export default {
},
body_class: 'panel-body ',
object_resizing: false,
- toolbar: this.toolbar.length > 0 ? this.toolbar : toolbar,
+ toolbar: (function () {
+ const tb = _this.toolbar;
+ const hasCustom =
+ (Array.isArray(tb) && tb.length > 0) ||
+ (typeof tb === 'string' && tb.length > 0);
+ /* 与 content.vue 默认一致,且含 insertRef;避免未传 toolbar 时引用到未定义的变量 */
+ const defaultToolbar = [
+ 'bold italic |customBlue removeBlue|LateX insertRef| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace'
+ ];
+ if (!hasCustom) return defaultToolbar;
+ const rows = Array.isArray(tb) ? tb : [tb];
+ return rows.map((row) =>
+ typeof row === 'string' && row.indexOf('insertRef') === -1
+ ? row.replace(/\|LateX\|/g, '|LateX insertRef|').replace(/\|\s*LateX\s*\|/g, '| LateX insertRef |')
+ : row
+ );
+ })(),
menubar: false, // 启用菜单栏并保持必要的项目
statusbar: false, // 关闭底部状态栏
custom_colors: false,
@@ -721,19 +847,29 @@ export default {
text: 'Ref',
tooltip: 'Insert Reference',
onAction: function () {
+ const refs = Array.isArray(_this.chanFerForm) ? _this.chanFerForm : [];
+ if (refs.length === 0) {
+ _this.$message.warning(_this.$t('wordCite.noRefs'));
+ return;
+ }
_this._refBookmark = ed.selection.getBookmark(2);
_this._editingAutocite = null;
- _this.$emit('openRefSelector', { currentRefId: null });
+ _this.$emit('openRefSelector', { currentRefIds: [] });
}
});
ed.on('click', function (e) {
const autociteEl = e.target.closest('autocite');
if (autociteEl) {
- const dataId = autociteEl.getAttribute('data-id');
+ const refs = Array.isArray(_this.chanFerForm) ? _this.chanFerForm : [];
+ if (refs.length === 0) {
+ _this.$message.warning(_this.$t('wordCite.noRefs'));
+ return;
+ }
+ const dataIds = _this.parseAutociteDataIds(autociteEl.getAttribute('data-id'));
_this._refBookmark = ed.selection.getBookmark(2);
_this._editingAutocite = autociteEl;
- _this.$emit('openRefSelector', { currentRefId: dataId });
+ _this.$emit('openRefSelector', { currentRefIds: dataIds });
return;
}
@@ -909,7 +1045,12 @@ export default {
ed.on('GetContent', function (e) {
e.content = e.content.replace(//g, '').replace(/<\/b>/g, '');
e.content = e.content.replace(//g, '').replace(/<\/em>/g, '');
- e.content = e.content.replace(/]*)>[^<]*<\/autocite>/gi, '');
+ e.content = e.content.replace(/<\/autocite>\s*/gi, '');
+ e.content = e.content.replace(/<\/autocite>\s*\u200b/g, '');
+ e.content = e.content.replace(/]*)>[^<]*<\/autocite>/gi, function (match, attrs) {
+ var clean = attrs.replace(/\s*style="[^"]*"/gi, '').replace(/\s*title="[^"]*"/gi, '').replace(/\s*contenteditable="[^"]*"/gi, '');
+ return '';
+ });
});
},
paste_preprocess: function (plugin, args) {
@@ -1075,7 +1216,8 @@ export default {
},
//设置内容
setContent(value) {
- window.tinymce.get(this.tinymceId).setContent(value);
+ const ed = window.tinymce.get(this.tinymceId);
+ if (ed) ed.setContent(this.normalizeAutociteHtmlForEditor(value || ''));
},
//获取内容
async getContent(type) {
diff --git a/src/components/page/components/table/content.vue b/src/components/page/components/table/content.vue
index 71c1def..bbd3e67 100644
--- a/src/components/page/components/table/content.vue
+++ b/src/components/page/components/table/content.vue
@@ -44,8 +44,8 @@ export default {
},
computed: {
toolbarConfig() {
- const hasRefs = Array.isArray(this.chanFerForm) && this.chanFerForm.length > 0;
- const refBtn = hasRefs ? ' insertRef' : '';
+ // Ref 必须始终出现在配置里:TinyMCE 只在 init 时读一次 toolbar,若首屏 chanFerForm 为空则按钮会永久缺失
+ const refBtn = ' insertRef';
if (!this.isAutomaticUpdate) {
return [`bold italic |customBlue removeBlue|LateX${refBtn}| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace`];
}
@@ -107,6 +107,19 @@ export default {
},
removeAutocite() {
this.$refs.tinymceChild1.removeAutocite();
+ },
+ /** 从当前 autocite 的 data-id 中移除指定 id;无剩余则删标签;未选 id 时删整段 */
+ stripAutociteIds(ids) {
+ if (this.$refs.tinymceChild1 && typeof this.$refs.tinymceChild1.stripAutociteIds === 'function') {
+ this.$refs.tinymceChild1.stripAutociteIds(ids);
+ }
+ },
+ /** 参考文献异步到达后刷新编辑器内 autocite 数字(供父组件在打开弹窗 / fetch 完成后调用) */
+ refreshAutociteDisplay() {
+ const t = this.$refs.tinymceChild1;
+ if (t && typeof t.renderAutociteInEditor === 'function' && t.editorInstance) {
+ t.renderAutociteInEditor(t.editorInstance);
+ }
}
}
};
diff --git a/src/components/page/components/table/word.vue b/src/components/page/components/table/word.vue
index 27d66fa..8acea78 100644
--- a/src/components/page/components/table/word.vue
+++ b/src/components/page/components/table/word.vue
@@ -963,7 +963,12 @@
-
+
H1
@@ -977,9 +982,21 @@
-
+
-
@@ -483,6 +477,29 @@ export default {
.prog-box { flex: 1; }
.prog-num { font-size: 12px; font-weight: bold; display: block; text-align: right; margin-bottom: 4px; min-width: 56px; }
.pagination-container { margin-top: 20px; display: flex; justify-content: flex-end; padding: 10px; }
+
+.btn-group {
+ display: inline-flex;
+ align-items: center;
+ gap: 12px;
+ flex-shrink: 0;
+}
+/* 单次抓取按钮单独线框(与右侧启停区分) */
+.crawl-once-frame {
+ display: inline-flex;
+ align-items: center;
+ padding: 2px 10px;
+ border: 1px solid #006699;
+ border-radius: 6px;
+ background: #fff;
+ line-height: 1;
+ box-sizing: border-box;
+}
+.crawl-once-frame .op-run-once {
+ margin-left: 0;
+ margin-right: 0;
+}
+
.btn-group .el-button.op-pause,
.btn-group .el-button.op-pause i { color: #fc4d4d !important; }
.btn-group .el-button.op-resume,
diff --git a/src/components/page/editPublicRefTableOnly.vue b/src/components/page/editPublicRefTableOnly.vue
index 1290d4d..c41494f 100644
--- a/src/components/page/editPublicRefTableOnly.vue
+++ b/src/components/page/editPublicRefTableOnly.vue
@@ -349,7 +349,7 @@ export default {
}
// 如果连续数字 >= 3 个,使用连字符 '-'
if (j - i >= 2) {
- result.push(`${sorted[i]}-${sorted[j]}`);
+ result.push(`${sorted[i]}–${sorted[j]}`);
} else {
// 否则(1个或2个)逐个列出
for (let k = i; k <= j; k++) {
@@ -368,11 +368,19 @@ export default {
let html = this.highlightText2(text, annotations, type, am_id);
// 2. 借鉴 EndNote:对处理完批注的 HTML 进行引用联动渲染
- // 正则匹配连续的 autocite 标签(允许中间有空格)
- const citeGroupRe = /(
<\/autocite>\s*)+/gi;
+ // 支持 data-id="a,b,c" 单标签 或 多个相邻单 id 标签
+ const citeGroupRe = /(?:<\/autocite>\s*)+/gi;
return html.replace(citeGroupRe, (groupMatch) => {
- const ids = [...groupMatch.matchAll(/data-id="(\d+)"/gi)].map((m) => m[1]);
+ const ids = [];
+ const innerRe = /<\/autocite>/gi;
+ let m;
+ while ((m = innerRe.exec(groupMatch)) !== null) {
+ m[1].split(',').forEach((part) => {
+ const id = part.trim();
+ if (id) ids.push(id);
+ });
+ }
// 从全局 citeMap 中获取序号(citeMap 是你根据全文顺序生成的 {ID: Index})
const nums = ids.map((id) => this.citeMap[id]).filter((n) => n);
@@ -1876,9 +1884,9 @@ export default {
border-radius: 30px;
}
.text-highlight {
- background-color: rgb(252, 98, 93);
+ /* background-color: rgb(252, 98, 93);
background-color: rgb(252 98 93 / 68%);
- color: #333;
+ color: #333; */
}
.template-info {
margin-top: 20px;