From 95b52b4d06becd0e103201f85ac496846ad2b6de 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: Thu, 2 Apr 2026 10:56:37 +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/components/common/langs/en.js | 7 +- src/components/common/langs/zh.js | 7 +- src/components/page/GenerateCharts.vue | 74 ++++++++- .../page/components/Tinymce/index.vue | 141 ++++++++++++++++-- .../page/components/table/content.vue | 26 +++- 5 files changed, 234 insertions(+), 21 deletions(-) diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js index 121ff73..30e97d0 100644 --- a/src/components/common/langs/en.js +++ b/src/components/common/langs/en.js @@ -1152,7 +1152,12 @@ colTitle: 'Template title', placeholder: 'Please enter email content' }, wordCite: { - notFoundById: 'No reference found for citation ID {id}' + notFoundById: 'No reference found for citation ID {id}', + selectRef: 'Select Reference', + reference: 'Reference', + cancel: 'Cancel', + confirm: 'Confirm', + remove: 'Remove' } diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js index 88aea53..a6d16ba 100644 --- a/src/components/common/langs/zh.js +++ b/src/components/common/langs/zh.js @@ -1137,7 +1137,12 @@ const zh = { placeholder: '请输入邮件内容' }, wordCite: { - notFoundById: '未查询到编号{id}相关参考文献' + notFoundById: '未查询到编号{id}相关参考文献', + selectRef: '选择参考文献', + reference: '参考文献', + cancel: '取消', + confirm: '确认', + remove: '移除' } diff --git a/src/components/page/GenerateCharts.vue b/src/components/page/GenerateCharts.vue index dccd520..1e0601c 100644 --- a/src/components/page/GenerateCharts.vue +++ b/src/components/page/GenerateCharts.vue @@ -388,6 +388,8 @@ :value="currentContent.content" @getContent="getContent" @openLatexEditor="openLatexEditor" + @openRefSelector="handleOpenRefSelector" + :chanFerForm="chanFerForm" v-if="editVisible" ref="commonContent" style="margin-left: -115px" @@ -422,6 +424,8 @@ @getContent="getContent" type="content" @openLatexEditor="openLatexEditor" + @openRefSelector="handleOpenRefSelector" + :chanFerForm="chanFerForm" v-if="addContentVisible" ref="addContent" style="margin-left: -115px" @@ -439,6 +443,40 @@ + + + + + + + + + + + + + {{ $t('wordCite.cancel') }} + {{ $t('wordCite.remove') }} + {{ $t('wordCite.confirm') }} + + @@ -576,7 +614,11 @@ export default { exegesis: "The following contents','are necessary for the generation phase, please do not delete them!!!", p_article_id: null, chanFerForm: [], - chanFerFormRepeatList: [] + chanFerFormRepeatList: [], + refSelectorVisible: false, + refSelectorCurrentRefId: null, + refSelectedRow: null, + refSelectorSource: 'commonContent' }; }, components: { @@ -685,6 +727,36 @@ export default { console.log(err); }); }, + handleOpenRefSelector(data) { + this.refSelectorCurrentRefId = data && data.currentRefId ? data.currentRefId : null; + this.refSelectedRow = null; + if (this.editVisible) { + this.refSelectorSource = 'commonContent'; + } else if (this.addContentVisible) { + this.refSelectorSource = 'addContent'; + } + this.refSelectorVisible = true; + }, + handleRefCurrentChange(row) { + this.refSelectedRow = row; + }, + handleConfirmRefCite() { + if (!this.refSelectedRow) return; + const ref = this.$refs[this.refSelectorSource]; + if (ref) { + ref.insertAutocite(this.refSelectedRow.p_refer_id); + } + this.refSelectorVisible = false; + this.refSelectedRow = null; + }, + handleRemoveRefCite() { + const ref = this.$refs[this.refSelectorSource]; + if (ref) { + ref.removeAutocite(); + } + this.refSelectorVisible = false; + this.refSelectedRow = null; + }, ChanFerMashUp(e) { this.$api .post('api/Production/referHB', e) diff --git a/src/components/page/components/Tinymce/index.vue b/src/components/page/components/Tinymce/index.vue index 3e5a49c..cfa1c18 100644 --- a/src/components/page/components/Tinymce/index.vue +++ b/src/components/page/components/Tinymce/index.vue @@ -63,6 +63,21 @@ export default { }, articleId: { default: '' + }, + chanFerForm: { + type: Array, + default: () => [] + } + }, + computed: { + citeMap() { + const map = {}; + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + 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; } }, data() { @@ -129,17 +144,22 @@ export default { }, watch: { value: { - handler(val) { - - - if (!this.hasChange && this.hasInit) { - this.$nextTick(() => { - window.tinymce.get(this.tinymceId).setContent(val); - }); - } + if (!this.hasChange && this.hasInit) { + this.$nextTick(() => { + window.tinymce.get(this.tinymceId).setContent(val); + }); + } + }, + immediate: true }, - immediate: true + chanFerForm: { + handler() { + if (this.editorInstance) { + this.renderAutociteInEditor(this.editorInstance); + } + }, + deep: true } }, mounted() { @@ -163,6 +183,60 @@ export default { this.destroyTinymce(); }, methods: { + renderAutociteInEditor(ed) { + const body = ed.getBody(); + if (!body) return; + const autocites = body.querySelectorAll('autocite'); + const refs = Array.isArray(this.chanFerForm) ? this.chanFerForm : []; + const refMap = {}; + refs.forEach((item) => { + const key = item && item.p_refer_id != null ? String(item.p_refer_id) : ''; + if (key) refMap[key] = item; + }); + + autocites.forEach((el) => { + ed.dom.setAttrib(el, 'contenteditable', 'false'); + const dataId = el.getAttribute('data-id'); + const num = this.citeMap[String(dataId)]; + el.textContent = num != null ? `[${num}]` : '[?]'; + + const ref = refMap[String(dataId)]; + if (ref) { + const content = ref.refer_frag || [ref.author, ref.title, ref.joura, ref.dateno].filter(Boolean).join(' ').trim() || ''; + const doi = ref.doilink || ref.isbn || ref.doi || ''; + const tip = `[${num || '?'}] ${content}${doi ? '\nDOI: ' + doi : ''}`; + ed.dom.setAttrib(el, 'title', tip); + } else { + ed.dom.setAttrib(el, 'title', this.$t('wordCite.notFoundById', { id: num != null ? num : dataId })); + } + }); + }, + insertAutocite(refId) { + const ed = this.editorInstance; + if (!ed) return; + if (this._editingAutocite) { + this._editingAutocite.setAttribute('data-id', refId); + const num = this.citeMap[String(refId)]; + this._editingAutocite.textContent = num != null ? `[${num}]` : '[?]'; + this._editingAutocite = null; + ed.fire('change'); + } else { + if (this._refBookmark) { + ed.selection.moveToBookmark(this._refBookmark); + } + const num = this.citeMap[String(refId)]; + const label = num != null ? `[${num}]` : '[?]'; + const html = `${label}`; + ed.insertContent(html); + } + }, + removeAutocite() { + const ed = this.editorInstance; + if (!ed || !this._editingAutocite) return; + ed.dom.remove(this._editingAutocite); + this._editingAutocite = null; + ed.fire('change'); + }, handleSetContent(val) { if (!this.editorInstance) return; @@ -497,9 +571,9 @@ export default { window.tinymce.init({ ..._this.tinymceOtherInit, trim_span_elements: false, // 禁止修剪内联标签周围的空格 - extended_valid_elements: 'blue[*]', - custom_elements: 'blue', - valid_children: '+blue[#text|i|em|b|strong|span],+body[blue],+p[blue]', + extended_valid_elements: 'blue[*],autocite[*]', + custom_elements: 'blue,autocite', + valid_children: '+blue[#text|i|em|b|strong|span],+body[blue|autocite],+p[blue|autocite]', inline: false, // 使用 iframe 模式 selector: `#${this.tinymceId}`, @@ -509,7 +583,7 @@ export default { valid_elements: this.type == 'table' ? '*[*]' - : `img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,myfigure,mytable,wmath${this.valid_elements}`, // 允许的标签和属性 + : `img[src|alt|width|height],strong,em,sub,sup,blue,table,b,i,myfigure,mytable,wmath,autocite[data-id|contenteditable|title]${this.valid_elements}`, // 允许的标签和属性 // valid_elements: '*[*]', // 允许所有 HTML 标签 noneditable_editable_class: 'MathJax', height: this.height, @@ -541,6 +615,22 @@ export default { font-weight: bold !important; } + autocite { + display: inline-block; + color: rgb(0, 130, 170); + font-weight: bold; + cursor: pointer; + padding: 0 2px; + border-radius: 3px; + background-color: rgba(0, 130, 170, 0.08); + user-select: all; + font-size: 12px; + } + autocite:hover { + background-color: rgba(0, 130, 170, 0.2); + text-shadow: 0 0 3px rgba(0, 130, 170, 0.3); + } + @keyframes blueGlow { 0%, 100% { @@ -626,7 +716,27 @@ export default { let currentPasteImages = []; _this.$commonJS.initEditorButton(_this, ed); var currentWmathElement = null; + + ed.ui.registry.addButton('insertRef', { + text: 'Ref', + tooltip: 'Insert Reference', + onAction: function () { + _this._refBookmark = ed.selection.getBookmark(2); + _this._editingAutocite = null; + _this.$emit('openRefSelector', { currentRefId: null }); + } + }); + ed.on('click', function (e) { + const autociteEl = e.target.closest('autocite'); + if (autociteEl) { + const dataId = autociteEl.getAttribute('data-id'); + _this._refBookmark = ed.selection.getBookmark(2); + _this._editingAutocite = autociteEl; + _this.$emit('openRefSelector', { currentRefId: dataId }); + return; + } + const wmathElement = e.target.closest('wmath'); if (wmathElement) { currentWmathElement = wmathElement; // 保存当前点击的元素 @@ -791,14 +901,15 @@ export default { const editorBody = ed.getBody(); ed.dom.select('wmath', editorBody).forEach(function (wmathElement) { ed.dom.setAttrib(wmathElement, 'contenteditable', 'false'); - // ed.dom.addClass(wmathElement, 'non-editable-wmath'); }); + _this.renderAutociteInEditor(ed); e.content = e.content.replace(//g, '').replace(/<\/strong>/g, ''); e.content = e.content.replace(//g, '').replace(/<\/em>/g, ''); }); ed.on('GetContent', function (e) { e.content = e.content.replace(//g, '').replace(/<\/b>/g, ''); - e.content = e.content.replace(//g, '').replace(/<\/i>/g, ''); + e.content = e.content.replace(//g, '').replace(/<\/em>/g, ''); + e.content = e.content.replace(/]*)>[^<]*<\/autocite>/gi, ''); }); }, paste_preprocess: function (plugin, args) { diff --git a/src/components/page/components/table/content.vue b/src/components/page/components/table/content.vue index bd9782b..71c1def 100644 --- a/src/components/page/components/table/content.vue +++ b/src/components/page/components/table/content.vue @@ -12,11 +12,13 @@ :isAutomaticUpdate="isAutomaticUpdate" @getContent="getContent" @openLatexEditor="openLatexEditor" + @openRefSelector="openRefSelector" @updateChange="updateChange" :value="value" + :chanFerForm="chanFerForm" :typesettingType="typesettingType" class="paste-area text-container" - :toolbar="!isAutomaticUpdate?['bold italic |customBlue removeBlue|LateX| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']:['bold italic |customBlue removeBlue| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace']" + :toolbar="toolbarConfig" style=" /* white-space: pre-line; */ line-height: 12px; @@ -36,10 +38,20 @@