This commit is contained in:
2026-04-02 10:56:37 +08:00
parent 1f29fb5baf
commit 95b52b4d06
5 changed files with 234 additions and 21 deletions

View File

@@ -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'
}

View File

@@ -1137,7 +1137,12 @@ const zh = {
placeholder: '请输入邮件内容'
},
wordCite: {
notFoundById: '未查询到编号{id}相关参考文献'
notFoundById: '未查询到编号{id}相关参考文献',
selectRef: '选择参考文献',
reference: '参考文献',
cancel: '取消',
confirm: '确认',
remove: '移除'
}

View File

@@ -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 @@
</el-dialog>
<common-late-x v-if="showLateX" @close="showLateX = false" @save="saveLateX" :LateXInfo="LateXInfo"></common-late-x>
<el-dialog
:title="$t('wordCite.selectRef')"
:visible.sync="refSelectorVisible"
width="860px"
append-to-body
:close-on-click-modal="false"
>
<el-table
:data="chanFerForm"
highlight-current-row
@current-change="handleRefCurrentChange"
style="width: 100%"
max-height="400"
size="small"
>
<el-table-column type="index" :label="'#'" width="60" :index="idx => idx + 1"></el-table-column>
<el-table-column :label="$t('wordCite.reference')">
<template slot-scope="scope">
{{ scope.row.refer_frag || [scope.row.author, scope.row.title, scope.row.joura, scope.row.dateno].filter(Boolean).join(' ') || '-' }}
</template>
</el-table-column>
<el-table-column label="DOI" width="200">
<template slot-scope="scope">
{{ scope.row.doilink || scope.row.doi || scope.row.isbn || '-' }}
</template>
</el-table-column>
</el-table>
<span slot="footer" class="dialog-footer">
<el-button @click="refSelectorVisible = false">{{ $t('wordCite.cancel') }}</el-button>
<el-button type="danger" v-if="refSelectorCurrentRefId" @click="handleRemoveRefCite">{{ $t('wordCite.remove') }}</el-button>
<el-button type="primary" :disabled="!refSelectedRow" @click="handleConfirmRefCite">{{ $t('wordCite.confirm') }}</el-button>
</span>
</el-dialog>
</div>
</template>
@@ -576,7 +614,11 @@ export default {
exegesis: "The following contents'<b></b>,<i></i>'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)

View File

@@ -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 = `<autocite data-id="${refId}" contenteditable="false">${label}</autocite>`;
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(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
e.content = e.content.replace(/<em>/g, '<i>').replace(/<\/em>/g, '</i>');
});
ed.on('GetContent', function (e) {
e.content = e.content.replace(/<b>/g, '<strong>').replace(/<\/b>/g, '</strong>');
e.content = e.content.replace(/<i>/g, '<em>').replace(/<\/i>/g, '</em>');
e.content = e.content.replace(/<i>/g, '<em>').replace(/<\/em>/g, '</em>');
e.content = e.content.replace(/<autocite([^>]*)>[^<]*<\/autocite>/gi, '<autocite$1></autocite>');
});
},
paste_preprocess: function (plugin, args) {

View File

@@ -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 @@
<script>
import Tinymce from '@/components/page/components/Tinymce';
export default {
props: ['value','isAutomaticUpdate','height','id'],
props: ['value', 'isAutomaticUpdate', 'height', 'id', 'chanFerForm'],
components: {
Tinymce
},
computed: {
toolbarConfig() {
const hasRefs = Array.isArray(this.chanFerForm) && this.chanFerForm.length > 0;
const refBtn = hasRefs ? ' insertRef' : '';
if (!this.isAutomaticUpdate) {
return [`bold italic |customBlue removeBlue|LateX${refBtn}| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace`];
}
return [`bold italic |customBlue removeBlue${refBtn}| myuppercase myuppercasea Line MoreSymbols|subscript superscript|clearButton|searchreplace`];
}
},
watch: {
lineStyle() {}
},
@@ -85,8 +97,16 @@ export default {
this.$refs.tinymceChild1.getContent(type);
},
getContent(type, content) {
this.$emit('getContent', type, content);
},
openRefSelector(data) {
this.$emit('openRefSelector', data);
},
insertAutocite(refId) {
this.$refs.tinymceChild1.insertAutocite(refId);
},
removeAutocite() {
this.$refs.tinymceChild1.removeAutocite();
}
}
};