From 0d35b76c3a1b6a244ee9b0a99f564dc4488516fb 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: Wed, 8 Apr 2026 13:03:54 +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 | 50 +- src/components/common/langs/en.js | 12 +- src/components/common/langs/zh.js | 12 +- src/components/page/GenerateCharts.vue | 1099 ++++++++- src/components/page/comArtHtmlCreatNew.vue | 2042 ++++++++++++++--- .../page/components/Tinymce/index.vue | 470 +++- .../page/components/table/content.vue | 39 +- .../page/components/table/table.vue | 43 +- src/components/page/components/table/word.vue | 614 ++++- .../page/editPublicRefTableOnly.vue | 52 +- src/utils/autociteHtml.js | 22 + 11 files changed, 3866 insertions(+), 589 deletions(-) create mode 100644 src/utils/autociteHtml.js diff --git a/src/common/js/commonJS.js b/src/common/js/commonJS.js index 53159c2..a4380f8 100644 --- a/src/common/js/commonJS.js +++ b/src/common/js/commonJS.js @@ -71,7 +71,51 @@ function findExtentElement(blipElement) { export default { - + + + async searchTitleByDOI(doi) { + if (!doi) { + this.$message.warning('Please enter a DOI'); + return null; + } + + // 开启全局 Loading,防止编辑重复点击 + + + try { + // 1. 数据清洗 + const cleanDoi = doi.trim() + .replace(/^doi:/i, '') + .replace(/https?:\/\/doi\.org\//i, ''); + + // 2. 请求 Crossref 接口 + const response = await fetch(`https://api.crossref.org/works/${encodeURIComponent(cleanDoi)}`, { + method: 'GET', + headers: { 'Accept': 'application/json' } + }); + + if (!response.ok) throw new Error('DOI not found'); + + const data = await response.json(); + + // 3. 提取标题并跳转 + const title = data.message?data.message.title?data.message.title[0]:'':''; + + if (title) { + const searchUrl = `https://www.google.com/search?q=${encodeURIComponent(title)}`; + window.open(searchUrl, '_blank'); + return title; + } else { + this.$message.error('Title not found in metadata.'); + } + } catch (error) { + console.error("DOI Retrieval Error:", error); + this.$message.error('Failed to fetch title. Please check the DOI.'); + } finally { + + } + return null; + }, getJournalTypeName(value) { var list = JSON.parse(localStorage.getItem('journalTypeDataAll')); @@ -916,9 +960,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|autocite))[^>]+>/g, ''); // 删除不需要的标签 + inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|img|myfigure|mytable|myh3|mycite))[^>]+>/g, ''); // 删除不需要的标签 } else { - inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable|myh3|autocite))[^>]+>/g, ''); // 删除不需要的标签 + inputHtml = inputHtml.replace(/<(?!\/?(strong|em|sub|sup|b|i|blue|wmath|myfigure|mytable|myh3|mycite))[^>]+>/g, ''); // 删除不需要的标签 } diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index d16aeb4..41e95ca 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1160,6 +1160,7 @@ colTitle: 'Template title', }, wordCite: { noRefs: 'Reference list is not loaded yet. Please wait or refresh the page.', + missingBoundRef: 'The bound reference no longer exists. Please cite again.', notFoundById: 'No reference found for citation ID {id}', selectRef: 'Select Reference', reference: 'Reference', @@ -1168,8 +1169,15 @@ colTitle: 'Template title', remove: 'Remove', selected: 'Selected', modifyRef: 'Edit citation', - removeRefTag: 'Remove citation', - citeUpdateFail: 'Could not update the citation in the text. Try again or use Edit.' + removeRefTag: 'Reference remove', + citeUpdateFail: 'Could not update the citation in the text. Try again or use Edit.', + matchBracketRefs: 'Auto-link References', + matchBracketRefsDone: 'Converted {n} bracket citation(s) to autocite.', + matchBracketRefsNone: "No active citation links were detected in the text. To enable automatic numbering, please use the 'Reference' tool to link your sources.", + 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' } diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index d3c084a..93fdb2a 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1145,6 +1145,7 @@ const zh = { }, wordCite: { noRefs: '参考文献列表尚未加载,请稍候再试或刷新页面', + missingBoundRef: '当前绑定的参考文献不存在,请重新引用', notFoundById: '未查询到编号{id}相关参考文献', selectRef: '选择参考文献', reference: '参考文献', @@ -1153,8 +1154,15 @@ const zh = { remove: '移除', selected: '已选择', modifyRef: '修改引用', - removeRefTag: '移除引用', - citeUpdateFail: '未能更新正文中的引用标签,请重试或进入编辑修改' + removeRefTag: '移除参考文献', + citeUpdateFail: '未能更新正文中的引用标签,请重试或进入编辑修改', + matchBracketRefs: '自动链接参考文献', + matchBracketRefsDone: '已转换 {n} 处 [n] 为可点击角标', + matchBracketRefsNone: '正文中未检测到可转换的纯文本引用 [n]。若要自动编号并关联参考文献,请使用工具栏中的「Reference」插入引用。', + removeRefNeedClickCite: '请先在正文中点击要删除的引用角标,再点「移除参考文献」。', + quickPickPlaceholder: '输入如 [5, 6, 10-15] 自动勾选对应参考文献', + quickPickApply: '链接参考文献', + quickPickClear: '清空勾选' } diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue index 583a329..8383638 100644 --- a/src/components/page/GenerateCharts.vue +++ b/src/components/page/GenerateCharts.vue @@ -123,6 +123,9 @@ @onAddRow="onAddRow" @changeComment="changeComment" @openRefSelector="handleOpenRefSelectorFromManuscript" + @reorderReferencesByBody="handleReorderReferencesByBody" + @saveTableManuscript="saveTableManuscript" + @saveImageManuscript="saveImageManuscript" style="width: calc(100%); height: calc(100%)" :style="`100%`" > @@ -131,10 +134,11 @@ + +
+
+ edit + + Add + + + delete + +
+
+
+
+ + + {{ $t('wordCite.quickPickApply') }} + + +
{{ $t('wordCite.cancel') }} - {{ $t('wordCite.remove') }} + {{ $t('wordCite.confirm') }} @@ -502,6 +623,7 @@ import Tinymce from '@/components/page/components/Tinymce'; import bottomTinymce from '@/components/page/components/Tinymce'; import catalogue from '@/components/page/components/table/catalogue.vue'; import editPublicRefTableOnly from './editPublicRefTableOnly.vue'; +import { extractAutociteIdsFromHtmlString } from '@/utils/autociteHtml.js'; export default { data() { return { @@ -628,10 +750,22 @@ export default { p_article_id: null, chanFerForm: [], chanFerFormRepeatList: [], + /** 全文 mycite 首次出现顺序(与稿面 common-word 一致),供 Edit Content 弹窗内 TinyMCE 显示 [1][2] 与重排列表 */ + articleCiteIdOrder: [], + /** Edit Content 弹窗内当前 HTML(与保存合并逻辑一致),用于实时算出与稿面相同的 [n],避免仅依赖防抖后的 articleCiteIdOrder */ + editModalDraftHtml: '', + /** 表格抽屉:Title+Table+Note 合并稿,驱动 tableModalBodyCiteOrder(与 editModalDraftHtml 对 Edit Content 一致) */ + tableModalDraftHtml: '', refSelectorVisible: false, refSelectorIsEdit: false, refSelectedRows: [], - refSelectorSource: 'commonContent' + refSelectorSource: 'commonContent', + /** 表格抽屉内合并稿防抖:按全文首次出现顺序更新 articleCiteIdOrder,避免角标停留在列表序号 [38] */ + _tableReorderTimer: null, + /** 打开选择器时若带 currentRefIds(编辑已有引用),排序用「传值计算的顺序」;关闭后清空 */ + refSelectorContextIds: [], + /** 选择参考文献弹窗:按 # 序号快速勾选,如 [5, 6, 10-15] */ + refSelectorQuickPick: '' }; }, components: { @@ -641,28 +775,86 @@ 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; + /** 选择器打开时:无传值用 articleCiteIdOrder;有传值且 Edit Content 打开时用合并稿 editModalBodyCiteOrder,与 # 列、正文 [n] 一致 */ + refSelectorOrderForPreview() { + if (!this.refSelectorVisible) { + return Array.isArray(this.articleCiteIdOrder) ? this.articleCiteIdOrder : []; } - return result.join(', '); + const hasCtx = Array.isArray(this.refSelectorContextIds) && this.refSelectorContextIds.length > 0; + if (hasCtx && this.editVisible) { + const o = this.editModalBodyCiteOrder; + return Array.isArray(o) && o.length > 0 ? o : this.articleCiteIdOrder || []; + } + if (hasCtx && this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + const o = this.tableModalBodyCiteOrder; + return Array.isArray(o) && o.length > 0 ? o : this.articleCiteIdOrder || []; + } + return Array.isArray(this.articleCiteIdOrder) ? this.articleCiteIdOrder : []; + }, + /** 选择器内表格行顺序:默认按正文 articleCiteIdOrder;有传值且处于编辑弹窗时按合并稿计算顺序 */ + refSelectorTableData() { + const refs = Array.isArray(this.chanFerForm) ? [...this.chanFerForm] : []; + if (!refs.length) return []; + let order; + const hasCtx = Array.isArray(this.refSelectorContextIds) && this.refSelectorContextIds.length > 0; + if (hasCtx && this.editVisible) { + order = this.editModalBodyCiteOrder; + } else if (hasCtx && this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + order = this.tableModalBodyCiteOrder; + } else { + order = this.articleCiteIdOrder; + } + if (!Array.isArray(order) || order.length === 0) return refs; + return this.sortChanFerRowsByOrder(refs, order); + }, + /** 与正文 mycite 一致:只展示已在列表中且有序号的文献,不显示「?」 */ + refPreviewLabel() { + const refList = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const refMap = refList.reduce((acc, item) => { + const key = item && item.p_refer_id != null ? String(item.p_refer_id) : ''; + if (key) acc[key] = item; + return acc; + }, {}); + + const order = (this.refSelectorVisible ? this.refSelectorOrderForPreview : this.articleCiteIdOrder) || []; + const citeMap = this.buildDisplayCiteMap(order); + + const ctx = Array.isArray(this.refSelectorContextIds) ? this.refSelectorContextIds.map(String) : []; + const selectedIds = (this.refSelectedRows || []) + .map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')) + .filter(Boolean); + + let idsForPreview = []; + if (this.refSelectorVisible && ctx.length > 0) { + const hasMissingInCtx = ctx.some((id) => !refMap[id]); + if (hasMissingInCtx) { + idsForPreview = ctx; + } else if (selectedIds.length > 0) { + idsForPreview = selectedIds; + } else { + idsForPreview = ctx; + } + } else if (selectedIds.length > 0) { + idsForPreview = selectedIds; + } + + if (!idsForPreview.length) return ''; + + const sortedIds = this.sortAutociteIdsByCiteNumber(idsForPreview, order); + const parts = sortedIds.map((id) => { + const ref = refMap[id]; + const no = ref ? citeMap[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); + + if (numsForLabel.length === 0) return ''; + return this.formatCiteNumbers(numsForLabel); }, catalogueContent() { const base = Array.isArray(this.Main_List) ? this.Main_List : []; @@ -676,9 +868,39 @@ export default { is_h2: 0 } ]; + }, + /** 弹窗内引用角标:按 Main_List + 当前草稿段合并后的全文首次出现顺序,与保存后一致 */ + editModalBodyCiteOrder() { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) { + return this.articleCiteIdOrder; + } + const draft = this.editModalDraftHtml; + if (draft == null || draft === '') { + return this.articleCiteIdOrder; + } + const order = this.extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, draft); + return order.length > 0 ? order : this.articleCiteIdOrder; + }, + /** 表格抽屉内角标:与 Edit Content 同源,按 Main_List + Title/表/Note 合并稿做全文首次出现排序,避免仍用 articleCiteIdOrder 列表下标 [38] */ + tableModalBodyCiteOrder() { + if (!this.threeVisible || !this.lineStyle || this.lineStyle.am_id == null) { + return this.articleCiteIdOrder; + } + const draft = this.tableModalDraftHtml; + if (draft == null || draft === '') { + return this.articleCiteIdOrder; + } + const order = this.extractAutociteOrderFromMainList(this.Main_List, this.lineStyle.am_id, draft); + return order.length > 0 ? order : this.articleCiteIdOrder; } }, watch: { + editVisible(val) { + if (!val) this.editModalDraftHtml = ''; + }, + threeVisible(val) { + if (!val) this.tableModalDraftHtml = ''; + }, // 监听计算属性 // combinedValue(newVal, oldVal) { // console.log('value1 或 value2 发生变化'); @@ -702,8 +924,9 @@ export default { async created() { localStorage.removeItem('scrollPosition'); this.isShowEditComment(); - this.loadPreacceptArticleDetail(); - this.getDate(); + /** 先拿 p_article_id 并拉参考文献,再拉正文段落,避免稿面/角标早于 chanFerForm 就绪 */ + await this.loadPreacceptArticleDetail(); + await this.getDate(); this.getCommentList(); }, mounted() { @@ -721,29 +944,112 @@ export default { }, async activated() { this.isShowEditComment(); - this.loadPreacceptArticleDetail(); - this.getDate(); + await this.loadPreacceptArticleDetail(); + await this.getDate(); this.getCommentList(); }, + beforeDestroy() { + if (this._editModalCiteTimer) { + clearTimeout(this._editModalCiteTimer); + this._editModalCiteTimer = null; + } + if (this._tableReorderTimer) { + clearTimeout(this._tableReorderTimer); + this._tableReorderTimer = null; + } + }, methods: { + formatTitle(title) { + if (!title) return ''; + // 使用正则匹配,'gi' 表示全局匹配且不区分大小写 + // \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词 + const reg = /\b(Retracted|Retraction)\b/gi; + + return title.replace(reg, (match) => { + return `${match}`; + }); + }, + getJournalDateno(dateno, type) { + if (dateno && typeof dateno === 'string') { + const hasInvalidColon = !dateno.includes(':') || (dateno.includes(':') && dateno.split(':').pop().trim() === ''); + + if (hasInvalidColon) { + return type === 'title' ? 'text-highlight' : 'warn'; + } + } + return ''; + }, + tableRowStyle({ row }) { + if (row.is_repeat === 1) { + return { backgroundColor: '#ffececa1' }; // 浅红色 + } + return {}; + }, + handleContainerClick(e) { + if (e.target.tagName === 'SPAN' && e.target.hasAttribute('data-ref')) { + const ref = Number(e.target.getAttribute('data-ref')); // 获取存储的 ref 值 + this.handleClickRef(ref); // 触发实际逻辑 + } + }, + + handleClickRef(ref) {}, + getRepeatRefHtml() { + let warningText = 'Please note that '; + this.chanFerFormRepeatList.forEach((pair, index) => { + let singlePairText = ''; + pair.forEach((ref, refIndex) => { + singlePairText += `${ + ref + 1 + }`; + if (refIndex !== pair.length - 1) { + singlePairText += ' and '; + } + }); + warningText += `ref. ${singlePairText}`; + if (index === this.chanFerFormRepeatList.length - 1) { + warningText += ' are duplicates.'; + } else { + warningText += ', and '; + } + }); + return warningText; + }, + getParsingInfo(data, index) { + const targetSubArr = this.chanFerFormRepeatList.find((subArr) => subArr.includes(index)); + if (targetSubArr) { + let warningText = 'Please note that '; + const refElements = targetSubArr.map((ref, refIndex) => { + return `${ref + 1}`; + }); + if (refElements.length > 0) { + if (refElements.length === 1) { + warningText += `ref. ${refElements[0]} is a duplicate.`; + } else { + const lastRef = refElements.pop(); // 取出最后一个 ref + warningText += `ref. ${refElements.join(', ')} and ${lastRef} are duplicates.`; + } + } + return warningText; + } + }, loadPreacceptArticleDetail() { - if (!this.articleId) return; - this.$api + if (!this.articleId) return Promise.resolve(); + return this.$api .post('api/Article/getPreacceptArticleDetail', { article_id: this.articleId }) .then((res) => { const pid = res.data && res.data.production && res.data.production.p_article_id; this.p_article_id = pid; if (pid != null && pid !== '') { if (!this.Art_P_Id) this.Art_P_Id = pid; - this.fetchReferList(); + return this.fetchReferList(); } }) .catch(() => {}); }, fetchReferList() { - if (!this.p_article_id) return; - this.$api + if (!this.p_article_id) return Promise.resolve(); + return this.$api .post('api/Production/getReferList', { p_article_id: this.p_article_id }) @@ -763,21 +1069,408 @@ export default { if (this.addContentVisible && this.$refs.addContent && this.$refs.addContent.refreshAutociteDisplay) { this.$refs.addContent.refreshAutociteDisplay(); } + if (this.threeVisible && this.$refs.commonTable && this.$refs.commonTable.refreshAutociteDisplay) { + this.$refs.commonTable.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.tinymceChildNote && this.$refs.tinymceChildNote.refreshAutociteDisplay) { + this.$refs.tinymceChildNote.refreshAutociteDisplay(); + } + if (this.pictVisible && this.$refs.tinymceChildImgNote && this.$refs.tinymceChildImgNote.refreshAutociteDisplay) { + this.$refs.tinymceChildImgNote.refreshAutociteDisplay(); + } + /** getReferList 会整表覆盖 chanFerForm,需再按正文首次出现顺序对齐,否则下方列表与 [n] 脱节 */ + this.applyRefOrderAfterFetchReferList(); }); }) .catch((err) => { console.log(err); }); }, + /** 参考文献列表接口返回后:弹窗内按合并稿重排;稿面由 word 同步正文顺序 */ + 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) || ''; + } + } + this.flushReorderFromEditModal(html); + return; + } + if (this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + this.$nextTick(() => { + this.flushReorderFromTableModal(); + this.refreshTableDrawerAutociteDisplays(); + }); + return; + } + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + /** 与稿面内部顺序对齐:父级 articleCiteIdOrder 供表格/图注等弹窗 TinyMCE 用,避免仍按列表下标显示 [38] */ + this.reorderReferencesFromMainListBody(null, null); + }, + /** + * 按 Main_List 各段正文中 mycite 首次出现顺序重排参考文献。 + * draftAmId + draftHtml:合并「当前将保存的这一段」后再扫全文(点 Save 时与保存后一致); + * 传 (null, null) 表示完全以 Main_List 已存 content 为准。 + */ + reorderReferencesFromMainListBody(draftAmId, draftHtml) { + const order = this.extractAutociteOrderFromMainList(this.Main_List, draftAmId, draftHtml); + if (order.length > 0) { + this.handleReorderReferencesByBody(order); + } + }, + /** Edit Table 抽屉:Title + 表格区 + Note 合并为一段,与 extractAutociteOrderFromMainList 的 draft 语义一致 */ + getTableDrawerMergedDraftHtml() { + const parts = []; + const titleInst = + this.$refs.tinymceChildTitle && + this.$refs.tinymceChildTitle.$refs && + this.$refs.tinymceChildTitle.$refs.tinymceChild1; + if (titleInst && titleInst.editorInstance) { + parts.push(titleInst.editorInstance.getContent({ format: 'raw' }) || ''); + } else if (this.lineStyle && typeof this.lineStyle.title === 'string') { + parts.push(this.lineStyle.title); + } + const tableInst = + this.$refs.commonTable && + this.$refs.commonTable.$refs && + this.$refs.commonTable.$refs.tinymceChild1; + if (tableInst && tableInst.editorInstance) { + parts.push(tableInst.editorInstance.getContent({ format: 'raw' }) || ''); + } else if (this.lineStyle && typeof this.lineStyle.html_data === 'string') { + parts.push(this.lineStyle.html_data); + } + const noteInst = + this.$refs.tinymceChildNote && + this.$refs.tinymceChildNote.$refs && + this.$refs.tinymceChildNote.$refs.tinymceChild1; + if (noteInst && noteInst.editorInstance) { + parts.push(noteInst.editorInstance.getContent({ format: 'raw' }) || ''); + } else if (this.lineStyle && typeof this.lineStyle.note === 'string') { + parts.push(this.lineStyle.note); + } + return parts + .map((p) => (p == null ? '' : String(p))) + .filter((s) => s.trim() !== '') + .join('\n'); + }, + refreshTableDrawerAutociteDisplays() { + if (this.$refs.commonTable && typeof this.$refs.commonTable.refreshAutociteDisplay === 'function') { + this.$refs.commonTable.refreshAutociteDisplay(); + } + if (this.$refs.tinymceChildNote && typeof this.$refs.tinymceChildNote.refreshAutociteDisplay === 'function') { + this.$refs.tinymceChildNote.refreshAutociteDisplay(); + } + }, + /** 按全文合并稿更新 articleCiteIdOrder,再刷新表格/表注内角标(与保存后稿面一致) */ + flushReorderFromTableModal() { + if (!this.threeVisible || !this.lineStyle || this.lineStyle.am_id == null) return; + const html = this.getTableDrawerMergedDraftHtml(); + this.tableModalDraftHtml = html || ''; + if (!html || !String(html).trim()) { + this.reorderReferencesFromMainListBody(null, null); + return; + } + this.reorderReferencesFromMainListBody(this.lineStyle.am_id, html); + }, + scheduleFlushReorderFromTableModal() { + if (!this.threeVisible || !this.lineStyle || this.lineStyle.am_id == null) return; + if (this._tableReorderTimer) clearTimeout(this._tableReorderTimer); + this._tableReorderTimer = setTimeout(() => { + this._tableReorderTimer = null; + this.flushReorderFromTableModal(); + this.$nextTick(() => { + this.refreshTableDrawerAutociteDisplays(); + }); + }, 180); + }, + /** 打开表格抽屉后待 TinyMCE 就绪再扫全文顺序,避免草稿态仍用列表下标 [38] */ + afterOpenTableDrawerSyncCiteOrder() { + const run = () => { + this.flushReorderFromTableModal(); + this.refreshTableDrawerAutociteDisplays(); + }; + this.$nextTick(() => { + this.$nextTick(() => { + run(); + setTimeout(run, 400); + }); + }); + }, handleOpenRefSelectorFromManuscript(data) { this.handleOpenRefSelector(data, 'manuscriptAutocite'); }, + onRefSelectorDialogClosed() { + this.refSelectorContextIds = []; + this.refSelectorQuickPick = ''; + }, + /** 仅排序展示用,不修改 chanFerForm(逻辑与 handleReorderReferencesByBody 一致) */ + sortChanFerRowsByOrder(refs, orderedIds) { + if (!Array.isArray(orderedIds) || orderedIds.length === 0) return refs; + const ids = orderedIds.map(String); + const idSet = new Set(ids); + const byId = {}; + refs.forEach((r) => { + if (r && r.p_refer_id != null) byId[String(r.p_refer_id)] = r; + }); + const next = []; + ids.forEach((id) => { + const row = byId[id]; + if (row) next.push(row); + }); + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const id = String(r.p_refer_id); + if (!idSet.has(id)) next.push(r); + }); + if (refs.length > 0 && next.length === 0) return refs; + return next; + }, + /** + * 与 word.vue citeMap:正文顺序中仅「当前参考文献列表里存在」的 id 依次占号,已删除/不存在的 id 不占位 + */ + buildDisplayCiteMap(orderArray) { + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const refIdSet = new Set( + refs.map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')).filter(Boolean) + ); + const map = {}; + if (!Array.isArray(orderArray) || orderArray.length === 0) { + refs.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 = []; + orderArray.forEach((id) => { + const k = String(id); + if (!k || !refIdSet.has(k)) return; + if (filtered.includes(k)) return; + filtered.push(k); + }); + filtered.forEach((id, idx) => { + map[id] = idx + 1; + }); + let next = filtered.length + 1; + refs.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; + }, + /** 与 word.vue sortAutociteIdsByCiteNumber:排序键使用 buildDisplayCiteMap */ + sortAutociteIdsByCiteNumber(ids, orderArray) { + const uniq = [...new Set((ids || []).map(String))]; + const map = this.buildDisplayCiteMap(orderArray); + 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)); + }); + }, + /** 与 word.vue:连续全局序号合并为 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(', '); + }, + /** 正文 mycite 顺序为唯一依据:按首次出现顺序重排 chanFerForm,未在正文出现的条目排在末尾 */ + handleReorderReferencesByBody(orderedIds) { + if (!Array.isArray(orderedIds) || orderedIds.length === 0) return; + const ids = orderedIds.map(String); + const prevOrder = this.articleCiteIdOrder || []; + const sameOrder = + ids.length === prevOrder.length && ids.every((id, i) => String(id) === String(prevOrder[i])); + if (!sameOrder) { + this.articleCiteIdOrder = ids.slice(); + } + const idSet = new Set(ids); + const refs = Array.isArray(this.chanFerForm) ? [...this.chanFerForm] : []; + const byId = {}; + refs.forEach((r) => { + if (r && r.p_refer_id != null) byId[String(r.p_refer_id)] = r; + }); + const next = []; + ids.forEach((id) => { + const row = byId[id]; + if (row) next.push(row); + }); + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const id = String(r.p_refer_id); + if (!idSet.has(id)) next.push(r); + }); + /** + * 若正文已出现引用 id,但列表尚未加载或 id 与 byId 对不上,会出现 next 为空而 refs 非空 + * (正文 id 全在 idSet 内,第二轮被跳过),此时切勿 chanFerForm=[],否则参考文献整块消失。 + */ + if (refs.length > 0 && next.length === 0) { + return; + } + const sig = (arr) => arr.map((r) => String(r.p_refer_id)).join('\0'); + if (sig(next) === sig(refs)) return; + this.chanFerForm = next; + }, + /** + * 单条 Main_List 段内、与稿面阅读顺序一致的 HTML 片段(用于统计 mycite 顺序)。 + * 表格段:标题 → 表体(html_data 或逐单元格 text)→ Note;图段:标题 → 说明。 + */ + collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml) { + if (draftAmId != null && p && p.am_id == draftAmId && draftHtml != null) { + return [draftHtml]; + } + const out = []; + const typ = p != null && p.type != null ? Number(p.type) : NaN; + if (typ === 2 && p.table) { + const t = p.table; + if (typeof t.title === 'string' && t.title.trim()) out.push(t.title); + if (typeof t.html_data === 'string' && t.html_data.trim()) { + out.push(t.html_data); + } else { + const pushRows = (rows) => { + if (!Array.isArray(rows)) return; + rows.forEach((row) => { + if (!Array.isArray(row)) return; + row.forEach((cell) => { + if (cell && typeof cell.text === 'string' && cell.text) out.push(cell.text); + }); + }); + }; + pushRows(t.tableHeader); + pushRows(t.tableContent); + } + if (typeof t.note === 'string' && t.note.trim()) out.push(t.note); + return out; + } + if (typ === 1 && p.image) { + const img = p.image; + if (typeof img.title === 'string' && img.title.trim()) out.push(img.title); + if (typeof img.note === 'string' && img.note.trim()) out.push(img.note); + if (out.length === 0) { + if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content); + if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text); + } + return out; + } + if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text); + if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content); + return out; + }, + /** 与 word.vue 一致:从各段 HTML 中按出现顺序收集 autocite(含表格单元格、表题、表注、图题图注) */ + extractAutociteOrderFromMainList(mainList, draftAmId, draftHtml) { + const list = Array.isArray(mainList) ? mainList : []; + const order = []; + list.forEach((p) => { + const candidates = this.collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml); + candidates.forEach((raw) => { + extractAutociteIdsFromHtmlString(raw).forEach((id) => { + if (!order.includes(id)) order.push(id); + }); + }); + }); + return order; + }, + /** 编辑弹窗内正文一变立即按全文合并稿重排参考文献(不再防抖,选中/输入后列表马上跟正文一致) */ + scheduleReorderFromEditModal(html) { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return; + this.$nextTick(() => { + this.flushReorderFromEditModal(html); + }); + }, + /** 立即按全文合并稿重排参考文献顺序(插入引用后调用,避免编号长期停留在列表序号 [8]) */ + flushReorderFromEditModal(html) { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return; + let order = this.extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, html); + if (order.length === 0 && html && /(autocite|mycite)/i.test(html)) { + order = extractAutociteIdsFromHtmlString(html); + } + if (order.length > 0) { + this.handleReorderReferencesByBody(order); + } + }, + /** + * 插入引用前先把新 p_refer_id 并入 articleCiteIdOrder,避免子组件 citeMap 缺键显示 [?]; + * 随后 flush 会按全文首次出现顺序覆盖为准确值。 + */ + ensureArticleCiteOrderIncludesIds(newIds) { + const add = (newIds || []).map(String).filter(Boolean); + if (!add.length) return; + let order = Array.isArray(this.articleCiteIdOrder) ? [...this.articleCiteIdOrder.map(String)] : []; + add.forEach((id) => { + if (!order.includes(id)) order.push(id); + }); + this.articleCiteIdOrder = order; + }, + onEditModalEditorInput(html) { + this.editModalDraftHtml = html || ''; + this.flushReorderFromEditModal(html); + }, + /** 打开表格抽屉时用已有 lineStyle 字段拼合并稿,先写入 tableModalDraftHtml,角标与 Edit Content 一样按全文算 */ + seedTableModalDraftFromLineStyle() { + if (!this.lineStyle || this.lineStyle.am_id == null) return; + const ls = this.lineStyle; + const parts = []; + if (typeof ls.title === 'string' && ls.title.trim()) parts.push(ls.title); + if (typeof ls.html_data === 'string' && ls.html_data.trim()) parts.push(ls.html_data); + if (typeof ls.note === 'string' && ls.note.trim()) parts.push(ls.note); + this.tableModalDraftHtml = parts.join('\n'); + }, + /** Title / 表格 / Note 任一输入,同步合并稿并防抖重排文献列表 */ + onTableModalEditorInput() { + this.$nextTick(() => { + if (!this.threeVisible || !this.lineStyle || this.lineStyle.am_id == null) return; + const html = this.getTableDrawerMergedDraftHtml(); + this.tableModalDraftHtml = html || ''; + }); + this.scheduleFlushReorderFromTableModal(); + }, handleOpenRefSelector(data, sourceOverride) { const currentIds = data && Array.isArray(data.currentRefIds) ? data.currentRefIds : []; + this.refSelectorContextIds = currentIds.map(String); this.refSelectorIsEdit = currentIds.length > 0; this.refSelectedRows = []; if (sourceOverride === 'manuscriptAutocite' || (data && data.source === 'manuscript')) { this.refSelectorSource = 'manuscriptAutocite'; + } else if ( + typeof sourceOverride === 'string' && + sourceOverride && + (sourceOverride === 'tinymceChildNote' || sourceOverride === 'tinymceChildImgNote') && + this.$refs[sourceOverride] + ) { + /** 表格 Note / 图片说明 独立编辑器,勿与 commonTable 混淆 */ + this.refSelectorSource = sourceOverride; + } else if (this.threeVisible) { + /** Edit Table 主表格区 */ + this.refSelectorSource = 'commonTable'; } else if (this.editVisible) { this.refSelectorSource = 'commonContent'; } else if (this.addContentVisible) { @@ -802,6 +1495,66 @@ export default { handleRefSelectionChange(rows) { this.refSelectedRows = rows; }, + parseQuickPickNumbers(input) { + const s = String(input || '').trim(); + if (!s) return []; + let t = s.replace(/^\s*\[/, '').replace(/\]\s*$/, ''); + t = t.replace(/[,]/g, ','); + const parts = t.split(',').map((x) => x.trim()).filter(Boolean); + const out = new Set(); + parts.forEach((p) => { + const m = p.match(/^(\d+)\s*[-–—]\s*(\d+)$/); + if (m) { + const a = Number(m[1]); + const b = Number(m[2]); + if (!Number.isNaN(a) && !Number.isNaN(b)) { + const lo = Math.min(a, b); + const hi = Math.max(a, b); + for (let i = lo; i <= hi; i++) out.add(i); + } + return; + } + const n = parseInt(p, 10); + if (!Number.isNaN(n)) out.add(n); + }); + return Array.from(out).sort((a, b) => a - b); + }, + clearRefSelectorSelection() { + const table = this.$refs.refSelectorTable; + if (table && typeof table.clearSelection === 'function') { + table.clearSelection(); + } + this.refSelectedRows = []; + }, + applyRefSelectorQuickPick() { + const table = this.$refs.refSelectorTable; + if (!table) return; + const nums = this.parseQuickPickNumbers(this.refSelectorQuickPick); + if (!nums.length) { + this.clearRefSelectorSelection(); + return; + } + table.clearSelection(); + const data = Array.isArray(this.refSelectorTableData) ? this.refSelectorTableData : []; + nums.forEach((n) => { + const idx = n - 1; + if (idx < 0 || idx >= data.length) return; + table.toggleRowSelection(data[idx], true); + }); + }, + /** 点击整行切换勾选(与点复选框一致);点链接或点复选框本身时不处理,避免重复或误触 */ + handleRefSelectorRowClick(row, column, event) { + if (column && column.type === 'selection') return; + if (event && event.target && typeof event.target.closest === 'function') { + if (event.target.closest('.el-checkbox') || event.target.closest('a[href]')) return; + } + const table = this.$refs.refSelectorTable; + if (!table || !row) return; + const isSelected = (this.refSelectedRows || []).some( + (r) => r && String(r.p_refer_id) === String(row.p_refer_id) + ); + table.toggleRowSelection(row, !isSelected); + }, handleConfirmRefCite() { if (this.refSelectedRows.length === 0) return; const ids = this.refSelectedRows.map((r) => r.p_refer_id); @@ -812,14 +1565,50 @@ export default { } this.refSelectorVisible = false; this.refSelectedRows = []; + this.$nextTick(() => { + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); return; } - const ref = this.$refs[this.refSelectorSource]; - if (ref) { - ref.insertAutocite(ids); - } - this.refSelectorVisible = false; - this.refSelectedRows = []; + this.ensureArticleCiteOrderIncludesIds(ids); + this.$nextTick(() => { + const ref = this.$refs[this.refSelectorSource]; + if (ref) { + ref.insertAutocite(ids); + } + this.refSelectorVisible = false; + this.refSelectedRows = []; + this.$nextTick(() => { + if (this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + 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 || ''; + this.flushReorderFromEditModal(html); + } + if (typeof this.$refs.commonContent.refreshAutociteDisplay === 'function') { + this.$refs.commonContent.refreshAutociteDisplay(); + } + } + if (this.addContentVisible && this.$refs.addContent && typeof this.$refs.addContent.refreshAutociteDisplay === 'function') { + this.$refs.addContent.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.commonTable && typeof this.$refs.commonTable.refreshAutociteDisplay === 'function') { + this.$refs.commonTable.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.tinymceChildNote && typeof this.$refs.tinymceChildNote.refreshAutociteDisplay === 'function') { + this.$refs.tinymceChildNote.refreshAutociteDisplay(); + } + if (this.pictVisible && this.$refs.tinymceChildImgNote && typeof this.$refs.tinymceChildImgNote.refreshAutociteDisplay === 'function') { + this.$refs.tinymceChildImgNote.refreshAutociteDisplay(); + } + }); + }); }, handleRemoveRefCite() { const idsToStrip = (this.refSelectedRows || []).map((r) => r.p_refer_id); @@ -840,6 +1629,12 @@ export default { } this.refSelectorVisible = false; this.refSelectedRows = []; + this.$nextTick(() => { + if (this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + this.flushReorderFromTableModal(); + this.refreshTableDrawerAutociteDisplays(); + } + }); }, ChanFerMashUp(e) { this.$api @@ -917,6 +1712,16 @@ export default { handleSaveContent() { this.$refs.commonContent.getTinymceContent('content'); }, + handleMatchBracketRefsInAddContent() { + const ref = this.$refs.addContent; + if (!ref || typeof ref.convertBracketRefsToAutocite !== 'function') return; + const { replaced } = ref.convertBracketRefsToAutocite(); + if (replaced > 0) { + this.$message.success(this.$t('wordCite.matchBracketRefsDone', { n: replaced })); + } else { + this.$message.info(this.$t('wordCite.matchBracketRefsNone')); + } + }, handleSaveAddContent() { this.$refs.addContent.getTinymceContent('addcontent'); }, @@ -1023,7 +1828,10 @@ export default { str = str.replace(//gi, ''); str = await that.$commonJS.decodeHtml(str); - + + /** 点 Save 立即按将写入的正文重排下方列表(不等待接口),与弹窗内 [n] 一致 */ + this.reorderReferencesFromMainListBody(am_id, str); + await that.$api .post(that.urlList.editContent, { am_id: am_id, @@ -1035,14 +1843,20 @@ export default { this.editVisible = false; this.refreshCurrentContent('content', am_id, res.data); this.getCommentList(); + /** 以接口回写后的 Main_List 再对齐一次;稿面 word 会由 contentList 监听触发 syncRefOrder */ + this.$nextTick(() => { + this.reorderReferencesFromMainListBody(null, null); + }); } else { loading.close(); this.$message.error(res.msg); + this.reorderReferencesFromMainListBody(null, null); } }) .catch((err) => { loading.close(); this.$message.error(err); + this.reorderReferencesFromMainListBody(null, null); }); }, async saveContentList(content, am_id) { @@ -1856,6 +2670,8 @@ export default { this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改) this.lineStyle.visiTitle = 'Edit Table'; this.threeVisible = true; + this.seedTableModalDraftFromLineStyle(); + this.afterOpenTableDrawerSyncCiteOrder(); } }, updateChange(content, type) { @@ -1868,6 +2684,9 @@ export default { } else { this.lineStyle1[type] = str; } + if (this.threeVisible && this.lineStyle && this.lineStyle.am_id != null) { + this.onTableModalEditorInput(); + } }, onEdit(dataId) { this.currentContent = {}; @@ -1903,8 +2722,11 @@ export default { }; this.lineStyle.visiTitle = 'Edit Table'; this.threeVisible = true; + this.seedTableModalDraftFromLineStyle(); + this.afterOpenTableDrawerSyncCiteOrder(); } else { data.content = data.content.replace(/]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签 + this.editModalDraftHtml = data.content || ''; this.currentContent = data; this.editVisible = true; @@ -1917,6 +2739,10 @@ export default { if (this.$refs.commonContent && this.$refs.commonContent.refreshAutociteDisplay) { this.$refs.commonContent.refreshAutociteDisplay(); } + const draft = this.currentContent && this.currentContent.content; + if (draft != null) { + this.flushReorderFromEditModal(draft); + } }); }); } @@ -2039,6 +2865,10 @@ export default { if (this.$refs.catalogue) { await this.$refs.catalogue.getCatalogueList(); } + /** 正文加载后按 Main_List 扫出全文首次出现顺序,写入 articleCiteIdOrder,弹窗角标与稿面一致 */ + this.$nextTick(() => { + this.reorderReferencesFromMainListBody(null, null); + }); loading.close(); }); // }, 1000); @@ -2223,6 +3053,109 @@ export default { this.$refs.tinymceChildComment.getContent('comment'); }); }, + /** 稿面图片说明/标题内修改/移除 mycite 后,走 editImage 写回 */ + async saveImageManuscript(payload) { + if (!payload || payload.ami_id == null) return; + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + let strNote = payload.note || ''; + let strTitle = payload.title || ''; + if (strNote !== '') { + strNote = await this.$commonJS.decodeHtml(strNote); + } + if (strTitle !== '') { + strTitle = await this.$commonJS.decodeHtml(strTitle); + } + strNote = strNote.replace(//gi, ''); + strTitle = strTitle.replace(//gi, ''); + try { + const res = await this.$api.post(this.urlList.editImage, { + ami_id: payload.ami_id, + url: payload.url || '', + note: strNote, + title: strTitle + }); + loading.close(); + if (res.code == 0) { + this.$message.success('Successfully updated.'); + if (this.$refs.commonWordHtmlTypeSetting && this.$refs.commonWordHtmlTypeSetting.refresh) { + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editImg', + res.data.image ? { ...res.data.image, has_selected: 1 } : res.data + ); + } + this.refreshCurrentContent('editImg', res.data.am_id, res.data); + this.getCommentList(); + this.$nextTick(() => { + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); + } else { + this.$message.error(res.msg); + } + } catch (err) { + loading.close(); + this.$message.error(err && err.message ? err.message : String(err)); + } + }, + /** 稿面表格单元格内修改/移除 mycite 后,走 editMainTable 写回 */ + async saveTableManuscript(payload) { + if (!payload || payload.amt_id == null) return; + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + let strTitle = payload.title || ''; + let strNote = payload.note || ''; + if (strNote !== '') { + strNote = await this.$commonJS.decodeHtml(strNote); + } + if (strTitle !== '') { + strTitle = await this.$commonJS.decodeHtml(strTitle); + } + strNote = strNote.replace(//gi, ''); + strTitle = strTitle.replace(//gi, ''); + try { + const res = await this.$api.post(this.urlList.editTable, { + amt_id: payload.amt_id, + table_data: payload.table_data, + html_data: payload.html_data || '', + note: strNote, + title: strTitle + }); + loading.close(); + if (res.code == 0) { + this.$message.success('Successfully updated.'); + if (this.$refs.commonWordHtmlTypeSetting && this.$refs.commonWordHtmlTypeSetting.refresh) { + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editTable', + res.data.table ? { ...res.data.table, has_selected: 1 } : res.data + ); + } + this.refreshCurrentContent('editTable', res.data.am_id, res.data); + this.getCommentList(); + this.$nextTick(() => { + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); + } else { + this.$message.error(res.msg); + } + } catch (err) { + loading.close(); + this.$message.error(err && err.message ? err.message : String(err)); + } + }, async saveTable(content) { const cleanTableData = (tableList) => { @@ -2857,4 +3790,48 @@ wmath[data-wrap='inline'] { display: inline-block !important; width: auto !important; } +.duplicateRefBox { + color: #606266; + margin-top: 0px; + margin-bottom: 20px; + + background: #fef0f0; + border: 1px solid #fbc4c4; + padding: 4px 10px; + font-size: 12px; + border-radius: 3px; + display: flex; + align-items: center; +} + +.text-highlight { + background-color: rgb(252, 98, 93); + background-color: rgb(252 98 93 / 68%); + color: #333; +}.status { + display: block; + width: 36px; + height: 36px; + border-radius: 36px; + font-size: 22px; + line-height: 36px; + color: #fff; + text-align: center; +} + +.status.ok { + background: #a7e389; +} + +.status.warn { + background: #ffd192; +} + +.status.float { + display: + inline-block; +} +.doiLink { + color: #409eff; +} diff --git a/src/components/page/comArtHtmlCreatNew.vue b/src/components/page/comArtHtmlCreatNew.vue index 3ae2f88..7ea11a6 100644 --- a/src/components/page/comArtHtmlCreatNew.vue +++ b/src/components/page/comArtHtmlCreatNew.vue @@ -1,41 +1,74 @@ @@ -46,10 +79,20 @@ import Tiff from 'tiff.js'; import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑 import Tinymce from '@/components/page/components/Tinymce'; import bottomTinymce from '@/components/page/components/Tinymce'; +import catalogue from '@/components/page/components/table/catalogue.vue'; +import editPublicRefTableOnly from './editPublicRefTableOnly.vue'; +import { extractAutociteIdsFromHtmlString } from '@/utils/autociteHtml.js'; export default { data() { return { + detailMes: {}, + zoomNum: (window.innerWidth * 0.38) / 850, + uploadWordTables: [], + tablesHtmlVisible: false, + tablesHtml: '', + LateXInfo: {}, isEditComment: false, + showLateX: false, comments: [], remarkImageUrl: 'https://submission.tmrjournals.com/public/usericon/20241222/4e77ba3f29ce3cf798b36f24dc411b76.png', isFirstComponentLoaded: false, @@ -61,7 +104,9 @@ export default { articleId: this.$route.query.id, isShowComment: false, urlList: { + executeProofreading: 'api/Proofread/change', delete: 'api/Preaccept/delArticleMains', + addRow: 'api/Preaccept/addBlankRow', addComment: 'api/Preaccept/createArticleMainCheckForEditor', editComment: 'api/Preaccept/editArticleMainCheck', solveComment: 'api/Preaccept/completeArticleMainCheckForAuthor', @@ -76,53 +121,55 @@ export default { setPositioningImage: 'api/Preaccept/positioningImage', removePositioningImage: 'api/Preaccept/removeImage', addImage: 'api/Preaccept/addMainImage', - editImage: 'api/Preaccept/editMainImage', addTable: 'api/Preaccept/addMainTable', - editTable: 'api/Preaccept/editMainTable' + editImage: 'api/Preaccept/editMainImage', + editTable: 'api/Preaccept/editMainTable', + deleteImage: 'api/Articlemain/removeMainImage', + deleteTable: 'api/Articlemain/removeMainTable' }, wordStyle: ` - // p { - // position: relative; - // padding: 8px 15px; - // min-height: 22px; - // border: 2px dashed #fff; - // border-radius: 5px; - // color: #606266; - // } - // p { - // font-size: 14px; - // line-height: 22px; - // } + // p { + // position: relative; + // padding: 8px 15px; + // min-height: 22px; + // border: 2px dashed #fff; + // border-radius: 5px; + // color: #606266; + // } + // p { + // font-size: 14px; + // line-height: 22px; + // } - .imgBox .chNumer { - position: absolute; - top: -2px; - right: -1px; - border-radius: 3px; - font-size: 10px; - background-color: rgb(0 102 153 / 85%); - color: #fff; - padding: 0 6px; - } + .imgBox .chNumer { + position: absolute; + top: -2px; + right: -1px; + border-radius: 3px; + font-size: 10px; + background-color: rgb(0 102 153 / 85%); + color: #fff; + padding: 0 6px; + } - .MaxPicture { - text-align: center; - } + .MaxPicture { + text-align: center; + } - .MaxPicture > img { - margin-bottom: 10px; - } + .MaxPicture > img { + margin-bottom: 10px; + } - .font { - display: block; - margin: 0 auto; - font-size: 13px; - } - .tableTitle{ - text-align:center!important; - font-weight: bold; - } - `, + .font { + display: block; + margin: 0 auto; + font-size: 13px; + } + .tableTitle{ + text-align:center!important; + font-weight: bold; + } + `, tables: [], htmlContent: '', @@ -142,31 +189,152 @@ export default { ChGtpTxt: '' }, txtVisible: false, + addContentVisible: false, + editProofreadingContentVisible: false, + proofreadingContent: {}, + addContent: {}, lineStyle: {}, - detailMes: {}, + lineStyle1: {}, contentStyle: {}, lineTable: [], threeVisible: false, picStyle: {}, + picStyle1: {}, commentForm: {}, commentVisible: false, pictVisible: false, typesettingType: 1, imagesList: [], - exegesis: "The following contents','are necessary for the generation phase, please do not delete them!!!" + exegesis: "The following contents','are necessary for the generation phase, please do not delete them!!!", + p_article_id: null, + chanFerForm: [], + chanFerFormRepeatList: [], + /** 全文 mycite 首次出现顺序(与稿面 common-word 一致),供 Edit Content 弹窗内 TinyMCE 显示 [1][2] 与重排列表 */ + articleCiteIdOrder: [], + /** Edit Content 弹窗内当前 HTML(与保存合并逻辑一致),用于实时算出与稿面相同的 [n],避免仅依赖防抖后的 articleCiteIdOrder */ + editModalDraftHtml: '', + refSelectorVisible: false, + refSelectorIsEdit: false, + refSelectedRows: [], + refSelectorSource: 'commonContent', + /** 打开选择器时若带 currentRefIds(编辑已有引用),排序用「传值计算的顺序」;关闭后清空 */ + refSelectorContextIds: [], + /** 选择参考文献弹窗:按 # 序号快速勾选,如 [5, 6, 10-15] */ + refSelectorQuickPick: '' }; }, components: { Tinymce, - bottomTinymce + bottomTinymce, + catalogue, + editPublicRefTableOnly }, computed: { - combinedValue() { - // 将两个值组合成一个新的值,可以是字符串、数组、对象等 - // return `${this.isFirstComponentLoaded}-${this.isWordComponentLoaded}`; + /** 选择器打开时:无传值用 articleCiteIdOrder;有传值且 Edit Content 打开时用合并稿 editModalBodyCiteOrder,与 # 列、正文 [n] 一致 */ + refSelectorOrderForPreview() { + if (!this.refSelectorVisible) { + return Array.isArray(this.articleCiteIdOrder) ? this.articleCiteIdOrder : []; + } + const hasCtx = Array.isArray(this.refSelectorContextIds) && this.refSelectorContextIds.length > 0; + if (hasCtx && this.editVisible) { + const o = this.editModalBodyCiteOrder; + return Array.isArray(o) && o.length > 0 ? o : this.articleCiteIdOrder || []; + } + return Array.isArray(this.articleCiteIdOrder) ? this.articleCiteIdOrder : []; + }, + /** 选择器内表格行顺序:默认按正文 articleCiteIdOrder;有传值且处于编辑弹窗时按合并稿计算顺序 */ + refSelectorTableData() { + const refs = Array.isArray(this.chanFerForm) ? [...this.chanFerForm] : []; + if (!refs.length) return []; + let order; + const hasCtx = Array.isArray(this.refSelectorContextIds) && this.refSelectorContextIds.length > 0; + if (hasCtx && this.editVisible) { + order = this.editModalBodyCiteOrder; + } else { + order = this.articleCiteIdOrder; + } + if (!Array.isArray(order) || order.length === 0) return refs; + return this.sortChanFerRowsByOrder(refs, order); + }, + /** 与正文 mycite 一致:只展示已在列表中且有序号的文献,不显示「?」 */ + refPreviewLabel() { + const refList = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const refMap = refList.reduce((acc, item) => { + const key = item && item.p_refer_id != null ? String(item.p_refer_id) : ''; + if (key) acc[key] = item; + return acc; + }, {}); + + const order = (this.refSelectorVisible ? this.refSelectorOrderForPreview : this.articleCiteIdOrder) || []; + const citeMap = this.buildDisplayCiteMap(order); + + const ctx = Array.isArray(this.refSelectorContextIds) ? this.refSelectorContextIds.map(String) : []; + const selectedIds = (this.refSelectedRows || []) + .map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')) + .filter(Boolean); + + let idsForPreview = []; + if (this.refSelectorVisible && ctx.length > 0) { + const hasMissingInCtx = ctx.some((id) => !refMap[id]); + if (hasMissingInCtx) { + idsForPreview = ctx; + } else if (selectedIds.length > 0) { + idsForPreview = selectedIds; + } else { + idsForPreview = ctx; + } + } else if (selectedIds.length > 0) { + idsForPreview = selectedIds; + } + + if (!idsForPreview.length) return ''; + + const sortedIds = this.sortAutociteIdsByCiteNumber(idsForPreview, order); + const parts = sortedIds.map((id) => { + const ref = refMap[id]; + const no = ref ? citeMap[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); + + if (numsForLabel.length === 0) return ''; + return this.formatCiteNumbers(numsForLabel); + }, + catalogueContent() { + const base = Array.isArray(this.Main_List) ? this.Main_List : []; + if (!Array.isArray(this.chanFerForm) || this.chanFerForm.length === 0) return base; + return [ + ...base, + { + am_id: 'References', + content: 'References', + is_h1: 1, + is_h2: 0 + } + ]; + }, + /** 弹窗内引用角标:按 Main_List + 当前草稿段合并后的全文首次出现顺序,与保存后一致 */ + editModalBodyCiteOrder() { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) { + return this.articleCiteIdOrder; + } + const draft = this.editModalDraftHtml; + if (draft == null || draft === '') { + return this.articleCiteIdOrder; + } + const order = this.extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, draft); + return order.length > 0 ? order : this.articleCiteIdOrder; } }, watch: { + editVisible(val) { + if (!val) this.editModalDraftHtml = ''; + }, // 监听计算属性 // combinedValue(newVal, oldVal) { // console.log('value1 或 value2 发生变化'); @@ -187,20 +355,40 @@ export default { } } }, - created() { + async created() { localStorage.removeItem('scrollPosition'); - - this.getInfo(); - - // this.loadDictionary().catch(console.error); + this.isShowEditComment(); + + await this.getInfo(); + }, - mounted() {}, - activated() { - this.getInfo(); + mounted() { + document.addEventListener('copy', (event) => { + // 获取用户选中的文本 + const selection = document.getSelection().toString(); + + // 你可以修改剪贴板内容 + // event.clipboardData.setData('text/plain', selection + '\n---来自我的系统---'); + + console.log('用户复制了内容:', selection); + // 阻止默认行为(如果需要自定义复制逻辑的话) + // event.preventDefault(); + }); + }, + async activated() { + this.isShowEditComment(); + + await this.getInfo(); + + }, + beforeDestroy() { + if (this._editModalCiteTimer) { + clearTimeout(this._editModalCiteTimer); + this._editModalCiteTimer = null; + } }, methods: { - goEdit() { window.open(this.$router.resolve({ path: '/GenerateCharts', query: { @@ -222,6 +410,8 @@ export default { if (res.code == 0) { this.detailTitle = res.data.production.title; this.detailMes = res.data.production; + await this.loadPreacceptArticleDetail(); + await this.fetchReferList(); await this.getDate(); await this.getCommentList(); @@ -236,6 +426,530 @@ export default { loading.close(); }); }, + loadPreacceptArticleDetail() { + // if (!this.articleId) return Promise.resolve(); + // return this.$api + // .post('api/Article/getPreacceptArticleDetail', { article_id: this.articleId }) + // .then((res) => { + + this.p_article_id = this.Art_Id; + // if (pid != null && pid !== '') { + // if (!this.Art_P_Id) this.Art_P_Id = pid; + this.fetchReferList(); + // } + // }) + // .catch(() => {}); + }, + fetchReferList() { + if (!this.p_article_id) return Promise.resolve(); + return this.$api + .post('api/Production/getReferList', { + p_article_id: this.p_article_id + }) + .then((res) => { + this.chanFerForm = res.data.refers; + this.chanFerFormRepeatList = Object.values(res.data.repeat || {}); + for (let i = 0; i < this.chanFerForm.length; i++) { + this.chanFerForm[i].edit_mark = 1; + } + this.$nextTick(() => { + 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(); + } + if (this.threeVisible && this.$refs.commonTable && this.$refs.commonTable.refreshAutociteDisplay) { + this.$refs.commonTable.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.tinymceChildNote && this.$refs.tinymceChildNote.refreshAutociteDisplay) { + this.$refs.tinymceChildNote.refreshAutociteDisplay(); + } + if (this.pictVisible && this.$refs.tinymceChildImgNote && this.$refs.tinymceChildImgNote.refreshAutociteDisplay) { + this.$refs.tinymceChildImgNote.refreshAutociteDisplay(); + } + /** getReferList 会整表覆盖 chanFerForm,需再按正文首次出现顺序对齐,否则下方列表与 [n] 脱节 */ + this.applyRefOrderAfterFetchReferList(); + }); + }) + .catch((err) => { + console.log(err); + }); + }, + /** 参考文献列表接口返回后:弹窗内按合并稿重排;稿面由 word 同步正文顺序 */ + 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) || ''; + } + } + this.flushReorderFromEditModal(html); + return; + } + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }, + /** + * 按 Main_List 各段正文中 mycite 首次出现顺序重排参考文献。 + * draftAmId + draftHtml:合并「当前将保存的这一段」后再扫全文(点 Save 时与保存后一致); + * 传 (null, null) 表示完全以 Main_List 已存 content 为准。 + */ + reorderReferencesFromMainListBody(draftAmId, draftHtml) { + const order = this.extractAutociteOrderFromMainList(this.Main_List, draftAmId, draftHtml); + if (order.length > 0) { + this.handleReorderReferencesByBody(order); + } + }, + handleOpenRefSelectorFromManuscript(data) { + this.handleOpenRefSelector(data, 'manuscriptAutocite'); + }, + onRefSelectorDialogClosed() { + this.refSelectorContextIds = []; + this.refSelectorQuickPick = ''; + }, + /** 仅排序展示用,不修改 chanFerForm(逻辑与 handleReorderReferencesByBody 一致) */ + sortChanFerRowsByOrder(refs, orderedIds) { + if (!Array.isArray(orderedIds) || orderedIds.length === 0) return refs; + const ids = orderedIds.map(String); + const idSet = new Set(ids); + const byId = {}; + refs.forEach((r) => { + if (r && r.p_refer_id != null) byId[String(r.p_refer_id)] = r; + }); + const next = []; + ids.forEach((id) => { + const row = byId[id]; + if (row) next.push(row); + }); + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const id = String(r.p_refer_id); + if (!idSet.has(id)) next.push(r); + }); + if (refs.length > 0 && next.length === 0) return refs; + return next; + }, + /** + * 与 word.vue citeMap:正文顺序中仅「当前参考文献列表里存在」的 id 依次占号,已删除/不存在的 id 不占位 + */ + buildDisplayCiteMap(orderArray) { + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const refIdSet = new Set( + refs.map((r) => (r && r.p_refer_id != null ? String(r.p_refer_id) : '')).filter(Boolean) + ); + const map = {}; + if (!Array.isArray(orderArray) || orderArray.length === 0) { + refs.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 = []; + orderArray.forEach((id) => { + const k = String(id); + if (!k || !refIdSet.has(k)) return; + if (filtered.includes(k)) return; + filtered.push(k); + }); + filtered.forEach((id, idx) => { + map[id] = idx + 1; + }); + let next = filtered.length + 1; + refs.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; + }, + /** 与 word.vue sortAutociteIdsByCiteNumber:排序键使用 buildDisplayCiteMap */ + sortAutociteIdsByCiteNumber(ids, orderArray) { + const uniq = [...new Set((ids || []).map(String))]; + const map = this.buildDisplayCiteMap(orderArray); + 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)); + }); + }, + /** 与 word.vue:连续全局序号合并为 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(', '); + }, + /** 正文 mycite 顺序为唯一依据:按首次出现顺序重排 chanFerForm,未在正文出现的条目排在末尾 */ + handleReorderReferencesByBody(orderedIds) { + if (!Array.isArray(orderedIds) || orderedIds.length === 0) return; + const ids = orderedIds.map(String); + const prevOrder = this.articleCiteIdOrder || []; + const sameOrder = + ids.length === prevOrder.length && ids.every((id, i) => String(id) === String(prevOrder[i])); + if (!sameOrder) { + this.articleCiteIdOrder = ids.slice(); + } + const idSet = new Set(ids); + const refs = Array.isArray(this.chanFerForm) ? [...this.chanFerForm] : []; + const byId = {}; + refs.forEach((r) => { + if (r && r.p_refer_id != null) byId[String(r.p_refer_id)] = r; + }); + const next = []; + ids.forEach((id) => { + const row = byId[id]; + if (row) next.push(row); + }); + refs.forEach((r) => { + if (!r || r.p_refer_id == null) return; + const id = String(r.p_refer_id); + if (!idSet.has(id)) next.push(r); + }); + /** + * 若正文已出现引用 id,但列表尚未加载或 id 与 byId 对不上,会出现 next 为空而 refs 非空 + * (正文 id 全在 idSet 内,第二轮被跳过),此时切勿 chanFerForm=[],否则参考文献整块消失。 + */ + if (refs.length > 0 && next.length === 0) { + return; + } + const sig = (arr) => arr.map((r) => String(r.p_refer_id)).join('\0'); + if (sig(next) === sig(refs)) return; + this.chanFerForm = next; + }, + /** + * 单条 Main_List 段内、与稿面阅读顺序一致的 HTML 片段(用于统计 mycite 顺序)。 + * 表格段:标题 → 表体(html_data 或逐单元格 text)→ Note;图段:标题 → 说明。 + */ + collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml) { + if (draftAmId != null && p && p.am_id == draftAmId && draftHtml != null) { + return [draftHtml]; + } + const out = []; + const typ = p != null && p.type != null ? Number(p.type) : NaN; + if (typ === 2 && p.table) { + const t = p.table; + if (typeof t.title === 'string' && t.title.trim()) out.push(t.title); + if (typeof t.html_data === 'string' && t.html_data.trim()) { + out.push(t.html_data); + } else { + const pushRows = (rows) => { + if (!Array.isArray(rows)) return; + rows.forEach((row) => { + if (!Array.isArray(row)) return; + row.forEach((cell) => { + if (cell && typeof cell.text === 'string' && cell.text) out.push(cell.text); + }); + }); + }; + pushRows(t.tableHeader); + pushRows(t.tableContent); + } + if (typeof t.note === 'string' && t.note.trim()) out.push(t.note); + return out; + } + if (typ === 1 && p.image) { + const img = p.image; + if (typeof img.title === 'string' && img.title.trim()) out.push(img.title); + if (typeof img.note === 'string' && img.note.trim()) out.push(img.note); + if (out.length === 0) { + if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content); + if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text); + } + return out; + } + if (p && typeof p.text === 'string' && p.text.trim()) out.push(p.text); + if (p && typeof p.content === 'string' && p.content.trim()) out.push(p.content); + return out; + }, + /** 与 word.vue 一致:从各段 HTML 中按出现顺序收集 autocite(含表格单元格、表题、表注、图题图注) */ + extractAutociteOrderFromMainList(mainList, draftAmId, draftHtml) { + const list = Array.isArray(mainList) ? mainList : []; + const order = []; + list.forEach((p) => { + const candidates = this.collectCiteHtmlSourcesForMainListItem(p, draftAmId, draftHtml); + candidates.forEach((raw) => { + extractAutociteIdsFromHtmlString(raw).forEach((id) => { + if (!order.includes(id)) order.push(id); + }); + }); + }); + return order; + }, + /** 编辑弹窗内正文一变立即按全文合并稿重排参考文献(不再防抖,选中/输入后列表马上跟正文一致) */ + scheduleReorderFromEditModal(html) { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return; + this.$nextTick(() => { + this.flushReorderFromEditModal(html); + }); + }, + /** 立即按全文合并稿重排参考文献顺序(插入引用后调用,避免编号长期停留在列表序号 [8]) */ + flushReorderFromEditModal(html) { + if (!this.editVisible || !this.currentContent || this.currentContent.am_id == null) return; + let order = this.extractAutociteOrderFromMainList(this.Main_List, this.currentContent.am_id, html); + if (order.length === 0 && html && /mycite/i.test(html)) { + order = extractAutociteIdsFromHtmlString(html); + } + if (order.length > 0) { + this.handleReorderReferencesByBody(order); + } + }, + /** + * 插入引用前先把新 p_refer_id 并入 articleCiteIdOrder,避免子组件 citeMap 缺键显示 [?]; + * 随后 flush 会按全文首次出现顺序覆盖为准确值。 + */ + ensureArticleCiteOrderIncludesIds(newIds) { + const add = (newIds || []).map(String).filter(Boolean); + if (!add.length) return; + let order = Array.isArray(this.articleCiteIdOrder) ? [...this.articleCiteIdOrder.map(String)] : []; + add.forEach((id) => { + if (!order.includes(id)) order.push(id); + }); + this.articleCiteIdOrder = order; + }, + onEditModalEditorInput(html) { + this.editModalDraftHtml = html || ''; + this.flushReorderFromEditModal(html); + }, + handleOpenRefSelector(data, sourceOverride) { + const currentIds = data && Array.isArray(data.currentRefIds) ? data.currentRefIds : []; + this.refSelectorContextIds = currentIds.map(String); + this.refSelectorIsEdit = currentIds.length > 0; + this.refSelectedRows = []; + if (sourceOverride === 'manuscriptAutocite' || (data && data.source === 'manuscript')) { + this.refSelectorSource = 'manuscriptAutocite'; + } else if ( + typeof sourceOverride === 'string' && + sourceOverride && + (sourceOverride === 'tinymceChildNote' || sourceOverride === 'tinymceChildImgNote') && + this.$refs[sourceOverride] + ) { + /** 表格 Note / 图片说明 独立编辑器,勿与 commonTable 混淆 */ + this.refSelectorSource = sourceOverride; + } else if (this.threeVisible) { + /** Edit Table 主表格区 */ + this.refSelectorSource = 'commonTable'; + } 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); + } + }); + } + } + }); + }, + handleRefSelectionChange(rows) { + this.refSelectedRows = rows; + }, + parseQuickPickNumbers(input) { + const s = String(input || '').trim(); + if (!s) return []; + let t = s.replace(/^\s*\[/, '').replace(/\]\s*$/, ''); + t = t.replace(/[,]/g, ','); + const parts = t.split(',').map((x) => x.trim()).filter(Boolean); + const out = new Set(); + parts.forEach((p) => { + const m = p.match(/^(\d+)\s*[-–—]\s*(\d+)$/); + if (m) { + const a = Number(m[1]); + const b = Number(m[2]); + if (!Number.isNaN(a) && !Number.isNaN(b)) { + const lo = Math.min(a, b); + const hi = Math.max(a, b); + for (let i = lo; i <= hi; i++) out.add(i); + } + return; + } + const n = parseInt(p, 10); + if (!Number.isNaN(n)) out.add(n); + }); + return Array.from(out).sort((a, b) => a - b); + }, + clearRefSelectorSelection() { + const table = this.$refs.refSelectorTable; + if (table && typeof table.clearSelection === 'function') { + table.clearSelection(); + } + this.refSelectedRows = []; + }, + applyRefSelectorQuickPick() { + const table = this.$refs.refSelectorTable; + if (!table) return; + const nums = this.parseQuickPickNumbers(this.refSelectorQuickPick); + if (!nums.length) { + this.clearRefSelectorSelection(); + return; + } + table.clearSelection(); + const data = Array.isArray(this.refSelectorTableData) ? this.refSelectorTableData : []; + nums.forEach((n) => { + const idx = n - 1; + if (idx < 0 || idx >= data.length) return; + table.toggleRowSelection(data[idx], true); + }); + }, + /** 点击整行切换勾选(与点复选框一致);点链接或点复选框本身时不处理,避免重复或误触 */ + handleRefSelectorRowClick(row, column, event) { + if (column && column.type === 'selection') return; + if (event && event.target && typeof event.target.closest === 'function') { + if (event.target.closest('.el-checkbox') || event.target.closest('a[href]')) return; + } + const table = this.$refs.refSelectorTable; + if (!table || !row) return; + const isSelected = (this.refSelectedRows || []).some( + (r) => r && String(r.p_refer_id) === String(row.p_refer_id) + ); + table.toggleRowSelection(row, !isSelected); + }, + handleConfirmRefCite() { + 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 = []; + this.$nextTick(() => { + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); + return; + } + this.ensureArticleCiteOrderIncludesIds(ids); + this.$nextTick(() => { + const ref = this.$refs[this.refSelectorSource]; + if (ref) { + ref.insertAutocite(ids); + } + this.refSelectorVisible = false; + this.refSelectedRows = []; + this.$nextTick(() => { + 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 || ''; + this.flushReorderFromEditModal(html); + } + if (typeof this.$refs.commonContent.refreshAutociteDisplay === 'function') { + this.$refs.commonContent.refreshAutociteDisplay(); + } + } + if (this.addContentVisible && this.$refs.addContent && typeof this.$refs.addContent.refreshAutociteDisplay === 'function') { + this.$refs.addContent.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.commonTable && typeof this.$refs.commonTable.refreshAutociteDisplay === 'function') { + this.$refs.commonTable.refreshAutociteDisplay(); + } + if (this.threeVisible && this.$refs.tinymceChildNote && typeof this.$refs.tinymceChildNote.refreshAutociteDisplay === 'function') { + this.$refs.tinymceChildNote.refreshAutociteDisplay(); + } + if (this.pictVisible && this.$refs.tinymceChildImgNote && typeof this.$refs.tinymceChildImgNote.refreshAutociteDisplay === 'function') { + this.$refs.tinymceChildImgNote.refreshAutociteDisplay(); + } + }); + }); + }, + 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 && typeof ref.stripAutociteIds === 'function') { + ref.stripAutociteIds(idsToStrip); + } else if (ref) { + ref.removeAutocite(); + } + this.refSelectorVisible = false; + this.refSelectedRows = []; + }, + ChanFerMashUp(e) { + this.$api + .post('api/Production/referHB', e) + .then((res) => { + if (res.code == 0) { + this.fetchReferList(); + } else { + this.$message.error(res.msg); + } + }) + .catch((err) => { + this.$message.error(err); + }); + }, + openAddTable(content) { + this.editVisible = false; + this.threeVisible = true; + this.$forceUpdate(); + }, + async copyArray(data) { + try { + // 将数组内容转换为字符串,使用换行符分隔 + const textToCopy = JSON.stringify(data); + + // 使用 Clipboard API 复制文本 + await navigator.clipboard.writeText(textToCopy); + alert('数组内容已复制到剪贴板!'); + } catch (err) { + console.error('复制失败:', err); + alert('复制失败,请重试!'); + } + }, + openLatexEditor(data) { + this.showLateX = true; + this.LateXInfo = data; + }, isShowEditComment() { if (localStorage.getItem('U_role')) { var identity = localStorage.getItem('U_role'); @@ -249,6 +963,11 @@ export default { loadedWord() { this.isWordComponentLoaded = true; + this.$nextTick(() => { + if (this.$refs.editPublicRefTableOnly) { + this.$refs.editPublicRefTableOnly.init(); + } + }); }, // 监听第一个兄弟组件加载完毕 // onFirstComponentLoaded(imagesList) { @@ -271,21 +990,29 @@ export default { handleSaveContent() { this.$refs.commonContent.getTinymceContent('content'); }, + handleMatchBracketRefsInAddContent() { + const ref = this.$refs.addContent; + if (!ref || typeof ref.convertBracketRefsToAutocite !== 'function') return; + const { replaced } = ref.convertBracketRefsToAutocite(); + if (replaced > 0) { + this.$message.success(this.$t('wordCite.matchBracketRefsDone', { n: replaced })); + } else { + this.$message.info(this.$t('wordCite.matchBracketRefsNone')); + } + }, + handleSaveAddContent() { + this.$refs.addContent.getTinymceContent('addcontent'); + }, + handleSaveEditProofreadingContent() { + this.$refs.addContent.getTinymceContent('addcontent'); + }, async getContent(type, content) { if (type == 'content') { - // 使用正则表达式移除所有不允许的标签 - // 1. 移除不允许的标签 - content = content.replace(/<(?!\/?(img|b|i|sub|sup|span|strong|em |blue)\b)[^>]+>/g, ''); - - // 2. 移除所有 style 属性 - content = content.replace(/\s*style="[^"]*"/g, ''); - - // 3. 将 转换为 转换为 + content = this.$commonJS.transformHtmlString(content); var div = document.createElement('div'); div.innerHTML = content; // 将 HTML 字符串加载到 div 中 - // 替换所有 var strongTags = div.getElementsByTagName('strong'); for (var i = 0; i < strongTags.length; i++) { @@ -293,7 +1020,6 @@ export default { bTag.innerHTML = strongTags[i].innerHTML; // 保留内容 strongTags[i].parentNode.replaceChild(bTag, strongTags[i]); } - // 替换所有 var emTags = div.getElementsByTagName('em'); for (var i = 0; i < emTags.length; i++) { @@ -304,26 +1030,164 @@ export default { // 获取最终修改后的 HTML content = div.innerHTML; - console.log('content at line 486:', content); - - // // 4. 去除多余的空格:替换连续的空格、换行符、制表符等 - // content = content.replace(/\s+/g, ' ').trim(); // 将多个空白字符替换为一个空格,并去除前后空白 - - // // 5. 去除标签之间的空格 - // content = content.replace(/>\s+<'); // 去除标签之间的空格 - - // 6. 如果需要,还可以去除 标签内部的空格 - // content = content.replace(/]*>\s*([^<]+)\s*<\/span>/g, '$1'); // 清理 标签内部的空格 - + this.saveContent(content, this.currentContent.am_id); + } else if (type == 'addcontent') { + var hasTable = /[\s\S]*?<\/table>/i.test(content); + + if (hasTable) { + this.$message({ + type: 'warning', + message: 'Table content is not supported!' + }); + return false; + } + var list = this.$commonJS.cleanAndParseWordContent(content); + + + this.saveContentList(list, this.currentId); } else if (type == 'table') { this.saveTable(content); } else if (type == 'comment') { this.addComment(content); } }, + handleUnbindLink(data) { + const { label, mainId, index, content } = data; + + const unwrapTag = (str) => { + return str.replace(/<(myfigure|mytable)[^>]*>([\s\S]*?)<\/\1>/gi, '$2'); + }; + + // 2. 执行替换 + // 我们只针对传入的这个特定的 label 进行剥壳 + const strippedLabel = unwrapTag(label); + + // 3. 将 content 中的原标签替换为剥壳后的文字 + // 使用 split/join 或者是精准 replace,确保只替换这一处 + const newContent = content.replace(label, strippedLabel); + this.saveContent(newContent, mainId); + }, + handleConfirmLink(selectedMedia) { + const targetId = selectedMedia.select.amt_id || selectedMedia.select.ami_id; + const type = selectedMedia.type; + const tagName = `my${type}`; + let originalContent = selectedMedia.linkData.content; + const label = selectedMedia.linkData.label; + if (!label || !originalContent) return; + const isAlreadyTagged = label.startsWith(''); + let newContent = ''; + if (isAlreadyTagged) { + const updatedTag = label + .replace(/$/, ``); + + newContent = originalContent.replace(label, updatedTag); + } else { + const replacement = `<${tagName} data-id="${targetId}">${label}`; + newContent = originalContent.replace(label, replacement); + } + this.saveContent(newContent, selectedMedia.linkData.mainId); + }, + + async saveContent(content, am_id) { + + + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + var that = this; + var str = content.replace(/^

\s*(.*?)\s*<\/p>$/, '$1').trim(); + + str = str.replace(//gi, ''); + + str = await that.$commonJS.decodeHtml(str); + + /** 点 Save 立即按将写入的正文重排下方列表(不等待接口),与弹窗内 [n] 一致 */ + this.reorderReferencesFromMainListBody(am_id, str); + + await that.$api + .post(that.urlList.editContent, { + am_id: am_id, + content: str + }) + .then(async (res) => { + if (res.code == 0) { + loading.close(); + this.editVisible = false; + this.refreshCurrentContent('content', am_id, res.data); + this.getCommentList(); + /** 以接口回写后的 Main_List 再对齐一次;稿面 word 会由 contentList 监听触发 syncRefOrder */ + this.$nextTick(() => { + this.reorderReferencesFromMainListBody(null, null); + }); + } else { + loading.close(); + this.$message.error(res.msg); + this.reorderReferencesFromMainListBody(null, null); + } + }) + .catch((err) => { + loading.close(); + this.$message.error(err); + this.reorderReferencesFromMainListBody(null, null); + }); + }, + async saveContentList(content, am_id) { + if (content.length == 0) { + this.$message({ + type: 'warning', + message: 'Please enter the content!' + }); + } + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + + content = content.map((str) => { + // 逻辑:去掉所有换行符后,如果剩下的内容是空的 + if (str.replace(//gi, '').trim() === '') { + return ''; // 变成真正的空字符串 + } + return str; // 如果有文字,保留原样 + }); + await this.$api + .post('api/Preaccept/addMoreRow', { + article_id: this.articleId, + am_id: am_id, + rows: content + }) + .then(async (res) => { + if (res.code == 0) { + loading.close(); + this.addContentVisible = false; + this.refreshCurrentContent('addMoreRow', am_id, res.data); + this.getCommentList(); + } else { + loading.close(); + this.$message.error(res.msg); + } + }) + .catch((err) => { + loading.close(); + this.$message.error(err); + }); + }, + isHeaderRow(rowIndex, table) { + var table = table; + + var head = table[0]; + + return rowIndex < head[0].rowspan; // 假设前两行是表头 + }, deleteComment(comment, index) { - console.log('comment at line 480:', comment); if (this.isEditComment) { this.$confirm(this.$t('commonTable.removeAnnotations'), 'Prompt', { confirmButtonText: 'Submit', @@ -348,27 +1212,45 @@ export default { .catch(() => {}); } }, - async saveContent(content, am_id) { - var that = this; - var str = content.replace(/^

(.*?)<\/p>$/, '$1') ? content.replace(/^

(.*?)<\/p>$/, '$1') : ''; - if (str == '') { - this.$message({ - type: 'warning', - message: 'Please enter the content!' - }); - } - await that.$api - .post(that.urlList.editContent, { - am_id: am_id, - content: str - }) - .then(async (res) => { - if (res.code == 0) { - this.editVisible = false; - this.getDate(); - this.getCommentList(); + saveLateX(data) { + + // 1. 从 data 中解构出 wrap (或者你命名的模式变量) + const { editorId, wmathId, latex, wrap } = data; + const newLatex = latex ? latex.trim() : ''; + + if (!editorId || !wmathId) return; + + const targetEditor = tinymce.get(editorId); + if (!targetEditor) return; + + // 2. 找到编辑器中现有的 wmath 标签 + const targetWmath = targetEditor.dom.select(`wmath[data-id="${wmathId}"]`, targetEditor.getBody())[0]; + + if (targetWmath) { + if (!newLatex) { + // ❌ 删除公式 + targetEditor.dom.remove(targetWmath); + } else { + // ✅ 更新公式 + // 保持属性纯净:同时更新 latex 内容和 wrap 属性 + targetWmath.setAttribute('data-latex', newLatex); + + // 如果 data 中传了 wrap 模式就更新它,否则可以保留原样或设为默认 + if (wrap) { + targetWmath.setAttribute('data-wrap', wrap); } - }); + + // 内部只放纯 latex 文本,不包裹 $ 符号 + targetWmath.innerHTML = newLatex; + + setTimeout(() => { + if (typeof renderMathJax === 'function') { + // 重新渲染该编辑器内的数学公式 + renderMathJax(editorId); + } + }, 10); + } + } }, async huifu(id) { var that = this; @@ -415,8 +1297,10 @@ export default { var am_id; if (type == 'img') { am_id = this.Main_List.find((item) => item.ami_id == id).am_id; - } else { + } else if (type == 'table') { am_id = this.Main_List.find((item) => item.amt_id == id).am_id; + } else { + am_id = id; } if (am_id) { this.goToComment(am_id); @@ -441,7 +1325,9 @@ export default { } }, async onDelete(dataId) { + var that = this; var dataInfo = this.Main_List.find((item) => item.am_id == dataId); + var dataIndex = this.Main_List.indexOf(dataInfo); var type = ''; if (dataInfo.type == 1) { @@ -451,7 +1337,72 @@ export default { } else if (dataInfo.type == 0) { type = 'content'; } + var url = ''; + switch (type) { + case 'table': + url = that.urlList.removePositioningTable; + break; + case 'img': + url = that.urlList.removePositioningImage; + break; + case 'content': + url = that.urlList.delete; + break; + } + if (dataInfo.type == 0 && dataInfo.content == '') { + await that.$api + .post(url, { + am_id: dataId + }) + .then(async (res) => { + if (res.code == 0) { + setTimeout(() => { + that.Main_List.splice(dataIndex, 1); + that.getCommentList(); + that.$forceUpdate(); + }); + } else { + this.$message.error(res.msg); + } + }) + .catch((err) => { + this.$message.error(err); + }); + } else { + await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', { + confirmButtonText: 'Submit', + cancelButtonText: 'Cancel', + type: 'warning' + }) + .then(async () => { + await that.$api + .post(url, { + am_id: dataId + }) + .then(async (res) => { + if (res.code == 0) { + setTimeout(() => { + that.Main_List.splice(dataIndex, 1); + that.$forceUpdate(); + if (type == 'img') { + that.$refs.commonWordHtmlTypeSetting.refresh('removeImg', { ami_id: dataInfo.ami_id }); + } else if (type == 'table') { + that.$refs.commonWordHtmlTypeSetting.refresh('removeTable', { amt_id: dataInfo.amt_id }); + } + that.getCommentList(); + + that.$forceUpdate(); + }); + } + }); + }) + .catch((err) => { + console.log('err at line 466:', err); + }); + } + }, + async onDeletes(dataId) { await this.$confirm(this.$t('commonTable.removeContent'), 'Prompt', { confirmButtonText: 'Submit', cancelButtonText: 'Cancel', @@ -459,32 +1410,16 @@ export default { }) .then(async () => { var that = this; - var url = ''; - switch (type) { - case 'table': - url = that.urlList.removePositioningTable; - break; - case 'img': - url = that.urlList.removePositioningImage; - break; - case 'content': - url = that.urlList.delete; - break; - } await that.$api - .post(url, { - am_id: dataId + .post('/api/Preaccept/delMoreArticleMains', { + ids: dataId }) .then(async (res) => { if (res.code == 0) { setTimeout(() => { that.getDate(); that.getCommentList(); - if (type == 'img') { - that.$refs.commonWordHtmlTypeSetting.refresh('img'); - } else { - that.$refs.commonWordHtmlTypeSetting.refresh('table'); - } + that.$refs.commonWordHtmlTypeSetting.reload(); that.$forceUpdate(); }); @@ -499,8 +1434,42 @@ export default { console.log('err at line 466:', err); }); }, + async changeSort(type, id) { + var that = this; + const index = this.Main_List.findIndex((item) => item.am_id == id); + if (type == 'up' && index == 0) { + return; + } + if (type == 'down' && index == this.Main_List.length - 1) { + return; + } + const load = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + await that.$api + .post(type == 'up' ? '/api/Preaccept/upArticleMain' : '/api/Preaccept/downArticleMain', { + am_id: id + }) + .then(async (res) => { + if (res.code == 0) { + load.close(); + await this.refreshCurrentContent(`${type}ArticleMain`, id); + that.$forceUpdate(); + } else { + load.close(); + this.$message.error(res.msg); + } + }) + .catch((err) => { + load.close(); + this.$message.error(err); + }); + }, async addCommentSetting(content) { - console.log('content at line 602:', content); + await this.$api .post(this.urlList.addComment, { am_id: content.am_id, @@ -523,8 +1492,10 @@ export default { }); }, async addComment(content) { - console.log('content at line 603:', this.commentForm); - var str = content.replace(/^

(.*?)<\/p>$/, '$1') ? content.replace(/^

(.*?)<\/p>$/, '$1') : ''; + + var str= this.$commonJS.transformHtmlString(content) + str=str.replace(//gi, ''); + console.log("🚀 ~ addComment ~ content:", str); if (str == '') { this.$message({ type: 'warning', @@ -606,13 +1577,13 @@ export default { this.getCommentList(); }, editComment(comment, type) { - console.log('comment at line 813:', comment); + this.commentForm = { ...comment, type: type, remark: type == 'user' ? comment.author_remark : comment.remark }; - console.log('this.commentForm at line 815:', this.commentForm); + this.commentVisible = true; }, async onAddComment(data) { @@ -635,8 +1606,17 @@ export default { this.commentVisible = true; }, async onEditTitle(data) { + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); var url; switch (data.value) { + case 0: + url = 'api/Preaccept/changeNormal'; + break; case 1: url = 'api/Preaccept/changeH1'; break; @@ -648,14 +1628,135 @@ export default { break; } - await this.$api .post(url, { am_id: data.mainId }) .then(async (res) => { - this.getDate(); - this.getCommentList(); + if (res.code == 0) { + await this.refreshCurrentContent('content', data.mainId, res.data); + loading.close(); + this.getCommentList(); + } + }) + .catch((err) => { + loading.close(); + this.$message.error(err.msg); + }); + }, + async refreshCurrentContent(type, mainId, resData) { + const index = this.Main_List.findIndex((item) => item.am_id == mainId); + if (type == 'upArticleMain') { + if (index == 0) { + return; + } + const item = this.Main_List[index]; + // 2. 从原位置删除 + this.Main_List.splice(index, 1); + // 3. 插入到前一个位置 + this.Main_List.splice(index - 1, 0, item); + return; + } + if (type == 'downArticleMain') { + if (index == this.Main_List.length - 1) { + return; + } + const item = this.Main_List[index]; + // 2. 从原位置删除 + this.Main_List.splice(index, 1); + // 3. 插入到前一个位置 + this.Main_List.splice(index + 1, 0, item); + return; + } + if (index !== -1 && resData) { + var newData = Array.isArray(resData) ? resData : resData ? [resData] : []; + const updatedItems = newData.map((item) => ({ + ...item, + checked: false, + getnum: 0 + })); + if (type == 'addRow') { + this.Main_List.splice(index + 1, 0, updatedItems[0]); + } else if (type == 'addMoreRow') { + this.Main_List.splice(index + 1, 0, ...updatedItems); + } else if (type == 'positioningImg' || type == 'positioningTable') { + this.Main_List.splice(index + 1, 0, ...updatedItems); + } else { + this.$set(this.Main_List, index, updatedItems[0]); + } + } else { + console.warn(`Item with am_id ${mainId} not found.`); + } + this.$forceUpdate(); + }, + handlePaperclip() { + this.uploadWordTables = []; + this.$refs.fileInput.click(); + }, + handleFileChange(event) { + var that = this; + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + // 处理文件上传并传递回调函数 + this.$commonJS.handleFileUpload(event, function (tables) { + + if (tables.length == 0) { + loading.close(); + that.$message({ + type: 'warning', + message: 'No table found!' + }); + return false; + } + + // 使用 Promise.all 等待所有表格解析完成 + Promise.all( + tables.map((table) => { + return new Promise((resolve, reject) => { + // 解析每个表格 + that.$commonJS.parseTableToArray(table, (tableList) => { + resolve({ table_data: tableList, html_data: '' }); + }); + }); + }) + ) + .then((result) => { + // 所有表格的解析完成后,处理结果 + that.uploadWordTables = result; + loading.close(); + that.tablesHtmlVisible = true; + }) + .catch((error) => { + loading.close(); + console.error('Error processing tables:', error); + }); + }); + const file = event.target.files[0]; + if (file) { + // 处理文件逻辑 + } + + // 关键:重置 input 的值 + event.target.value = ''; + }, + + async onAddRow(mainId) { + await this.$api + .post(this.urlList.addRow, { + am_id: mainId, + article_id: this.articleId + }) + .then(async (res) => { + if (res.code == 0) { + this.refreshCurrentContent('addRow', mainId, res.data); + this.getCommentList(); + } else { + this.$message.error(res.msg); + } }) .catch((err) => { this.$message.error(err.msg); @@ -678,6 +1779,50 @@ export default { this.$message.error(err.msg); }); }, + + async executeProofreading(data) { + await this.$api + .post(this.urlList.executeProofreading, { + am_id: data.am_id, + record_id: data.id, + state: 1, + article_id: this.$route.query.id + }) + .then(async (res) => { + if (res.status == 1) { + this.getDate(); + this.getCommentList(); + } else { + this.$message.error(res.msg); + } + }) + .catch((err) => { + this.$message.error(err.msg); + }); + }, + async revokeProofreading(data) { + await this.$api + .post(this.urlList.executeProofreading, { + am_id: data.am_id, + record_id: data.id, + state: 2, + article_id: this.$route.query.id + }) + .then(async (res) => { + if (res.status == 1) { + this.getDate(); + this.getCommentList(); + } else { + this.$message.error(res.msg); + } + }) + .catch((err) => { + this.$message.error(err.msg); + }); + }, + async editProofreading(data) {}, + deleteProofreading(data, index) {}, + async cancelSolveComment(data) { await this.$api .post(this.urlList.cancelSolveComment, { @@ -702,43 +1847,124 @@ export default { }); }, handleImageAdd(type) { - this.picStyle = { note: '', picUrl: '' }; + this.picStyle = { note: '', picUrl: '', title: '' }; + this.picStyle1 = { note: '', picUrl: '', title: '' }; this.picStyle.visiTitle = 'Add Figure'; this.pictVisible = true; }, handleTableAdd(type) { this.lineStyle = { note: '', table_data: '', html_data: '' }; + this.lineStyle1 = { note: '', table_data: '', html_data: '' }; this.lineStyle.visiTitle = 'Add Table'; this.threeVisible = true; }, - handleImageEdit(data, type) { - console.log('data at line 600:', data); + addUploadWordTable(data) { + this.lineStyle = { note: '', table: data.table_data, html_data: data.html_data }; + this.lineStyle1 = { note: '', table: data.table_data, html_data: data.html_data }; + + this.lineStyle.visiTitle = 'Add Table'; + this.threeVisible = true; + }, + + async handleFigureAndTableDelete(data, type) { + await this.$confirm(this.$t('commonTable.remove' + type), 'Prompt', { + confirmButtonText: 'Submit', + cancelButtonText: 'Cancel', + type: 'warning' + }) + .then(async (res) => { + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + if (type == 'img') { + this.$api + .post(this.urlList.deleteImage, { + ami_id: data.ami_id, + + article_id: this.articleId + }) + .then(async (res) => { + if (res.status == 1) { + loading.close(); + this.$message.success(res.msg); + this.$refs.commonWordHtmlTypeSetting.replacement('img', data.ami_id); + } else { + loading.close(); + this.$message.error(res.msg); + } + }) + .catch((err) => { + loading.close(); + this.$message.error(err.msg); + }); + } + if (type == 'table') { + this.$api + .post(this.urlList.deleteTable, { + amt_id: data.amt_id, + + article_id: this.articleId + }) + .then(async (res) => { + if (res.status == 1) { + loading.close(); + this.$refs.commonWordHtmlTypeSetting.replacement('table', data.amt_id); + } else { + loading.close(); + this.$message.error(res.msg); + } + }) + .catch((err) => { + loading.close(); + this.$message.error(err.msg); + }); + } + }) + .catch((err) => { + // this.$message.error(err.msg); + }); + }, + handleFigureAndTableEdit(data, type) { if (type == 'img') { var extension = data.url.split('.').pop().toLowerCase(); + this.picStyle = {}; this.picStyle = { ...data, extension: extension, picUrl: data.url }; this.picStyle.visiTitle = 'Edit Figure'; this.pictVisible = true; } else if (type == 'table') { - this.lineStyle = { + this.lineStyle = {}; + this.lineStyle1 = {}; + // 1. 提取处理逻辑 + const formattedData = { ...data, - table: JSON.parse(data.table_data), - html_data: data.html_data, - note: data.note, - title: data.title + table: JSON.parse(data.table_data) }; + + // 2. 统一赋值 + this.lineStyle = formattedData; + this.lineStyle1 = { ...formattedData }; // 使用浅拷贝确保两个变量指向不同引用(如果需要独立修改) this.lineStyle.visiTitle = 'Edit Table'; this.threeVisible = true; } }, updateChange(content, type) { + var str = this.$commonJS.transformHtmlString(content); if (type == 'imgNote') { - this.picStyle.note = str; + this.picStyle1.note = str; + } else if (type == 'imgTitle') { + this.picStyle1.title = str; } else { - this.lineStyle[type] = str; + this.lineStyle1[type] = str; } }, onEdit(dataId) { + this.currentContent = {}; + this.picStyle = {}; + this.lineStyle = {}; this.currentId = null; this.clearButton(); var data = this.Main_List.find((item) => item.am_id == dataId); @@ -755,7 +1981,7 @@ export default { } else { } this.currentContent = { ...data, extension: extension }; - this.picStyle = { ...data, extension: extension, picUrl: data.image.url, note: data.image.note }; + this.picStyle = { ...data, extension: extension, picUrl: data.image.url, note: data.image.note, title: data.image.title }; this.picStyle.visiTitle = 'Edit Figure'; this.pictVisible = true; } else if (data.type == 2) { @@ -770,12 +1996,42 @@ export default { this.lineStyle.visiTitle = 'Edit Table'; this.threeVisible = true; } else { + data.content = data.content.replace(/]*>/g, '').replace(/<\/span>/g, ''); // 去除span标签 + this.editModalDraftHtml = data.content || ''; this.currentContent = data; + 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(); + } + const draft = this.currentContent && this.currentContent.content; + if (draft != null) { + this.flushReorderFromEditModal(draft); + } + }); + }); } }, + onAddContent(dataId) { + this.addContentVisible = true; + this.addContent = {}; + + this.currentId = dataId; + }, + async onDrop(event, dataId) { + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); if (event.dataTransfer.getData('image')) { const draggedImage = JSON.parse(event.dataTransfer.getData('image')); const draggedImageIndex = JSON.parse(event.dataTransfer.getData('imageIndex')); @@ -787,23 +2043,22 @@ export default { }) .then(async (res) => { if (res.code == 0) { - this.getDate(); + loading.close(); + this.refreshCurrentContent('positioningImg', dataId, res.data); this.$nextTick(() => { - this.$refs.commonWordHtmlTypeSetting.refresh('img'); + this.$refs.commonWordHtmlTypeSetting.refresh('positioningImg', res.data); }); this.$forceUpdate(); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { + loading.close(); this.$message.error(err.msg); }); }); - // this.Main_List.splice(index + 1, 0, draggedImage); - // this.$nextTick(() => { - // this.$refs.commonWordHtmlTypeSetting.changeIsHidden(draggedImageIndex, true, 'img'); - // }); } else { const draggedtable = JSON.parse(event.dataTransfer.getData('table')); @@ -815,33 +2070,28 @@ export default { }) .then(async (res) => { if (res.code == 0) { - this.getDate(); + loading.close(); + this.refreshCurrentContent('positioningTable', dataId, res.data); this.$nextTick(() => { - this.$refs.commonWordHtmlTypeSetting.refresh('table'); + this.$refs.commonWordHtmlTypeSetting.refresh('positioningTable', res.data); }); this.$forceUpdate(); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { + loading.close(); this.$message.error(err.msg); }); }); - - // const draggedtableIndex = JSON.parse(event.dataTransfer.getData('tableIndex')); - - // this.Main_List.splice(index + 1, 0, draggedtable); - // console.log('this.Main_List.splice at line 447:', this.Main_List); } - - // this.getWord(); }, getCommentList() { this.$api .post('api/Preaccept/getArticleMainCheckList', { - article_id: this.detailMes.article_id - + article_id: this.articleId }) .then((res) => { this.comments = res.data.list; @@ -849,113 +2099,6 @@ export default { }); }, getWord() { - // var htmlContent = `

${this.detailTitle}

`; - // htmlContent += this.Main_List.map((item) => { - // //批注 - // let contentHtml = ''; - // var isRemark = ``; - // if (item.remark && item.remark != '') { - // // isRemark = ``; - // isRemark = ` - - // (${item.am_id}) - // ${item.state == 0 ? `Resolved` : ''} - // - - // `; - // } - // // 判断是否是图片 - // if (item.type == 1) { - // var extension = item.image.url.split('.').pop().toLowerCase(); - // if (extension == 'tif') { - // contentHtml = ` - //

- // - // ${item.image.note ? item.image.note : ''} - //

- // `; - // } else if (['jpg', 'jpeg', 'png'].includes(extension)) { - // contentHtml = ` - //

- // ${isRemark} - // - // ${ - // item.image.note ? item.image.note : '' - // } - //

- // `; - // } else { - // contentHtml = ` - //

- // ${isRemark} - // - // Figures ( .${item.image.url.split('.').pop().toUpperCase()}) - // - - // ${ - // item.image.note ? item.image.note : '' - // } - //

- // `; - // } - // } else if (item.type == 2) { - // var tableList = JSON.parse(item.table.table_data); - - // contentHtml = ` - //
- // ${isRemark} - // ${item.table.title ? item.table.title : ''} - // - // ${tableList - // .map((row) => { - // return ` - // - // ${row - // .map((cell) => { - // return ` - // - // `; - // }) - // .join('')} - // - // `; - // }) - // .join('')} - //
- // ${cell.text || ''} - //
- // ${item.table.note ? item.table.note : ''} - //
- // `; - // } else { - // contentHtml = `

${isRemark}${item.content}

`; - // } - - // // 判断是否是表格类型 - - // return contentHtml; - // }).join(''); - // this.htmlContent = htmlContent; this.htmlContent = 'true'; }, // 获取数据 @@ -966,32 +2109,44 @@ export default { if (this.Art_Id != undefined) { urlLInk = 'api/Preaccept/getArticleMains'; urlTask.article_id = this.detailMes.article_id; - } + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); // 获取文章信息 await this.$api .post(urlLInk, urlTask) .then(async (res) => { if (res.code == 0) { - this.Main_List = res.data.list; + this.Main_List = res.data.list.map((e) => { + e.checked = false; + return e; + }); this.getManuscirptContent(); for (let i = 0; i < this.Main_List.length; i++) { this.Main_List[i].text = this.Main_List[i].content; this.Main_List[i].getnum = 0; } - - // setTimeout(async () => { this.$nextTick(async () => { await this.getWord(); + if (this.$refs.catalogue) { + await this.$refs.catalogue.getCatalogueList(); + } + loading.close(); }); // }, 1000); } else { this.$message.error(res.msg); + loading.close(); } }) .catch((err) => { this.$message.error(err); + loading.close(); }); }, @@ -1075,32 +2230,58 @@ export default { }, // 确定保存图片 - savePic() { - this.picStyle.picUrl; + async savePic() { + var str = this.picStyle1.note ? await this.$commonJS.decodeHtml(this.picStyle1.note) : ''; + + + var titleStr = this.picStyle1.title ? await this.$commonJS.decodeHtml(this.picStyle1.title) : ''; + + if (!this.picStyle.picUrl) { + this.$message.error('Please upload a picture'); + return; + } + if (!titleStr) { + this.$message.error('Please enter a title'); + return; + } + str = str.replace(//gi, ''); + titleStr = titleStr.replace(//gi, ''); + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); if (this.picStyle.visiTitle == 'Edit Figure') { this.$api .post(this.urlList.editImage, { ami_id: this.picStyle.ami_id, url: this.picStyle.picUrl, - note: this.picStyle.note + note: str, + title: titleStr }) .then((res) => { if (res.code == 0) { + loading.close(); this.$message.success('Successfully edit Figure!'); this.pictVisible = false; + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editImg', + res.data.image ? { ...res.data.image, has_selected: 1 } : res.data + ); + this.refreshCurrentContent('editImg', res.data.am_id, res.data); this.$nextTick(() => { - this.getDate(); this.getCommentList(); this.$forceUpdate(); }); - - this.$refs.commonWordHtmlTypeSetting.refresh('img'); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { + loading.close(); this.$message.error(err); }); } else { @@ -1108,19 +2289,23 @@ export default { .post(this.urlList.addImage, { article_id: this.articleId, url: this.picStyle.picUrl, - note: this.picStyle.note + note: str, + title: titleStr }) .then((res) => { if (res.code == 0) { + loading.close(); this.$message.success('Successfully Add Figure!'); this.pictVisible = false; - this.getDate(); - this.$refs.commonWordHtmlTypeSetting.refresh('img'); + + this.$refs.commonWordHtmlTypeSetting.refresh('addImg', res.data); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { + loading.close(); this.$message.error(err); }); } @@ -1135,60 +2320,236 @@ export default { this.$refs.tinymceChildComment.getContent('comment'); }); }, - saveTable(content) { - console.log('content at line 998:', content); + /** 稿面图片说明/标题内修改/移除 mycite 后,走 editImage 写回 */ + async saveImageManuscript(payload) { + if (!payload || payload.ami_id == null) return; + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + let strNote = payload.note || ''; + let strTitle = payload.title || ''; + if (strNote !== '') { + strNote = await this.$commonJS.decodeHtml(strNote); + } + if (strTitle !== '') { + strTitle = await this.$commonJS.decodeHtml(strTitle); + } + strNote = strNote.replace(//gi, ''); + strTitle = strTitle.replace(//gi, ''); + try { + const res = await this.$api.post(this.urlList.editImage, { + ami_id: payload.ami_id, + url: payload.url || '', + note: strNote, + title: strTitle + }); + loading.close(); + if (res.code == 0) { + this.$message.success('Successfully updated.'); + if (this.$refs.commonWordHtmlTypeSetting && this.$refs.commonWordHtmlTypeSetting.refresh) { + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editImg', + res.data.image ? { ...res.data.image, has_selected: 1 } : res.data + ); + } + this.refreshCurrentContent('editImg', res.data.am_id, res.data); + this.getCommentList(); + this.$nextTick(() => { + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); + } else { + this.$message.error(res.msg); + } + } catch (err) { + loading.close(); + this.$message.error(err && err.message ? err.message : String(err)); + } + }, + /** 稿面表格单元格内修改/移除 mycite 后,走 editMainTable 写回 */ + async saveTableManuscript(payload) { + if (!payload || payload.amt_id == null) return; + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + let strTitle = payload.title || ''; + let strNote = payload.note || ''; + if (strNote !== '') { + strNote = await this.$commonJS.decodeHtml(strNote); + } + if (strTitle !== '') { + strTitle = await this.$commonJS.decodeHtml(strTitle); + } + strNote = strNote.replace(//gi, ''); + strTitle = strTitle.replace(//gi, ''); + try { + const res = await this.$api.post(this.urlList.editTable, { + amt_id: payload.amt_id, + table_data: payload.table_data, + html_data: payload.html_data || '', + note: strNote, + title: strTitle + }); + loading.close(); + if (res.code == 0) { + this.$message.success('Successfully updated.'); + if (this.$refs.commonWordHtmlTypeSetting && this.$refs.commonWordHtmlTypeSetting.refresh) { + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editTable', + res.data.table ? { ...res.data.table, has_selected: 1 } : res.data + ); + } + this.refreshCurrentContent('editTable', res.data.am_id, res.data); + this.getCommentList(); + this.$nextTick(() => { + const w = this.$refs.commonWord; + if (w && typeof w.syncRefOrder === 'function') { + w.syncRefOrder(); + } + }); + } else { + this.$message.error(res.msg); + } + } catch (err) { + loading.close(); + this.$message.error(err && err.message ? err.message : String(err)); + } + }, + async saveTable(content) { + + const cleanTableData = (tableList) => { + if (tableList.length == 0) { + return []; + } else { + // 定义清理函数:去掉所有 br 标签和 TinyMCE 占位符 + const cleanText = (text) => { + if (!text) return ""; + // return text.replace(//gi, '').trim(); + return text + }; - if (content && content.table && content.table.length > 0) { + // 1. 获取处理后的干净表头 + const header = tableList[0].map(cell => ({ + ...cell, + text: cleanText(cell.text) + })); + + // 2. 过滤逻辑 + const cleanedTable = tableList.map((row) => { + // 首先:把每一行里的每个 cell.text 里的
都去掉 + return row.map(cell => ({ + ...cell, + text: cleanText(cell.text) + })); + }).filter((row, index) => { + if (index === 0) return true; + + // 3. 此时比较的就是没有
的文本了 + const isHeaderRow = row.every((cell, cellIndex) => { + return cell.text === header[cellIndex].text; + }); + + return !isHeaderRow; + }); + + return cleanedTable; + } +}; + var cleanedTableList = content.table ? content.table : []; + + cleanedTableList = cleanTableData(content.table); + var strTitle = this.lineStyle1.title ? this.lineStyle1.title : ''; + + var strNote = this.lineStyle1.note ? this.lineStyle1.note : ''; + if (strNote != '') { + strNote = await this.$commonJS.decodeHtml(strNote); + } + if (strTitle != '') { + strTitle = await this.$commonJS.decodeHtml(strTitle); + } else { + this.$message.error('Please enter a title'); + return; + } + + if (content && cleanedTableList && cleanedTableList.length > 0) { + const loading = this.$loading({ + lock: true, + text: 'Loading...', + spinner: 'el-icon-loading', + background: 'rgba(0, 0, 0, 0.7)' + }); + strNote = strNote.replace(//gi, ''); + strTitle = strTitle.replace(//gi, ''); + var tableStr=JSON.stringify(cleanedTableList) + if (this.lineStyle.visiTitle == 'Edit Table') { this.$api .post(this.urlList.editTable, { amt_id: this.lineStyle.amt_id, - table_data: JSON.stringify(content.table), + table_data: tableStr, html_data: content.html_data, - note: this.lineStyle.note, - title: this.lineStyle.title + note: strNote, + title: strTitle }) .then((res) => { if (res.code == 0) { + loading.close(); this.$message.success('Successfully edit Table!'); this.threeVisible = false; setTimeout(() => { - this.getDate(); + this.$refs.commonWordHtmlTypeSetting.refresh( + 'editTable', + res.data.table ? { ...res.data.table, has_selected: 1 } : res.data + ); + this.refreshCurrentContent('editTable', res.data.am_id, res.data); this.getCommentList(); - this.$refs.commonWordHtmlTypeSetting.refresh('table'); }); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { + loading.close(); this.$message.error(err); }); } else { this.$api .post(this.urlList.addTable, { article_id: this.articleId, - table_data: JSON.stringify(content.table), + table_data: JSON.stringify(cleanedTableList), html_data: content.html_data, - note: this.lineStyle.note, - title: this.lineStyle.title + note: strNote, + title: strTitle }) .then((res) => { if (res.code == 0) { + loading.close(); this.$message.success('Successfully Add Table!'); this.threeVisible = false; - this.getDate(); - this.$refs.commonWordHtmlTypeSetting.refresh('table'); + + this.$refs.commonWordHtmlTypeSetting.refresh('addTable', res.data); } else { + loading.close(); this.$message.error(res.msg); } }) .catch((err) => { this.$message.error(err); + loading.close(); }); } } else { this.$message.error('Please fill in the table content!'); + loading.close(); } }, async removeEvent(row) { @@ -1290,11 +2651,12 @@ export default { }, handleAvatarError(res, file) {}, beforeAvatarUpload(file) { - // const isLt2M = file.size / 1024 / 1024 < 10; - // if (!isLt2M) { - // this.$message.error('Picture size cannot exceed 10M!'); - // } - // return isLt2M; + const isLt2M = file.size / 1024 / 1024 < 20; + if (!isLt2M) { + this.$message.error('Picture size cannot exceed 20M!'); + return false; + } + const isValidFormat = ['image/jpeg', 'image/png', 'image/tiff'].includes(file.type); if (!isValidFormat) { this.$message.error(this.$t('commonTable.uploadImageInfo')); @@ -1417,8 +2779,8 @@ export default { border: 2px dashed rgb(0 102 153 / 50%); } /* .type_MTxt > .imgBox:hover { - background-color: rgb(0 102 153 / 10%); - border: 2px dashed rgb(0 102 153 / 50%); + background-color: rgb(0 102 153 / 10%); + border: 2px dashed rgb(0 102 153 / 50%); } */ .type_MTxt > div > p { @@ -1582,6 +2944,12 @@ export default { ::v-deep .wordTableHtml table span blue { color: rgb(0, 130, 170) !important; } +::v-deep .wordTableHtml table span blue sup { + color: rgb(0, 130, 170) !important; +} +::v-deep .wordTableHtml table span blue sub { + color: rgb(0, 130, 170) !important; +} .toolbar { display: flex; align-items: center; @@ -1663,4 +3031,30 @@ export default { ::-webkit-scrollbar-thumb:hover { background: #555; /* 滑块悬停时的颜色 */ } +.uploadWordTableBox { + width: 38vw; + position: relative; + height: auto; + overflow: hidden; + padding: 10px; + box-sizing: border-box; + box-shadow: rgba(0, 0, 0, 0.1) 0px 2px 12px 0px; +} +::v-deep .el-drawer__header { + margin-bottom: 0; + padding: 15px; +} +.uploadWordTableBox .insertTable { + /* display: none; */ + position: absolute; + right: 10px; +} +.uploadWordTableBox:hover { + background-color: #0066990d; + /* display: block !important; */ +} +wmath[data-wrap='inline'] { + display: inline-block !important; + width: auto !important; +} diff --git a/src/components/page/components/Tinymce/index.vue b/src/components/page/components/Tinymce/index.vue index 185ded0..1085785 100644 --- a/src/components/page/components/Tinymce/index.vue +++ b/src/components/page/components/Tinymce/index.vue @@ -1,6 +1,17 @@