This commit is contained in:
2026-04-08 17:25:19 +08:00
parent 4fc78e1fe7
commit c4b86be0d5
5 changed files with 604 additions and 206 deletions

View File

@@ -1169,7 +1169,7 @@ colTitle: 'Template title',
remove: 'Remove',
selected: 'Selected',
modifyRef: 'Edit citation',
removeRefTag: 'Reference remove',
removeRefTag: 'Remove citation',
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.',

View File

@@ -1154,7 +1154,7 @@ const zh = {
remove: '移除',
selected: '已选择',
modifyRef: '修改引用',
removeRefTag: '移除参考文献',
removeRefTag: '移除引用',
citeUpdateFail: '未能更新正文中的引用标签,请重试或进入编辑修改',
matchBracketRefs: '自动链接参考文献',
matchBracketRefsDone: '已转换 {n} 处 [n] 为可点击角标',

View File

@@ -454,7 +454,6 @@
ref="addContent"
style="margin-left: -115px"
></common-content>
</el-form-item>
</el-form>
@@ -477,19 +476,41 @@
:close-on-click-modal="false"
@closed="onRefSelectorDialogClosed"
>
<div v-if="refSelectedRows.length > 0" style="margin-bottom: 10px; font-size: 13px; color: #606266;">
{{ $t('wordCite.selected') }}:
<b v-if="refPreviewLabel" style="color: #0082aa">[{{ refPreviewLabel }}]</b>
</div>
<div class="duplicateRefBox" v-if="chanFerFormRepeatList.length > 0">
<el-tooltip class="item" effect="dark" content="Duplicate references" placement="top">
<img src="../../assets/img/repeat.png" alt="" style="width: 22px; height: 22px" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="Duplicate references" placement="top">
<img src="../../assets/img/repeat.png" alt="" style="width: 22px; height: 22px" />
</el-tooltip>
<span @click="handleContainerClick" style="margin-left: 5px" v-html="getRepeatRefHtml()"> </span>
</div>
<el-table :row-style="tableRowStyle"
<span @click="handleContainerClick" style="margin-left: 5px" v-html="getRepeatRefHtml()"> </span>
</div>
<div>
<el-input
v-model="refSelectorQuickPick"
size="small"
clearable
:placeholder="$t('wordCite.quickPickPlaceholder')"
style="width: calc(100% - 340px);"
/>
<el-button size="small" type="primary" plain @click="applyRefSelectorQuickPick" style="margin-left: 20px;">
{{ $t('wordCite.quickPickApply') }}
</el-button>
<el-button
style="float: right; margin-bottom: 30px"
icon="el-icon-plus"
@mousedown.stop
@click.stop="addLine('Add')"
type="success"
size="small"
plain
>Cite new reference</el-button
>
</div>
<el-table
:row-style="tableRowStyle"
ref="refSelectorTable"
:data="refSelectorTableData"
@selection-change="handleRefSelectionChange"
@@ -501,114 +522,205 @@
>
<el-table-column type="selection" width="45" :reserve-selection="true"></el-table-column>
<el-table-column type="index" label="No." width="60" align="center">
<template slot-scope="scope">
<i v-if="scope.row.is_change == 1" class="itemChanged"></i>
<!-- <el-tooltip
<template slot-scope="scope">
<i v-if="scope.row.is_change == 1" class="itemChanged"></i>
<!-- <el-tooltip
class="Duplicate-item"
effect="dark"
:content="getParsingInfo(scope.row, scope.$index)"
placement="top"
> -->
<img v-if="scope.row.is_repeat == 1" src="../../assets/img/repeat.png" alt="" style="width: 22px; height: 22px" />
<!-- </el-tooltip> -->
<img v-if="scope.row.is_repeat == 1" src="../../assets/img/repeat.png" alt="" style="width: 22px; height: 22px" />
<!-- </el-tooltip> -->
<span>{{ scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="state" width="55" align="center">
<template slot-scope="scope">
<span
class="status ok"
:class="scope.row.refer_type == 'journal' ? getJournalDateno(scope.row.dateno, 'status') : ''"
v-if="
(
(scope.row.refer_type == 'journal' && scope.row.doilink != '' && scope.row.cs == 1) ||
(scope.row.refer_type == 'book' && scope.row.isbn != '' && scope.row.cs == 1)
) && scope.row.retract == 0
"
>
<i class="el-icon-circle-check"></i>
</span>
<span class="status warn" v-else>
<i class="el-icon-warning-outline"></i>
</span>
</template>
</el-table-column>
<span>{{ scope.$index + 1 }}</span>
</template>
</el-table-column>
<el-table-column label="State" width="55" align="center">
<template slot-scope="scope">
<span
class="status ok"
:class="scope.row.refer_type == 'journal' ? getJournalDateno(scope.row.dateno, 'status') : ''"
v-if="
((scope.row.refer_type == 'journal' && scope.row.doilink != '' && scope.row.cs == 1) ||
(scope.row.refer_type == 'book' && scope.row.isbn != '' && scope.row.cs == 1)) &&
scope.row.retract == 0
"
>
<i class="el-icon-circle-check"></i>
</span>
<span class="status warn" v-else>
<i class="el-icon-warning-outline"></i>
</span>
</template>
</el-table-column>
<el-table-column :label="$t('wordCite.reference')">
<template slot-scope="scope">
<!-- journal 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'journal'" class="reference-item">
<p>
{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>. &nbsp;<em>{{ scope.row.joura }}</em
>.&nbsp;<span :class="getJournalDateno(scope.row.dateno, 'title')">{{ scope.row.dateno }}</span
>.<br />
</p>
<a class="doiLink" :href="scope.row.doilink" target="_blank">{{ scope.row.doilink }}</a>
</div>
<!-- book 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'book'" class="reference-item">
<p>{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>.&nbsp;{{ scope.row.dateno }}.&nbsp;<br /></p>
<a class="doiLink" :href="scope.row.isbn" target="_blank">{{ scope.row.isbn }}</a>
</div>
<!-- other 形式 -->
<p class="wrongLine reference-item" style="text-align: left" v-if="scope.row.refer_type == 'other'">
<span v-html="formatTitle(scope.row.refer_frag)"></span>
</p>
</template>
<!-- journal 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'journal'" class="reference-item">
<p>
{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>. &nbsp;<em>{{
scope.row.joura
}}</em
>.&nbsp;<span :class="getJournalDateno(scope.row.dateno, 'title')">{{ scope.row.dateno }}</span
>.<br />
</p>
<a class="doiLink" :href="scope.row.doilink" target="_blank">{{ scope.row.doilink }}</a>
</div>
<!-- book 形式 -->
<div style="text-align: left" v-if="scope.row.refer_type == 'book'" class="reference-item">
<p>
{{ scope.row.author }}&nbsp;<span v-html="formatTitle(scope.row.title)"></span>.&nbsp;{{
scope.row.dateno
}}.&nbsp;<br />
</p>
<a class="doiLink" :href="scope.row.isbn" target="_blank">{{ scope.row.isbn }}</a>
</div>
<!-- other 形式 -->
<p class="wrongLine reference-item" style="text-align: left" v-if="scope.row.refer_type == 'other'">
<span v-html="formatTitle(scope.row.refer_frag)"></span>
</p>
</template>
</el-table-column>
<el-table-column align="center" :width="'200'">
<div slot-scope="scope">
<div class="operation" style="">
<el-button
style="margin-left: 10px"
@mousedown.stop
@click.stop="change(scope.row, 'Edit')"
plain
type="primary"
size="mini"
icon="el-icon-edit"
>edit</el-button
>
<el-button
type="danger"
icon="el-icon-delete"
size="mini"
style=""
plain
@mousedown.stop
@click.stop="deleteLine(scope.row)"
>delete</el-button
>
</div>
</div>
</el-table-column>
<el-table-column align="center" :width="'290'">
<div slot-scope="scope">
<div class="operation" style="">
<el-button
style="margin-left: 10px"
@click="change(scope.row, 'Edit')"
plain
type="primary"
size="mini"
icon="el-icon-edit"
>edit</el-button
>
<el-tooltip
popper-class="tps"
class="item"
effect="light"
content="Add one under this line"
placement="top"
>
<el-button @click="addLine(scope.row, 'Add')" type="success" size="mini" plain>Add</el-button>
</el-tooltip>
<el-button type="danger" icon="el-icon-delete" size="mini" style="" plain @click="deleteLine(scope.row)"
>delete</el-button
>
<img
v-if="role == 'editor' && scope.row.is_ai_check == 1"
src="@/assets/img/ai.png"
class="beautiful-gradient"
style="width: 22px; height: 22px; position: absolute; right: 0px; top: 2px"
/>
</div>
</div>
</el-table-column>
</el-table>
<div style="display: flex; gap: 10px; align-items: center; margin: 10px 0 10px;">
<el-input
v-model="refSelectorQuickPick"
size="small"
clearable
:placeholder="$t('wordCite.quickPickPlaceholder')"
/>
<el-button size="small" type="primary" plain @click="applyRefSelectorQuickPick">
{{ $t('wordCite.quickPickApply') }}
</el-button>
<div style="display: flex; gap: 10px; align-items: center; margin: 10px 0;">
<div v-if="refSelectedRows.length > 0" style="margin-bottom: 10px; font-size: 13px; color: #606266">
{{ $t('wordCite.selected') }}:
<b v-if="refPreviewLabel" style="color: #0082aa">[{{ refPreviewLabel }}]</b>
</div>
<div style="flex: 1;"></div>
<el-button size="small" @click="refSelectorVisible = false">
{{ $t('wordCite.cancel') }}
</el-button>
<el-button
size="small"
type="primary"
:disabled="refSelectedRows.length === 0"
@click="handleConfirmRefCite"
>
{{ $t('wordCite.confirm') }}
</el-button>
</div>
</el-dialog>
<!-- 引用:编辑 / 新增 / 删除(复用 editPublicRefRdit.vue 逻辑) -->
<el-dialog
:close-on-click-modal="false"
destroy-on-close
:append-to-body="true"
v-loading="addLoading"
:title="dialogTitle + ' References'"
:visible.sync="editboxVisible"
width="1200px"
@close="cancelSave"
>
<p class="yinyongPre c888" style="margin-bottom: 20px; color: #888; line-height: 24px">
You may add or modify the references. The system will automatically identify and retrieve the reference information. Please
note that if the reference type is set to “Other”, the “Parse” button will not be available.
</p>
<el-form :model="refenceForm" :rules="refenceFormrules" ref="refenceForm" label-width="150px" class="editForm mt10">
<el-form-item label="Source:">
<el-select v-model="SourceType" placeholder="please pick">
<el-option v-for="item in sourceOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
</el-select>
</el-form-item>
<el-form-item label="Content:" required prop="content">
<div class="automatic-parsing-box">
<el-input
style="border: none"
class="automaticParsing"
type="textarea"
rows="5"
v-model="refenceForm.content"
placeholder=""
></el-input>
<span v-if="isShowParsing" style="color: #409eff"
>We have detected updates to the reference content. You need to click the "Automatic parsing" button to
recognize them.</span
>
</div>
</el-form-item>
<div v-if="!isShowParsing && isShowParsingData && SourceType != 'other'">
<el-form-item style="margin: 0 0 30px">
<div class="line" style="border: 1px dashed #dcdfe6"></div>
</el-form-item>
<div v-show="SourceType == 'journal'">
<el-form-item label="Doi:" prop="doi">
<el-input v-model="refenceForm.doi"></el-input>
</el-form-item>
</div>
<div v-show="SourceType != 'other'">
<el-form-item label="Author(s):" required prop="author">
<el-input v-model="refenceForm.author"></el-input>
</el-form-item>
<el-form-item :label="SourceType == 'journal' ? 'Title:' : 'Book'" required prop="title">
<el-input v-model="refenceForm.title"></el-input>
</el-form-item>
<el-form-item label="Publication Details:" required prop="dateno">
<el-input v-model="refenceForm.dateno"></el-input>
</el-form-item>
</div>
<div v-show="SourceType == 'journal'">
<el-form-item label="Journal:" required prop="joura">
<el-input v-model="refenceForm.joura"></el-input>
</el-form-item>
<el-form-item label="DOI/URL:" required prop="doilink">
<el-input v-model="refenceForm.doilink"></el-input>
</el-form-item>
</div>
<div v-show="SourceType == 'book'">
<el-form-item label="ISBN:" required prop="isbn">
<el-input v-model="refenceForm.isbn"></el-input>
</el-form-item>
</div>
</div>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="refSelectorVisible = false">{{ $t('wordCite.cancel') }}</el-button>
<el-button type="primary" :disabled="refSelectedRows.length === 0" @click="handleConfirmRefCite">{{ $t('wordCite.confirm') }}</el-button>
<el-button @click="cancelSave">Cancel</el-button>
<el-button
v-if="isShowParsing"
type="primary"
style="background-color: #409eff !important; border-color: #409eff !important"
@click="getParsingData()"
>Automatic parsing</el-button
>
<el-button v-else type="primary" @click="dialogTitle == 'Edit' ? saveChange() : saveAdd()">Save</el-button>
</span>
</el-dialog>
</div>
@@ -765,7 +877,46 @@ export default {
/** 打开选择器时若带 currentRefIds编辑已有引用排序用「传值计算的顺序」关闭后清空 */
refSelectorContextIds: [],
/** 选择参考文献弹窗:按 # 序号快速勾选,如 [5, 6, 10-15] */
refSelectorQuickPick: ''
refSelectorQuickPick: '',
/** 引用编辑弹窗(复用 editPublicRefRdit.vue 的新增/编辑/删除逻辑) */
editboxVisible: false,
dialogTitle: 'Edit',
SourceType: 'journal',
sourceOptions: [
{ label: 'Journal', value: 'journal' },
{ label: 'Book', value: 'book' },
{ label: 'Other', value: 'other' }
],
refenceForm: {
doi: '',
p_article_id: null,
p_refer_id: null,
pre_p_refer_id: null,
refer_type: '',
author: '',
title: '',
joura: '',
dateno: '',
doilink: '',
isbn: '',
content: ''
},
old_refence_content: '',
old_refence_type: '',
isShowParsing: false,
isShowParsingData: false,
addLoading: false,
refenceFormrules: {
doi: [{ required: true, message: 'The Doi cannot be empty', trigger: 'blur' }],
author: [{ required: true, message: 'The Author(s) cannot be empty', trigger: 'blur' }],
title: [{ required: true, message: 'The Title cannot be empty', trigger: 'blur' }],
joura: [{ required: true, message: 'The Journal cannot be empty', trigger: 'blur' }],
doilink: [{ required: true, message: 'The DOI/URL cannot be empty', trigger: 'blur' }],
dateno: [{ required: true, message: 'The Publication Details cannot be empty', trigger: 'blur' }],
isbn: [{ required: true, message: 'The ISBN cannot be empty', trigger: 'blur' }],
content: [{ required: true, message: 'The Content cannot be empty', trigger: 'blur' }]
}
};
},
components: {
@@ -900,7 +1051,7 @@ export default {
},
threeVisible(val) {
if (!val) this.tableModalDraftHtml = '';
},
}
// 监听计算属性
// combinedValue(newVal, oldVal) {
// console.log('value1 或 value2 发生变化');
@@ -960,16 +1111,237 @@ export default {
},
methods: {
/** 参考文献:刷新(供编辑/新增/删除后更新选择器列表) */
changeRefer() {
return this.fetchReferList();
},
cancelSave() {
this.editboxVisible = false;
this.isShowParsing = false;
this.isShowParsingData = false;
this.old_refence_content = '';
this.old_refence_type = '';
this.$nextTick(() => {
if (this.$refs.refenceForm) {
try {
this.$refs.refenceForm.clearValidate();
} catch (e) {
/* ignore */
}
}
});
},
getParsingData() {
if (!this.refenceForm || !this.refenceForm.content) {
this.$message.error('The Content cannot be empty');
return;
}
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.$api
.post('api/References/dealContent', {
content: this.refenceForm.content
})
.then((res) => {
loading.close();
this.isShowParsing = false;
this.isShowParsingData = true;
this.refenceForm.joura = res.data.joura;
this.refenceForm.author = res.data.author;
this.refenceForm.dateno = res.data.dateno;
this.refenceForm.doilink = res.data.doilink;
this.refenceForm.doi = res.data.doi;
this.refenceForm.title = res.data.title;
this.refenceForm.isbn = this.refenceForm.refer_type == 'book' ? res.data.doilink : res.data.isbn;
})
.catch((err) => {
loading.close();
this.$message.error(err);
});
},
getRefData(id) {
return this.$api
.post('api/References/get', {
account: localStorage.getItem('U_name'),
p_refer_id: id
})
.then((res) => {
if (res && res.status == 1) return res.data;
throw (res && res.msg) || 'get ref failed';
});
},
/** 引用:编辑 */
async change(row, optitle) {
this.dialogTitle = optitle;
this.isShowParsing = false;
this.isShowParsingData = false;
const loading = this.$loading({
lock: true,
text: 'Loading...',
background: 'rgba(0, 0, 0, 0.7)'
});
try {
const data = await this.getRefData(row.p_refer_id);
this.SourceType = data.refer_type ? data.refer_type : '';
this.refenceForm = {
doi: '',
p_article_id: this.p_article_id,
p_refer_id: row.p_refer_id,
pre_p_refer_id: null,
refer_type: data.refer_type ? data.refer_type : '',
author: data.author ? data.author : '',
doilink: data.doilink ? data.doilink : '',
dateno: data.dateno ? data.dateno : '',
isbn: data.isbn ? data.isbn : '',
joura: data.joura ? data.joura : '',
content: data.deal_content ? data.deal_content : '',
title: data.title ? data.title : ''
};
this.old_refence_content = JSON.parse(JSON.stringify(data.deal_content));
this.old_refence_type = JSON.parse(JSON.stringify(data.refer_type));
if (this.SourceType === 'book') {
this.refenceForm.isbn = data.doilink ? data.doilink : '';
}
this.editboxVisible = true;
} catch (e) {
this.$message.error(e);
} finally {
loading.close();
}
},
/** 引用:新增(在当前行下方插入) */
addLine(optitle) {
this.dialogTitle = optitle;
this.old_refence_type = '';
this.old_refence_content = '';
this.SourceType = 'journal';
this.refenceForm = {
doi: '',
p_article_id: this.p_article_id,
p_refer_id: null,
pre_p_refer_id: this.chanFerForm.length > 0 ? this.chanFerForm[this.chanFerForm.length - 1].p_refer_id : null,
refer_type: '',
joura: '',
author: '',
doilink: '',
dateno: '',
isbn: '',
content: '',
title: ''
};
this.isShowParsing = true;
this.isShowParsingData = false;
this.editboxVisible = true;
this.$nextTick(() => {
if (this.$refs.refenceForm) {
this.$refs.refenceForm.clearValidate(['content']);
this.$refs.refenceForm.clearValidate(['doi']);
}
});
},
/** 引用:删除 */
deleteLine(row) {
this.$confirm('Are you sure you want to remove this reference?', 'Tips', {
confirmButtonText: 'Sure',
cancelButtonText: 'Cancel',
type: 'warning'
})
.then(() => {
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
this.$api
.post('api/Preaccept/delRefer', {
p_refer_id: row.p_refer_id
})
.then((res) => {
loading.close();
if (res.code == 0) {
this.$message.success('remove successed!');
this.changeRefer();
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close();
this.$message.error(err);
});
})
.catch(() => {});
},
saveChange() {
this.editRefSave();
},
editRefSave() {
this.refenceForm.refer_type = this.SourceType;
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const form = { ...this.refenceForm };
this.$api
.post('api/Preaccept/editRefer', form)
.then((res) => {
loading.close();
if (res.code == 0) {
this.$message.success('successed');
this.changeRefer();
this.cancelSave();
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close();
this.$message.error(err);
});
},
saveAdd() {
this.refenceForm.refer_type = this.SourceType;
const loading = this.$loading({
lock: true,
text: 'Loading...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
const form = { ...this.refenceForm };
this.$api
.post('api/Preaccept/addReferByParticleid', form)
.then((res) => {
loading.close();
if (res.code == 0) {
this.$message.success('successed');
this.changeRefer();
this.cancelSave();
} else {
this.$message.error(res.msg);
}
})
.catch((err) => {
loading.close();
this.$message.error(err);
});
},
formatTitle(title) {
if (!title) return '';
// 使用正则匹配,'gi' 表示全局匹配且不区分大小写
// \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词
const reg = /\b(Retracted|Retraction)\b/gi;
return title.replace(reg, (match) => {
return `<span style="color: red; font-weight: bold;">${match}</span>`;
});
},
if (!title) return '';
// 使用正则匹配,'gi' 表示全局匹配且不区分大小写
// \b 确保是完整单词匹配,防止误伤含有这些字母的其他单词
const reg = /\b(Retracted|Retraction)\b/gi;
return title.replace(reg, (match) => {
return `<span style="color: red; font-weight: bold;">${match}</span>`;
});
},
getJournalDateno(dateno, type) {
if (dateno && typeof dateno === 'string') {
const hasInvalidColon = !dateno.includes(':') || (dateno.includes(':') && dateno.split(':').pop().trim() === '');
@@ -1091,10 +1463,7 @@ export default {
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;
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 {
@@ -1133,27 +1502,20 @@ export default {
getTableDrawerMergedDraftHtml() {
const parts = [];
const titleInst =
this.$refs.tinymceChildTitle &&
this.$refs.tinymceChildTitle.$refs &&
this.$refs.tinymceChildTitle.$refs.tinymceChild1;
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;
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;
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') {
@@ -1241,9 +1603,7 @@ export default {
*/
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 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) => {
@@ -1308,8 +1668,7 @@ export default {
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]));
const sameOrder = ids.length === prevOrder.length && ids.every((id, i) => String(id) === String(prevOrder[i]));
if (!sameOrder) {
this.articleCiteIdOrder = ids.slice();
}
@@ -1500,7 +1859,10 @@ export default {
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 parts = t
.split(',')
.map((x) => x.trim())
.filter(Boolean);
const out = new Set();
parts.forEach((p) => {
const m = p.match(/^(\d+)\s*[-–—]\s*(\d+)$/);
@@ -1546,13 +1908,17 @@ export default {
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;
if (
event.target.closest('.el-checkbox') ||
event.target.closest('a[href]') ||
event.target.closest('.operation') ||
event.target.closest('.el-button')
)
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)
);
const isSelected = (this.refSelectedRows || []).some((r) => r && String(r.p_refer_id) === String(row.p_refer_id));
table.toggleRowSelection(row, !isSelected);
},
handleConfirmRefCite() {
@@ -1595,16 +1961,32 @@ export default {
this.$refs.commonContent.refreshAutociteDisplay();
}
}
if (this.addContentVisible && this.$refs.addContent && typeof this.$refs.addContent.refreshAutociteDisplay === 'function') {
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') {
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') {
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') {
if (
this.pictVisible &&
this.$refs.tinymceChildImgNote &&
typeof this.$refs.tinymceChildImgNote.refreshAutociteDisplay === 'function'
) {
this.$refs.tinymceChildImgNote.refreshAutociteDisplay();
}
});
@@ -1752,7 +2134,7 @@ export default {
// 获取最终修改后的 HTML
content = div.innerHTML;
this.saveContent(content, this.currentContent.am_id);
} else if (type == 'addcontent') {
var hasTable = /<table[\s\S]*?>[\s\S]*?<\/table>/i.test(content);
@@ -1765,7 +2147,6 @@ export default {
return false;
}
var list = this.$commonJS.cleanAndParseWordContent(content);
this.saveContentList(list, this.currentId);
} else if (type == 'table') {
@@ -1814,8 +2195,6 @@ export default {
},
async saveContent(content, am_id) {
const loading = this.$loading({
lock: true,
text: 'Loading...',
@@ -1825,8 +2204,8 @@ export default {
var that = this;
var str = content.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1').trim();
str = str.replace(/<br\s*\/?>/gi, '');
str = str.replace(/<br\s*\/?>/gi, '');
str = await that.$commonJS.decodeHtml(str);
/** 点 Save 立即按将写入的正文重排下方列表(不等待接口),与弹窗内 [n] 一致 */
@@ -1935,7 +2314,6 @@ export default {
}
},
saveLateX(data) {
// 1. 从 data 中解构出 wrap (或者你命名的模式变量)
const { editorId, wmathId, latex, wrap } = data;
const newLatex = latex ? latex.trim() : '';
@@ -2191,7 +2569,6 @@ export default {
});
},
async addCommentSetting(content) {
await this.$api
.post(this.urlList.addComment, {
am_id: content.am_id,
@@ -2214,10 +2591,9 @@ export default {
});
},
async addComment(content) {
var str= this.$commonJS.transformHtmlString(content)
str=str.replace(/<br\s*\/?>/gi, '');
console.log("🚀 ~ addComment ~ content:", str);
var str = this.$commonJS.transformHtmlString(content);
str = str.replace(/<br\s*\/?>/gi, '');
console.log('🚀 ~ addComment ~ content:', str);
if (str == '') {
this.$message({
type: 'warning',
@@ -2299,13 +2675,12 @@ export default {
this.getCommentList();
},
editComment(comment, type) {
this.commentForm = {
...comment,
type: type,
remark: type == 'user' ? comment.author_remark : comment.remark
};
this.commentVisible = true;
},
async onAddComment(data) {
@@ -2425,7 +2800,6 @@ export default {
});
// 处理文件上传并传递回调函数
this.$commonJS.handleFileUpload(event, function (tables) {
if (tables.length == 0) {
loading.close();
that.$message({
@@ -2675,7 +3049,6 @@ export default {
}
},
updateChange(content, type) {
var str = this.$commonJS.transformHtmlString(content);
if (type == 'imgNote') {
this.picStyle1.note = str;
@@ -2965,8 +3338,6 @@ export default {
// 确定保存图片
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) : '';
@@ -3157,45 +3528,46 @@ export default {
}
},
async saveTable(content) {
const cleanTableData = (tableList) => {
if (tableList.length == 0) {
return [];
} else {
// 定义清理函数:去掉所有 br 标签和 TinyMCE 占位符
const cleanText = (text) => {
if (!text) return "";
// return text.replace(/<br\s*\/?>/gi, '').trim();
return text
};
if (tableList.length == 0) {
return [];
} else {
// 定义清理函数:去掉所有 br 标签和 TinyMCE 占位符
const cleanText = (text) => {
if (!text) return '';
// return text.replace(/<br\s*\/?>/gi, '').trim();
return text;
};
// 1. 获取处理后的干净表头
const header = tableList[0].map(cell => ({
...cell,
text: cleanText(cell.text)
}));
// 1. 获取处理后的干净表头
const header = tableList[0].map((cell) => ({
...cell,
text: cleanText(cell.text)
}));
// 2. 过滤逻辑
const cleanedTable = tableList.map((row) => {
// 首先:把每一行里的每个 cell.text 里的 <br> 都去掉
return row.map(cell => ({
...cell,
text: cleanText(cell.text)
}));
}).filter((row, index) => {
if (index === 0) return true;
// 2. 过滤逻辑
const cleanedTable = tableList
.map((row) => {
// 首先:把每一行里的每个 cell.text 里的 <br> 都去掉
return row.map((cell) => ({
...cell,
text: cleanText(cell.text)
}));
})
.filter((row, index) => {
if (index === 0) return true;
// 3. 此时比较的就是没有 <br> 的文本了
const isHeaderRow = row.every((cell, cellIndex) => {
return cell.text === header[cellIndex].text;
});
// 3. 此时比较的就是没有 <br> 的文本了
const isHeaderRow = row.every((cell, cellIndex) => {
return cell.text === header[cellIndex].text;
});
return !isHeaderRow;
});
return !isHeaderRow;
});
return cleanedTable;
}
};
return cleanedTable;
}
};
var cleanedTableList = content.table ? content.table : [];
cleanedTableList = cleanTableData(content.table);
@@ -3221,8 +3593,8 @@ export default {
});
strNote = strNote.replace(/<br\s*\/?>/gi, '');
strTitle = strTitle.replace(/<br\s*\/?>/gi, '');
var tableStr=JSON.stringify(cleanedTableList)
var tableStr = JSON.stringify(cleanedTableList);
if (this.lineStyle.visiTitle == 'Edit Table') {
this.$api
.post(this.urlList.editTable, {
@@ -3808,7 +4180,8 @@ wmath[data-wrap='inline'] {
background-color: rgb(252, 98, 93);
background-color: rgb(252 98 93 / 68%);
color: #333;
}.status {
}
.status {
display: block;
width: 36px;
height: 36px;
@@ -3828,8 +4201,7 @@ wmath[data-wrap='inline'] {
}
.status.float {
display:
inline-block;
display: inline-block;
}
.doiLink {
color: #409eff;

View File

@@ -432,8 +432,17 @@ export default {
let lastIndex = 0;
const pieces = [];
let replaced = 0;
let skippedSpecial = 0;
while ((m = re.exec(text)) !== null) {
const nums = this.parseBracketInnerToNumbers(m[1]);
// 规则:只要括号里包含 0 或 -1这一段不做解析替换但不影响其它正常 [1][2]
if (nums.some((n) => n === 0 || n === -1)) {
pieces.push({ type: 'text', s: text.slice(lastIndex, m.index) });
pieces.push({ type: 'text', s: m[0] });
lastIndex = m.index + m[0].length;
skippedSpecial++;
continue;
}
const ids = [];
const seen = new Set();
nums.forEach((n) => {
@@ -470,6 +479,13 @@ export default {
}
});
parent.replaceChild(frag, textNode);
if (skippedSpecial > 0) {
// 提示:仅提示一次即可;这里在节点级别提示可能重复,放到宏任务末尾合并展示
clearTimeout(this._autoLinkSkipToastTimer);
this._autoLinkSkipToastTimer = setTimeout(() => {
this.$message.info(`Skipped ${skippedSpecial} bracket cite(s) containing 0/-1.`);
}, 0);
}
return replaced;
},
renderAutociteInEditor(ed) {

View File

@@ -3679,8 +3679,8 @@ renderCiteLabels(html) {
text = this.highlightText3(raw, [], type, item.am_id);
}
} else {
// 预览与编辑「显示批注」同源:先切 wmath/autocite、再 renderCiteLabels避免仅跑片段导致角标/公式不一致
text = this.highlightText1(String(raw || ''), annotations || [], type);
// 预览:仅正文+角标+公式不传批注highlightText1 内 isPreview 会跳过 remarkbg
text = this.highlightText1(String(raw || ''), [], type);
}
// const finalHtml = text.replace(/<(?!(\/?(span|p|div|table|tr|td|th|b|i|strong|em|ul|ol|li|br|img|myh3|myfigure|mytable|blue|wmath)))/gi, '&lt;');
@@ -3821,6 +3821,16 @@ renderCiteLabels(html) {
}
if (lastIdx < src.length) parts.push({ type: 'plain', html: src.slice(lastIdx) });
// 预览:只拼回正文与公式块,再 renderCiteLabels不批注高亮、不做 AI/批注占位
if (this.isPreview) {
let htmlPrev = parts.map((p) => p.html).join('');
htmlPrev = this.renderCiteLabels(htmlPrev);
if (type === 0 && htmlPrev.trim() === '') {
htmlPrev += `<img contenteditable="false" src="${this.imagePath || ''}" alt="" style="width:20px;height:20px;opacity:.6;">`;
}
return htmlPrev;
}
// 2) 预处理批注(用 annotation.content
const anns = (annotations || [])
.map((a, i) => ({
@@ -4945,7 +4955,7 @@ renderCiteLabels(html) {
}
.proofreading-num {
z-index: 2;
background: #0082aa;
background: #e61a12;
color: rgb(255, 255, 255);
border-radius: 20px;
padding: 0px 6px;