添加期刊引用分析
This commit is contained in:
@@ -165,6 +165,22 @@ const en = {
|
||||
databases:'Database inclusion',
|
||||
|
||||
},
|
||||
journalCiteMate: {
|
||||
button: 'Citation analysis',
|
||||
yearRange: 'Time range',
|
||||
yearOptionOne: 'Last {n} year',
|
||||
yearOptionOther: 'Last {n} years',
|
||||
yearPlaceholder: 'Enter years',
|
||||
yearInvalid: 'Please enter a whole number greater than 0.',
|
||||
yearMax: 'Time range cannot exceed 100 years.',
|
||||
refresh: 'Refresh',
|
||||
topicTitle: 'Topics',
|
||||
countryTitle: 'country',
|
||||
name: 'Name',
|
||||
citeCount: 'Citations',
|
||||
totalCites: 'Total citations',
|
||||
loadFail: 'Failed to load citation analysis',
|
||||
},
|
||||
|
||||
partyRole: {
|
||||
identity: 'Identity',
|
||||
@@ -939,6 +955,8 @@ const en = {
|
||||
exitSourceMode: 'Exit source mode',
|
||||
sourcePlaceholder: 'Paste or edit full HTML here (DOCTYPE, html, head, body supported)',
|
||||
editorPlaceholder: 'Please enter...',
|
||||
clearEditor: 'Clear',
|
||||
clearEditorConfirm: 'Clear all email content?',
|
||||
validateTo: 'Please add at least one addressee',
|
||||
validateSubject: 'Please enter mail subject',
|
||||
sendSuccess: 'Send success',
|
||||
@@ -1128,6 +1146,54 @@ const en = {
|
||||
AnnotationList: 'Annotation List',
|
||||
Annotations: 'Comments',
|
||||
exportWord: 'Export Word',
|
||||
exportManuscriptEmpty: 'No content to export.',
|
||||
exportManuscriptSuccess: 'Word downloaded.',
|
||||
exportManuscriptFail: 'Failed to export Word.',
|
||||
citeRelevanceDetect: 'Download relevance HTML',
|
||||
citeRelevanceTitle: 'Citation relevance check',
|
||||
citeRelevanceTitleProgress: 'Citation relevance ({current}/{total})',
|
||||
citeRelevanceEmpty: 'No citations to review.',
|
||||
citeRelevanceNoContent: 'No manuscript content.',
|
||||
citeRelevanceNoCitations: 'No citation markers found (e.g. [1], [1-7]).',
|
||||
citeRelevanceLoadFail: 'Failed to load references.',
|
||||
citeRelevanceParagraph: 'Paragraph {n}',
|
||||
citeRelevanceRefLabel: 'Reference [{n}]',
|
||||
citeRelevanceRefMissing: 'Reference not found',
|
||||
citeRelevanceParagraphTitle: 'Paragraph',
|
||||
citeRelevanceReferenceTitle: 'Reference [{n}]',
|
||||
citeRelevanceWenaiTitle: 'Full prompt for WenAI',
|
||||
citeRelevanceCopy: 'Copy',
|
||||
citeRelevanceCopyWenai: 'Copy full prompt',
|
||||
citeRelevanceCopySuccess: 'Copied to clipboard',
|
||||
citeRelevanceCopyFail: 'Copy failed',
|
||||
citeRelevanceCopyEmpty: 'Nothing to copy',
|
||||
citeRelevancePrev: 'Previous',
|
||||
citeRelevanceNext: 'Next',
|
||||
citeRelevanceClose: 'Close',
|
||||
citeRelevanceCopyAllWenai: 'Copy all prompts',
|
||||
citeRelevanceBatchTip: '{n} references per batch within each paragraph',
|
||||
citeRelevanceRefBatch: 'Batch refs {nums}',
|
||||
citeRelevanceBatchProgress: 'Paragraph batch {current}/{total}',
|
||||
citeRelevanceGroupTotal: '{n} paragraphs to review',
|
||||
citeRelevanceGroupNavLabel: 'Para {paragraph} · {refs}',
|
||||
citeRelevanceJumpTo: 'Jump to',
|
||||
citeRelevanceCopyGroup: 'Copy paragraph',
|
||||
citeRelevanceDownloadHtml: 'Download HTML',
|
||||
citeRelevanceDownloadHtmlSuccess: 'HTML downloaded',
|
||||
refHtmlDownload: 'Download references HTML',
|
||||
refHtmlDownloadSuccess: 'References HTML downloaded',
|
||||
refHtmlToWord: 'References HTML to Word',
|
||||
refHtmlToWordSuccess: 'Word exported with edited references',
|
||||
refHtmlToWordFail: 'Failed to convert references HTML to Word',
|
||||
refHtmlLoadFail: 'Failed to load references',
|
||||
refHtmlEmpty: 'No references found',
|
||||
refHtmlParseEmpty: 'No reference entries found in HTML. Please use the HTML downloaded from this system.',
|
||||
refHtmlPageTitle: 'References editor',
|
||||
refHtmlReferencesTitle: 'References',
|
||||
refHtmlIntro: 'Edit each reference directly, or paste AI output (supports <i> italics and other HTML). When done, click "Download edited HTML", then use "References HTML to Word" on the typesetting page.',
|
||||
refHtmlSaveTip: 'Tip: save the HTML after editing, then import it on the typesetting page to export Word.',
|
||||
refHtmlDownloadEdited: 'Download edited HTML',
|
||||
refHtmlDownloadFileName: 'references-edited.html',
|
||||
exportImg: 'Export PNG',
|
||||
PaperRotation: 'Paper Rotation',
|
||||
removeAnnotations: 'Are you sure you want to delete this Annotation?',
|
||||
|
||||
@@ -158,6 +158,22 @@ const zh = {
|
||||
databases:'收录数据库',
|
||||
|
||||
},
|
||||
journalCiteMate: {
|
||||
button: '期刊引用分析',
|
||||
yearRange: '统计范围',
|
||||
yearOptionOne: '近 {n} 年',
|
||||
yearOptionOther: '近 {n} 年',
|
||||
yearPlaceholder: '输入年数',
|
||||
yearInvalid: '请输入大于 0 的整数',
|
||||
yearMax: '统计范围最多为 100 年',
|
||||
refresh: '刷新',
|
||||
topicTitle: '话题引用',
|
||||
countryTitle: 'country',
|
||||
name: '名称',
|
||||
citeCount: '引用数量',
|
||||
totalCites: '引用总数',
|
||||
loadFail: '加载期刊引用分析失败',
|
||||
},
|
||||
paperArticleCount: {
|
||||
Periodroll: '期卷',
|
||||
article: '文章',
|
||||
@@ -924,6 +940,8 @@ const zh = {
|
||||
exitSourceMode: '退出源码编辑',
|
||||
sourcePlaceholder: '在此粘贴或编辑完整 HTML,支持 <!DOCTYPE>、<html>、<head>、<body> 等',
|
||||
editorPlaceholder: '请输入邮件内容...',
|
||||
clearEditor: '清空',
|
||||
clearEditorConfirm: '确定清空邮件正文吗?',
|
||||
validateTo: '请至少添加一个收件人',
|
||||
validateSubject: '请输入邮件主题',
|
||||
sendSuccess: '发送成功',
|
||||
@@ -1114,6 +1132,54 @@ const zh = {
|
||||
AnnotationList: '批注列表',
|
||||
Annotations: '批注',
|
||||
exportWord: '导出 Word',
|
||||
exportManuscriptEmpty: '没有可导出的内容。',
|
||||
exportManuscriptSuccess: 'Word 已下载。',
|
||||
exportManuscriptFail: 'Word 导出失败。',
|
||||
citeRelevanceDetect: '下载相关性 HTML',
|
||||
citeRelevanceTitle: '引文相关性核查',
|
||||
citeRelevanceTitleProgress: '引文相关性核查 ({current}/{total})',
|
||||
citeRelevanceEmpty: '当前没有可核查的引文。',
|
||||
citeRelevanceNoContent: '没有正文内容。',
|
||||
citeRelevanceNoCitations: '正文中未找到引用标记(如 [1]、[1-7])。',
|
||||
citeRelevanceLoadFail: '加载参考文献失败。',
|
||||
citeRelevanceParagraph: '正文段落 {n}',
|
||||
citeRelevanceRefLabel: '参考文献 [{n}]',
|
||||
citeRelevanceRefMissing: '未找到对应文献',
|
||||
citeRelevanceParagraphTitle: '正文段落',
|
||||
citeRelevanceReferenceTitle: '参考文献 [{n}]',
|
||||
citeRelevanceWenaiTitle: '复制到 WenAI 的完整提示',
|
||||
citeRelevanceCopy: '复制',
|
||||
citeRelevanceCopyWenai: '复制完整提示',
|
||||
citeRelevanceCopySuccess: '已复制到剪贴板',
|
||||
citeRelevanceCopyFail: '复制失败',
|
||||
citeRelevanceCopyEmpty: '没有可复制的内容',
|
||||
citeRelevancePrev: '上一条',
|
||||
citeRelevanceNext: '下一条',
|
||||
citeRelevanceClose: '关闭',
|
||||
citeRelevanceCopyAllWenai: '复制全部提示',
|
||||
citeRelevanceBatchTip: '每段正文按 {n} 条参考文献一批提问',
|
||||
citeRelevanceRefBatch: '本批文献 {nums}',
|
||||
citeRelevanceBatchProgress: '本段第 {current}/{total} 批',
|
||||
citeRelevanceGroupTotal: '共 {n} 段待核查',
|
||||
citeRelevanceGroupNavLabel: '段落 {paragraph} · {refs}',
|
||||
citeRelevanceJumpTo: '快速跳转',
|
||||
citeRelevanceCopyGroup: '复制本段',
|
||||
citeRelevanceDownloadHtml: '下载 HTML',
|
||||
citeRelevanceDownloadHtmlSuccess: 'HTML 已下载',
|
||||
refHtmlDownload: '下载参考文献 HTML',
|
||||
refHtmlDownloadSuccess: '参考文献 HTML 已下载',
|
||||
refHtmlToWord: '参考文献 HTML 转 Word',
|
||||
refHtmlToWordSuccess: 'Word 已导出(含编辑后的参考文献)',
|
||||
refHtmlToWordFail: '参考文献 HTML 转 Word 失败',
|
||||
refHtmlLoadFail: '加载参考文献失败',
|
||||
refHtmlEmpty: '暂无参考文献',
|
||||
refHtmlParseEmpty: 'HTML 中未找到参考文献条目,请使用本系统下载的参考文献 HTML',
|
||||
refHtmlPageTitle: '参考文献编辑',
|
||||
refHtmlReferencesTitle: 'References',
|
||||
refHtmlIntro: '可直接编辑每条文献,或粘贴 AI 返回的内容(支持 <i> 斜体等 HTML 标签)。编辑完成后点击「下载编辑后的 HTML」,再在排版页使用「参考文献 HTML 转 Word」。',
|
||||
refHtmlSaveTip: '提示:编辑后请点击上方按钮保存 HTML;也可在排版页导入该文件导出 Word。',
|
||||
refHtmlDownloadEdited: '下载编辑后的 HTML',
|
||||
refHtmlDownloadFileName: 'references-edited.html',
|
||||
exportImg: '导出 图片',
|
||||
PaperRotation: '纸张方向',
|
||||
removeAnnotations: '确定要删除这条批注吗?',
|
||||
|
||||
@@ -92,6 +92,7 @@
|
||||
<div style="width: 100%; width: calc(100% - 260px); float: right; height: calc(100% - 0px); background-color: #e4e9ed">
|
||||
<common-word
|
||||
:articleId="articleId"
|
||||
:pArticleId="pArticleId"
|
||||
v-if="htmlContent"
|
||||
ref="commonWord"
|
||||
:value="htmlContent"
|
||||
@@ -464,6 +465,7 @@ export default {
|
||||
ManuscirptContent: [],
|
||||
mathFormulasList: [],
|
||||
articleId: this.$route.query.id,
|
||||
pArticleId: this.$route.query.p_article_id || '',
|
||||
isShowComment: false,
|
||||
urlList: {
|
||||
executeProofreading: 'api/Proofread/change',
|
||||
|
||||
@@ -143,6 +143,21 @@
|
||||
<i class="el-icon-connection" style="color: #4665b0"></i> {{ $t('menu.ClassificationmanagementInfo') }}
|
||||
</p>
|
||||
|
||||
<p
|
||||
@click="openJournalCiteMate(scope.row)"
|
||||
style="
|
||||
background-color: #fdf6ec;
|
||||
color: #e6a23c;
|
||||
cursor: pointer;
|
||||
padding: 2px 2px;
|
||||
font-weight: bold;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 5px;
|
||||
"
|
||||
>
|
||||
<i class="el-icon-data-analysis" style="color: #e6a23c"></i> {{ $t('journalCiteMate.button') }}
|
||||
</p>
|
||||
|
||||
<p
|
||||
@click="
|
||||
openJournalAgreement({
|
||||
@@ -382,6 +397,91 @@
|
||||
>
|
||||
<common-agreement ref="commonAgreementRef" :journal="currentJournal" :urlList="urlList"></common-agreement>
|
||||
</el-drawer>
|
||||
<el-drawer
|
||||
append-to-body
|
||||
destroy-on-close
|
||||
custom-class="journal-cite-mate-drawer"
|
||||
:title="$t('GroupClassification.Journal') + ' : ' + currentJournal.title"
|
||||
:visible.sync="drawerCiteMate"
|
||||
direction="rtl"
|
||||
:before-close="handleCiteMateClose"
|
||||
:wrapperClosable="false"
|
||||
size="70%"
|
||||
@opened="updateCiteMateTableHeight"
|
||||
>
|
||||
<div class="journal-cite-mate-panel" v-loading="citeMateLoading">
|
||||
<div class="journal-cite-mate-toolbar">
|
||||
<span class="journal-cite-mate-label">{{ $t('journalCiteMate.yearRange') }}</span>
|
||||
<el-select
|
||||
v-model="citeMateYear"
|
||||
size="small"
|
||||
class="journal-cite-mate-year-select"
|
||||
filterable
|
||||
allow-create
|
||||
default-first-option
|
||||
:placeholder="$t('journalCiteMate.yearPlaceholder')"
|
||||
@change="handleCiteMateYearChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in citeMateYearOptionList"
|
||||
:key="item"
|
||||
:label="formatCiteMateYearLabel(item)"
|
||||
:value="String(item)"
|
||||
></el-option>
|
||||
</el-select>
|
||||
<el-button size="small" type="primary" plain icon="el-icon-refresh" @click="loadJournalCiteMate">
|
||||
{{ $t('journalCiteMate.refresh') }}
|
||||
</el-button>
|
||||
<span v-if="citeMateCitesNum != null" class="journal-cite-mate-total">
|
||||
{{ $t('journalCiteMate.totalCites') }}:<strong>{{ citeMateCitesNum }}</strong>
|
||||
</span>
|
||||
</div>
|
||||
<el-row :gutter="16" class="journal-cite-mate-body">
|
||||
<el-col :span="12">
|
||||
<div ref="citeMateBlock" class="journal-cite-mate-block">
|
||||
<div class="journal-cite-mate-block-title">
|
||||
<span class="journal-cite-mate-block-name">{{ $t('journalCiteMate.topicTitle') }}</span>
|
||||
<span class="journal-cite-mate-count">{{ citeMateTopicList.length }}</span>
|
||||
</div>
|
||||
<el-table :data="citeMateTopicList" border size="mini" :height="citeMateTableHeight" empty-text="—">
|
||||
<el-table-column type="index" label="#" width="50" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('journalCiteMate.name')" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
{{ getCiteMateRowName(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('journalCiteMate.citeCount')" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getCiteMateRowCount(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<div class="journal-cite-mate-block">
|
||||
<div class="journal-cite-mate-block-title">
|
||||
<span class="journal-cite-mate-block-name">{{ $t('journalCiteMate.countryTitle') }}</span>
|
||||
<span class="journal-cite-mate-count">{{ citeMateCountryList.length }}</span>
|
||||
</div>
|
||||
<el-table :data="citeMateCountryList" border size="mini" :height="citeMateTableHeight" empty-text="—">
|
||||
<el-table-column type="index" label="#" width="50" align="center"></el-table-column>
|
||||
<el-table-column :label="$t('journalCiteMate.name')" min-width="160">
|
||||
<template slot-scope="scope">
|
||||
{{ getCiteMateRowName(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column :label="$t('journalCiteMate.citeCount')" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
{{ getCiteMateRowCount(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</el-drawer>
|
||||
<el-dialog :title="$t('JournalCitationAnalysis.feeDialogTitle')" :visible.sync="feeDialogVisible" width="800px">
|
||||
<el-alert
|
||||
:title="$t('JournalCitationAnalysis.feeWarningTitle')"
|
||||
@@ -442,6 +542,23 @@ import commonclass from './class.vue';
|
||||
import commonAgreement from './agreement.vue';
|
||||
import commonJournalInstallment from '../JournalInstallment/index.vue';
|
||||
import bus from '@/components/common/bus';
|
||||
|
||||
function normalizeCiteMateLists(data) {
|
||||
if (!data) {
|
||||
return { topics: [], countries: [], citesNum: null };
|
||||
}
|
||||
if (Array.isArray(data)) {
|
||||
return { topics: data, countries: [], citesNum: null };
|
||||
}
|
||||
const topics = data.topic || data.topics || data.topic_list || data.topicList || [];
|
||||
const countries = data.country || data.countries || data.country_list || data.countryList || data.nation || data.nation_list || [];
|
||||
const citesNum = data.cites_num != null ? data.cites_num : data.cite_num != null ? data.cite_num : data.total != null ? data.total : null;
|
||||
return {
|
||||
topics: Array.isArray(topics) ? topics : [],
|
||||
countries: Array.isArray(countries) ? countries : [],
|
||||
citesNum: citesNum
|
||||
};
|
||||
}
|
||||
export default {
|
||||
name: 'app',
|
||||
props: ['urlList', 'source'],
|
||||
@@ -500,6 +617,14 @@ export default {
|
||||
drawer: false,
|
||||
drawerJournalInstallment: false,
|
||||
drawerAgreement: false,
|
||||
drawerCiteMate: false,
|
||||
citeMateLoading: false,
|
||||
citeMateYear: '2',
|
||||
citeMateYearOptions: [1, 2, 3, 4, 5, 6],
|
||||
citeMateTopicList: [],
|
||||
citeMateCountryList: [],
|
||||
citeMateCitesNum: null,
|
||||
citeMateTableHeight: 420,
|
||||
bd: true,
|
||||
ruleForm: {
|
||||
major_title: '',
|
||||
@@ -607,6 +732,28 @@ export default {
|
||||
this.ruleForm = {};
|
||||
}
|
||||
}
|
||||
},
|
||||
drawerCiteMate(val) {
|
||||
if (val) {
|
||||
this.$nextTick(() => {
|
||||
this.updateCiteMateTableHeight();
|
||||
window.addEventListener('resize', this.updateCiteMateTableHeight);
|
||||
});
|
||||
} else {
|
||||
window.removeEventListener('resize', this.updateCiteMateTableHeight);
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
citeMateYearOptionList() {
|
||||
const presets = this.citeMateYearOptions.slice();
|
||||
const current = parseInt(this.citeMateYear, 10);
|
||||
if (Number.isFinite(current) && current > 0 && presets.indexOf(current) === -1) {
|
||||
presets.push(current);
|
||||
}
|
||||
return presets.sort(function (a, b) {
|
||||
return a - b;
|
||||
});
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
@@ -617,6 +764,9 @@ export default {
|
||||
async activated() {
|
||||
await this.getDate();
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.updateCiteMateTableHeight);
|
||||
},
|
||||
methods: {
|
||||
// 失去焦点事件
|
||||
onEditorBlur(quill) {
|
||||
@@ -960,6 +1110,118 @@ export default {
|
||||
that.$refs.commonAgreementRef.init();
|
||||
});
|
||||
},
|
||||
openJournalCiteMate(data) {
|
||||
this.currentJournal = data || {};
|
||||
this.citeMateYear = '2';
|
||||
this.drawerCiteMate = true;
|
||||
this.loadJournalCiteMate();
|
||||
},
|
||||
handleCiteMateClose(done) {
|
||||
this.drawerCiteMate = false;
|
||||
if (typeof done === 'function') {
|
||||
done();
|
||||
}
|
||||
},
|
||||
updateCiteMateTableHeight() {
|
||||
this.$nextTick(() => {
|
||||
const block = this.$refs.citeMateBlock;
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
const el = Array.isArray(block) ? block[0] : block;
|
||||
const title = el.querySelector('.journal-cite-mate-block-title');
|
||||
const style = window.getComputedStyle(el);
|
||||
const paddingTop = parseFloat(style.paddingTop) || 0;
|
||||
const paddingBottom = parseFloat(style.paddingBottom) || 0;
|
||||
let titleHeight = 0;
|
||||
if (title) {
|
||||
const titleStyle = window.getComputedStyle(title);
|
||||
titleHeight = title.offsetHeight + (parseFloat(titleStyle.marginBottom) || 0);
|
||||
}
|
||||
const nextHeight = el.clientHeight - paddingTop - paddingBottom - titleHeight;
|
||||
if (nextHeight > 0) {
|
||||
this.citeMateTableHeight = nextHeight;
|
||||
}
|
||||
});
|
||||
},
|
||||
formatCiteMateYearLabel(years) {
|
||||
const num = parseInt(years, 10);
|
||||
if (!Number.isFinite(num) || num < 1) {
|
||||
return String(years);
|
||||
}
|
||||
if (num === 1) {
|
||||
return this.$t('journalCiteMate.yearOptionOne', { n: num });
|
||||
}
|
||||
return this.$t('journalCiteMate.yearOptionOther', { n: num });
|
||||
},
|
||||
normalizeCiteMateYear(value) {
|
||||
const parsed = parseInt(String(value || '').trim(), 10);
|
||||
if (!Number.isFinite(parsed) || parsed < 1) {
|
||||
this.$message.warning(this.$t('journalCiteMate.yearInvalid'));
|
||||
return '2';
|
||||
}
|
||||
if (parsed > 100) {
|
||||
this.$message.warning(this.$t('journalCiteMate.yearMax'));
|
||||
return '100';
|
||||
}
|
||||
return String(parsed);
|
||||
},
|
||||
handleCiteMateYearChange(value) {
|
||||
this.citeMateYear = this.normalizeCiteMateYear(value);
|
||||
this.loadJournalCiteMate();
|
||||
},
|
||||
getCiteMateRowName(row) {
|
||||
if (!row) {
|
||||
return '—';
|
||||
}
|
||||
return row.topic_name || row.country || row.country_name || row.nation_name || row.name || row.title || '—';
|
||||
},
|
||||
getCiteMateRowCount(row) {
|
||||
const value = row.num != null ? row.num : row.count != null ? row.count : row.cite_num;
|
||||
return value != null ? value : '—';
|
||||
},
|
||||
loadJournalCiteMate() {
|
||||
const journalId = this.currentJournal && this.currentJournal.journal_id;
|
||||
if (!journalId) {
|
||||
this.citeMateTopicList = [];
|
||||
this.citeMateCountryList = [];
|
||||
this.citeMateCitesNum = null;
|
||||
return;
|
||||
}
|
||||
|
||||
this.citeMateLoading = true;
|
||||
this.$api
|
||||
.post('api/journal/citeMate', {
|
||||
journal_id: String(journalId),
|
||||
year: String(this.citeMateYear || '2')
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
const lists = normalizeCiteMateLists(res.data);
|
||||
this.citeMateTopicList = lists.topics;
|
||||
this.citeMateCountryList = lists.countries;
|
||||
this.citeMateCitesNum = lists.citesNum;
|
||||
} else {
|
||||
this.citeMateTopicList = [];
|
||||
this.citeMateCountryList = [];
|
||||
this.citeMateCitesNum = null;
|
||||
this.$message.error(res.msg || this.$t('journalCiteMate.loadFail'));
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
this.citeMateTopicList = [];
|
||||
this.citeMateCountryList = [];
|
||||
this.citeMateCitesNum = null;
|
||||
this.$message.error(this.$t('journalCiteMate.loadFail'));
|
||||
})
|
||||
.finally(() => {
|
||||
this.citeMateLoading = false;
|
||||
if (this.drawerCiteMate) {
|
||||
this.updateCiteMateTableHeight();
|
||||
}
|
||||
});
|
||||
},
|
||||
renameKeys(obj, oldKey, newKey) {
|
||||
if (obj[oldKey]) {
|
||||
obj[newKey] = obj[oldKey];
|
||||
@@ -1457,6 +1719,100 @@ export default {
|
||||
padding: 22px 0 0 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.journal-cite-mate-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
padding: 0 20px 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.journal-cite-mate-drawer .el-drawer__body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.journal-cite-mate-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.journal-cite-mate-body.el-row {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.journal-cite-mate-body > .el-col {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.journal-cite-mate-block {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
min-height: 0;
|
||||
box-sizing: border-box;
|
||||
background: #fff;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.journal-cite-mate-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin-bottom: 16px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.journal-cite-mate-year-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.journal-cite-mate-label {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.journal-cite-mate-block-title {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.journal-cite-mate-block-name {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.journal-cite-mate-count {
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.journal-cite-mate-total {
|
||||
margin-left: auto;
|
||||
font-size: 14px;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.journal-cite-mate-total strong {
|
||||
color: #409eff;
|
||||
font-size: 16px;
|
||||
}
|
||||
.WeChatCode .avatar-uploader .el-upload {
|
||||
width: 120px !important;
|
||||
height: 120px !important;
|
||||
|
||||
@@ -1421,7 +1421,8 @@ export default {
|
||||
this.$router.resolve({
|
||||
path: '/GenerateCharts',
|
||||
query: {
|
||||
id: this.$route.query.id
|
||||
id: this.$route.query.id,
|
||||
p_article_id: this.p_article_id
|
||||
}
|
||||
}).href,
|
||||
'_blank'
|
||||
|
||||
@@ -54,7 +54,21 @@
|
||||
<!-- 基本信息 -->
|
||||
<div :ref="tabsList[0].refName" class="scroll-item">
|
||||
<div class="bor_style_onli" style="margin: 20px 0">
|
||||
<h4>{{ tabsList[0].name }}</h4>
|
||||
<h4>
|
||||
{{ tabsList[0].name }}
|
||||
<el-button
|
||||
v-if="zyModeEnabled"
|
||||
style="float: right"
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-download"
|
||||
:loading="importingSubmission"
|
||||
:disabled="!detailMes.article_id"
|
||||
@click="importFromSubmission"
|
||||
>
|
||||
Import from Submission
|
||||
</el-button>
|
||||
</h4>
|
||||
<el-form ref="Mes_Form" :model="detailMes" :rules="rules" label-width="220px">
|
||||
<el-form-item label="Title :" prop="title">
|
||||
<el-input v-model="detailMes.title"></el-input>
|
||||
@@ -235,7 +249,14 @@
|
||||
Add Author</el-button
|
||||
>
|
||||
</h4>
|
||||
<el-table :data="authorData" border class="table" ref="multipleTable" header-cell-class-name="table-header">
|
||||
<el-table
|
||||
:key="'author-' + authorTableKey"
|
||||
:data="authorData"
|
||||
border
|
||||
class="table"
|
||||
ref="authorTable"
|
||||
header-cell-class-name="table-header"
|
||||
>
|
||||
<el-table-column prop="author_name" label="Author"></el-table-column>
|
||||
<el-table-column label="First author" align="center" width="100px">
|
||||
<template slot-scope="scope">
|
||||
@@ -454,7 +475,14 @@
|
||||
>
|
||||
</h4>
|
||||
<!-- 机构列表 -->
|
||||
<el-table :data="schoolData" border class="table" ref="multipleTable" header-cell-class-name="table-header">
|
||||
<el-table
|
||||
:key="'school-' + schoolTableKey"
|
||||
:data="schoolData"
|
||||
border
|
||||
class="table"
|
||||
ref="schoolTable"
|
||||
header-cell-class-name="table-header"
|
||||
>
|
||||
<el-table-column type="index" label="No." width="55" align="center"></el-table-column>
|
||||
<el-table-column prop="organ_name" label="Affiliation"></el-table-column>
|
||||
<el-table-column label="" width="190" align="center">
|
||||
@@ -952,6 +980,79 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 导入前作者预览 -->
|
||||
<el-dialog
|
||||
title="Review Authors Before Import"
|
||||
:visible.sync="importAuthorPreviewVisible"
|
||||
width="960px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<p style="color: #666; line-height: 22px; margin: 0 0 12px 0">
|
||||
Check recognized authors, adjust order manually, or paste a name sequence to auto-sort. Required empty fields will be saved as a space.
|
||||
</p>
|
||||
<el-input
|
||||
v-model="importAuthorOrderText"
|
||||
type="textarea"
|
||||
:autosize="{ minRows: 2, maxRows: 4 }"
|
||||
placeholder="eg: Sijing PENG1,¶, Ting ZHENG1,¶, Jin CONG1, Xiang WANG1, Chuanying ZHANG1, Jiejie GE1,2*"
|
||||
></el-input>
|
||||
<div style="margin: 10px 0 16px 0">
|
||||
<el-button size="mini" type="primary" plain @click="applyImportAuthorOrder">Apply Order</el-button>
|
||||
</div>
|
||||
<el-table :data="importPreviewAuthors" border size="small" max-height="360">
|
||||
<el-table-column type="index" label="No." width="50" align="center"></el-table-column>
|
||||
<el-table-column label="First name" min-width="120">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.firstname || scope.row.first_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Last name" min-width="100">
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.lastname || scope.row.last_name }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="First author" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag round :type="Number(scope.row.is_super) === 1 ? 'success' : 'danger'" size="mini">
|
||||
{{ Number(scope.row.is_super) === 1 ? '√' : '×' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Corresponding" width="110" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-tag round :type="Number(scope.row.is_report) === 1 ? 'success' : 'danger'" size="mini">
|
||||
{{ Number(scope.row.is_report) === 1 ? '√' : '×' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="email" label="Email" min-width="150" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ Number(scope.row.is_report) === 1 ? scope.row.email || ' ' : '' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Affiliation" min-width="220" show-overflow-tooltip>
|
||||
<template slot-scope="scope">
|
||||
{{ getImportAuthorAffiliation(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="Order" width="100" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" :disabled="scope.$index === 0" @click="moveImportAuthor(scope.$index, 'up')">Up</el-button>
|
||||
<el-button
|
||||
type="text"
|
||||
:disabled="scope.$index === importPreviewAuthors.length - 1"
|
||||
@click="moveImportAuthor(scope.$index, 'down')"
|
||||
>Down</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="importAuthorPreviewVisible = false">Cancel</el-button>
|
||||
<el-button type="primary" :loading="importingSubmission" @click="confirmImportFromSubmission">Confirm Import</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 上线弹出框 --->
|
||||
<el-dialog title="Tips" :visible.sync="onlineVisible" width="680px" :close-on-click-modal="false">
|
||||
<p style="line-height: 24px">Are you sure you want to push this manuscript to the official website? You can preview first.</p>
|
||||
@@ -1067,9 +1168,31 @@ import comArtHtmlCreatNewProduce from './comArtHtmlCreatNewProduce.vue';
|
||||
import commonTopic from '../page/components/article/topic.vue';
|
||||
import commonRelated from '../page/components/article/Related.vue';
|
||||
import Tinymce from '@/components/page/components/Tinymce';
|
||||
import {
|
||||
mapEssentialFields,
|
||||
buildOrganIdMap,
|
||||
buildProductionAuthorPayload,
|
||||
buildAuthorReferenceAbbr,
|
||||
collectSubmissionOrgans,
|
||||
resolveSubmissionAuthors,
|
||||
normalizeSubmissionAuthors,
|
||||
normalizeRichTextContent,
|
||||
sortAuthorsByOrderString,
|
||||
moveAuthorInList,
|
||||
getAuthorDisplayName,
|
||||
getAuthorAffiliationPreview
|
||||
} from '@/utils/productionSubmissionImport';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
importingSubmission: false,
|
||||
importAuthorPreviewVisible: false,
|
||||
importAuthorOrderText: '',
|
||||
importPreviewAuthors: [],
|
||||
importPreviewContext: null,
|
||||
authorTableKey: 0,
|
||||
schoolTableKey: 0,
|
||||
isGenerating: false, // 按钮加载状态
|
||||
percentage: 0, // 进度条
|
||||
finalReview: null,
|
||||
@@ -1609,6 +1732,19 @@ export default {
|
||||
updateChange(type, content) {
|
||||
this[type] = content;
|
||||
},
|
||||
applyRichTextToEditor(refName, field, content) {
|
||||
const normalized = normalizeRichTextContent(content);
|
||||
this[field] = normalized;
|
||||
if (field === 'abstract') {
|
||||
this.detailMes.abstract = normalized;
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
const editor = this.$refs[refName];
|
||||
if (editor && typeof editor.setContent === 'function') {
|
||||
editor.setContent(normalized);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
getTinymceContent(type) {
|
||||
this.$refs.tinymceChild1.getContent(type);
|
||||
@@ -1905,34 +2041,278 @@ export default {
|
||||
},
|
||||
|
||||
getAuthorJG() {
|
||||
// 获取作者列表
|
||||
this.$api
|
||||
.post('api/Production/getAuthorlist', this.idform)
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.authorData = res.data.authors;
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error(err);
|
||||
});
|
||||
return this.refreshAuthorOrganLists().catch((err) => {
|
||||
this.$message.error(err);
|
||||
});
|
||||
},
|
||||
refreshAuthorOrganLists() {
|
||||
this.idform.p_article_id = this.p_article_id;
|
||||
return Promise.all([
|
||||
this.$api.post('api/Production/getAuthorlist', this.idform),
|
||||
this.$api.post('api/Production/getOrganList', this.idform)
|
||||
]).then(([authorRes, organRes]) => {
|
||||
if (authorRes.code == 0) {
|
||||
this.$set(this, 'authorData', [...(authorRes.data.authors || [])]);
|
||||
} else {
|
||||
throw authorRes.msg || 'Failed to load authors';
|
||||
}
|
||||
if (organRes.code == 0) {
|
||||
this.$set(this, 'schoolData', [...(organRes.data.organs || [])]);
|
||||
this.$set(this, 'mechanism', [...(organRes.data.organs || [])]);
|
||||
} else {
|
||||
throw organRes.msg || 'Failed to load affiliations';
|
||||
}
|
||||
});
|
||||
},
|
||||
fetchAuthorList() {
|
||||
this.idform.p_article_id = this.p_article_id;
|
||||
return this.$api.post('api/Production/getAuthorlist', this.idform).then((authorRes) => {
|
||||
if (authorRes.code == 0) {
|
||||
this.$set(this, 'authorData', [...(authorRes.data.authors || [])]);
|
||||
return authorRes;
|
||||
}
|
||||
throw authorRes.msg || 'Failed to load authors';
|
||||
});
|
||||
},
|
||||
fetchOrganList() {
|
||||
this.idform.p_article_id = this.p_article_id;
|
||||
return this.$api.post('api/Production/getOrganList', this.idform).then((organRes) => {
|
||||
if (organRes.code == 0) {
|
||||
this.$set(this, 'schoolData', [...(organRes.data.organs || [])]);
|
||||
this.$set(this, 'mechanism', [...(organRes.data.organs || [])]);
|
||||
return organRes;
|
||||
}
|
||||
throw organRes.msg || 'Failed to load affiliations';
|
||||
});
|
||||
},
|
||||
async refreshAuthorSection(options = {}) {
|
||||
const { authorsOnly = false, organsOnly = false } = options;
|
||||
if (organsOnly) {
|
||||
await this.fetchOrganList();
|
||||
this.schoolTableKey += 1;
|
||||
} else if (authorsOnly) {
|
||||
await this.fetchAuthorList();
|
||||
this.authorTableKey += 1;
|
||||
} else {
|
||||
await this.refreshAuthorOrganLists();
|
||||
this.authorTableKey += 1;
|
||||
this.schoolTableKey += 1;
|
||||
}
|
||||
await this.$nextTick();
|
||||
this.$forceUpdate();
|
||||
await this.$nextTick();
|
||||
if (this.$refs.authorTable && this.$refs.authorTable.doLayout) {
|
||||
this.$refs.authorTable.doLayout();
|
||||
}
|
||||
if (this.$refs.schoolTable && this.$refs.schoolTable.doLayout) {
|
||||
this.$refs.schoolTable.doLayout();
|
||||
}
|
||||
},
|
||||
getImportAuthorName(author) {
|
||||
return getAuthorDisplayName(author);
|
||||
},
|
||||
getImportAuthorAffiliation(author) {
|
||||
return getAuthorAffiliationPreview(author);
|
||||
},
|
||||
applyImportAuthorOrder() {
|
||||
if (!this.importAuthorOrderText.trim()) {
|
||||
this.$message.warning('Please paste an author order string first.');
|
||||
return;
|
||||
}
|
||||
this.importPreviewAuthors = sortAuthorsByOrderString(this.importPreviewAuthors, this.importAuthorOrderText);
|
||||
this.$message.success('Author order updated.');
|
||||
},
|
||||
moveImportAuthor(index, direction) {
|
||||
this.importPreviewAuthors = moveAuthorInList(this.importPreviewAuthors, index, direction);
|
||||
},
|
||||
async fetchSubmissionImportContext(articleId) {
|
||||
const detailRes = await this.$api.post('api/Article/getArticleDetail', {
|
||||
articleId,
|
||||
human: 'editor'
|
||||
});
|
||||
const article = (detailRes && detailRes.article) || {};
|
||||
const userId = article.user_id;
|
||||
|
||||
// 获取机构列表
|
||||
this.$api
|
||||
.post('api/Production/getOrganList', this.idform)
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.schoolData = res.data.organs;
|
||||
this.mechanism = res.data.organs;
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
const requestList = [this.$api.post('api/Article/getAuthors', { article_id: articleId }).catch(() => null)];
|
||||
if (userId) {
|
||||
requestList.push(
|
||||
this.$api.post('api/Organ/lists', { article_id: articleId, user_id: userId }).catch(() => null)
|
||||
);
|
||||
}
|
||||
const [authorsRes, organsRes] = await Promise.all(requestList);
|
||||
|
||||
const authors = normalizeSubmissionAuthors(resolveSubmissionAuthors(authorsRes, detailRes));
|
||||
const submissionOrgans = collectSubmissionOrgans(
|
||||
organsRes && Number(organsRes.status) === 1 && Array.isArray(organsRes.data) ? organsRes.data : [],
|
||||
authors
|
||||
);
|
||||
const mapped = mapEssentialFields(article, authors);
|
||||
|
||||
return {
|
||||
article,
|
||||
authors,
|
||||
submissionOrgans,
|
||||
mapped
|
||||
};
|
||||
},
|
||||
applyImportedEssentialFields(mapped) {
|
||||
this.detailMes.title = mapped.title;
|
||||
this.detailMes.type = mapped.type;
|
||||
this.detailMes.keywords = mapped.keywords;
|
||||
this.detailMes.acknowledgment = mapped.acknowledgment;
|
||||
this.detailMes.author_contribution = mapped.author_contribution;
|
||||
this.detailMes.abbreviation = mapped.abbreviation;
|
||||
this.applyRichTextToEditor('tinymceChild1', 'abstract', mapped.abstract);
|
||||
},
|
||||
async importFromSubmission() {
|
||||
const articleId = this.detailMes && this.detailMes.article_id;
|
||||
if (!articleId) {
|
||||
this.$message.error('No linked submission article found.');
|
||||
return;
|
||||
}
|
||||
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: 'Loading submission data...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
this.importingSubmission = true;
|
||||
|
||||
try {
|
||||
const context = await this.fetchSubmissionImportContext(articleId);
|
||||
this.importPreviewContext = context;
|
||||
this.importPreviewAuthors = [...context.authors];
|
||||
this.importAuthorOrderText = '';
|
||||
this.importAuthorPreviewVisible = true;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error(typeof err === 'string' ? err : 'Failed to load submission data.');
|
||||
} finally {
|
||||
loading.close();
|
||||
this.importingSubmission = false;
|
||||
}
|
||||
},
|
||||
async confirmImportFromSubmission() {
|
||||
if (!this.importPreviewContext) {
|
||||
return;
|
||||
}
|
||||
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: 'Importing from submission...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
this.importingSubmission = true;
|
||||
|
||||
try {
|
||||
const { submissionOrgans, mapped } = this.importPreviewContext;
|
||||
const authors = [...this.importPreviewAuthors];
|
||||
|
||||
this.applyImportedEssentialFields(mapped);
|
||||
|
||||
let organIdMap = {};
|
||||
let authorImported = false;
|
||||
let affiliationImported = false;
|
||||
let addedAuthorCount = 0;
|
||||
const importedAuthors = [];
|
||||
|
||||
if (!this.schoolData.length && submissionOrgans.length) {
|
||||
for (const organ of submissionOrgans) {
|
||||
const organName = String(organ.organ_name || '').trim();
|
||||
if (!organName) {
|
||||
continue;
|
||||
}
|
||||
const addRes = await this.$api.post('api/Production/addAuthorOrgan', {
|
||||
p_article_id: this.p_article_id,
|
||||
organ_name: organName
|
||||
});
|
||||
if (addRes.code != 0) {
|
||||
throw addRes.msg || 'Failed to add affiliation';
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
this.$message.error(err);
|
||||
});
|
||||
affiliationImported = true;
|
||||
await this.refreshAuthorSection({ organsOnly: true });
|
||||
}
|
||||
|
||||
organIdMap = buildOrganIdMap(submissionOrgans, this.schoolData);
|
||||
|
||||
if (!this.authorData.length && authors.length) {
|
||||
if (!this.schoolData.length) {
|
||||
throw 'Please import affiliations before authors.';
|
||||
}
|
||||
for (const author of authors) {
|
||||
const payload = buildProductionAuthorPayload(
|
||||
author,
|
||||
this.p_article_id,
|
||||
organIdMap,
|
||||
this.schoolData
|
||||
);
|
||||
if (!payload.first_name || !payload.last_name) {
|
||||
continue;
|
||||
}
|
||||
if (!payload.organs.length) {
|
||||
throw `Author ${payload.first_name} ${payload.last_name} has no matched affiliation.`;
|
||||
}
|
||||
const addRes = await this.$api.post('api/Production/addAuthor', payload);
|
||||
if (addRes.code != 0) {
|
||||
throw addRes.msg || `Failed to add author ${payload.first_name} ${payload.last_name}`;
|
||||
}
|
||||
addedAuthorCount += 1;
|
||||
importedAuthors.push({
|
||||
...author,
|
||||
firstname: payload.first_name,
|
||||
first_name: payload.first_name,
|
||||
lastname: payload.last_name,
|
||||
last_name: payload.last_name
|
||||
});
|
||||
}
|
||||
if (addedAuthorCount > 0) {
|
||||
authorImported = true;
|
||||
this.detailMes.abbr = buildAuthorReferenceAbbr(importedAuthors);
|
||||
await this.refreshAuthorSection({ authorsOnly: true });
|
||||
}
|
||||
}
|
||||
|
||||
if (affiliationImported && !authorImported) {
|
||||
await this.refreshAuthorSection({ organsOnly: true });
|
||||
}
|
||||
|
||||
const messages = ['Essential Information filled from submission.'];
|
||||
if (affiliationImported) {
|
||||
messages.push('Affiliations imported.');
|
||||
} else if (submissionOrgans.length && this.schoolData.length) {
|
||||
messages.push('Affiliations already exist, skipped.');
|
||||
}
|
||||
if (authorImported) {
|
||||
messages.push(`${addedAuthorCount} author(s) imported.`);
|
||||
} else if (authors.length && this.authorData.length) {
|
||||
messages.push('Authors already exist, skipped.');
|
||||
} else if (authors.length && !addedAuthorCount) {
|
||||
messages.push('No authors were imported. Please check author data.');
|
||||
}
|
||||
messages.push('Please review the fields and click Save Essential Information.');
|
||||
this.$message.success(messages.join(' '));
|
||||
|
||||
this.importAuthorPreviewVisible = false;
|
||||
this.importPreviewContext = null;
|
||||
|
||||
if (authorImported || affiliationImported) {
|
||||
await this.$nextTick();
|
||||
this.jumpTab(1, this.tabsList[1]);
|
||||
} else {
|
||||
await this.$nextTick();
|
||||
this.$forceUpdate();
|
||||
this.jumpTab(0, this.tabsList[0]);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error(typeof err === 'string' ? err : 'Import failed. Please try again.');
|
||||
} finally {
|
||||
loading.close();
|
||||
this.importingSubmission = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 1----时间初始化
|
||||
@@ -2125,7 +2505,7 @@ export default {
|
||||
this.addAuthor = false;
|
||||
this.$refs.add_Author.resetFields();
|
||||
this.addFomauthor.organs = [];
|
||||
this.getAuthorJG();
|
||||
this.refreshAuthorSection({ authorsOnly: true });
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
@@ -2213,7 +2593,7 @@ export default {
|
||||
if (res.code == 0) {
|
||||
this.addSchool = false;
|
||||
this.$refs.add_School.resetFields();
|
||||
this.getAuthorJG();
|
||||
this.refreshAuthorSection({ organsOnly: true });
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
@@ -3210,6 +3590,11 @@ export default {
|
||||
// this.jumpTab(0,this.tabsList[0])
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
}
|
||||
},
|
||||
components: {
|
||||
quillEditor,
|
||||
Tinymce,
|
||||
|
||||
@@ -204,7 +204,8 @@ export default {
|
||||
goEdit() {
|
||||
window.open(this.$router.resolve({ path: '/GenerateCharts',
|
||||
query: {
|
||||
id: this.detailMes.article_id
|
||||
id: this.detailMes.article_id,
|
||||
p_article_id: this.Art_Id
|
||||
} }).href, '_blank');
|
||||
|
||||
},
|
||||
|
||||
87
src/components/page/components/ReferenceSearchLinks.vue
Normal file
87
src/components/page/components/ReferenceSearchLinks.vue
Normal file
@@ -0,0 +1,87 @@
|
||||
<template>
|
||||
<span v-if="zyModeEnabled" class="ref-search-links">
|
||||
<template v-if="links.isBook">
|
||||
<a
|
||||
v-if="links.googleLink"
|
||||
class="ref-search-link"
|
||||
:href="links.googleLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@click.stop
|
||||
>
|
||||
<i class="el-icon-search"></i>
|
||||
</a>
|
||||
</template>
|
||||
<template v-else>
|
||||
<a
|
||||
v-if="links.pubmedLink"
|
||||
class="ref-search-link"
|
||||
:href="links.pubmedLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@click.stop
|
||||
>
|
||||
<i class="el-icon-search"></i>
|
||||
</a>
|
||||
<a
|
||||
v-else-if="links.scholarLink"
|
||||
class="ref-search-link"
|
||||
:href="links.scholarLink"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
@click.stop
|
||||
>
|
||||
<i class="el-icon-search"></i>
|
||||
</a>
|
||||
</template>
|
||||
</span>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getReferenceLinksFromRow } from '@/utils/referenceLinks';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
|
||||
export default {
|
||||
name: 'ReferenceSearchLinks',
|
||||
props: {
|
||||
row: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
},
|
||||
links() {
|
||||
return getReferenceLinksFromRow(this.row);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.ref-search-links {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
margin-left: 4px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ref-search-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
color: #909399;
|
||||
font-size: 11px;
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.ref-search-link:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
</style>
|
||||
@@ -13,6 +13,7 @@ export default {
|
||||
value: { type: String, default: '' },
|
||||
id: { type: String, default: () => 'tiny-' + +new Date() },
|
||||
showSelectTemplateButton: { type: Boolean, default: false },
|
||||
showEmptyButton: { type: Boolean, default: true },
|
||||
// 仅在需要的页面开启“安全初始化内容”
|
||||
useSafeInitContent: { type: Boolean, default: false }
|
||||
},
|
||||
@@ -121,6 +122,8 @@ autoInlineStyles(htmlContent) {
|
||||
initTiny() {
|
||||
const vueInstance = this;
|
||||
const hasTemplateBtn = this.showSelectTemplateButton;
|
||||
const hasEmptyBtn = this.showEmptyButton;
|
||||
const toolbarSuffix = hasEmptyBtn ? ' | clearButton | fullscreen' : ' | fullscreen';
|
||||
window.tinymce.init({
|
||||
selector: `#${this.tinymceId}`,
|
||||
language: 'zh_CN', // 记得下载语言包,没有就删掉这行
|
||||
@@ -137,8 +140,11 @@ autoInlineStyles(htmlContent) {
|
||||
|
||||
// 按照图片布局精准排序:
|
||||
toolbar: hasTemplateBtn
|
||||
? 'undo redo | selectTemplate | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link | fullscreen'
|
||||
: 'undo redo | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link | fullscreen',
|
||||
? 'undo redo | selectTemplate | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link' +
|
||||
toolbarSuffix
|
||||
: 'undo redo | code preview | fontselect fontsizeselect | formatselect | bold italic underline strikethrough | forecolor backcolor | bullist numlist | lineheight outdent indent | alignleft aligncenter alignright alignjustify | table link' +
|
||||
toolbarSuffix,
|
||||
menubar: 'file edit view insert format tools table',
|
||||
|
||||
// 补充:让字体和字号选择器更有序
|
||||
fontsize_formats: '12px 14px 16px 18px 24px 36px 48px',
|
||||
@@ -158,6 +164,16 @@ autoInlineStyles(htmlContent) {
|
||||
});
|
||||
}
|
||||
|
||||
if (hasEmptyBtn) {
|
||||
editor.ui.registry.addButton('clearButton', {
|
||||
text: 'Empty',
|
||||
onAction() {
|
||||
editor.setContent('');
|
||||
vueInstance.$emit('input', '');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 统一给自定义按钮加样式(无按钮时不会产生影响)
|
||||
CommonJS.inTinymceButtonClass();
|
||||
},
|
||||
@@ -177,7 +193,6 @@ autoInlineStyles(htmlContent) {
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// 假设 rawHtml 是你那一大串完整的 HTML
|
||||
prepareContentForEditor(rawHtml) {
|
||||
// 1. 分离出 Head 及其之前的部分 (包含 doctype, html, head)
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
<span v-html="renderText(processedItem.title)"></span>
|
||||
</div>
|
||||
|
||||
<table border="1" class="custom-table-core">
|
||||
<table class="custom-table-core">
|
||||
<tbody>
|
||||
<tr v-for="(row, rIdx) in processedItem.table.tableHeader" :key="'h-' + rIdx" class="table-header-row">
|
||||
<td v-for="(cell, cIdx) in row" :key="'hc-'+cIdx" :rowspan="cell.rowspan" :colspan="cell.colspan">
|
||||
@@ -201,7 +201,7 @@ export default {
|
||||
min-width: 794px; /* A4 宽度参考 */
|
||||
max-width: 90vw;
|
||||
background-color: #fff;
|
||||
padding: 10px 20px;
|
||||
padding: 2.54cm 1.91cm;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 12px 40px rgba(0,0,0,0.5);
|
||||
border-radius: 4px;
|
||||
@@ -243,8 +243,20 @@ export default {
|
||||
}
|
||||
|
||||
.custom-table-core {
|
||||
width: 100%;
|
||||
width: 98.3%;
|
||||
margin: 0 auto;
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
table-layout: auto;
|
||||
font-family: 'Charis SIL', serif;
|
||||
font-size: 7.5pt;
|
||||
}
|
||||
|
||||
.table-paper-view ::v-deep .custom-table-core tr td {
|
||||
padding: 0 0.19cm;
|
||||
line-height: 10pt;
|
||||
vertical-align: middle;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.font {
|
||||
@@ -258,22 +270,56 @@ export default {
|
||||
text-align: center;
|
||||
font-weight: bold;
|
||||
color: rgb(210, 90, 90);
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
font-family: 'Charis SIL', serif;
|
||||
line-height: 13px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.imageTitle {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.tableNote, .imageNote {
|
||||
color: #333;
|
||||
margin-top: 15px;
|
||||
font-size: 7.5pt;
|
||||
font-family: 'Charis SIL', serif;
|
||||
line-height: 10pt;
|
||||
}
|
||||
|
||||
.imageNote {
|
||||
text-align: center;
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
.oddColor td {
|
||||
background: rgb(250, 231, 232) !important;
|
||||
}
|
||||
|
||||
.table-paper-view ::v-deep .custom-table-core tr.table-header-row td span + span::before {
|
||||
content: ' ';
|
||||
}
|
||||
|
||||
.table-paper-view ::v-deep .custom-table-core tr.table-header-row td {
|
||||
font-size: 7.5pt;
|
||||
font-family: 'Charis SIL', serif;
|
||||
border-left: none;
|
||||
border-right: none;
|
||||
border-top: 0.5pt solid #000;
|
||||
border-bottom: 0.5pt solid #000;
|
||||
}
|
||||
|
||||
.table-paper-view ::v-deep .custom-table-core tr.table-content-row td {
|
||||
font-size: 7.5pt;
|
||||
font-family: 'Charis SIL', serif;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.table-paper-view ::v-deep .custom-table-core tr.table-content-row:last-child td {
|
||||
border-bottom: 0.5pt solid #000;
|
||||
}
|
||||
|
||||
.table-fade-enter-active, .table-fade-leave-active { transition: opacity 0.3s ease; }
|
||||
.table-fade-enter, .table-fade-leave-to { opacity: 0; }
|
||||
|
||||
|
||||
@@ -74,6 +74,85 @@
|
||||
<i class="el-icon-document base-margin" :style="{ '--m-r': '2px' }"> </i>
|
||||
{{ $t('commonTable.BatchAddcontent') }}
|
||||
</li>
|
||||
<li
|
||||
v-if="zyModeEnabled"
|
||||
@click="handleExportManuscriptWord"
|
||||
class="base-font-size base-bg-imp base-padding-all"
|
||||
:style="{ '--f-s': '12px', '--f-c': '#333' }"
|
||||
>
|
||||
<i
|
||||
class="el-icon-download base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-if="!exportingManuscriptWord"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-loading base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-else
|
||||
></i>
|
||||
{{ $t('commonTable.exportWord') }}
|
||||
</li>
|
||||
<li
|
||||
v-if="zyModeEnabled"
|
||||
@click="handleDownloadReferencesHtml"
|
||||
class="base-font-size base-bg-imp base-padding-all"
|
||||
:style="{ '--f-s': '12px', '--f-c': '#333' }"
|
||||
>
|
||||
<i
|
||||
class="el-icon-document base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-if="!referencesHtmlLoading"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-loading base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-else
|
||||
></i>
|
||||
{{ $t('commonTable.refHtmlDownload') }}
|
||||
</li>
|
||||
<li
|
||||
v-if="zyModeEnabled"
|
||||
@click="triggerReferencesHtmlToWord"
|
||||
class="base-font-size base-bg-imp base-padding-all"
|
||||
:style="{ '--f-s': '12px', '--f-c': '#333' }"
|
||||
>
|
||||
<i
|
||||
class="el-icon-upload2 base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-if="!exportingReferencesHtmlWord"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-loading base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-else
|
||||
></i>
|
||||
{{ $t('commonTable.refHtmlToWord') }}
|
||||
</li>
|
||||
<input
|
||||
ref="referencesHtmlFileInput"
|
||||
type="file"
|
||||
accept=".html,text/html"
|
||||
style="display: none"
|
||||
@change="handleReferencesHtmlToWord"
|
||||
/>
|
||||
<li
|
||||
v-if="zyModeEnabled"
|
||||
@click="handleOpenCitationRelevance"
|
||||
class="base-font-size base-bg-imp base-padding-all"
|
||||
:style="{ '--f-s': '12px', '--f-c': '#333' }"
|
||||
>
|
||||
<i
|
||||
class="el-icon-connection base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-if="!citationRelevanceLoading"
|
||||
></i>
|
||||
<i
|
||||
class="el-icon-loading base-margin"
|
||||
:style="{ '--m-r': '2px' }"
|
||||
v-else
|
||||
></i>
|
||||
{{ $t('commonTable.citeRelevanceDetect') }}
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<li
|
||||
@@ -1064,6 +1143,18 @@ import { TableUtils } from '@/common/js/TableUtils';
|
||||
import { debounce, throttle } from '@/common/js/debounce';
|
||||
import { tableStyle, commonWordStyle } from '@/utils/tinymceStyles';
|
||||
import LatexDataPanel from './LatexDataPanel.vue';
|
||||
import { downloadManuscriptWord, fetchManuscriptReferenceList } from '@/utils/exportManuscriptWord';
|
||||
import {
|
||||
buildReferencesHtmlLabels,
|
||||
downloadReferencesEditableHtml,
|
||||
parseReferencesEditableHtml
|
||||
} from '@/utils/manuscriptReferenceHtml';
|
||||
import {
|
||||
buildCitationReviewQueue,
|
||||
buildCitationReviewHtmlLabels,
|
||||
downloadCitationReviewHtml
|
||||
} from '@/utils/manuscriptCitationRelevance';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
export default {
|
||||
name: 'tinymce',
|
||||
components: {
|
||||
@@ -1076,6 +1167,9 @@ export default {
|
||||
articleId: {
|
||||
default: ''
|
||||
},
|
||||
pArticleId: {
|
||||
default: ''
|
||||
},
|
||||
isPreview: {
|
||||
type: Boolean,
|
||||
default() {
|
||||
@@ -1185,6 +1279,11 @@ export default {
|
||||
imagePath: require('@/assets/img/carriageReturn.png'), // 或者你可以设置其他路径
|
||||
scrollPosition: 0,
|
||||
wordList: [],
|
||||
exportingManuscriptWord: false,
|
||||
referencesHtmlLoading: false,
|
||||
exportingReferencesHtmlWord: false,
|
||||
citationRelevanceLoading: false,
|
||||
manuscriptReferences: [],
|
||||
proofreadingList: [],
|
||||
commentList: [],
|
||||
visibleDivs: [], // 存储当前可见的 div id
|
||||
@@ -1309,6 +1408,9 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
},
|
||||
sortedProofreadingList() {
|
||||
const order = [2, 1, 3];
|
||||
const rank = { 2: 0, 1: 1, 3: 2 };
|
||||
@@ -1444,6 +1546,170 @@ export default {
|
||||
this.editors = {};
|
||||
},
|
||||
methods: {
|
||||
async handleExportManuscriptWord() {
|
||||
if (this.exportingManuscriptWord) {
|
||||
return;
|
||||
}
|
||||
if (!this.wordList || !this.wordList.length) {
|
||||
this.$message.warning(this.$t('commonTable.exportManuscriptEmpty') || 'No content to export.');
|
||||
return;
|
||||
}
|
||||
this.exportingManuscriptWord = true;
|
||||
try {
|
||||
await downloadManuscriptWord(this.wordList, this.mediaUrl, 'manuscript', {
|
||||
fetchReferences: true,
|
||||
apiClient: this.$api,
|
||||
articleId: this.articleId,
|
||||
pArticleId: this.pArticleId
|
||||
});
|
||||
this.$message.success(this.$t('commonTable.exportManuscriptSuccess') || 'Word downloaded.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err && err.message === 'NO_CONTENT') {
|
||||
this.$message.warning(this.$t('commonTable.exportManuscriptEmpty') || 'No content to export.');
|
||||
} else {
|
||||
this.$message.error(this.$t('commonTable.exportManuscriptFail') || 'Failed to export Word.');
|
||||
}
|
||||
} finally {
|
||||
this.exportingManuscriptWord = false;
|
||||
}
|
||||
},
|
||||
async handleDownloadReferencesHtml() {
|
||||
if (this.referencesHtmlLoading) {
|
||||
return;
|
||||
}
|
||||
if (!this.$api) {
|
||||
this.$message.error(this.$t('commonTable.refHtmlLoadFail'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.referencesHtmlLoading = true;
|
||||
try {
|
||||
let references = this.manuscriptReferences;
|
||||
if (!references || !references.length) {
|
||||
references = await fetchManuscriptReferenceList(this.$api, this.articleId, this.pArticleId);
|
||||
this.manuscriptReferences = references || [];
|
||||
}
|
||||
if (!references || !references.length) {
|
||||
this.$message.warning(this.$t('commonTable.refHtmlEmpty'));
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = buildReferencesHtmlLabels(this.$t.bind(this));
|
||||
const fileName = 'references-editor' + (this.articleId ? '-' + this.articleId : '') + '.html';
|
||||
downloadReferencesEditableHtml(references, labels, fileName);
|
||||
this.$message.success(this.$t('commonTable.refHtmlDownloadSuccess'));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error(this.$t('commonTable.refHtmlLoadFail'));
|
||||
} finally {
|
||||
this.referencesHtmlLoading = false;
|
||||
}
|
||||
},
|
||||
triggerReferencesHtmlToWord() {
|
||||
if (this.exportingReferencesHtmlWord) {
|
||||
return;
|
||||
}
|
||||
const input = this.$refs.referencesHtmlFileInput;
|
||||
if (input) {
|
||||
input.value = '';
|
||||
input.click();
|
||||
}
|
||||
},
|
||||
handleReferencesHtmlToWord(event) {
|
||||
const input = event && event.target;
|
||||
const file = input && input.files && input.files[0];
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.wordList || !this.wordList.length) {
|
||||
this.$message.warning(this.$t('commonTable.exportManuscriptEmpty'));
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
this.exportingReferencesHtmlWord = true;
|
||||
const reader = new FileReader();
|
||||
reader.onload = async () => {
|
||||
try {
|
||||
const referenceHtmlItems = parseReferencesEditableHtml(String(reader.result || ''));
|
||||
if (!referenceHtmlItems.length) {
|
||||
this.$message.warning(this.$t('commonTable.refHtmlParseEmpty'));
|
||||
return;
|
||||
}
|
||||
|
||||
await downloadManuscriptWord(this.wordList, this.mediaUrl, 'manuscript', {
|
||||
fetchReferences: false,
|
||||
referenceHtmlItems: referenceHtmlItems,
|
||||
apiClient: this.$api,
|
||||
articleId: this.articleId,
|
||||
pArticleId: this.pArticleId
|
||||
});
|
||||
this.$message.success(this.$t('commonTable.refHtmlToWordSuccess'));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err && err.message === 'NO_CONTENT') {
|
||||
this.$message.warning(this.$t('commonTable.exportManuscriptEmpty'));
|
||||
} else {
|
||||
this.$message.error(this.$t('commonTable.refHtmlToWordFail'));
|
||||
}
|
||||
} finally {
|
||||
this.exportingReferencesHtmlWord = false;
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
}
|
||||
};
|
||||
reader.onerror = () => {
|
||||
this.exportingReferencesHtmlWord = false;
|
||||
if (input) {
|
||||
input.value = '';
|
||||
}
|
||||
this.$message.error(this.$t('commonTable.refHtmlToWordFail'));
|
||||
};
|
||||
reader.readAsText(file, 'UTF-8');
|
||||
},
|
||||
async handleOpenCitationRelevance() {
|
||||
if (this.citationRelevanceLoading) {
|
||||
return;
|
||||
}
|
||||
if (!this.wordList || !this.wordList.length) {
|
||||
this.$message.warning(this.$t('commonTable.citeRelevanceNoContent'));
|
||||
return;
|
||||
}
|
||||
if (!this.$api) {
|
||||
this.$message.error(this.$t('commonTable.citeRelevanceLoadFail'));
|
||||
return;
|
||||
}
|
||||
|
||||
this.citationRelevanceLoading = true;
|
||||
try {
|
||||
let references = this.manuscriptReferences;
|
||||
if (!references || !references.length) {
|
||||
references = await fetchManuscriptReferenceList(this.$api, this.articleId, this.pArticleId);
|
||||
this.manuscriptReferences = references || [];
|
||||
}
|
||||
|
||||
const items = buildCitationReviewQueue(this.wordList, this.manuscriptReferences);
|
||||
if (!items.length) {
|
||||
this.$message.warning(this.$t('commonTable.citeRelevanceNoCitations'));
|
||||
return;
|
||||
}
|
||||
|
||||
const labels = buildCitationReviewHtmlLabels(this.$t.bind(this));
|
||||
const fileName = 'citation-relevance' + (this.articleId ? '-' + this.articleId : '') + '.html';
|
||||
downloadCitationReviewHtml(items, labels, fileName);
|
||||
this.$message.success(this.$t('commonTable.citeRelevanceDownloadHtmlSuccess'));
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error(this.$t('commonTable.citeRelevanceLoadFail'));
|
||||
} finally {
|
||||
this.citationRelevanceLoading = false;
|
||||
}
|
||||
},
|
||||
getInvolvedPMain(range) {
|
||||
// 1. 找到起始节点所属的 .pMain
|
||||
let startNode = range.startContainer;
|
||||
|
||||
@@ -65,6 +65,24 @@
|
||||
style="width: 200px; float: right; padding: 10px; height: calc(100% - 28px); box-sizing: border-box; overflow-y: auto"
|
||||
class="arrlist"
|
||||
>
|
||||
<span
|
||||
v-if="zyModeEnabled && currentMenu == 1 && exportableImages.length > 0"
|
||||
class="tables-panel-download-icon"
|
||||
@click.stop="downloadAllFiguresWord"
|
||||
>
|
||||
<el-tooltip effect="dark" content="Download All Figures Word" placement="left">
|
||||
<i :class="exportingAllFiguresWord ? 'el-icon-loading' : 'el-icon-download'"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span
|
||||
v-if="zyModeEnabled && currentMenu == 2 && tables.length > 0"
|
||||
class="tables-panel-download-icon"
|
||||
@click.stop="downloadAllTablesWord"
|
||||
>
|
||||
<el-tooltip effect="dark" content="Download All Tables Word" placement="left">
|
||||
<i :class="exportingAllWord ? 'el-icon-loading' : 'el-icon-download'"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<ul style="width: 100%; height: auto">
|
||||
<li v-show="currentMenu == 1">
|
||||
<div style="" class="go-content-charts-item-box" :class="isCollapse ? 'double' : 'single'">
|
||||
@@ -137,7 +155,15 @@
|
||||
</a>
|
||||
</span>
|
||||
<p>
|
||||
<span style="" @click="edit(img, 'img')">
|
||||
<span v-if="zyModeEnabled && isFigureExportable(img)" @click.stop="downloadSingleFigureWord(img)">
|
||||
<el-tooltip effect="dark" content="Download Word" placement="top">
|
||||
<i
|
||||
:class="exportingFigureId === (img.article_image_id || img.ami_id) ? 'el-icon-loading' : 'el-icon-download'"
|
||||
style="color: #909399; font-size: 12px"
|
||||
></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span style="margin-left: 6px" @click="edit(img, 'img')">
|
||||
<i class="el-icon-edit" style="color: #409eff; font-size: 12px"></i>
|
||||
</span>
|
||||
<span style="margin-left: 6px" @click="handledelete(img, 'img')">
|
||||
@@ -329,7 +355,15 @@
|
||||
</el-tooltip> </span
|
||||
></span>
|
||||
<p>
|
||||
<span style="" @click="edit(table, 'table')">
|
||||
<span v-if="zyModeEnabled && !isMathFormulaTable(table)" @click.stop="downloadSingleTableWord(table)">
|
||||
<el-tooltip effect="dark" content="Download Word" placement="top">
|
||||
<i
|
||||
:class="exportingTableId === table.article_table_id ? 'el-icon-loading' : 'el-icon-download'"
|
||||
style="color: #909399; font-size: 12px"
|
||||
></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<span style="margin-left: 6px" @click="edit(table, 'table')">
|
||||
<i class="el-icon-edit" style="color: #409eff; font-size: 12px"></i>
|
||||
</span>
|
||||
<span style="margin-left: 6px" @click="handledelete(table, 'table')">
|
||||
@@ -475,6 +509,13 @@
|
||||
import DynamicTable from './DynamicTable.vue';
|
||||
import { mediaUrl } from '@/common/js/commonJS.js';
|
||||
import { isMathFormulaTableRecord, normalizeLatexTableApiResponse } from '@/utils/mathFormulaModule';
|
||||
import { downloadAllTablesWord as exportAllTablesWordFile, downloadTableWord, prepareTableExportItem } from '@/utils/exportTableWord';
|
||||
import {
|
||||
downloadAllFiguresWord as exportAllFiguresWordFile,
|
||||
downloadFigureWord,
|
||||
isExportableFigureItem as canExportFigureItem
|
||||
} from '@/utils/exportFigureWord';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
|
||||
export default {
|
||||
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'],
|
||||
@@ -505,12 +546,24 @@ export default {
|
||||
mathFormulasList: [],
|
||||
tablesHtml: [],
|
||||
imagesHtml: [],
|
||||
activeNames: ['images', 'tables']
|
||||
activeNames: ['images', 'tables'],
|
||||
exportingAllWord: false,
|
||||
exportingTableId: null,
|
||||
exportingAllFiguresWord: false,
|
||||
exportingFigureId: null
|
||||
};
|
||||
},
|
||||
components: {
|
||||
DynamicTable
|
||||
},
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
},
|
||||
exportableImages() {
|
||||
return (this.images || []).filter((img) => canExportFigureItem(img));
|
||||
}
|
||||
},
|
||||
directives: {
|
||||
// 注册一个局部的自定义指令 v-focus
|
||||
focus: {
|
||||
@@ -526,6 +579,9 @@ export default {
|
||||
changeIsCollapse(e) {
|
||||
localStorage.setItem('isCollapse', e);
|
||||
},
|
||||
isFigureExportable(item) {
|
||||
return canExportFigureItem(item);
|
||||
},
|
||||
isShowEditComment() {
|
||||
if (localStorage.getItem('U_role')) {
|
||||
var identity = localStorage.getItem('U_role');
|
||||
@@ -930,7 +986,85 @@ if(newData.ami_id) {
|
||||
},
|
||||
openPreview(index, type,data) {
|
||||
this.$refs.myTableModal.open(type,data);
|
||||
|
||||
},
|
||||
async downloadAllTablesWord() {
|
||||
if (!this.tables.length) {
|
||||
this.$message.warning('No tables to export.');
|
||||
return;
|
||||
}
|
||||
this.exportingAllWord = true;
|
||||
try {
|
||||
await exportAllTablesWordFile(this.tables, 'tables');
|
||||
this.$message.success('Word downloaded.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err && err.message === 'NO_TABLES') {
|
||||
this.$message.warning('No exportable tables found.');
|
||||
} else {
|
||||
this.$message.error('Failed to export Word.');
|
||||
}
|
||||
} finally {
|
||||
this.exportingAllWord = false;
|
||||
}
|
||||
},
|
||||
async downloadSingleTableWord(table) {
|
||||
if (!table || this.isMathFormulaTable(table)) {
|
||||
this.$message.warning('This table cannot be exported.');
|
||||
return;
|
||||
}
|
||||
const tableId = table.article_table_id || table.amt_id;
|
||||
this.exportingTableId = tableId;
|
||||
try {
|
||||
const prepared = prepareTableExportItem(table);
|
||||
if (!prepared) {
|
||||
this.$message.warning('Failed to prepare table.');
|
||||
return;
|
||||
}
|
||||
await downloadTableWord(prepared, table.title);
|
||||
this.$message.success('Word downloaded.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error('Failed to export Word.');
|
||||
} finally {
|
||||
this.exportingTableId = null;
|
||||
}
|
||||
},
|
||||
async downloadAllFiguresWord() {
|
||||
if (!this.exportableImages.length) {
|
||||
this.$message.warning('No figures to export.');
|
||||
return;
|
||||
}
|
||||
this.exportingAllFiguresWord = true;
|
||||
try {
|
||||
await exportAllFiguresWordFile(this.images, this.mediaUrl, 'figures');
|
||||
this.$message.success('Word downloaded.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err && err.message === 'NO_FIGURES') {
|
||||
this.$message.warning('No exportable figures found.');
|
||||
} else {
|
||||
this.$message.error('Failed to export Word.');
|
||||
}
|
||||
} finally {
|
||||
this.exportingAllFiguresWord = false;
|
||||
}
|
||||
},
|
||||
async downloadSingleFigureWord(img) {
|
||||
if (!img || !canExportFigureItem(img)) {
|
||||
this.$message.warning('This figure cannot be exported.');
|
||||
return;
|
||||
}
|
||||
const figureId = img.article_image_id || img.ami_id;
|
||||
this.exportingFigureId = figureId;
|
||||
try {
|
||||
await downloadFigureWord(img, this.mediaUrl, img.title);
|
||||
this.$message.success('Word downloaded.');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error('Failed to export Word.');
|
||||
} finally {
|
||||
this.exportingFigureId = null;
|
||||
}
|
||||
},
|
||||
async getWordimgList() {
|
||||
var that = this;
|
||||
@@ -1049,6 +1183,25 @@ if(newData.ami_id) {
|
||||
.el-menu-vertical-demo {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
.arrlist {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tables-panel-download-icon {
|
||||
position: absolute;
|
||||
top: 6px;
|
||||
right: 8px;
|
||||
z-index: 2;
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tables-panel-download-icon:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
.el-menu-vertical-demo li {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -297,19 +297,21 @@
|
||||
<p>
|
||||
{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. <em>{{ scope.row.joura }}</em
|
||||
>. <span :class="getJournalDateno(scope.row.dateno, 'title')">{{ scope.row.dateno }}</span
|
||||
>.<br />
|
||||
>.<reference-search-links :row="scope.row"></reference-search-links><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 }} <span v-html="formatTitle(scope.row.title)"></span>. {{ scope.row.dateno }}. <br /></p>
|
||||
<p>
|
||||
{{ scope.row.author }} <span v-html="formatTitle(scope.row.title)"></span>. {{ scope.row.dateno }}. <reference-search-links :row="scope.row"></reference-search-links><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>
|
||||
|
||||
<reference-search-links :row="scope.row"></reference-search-links>
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -895,6 +897,7 @@
|
||||
|
||||
<script>
|
||||
import VueUeditorWrap from 'vue-ueditor-wrap'; // ES6 Module
|
||||
import ReferenceSearchLinks from '@/components/page/components/ReferenceSearchLinks.vue';
|
||||
|
||||
/** 单条引用 records[].status:0 待检测 2 已完成 3 检测失败 */
|
||||
const REF_RELEVANCE_RECORD_STATUS_PENDING = 0;
|
||||
@@ -1450,8 +1453,7 @@ export default {
|
||||
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() === '');
|
||||
@@ -5244,7 +5246,8 @@ export default {
|
||||
}
|
||||
},
|
||||
components: {
|
||||
VueUeditorWrap
|
||||
VueUeditorWrap,
|
||||
ReferenceSearchLinks
|
||||
},
|
||||
watch: {
|
||||
role: {
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<el-dialog
|
||||
:title="articleContextDialogTitle"
|
||||
:visible.sync="articleContextDialogVisible"
|
||||
width="1100px"
|
||||
width="1140px"
|
||||
top="8vh"
|
||||
append-to-body
|
||||
custom-class="article-context-dialog"
|
||||
@@ -54,23 +54,16 @@
|
||||
<el-button type="primary" size="small" :loading="articleContextLoading" @click="loadArticleContext()">
|
||||
{{ $t('mailboxSend.articleContextSearch') }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="articleContext.loaded && hasArticleTemplatePlaceholders"
|
||||
size="small"
|
||||
type="warning"
|
||||
plain
|
||||
icon="el-icon-magic-stick"
|
||||
@click="applyArticleTemplateVariables"
|
||||
>
|
||||
{{ $t('mailboxSend.articleContextReplaceVars') }}
|
||||
</el-button>
|
||||
</div>
|
||||
|
||||
<div v-if="articleContext.loaded" class="article-context-loaded-hint">
|
||||
<div class="article-loaded-line">
|
||||
<span class="article-loaded-sn">SN {{ articleContext.acceptSn || '—' }}</span>
|
||||
<div v-if="articleContext.loaded" class="article-context-meta-card">
|
||||
<div class="article-context-meta-card__row">
|
||||
<span class="article-meta-label">SN</span>
|
||||
<span class="article-loaded-sn">{{ articleContext.acceptSn || '—' }}</span>
|
||||
</div>
|
||||
<div v-if="articleContext.title" class="article-context-meta-card__title">
|
||||
{{ articleContext.title }}
|
||||
</div>
|
||||
<div v-if="articleContext.title" class="article-loaded-title">{{ articleContext.title }}</div>
|
||||
</div>
|
||||
<div v-else-if="!articleContextLoading" class="article-contact-empty article-contact-empty--hint">
|
||||
{{ $t('mailboxSend.articleContextSearchHint') }}
|
||||
@@ -123,7 +116,7 @@
|
||||
/>
|
||||
</th>
|
||||
<th class="review_table_index">No.</th>
|
||||
<th>{{ $t('mailboxSend.tableName') }}</th>
|
||||
<th class="review_table_name">{{ $t('mailboxSend.tableName') }}</th>
|
||||
<th class="review_table_role">{{ $t('mailboxSend.tableAuthorRole') }}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
@@ -131,7 +124,12 @@
|
||||
<tr
|
||||
v-for="(author, idx) in articleContext.authors"
|
||||
:key="author.key"
|
||||
:class="{ 'review_table_row--disabled': !hasContactEmail(author) }"
|
||||
class="review_table_row--clickable"
|
||||
:class="{
|
||||
'review_table_row--disabled': !hasContactEmail(author),
|
||||
'review_table_row--selected': isContactSelected('authors', author.key)
|
||||
}"
|
||||
@click="handleContactRowClick('authors', author, $event)"
|
||||
>
|
||||
<td class="review_table_check">
|
||||
<el-checkbox
|
||||
@@ -141,17 +139,36 @@
|
||||
/>
|
||||
</td>
|
||||
<td class="review_table_index">{{ idx + 1 }}</td>
|
||||
<td>
|
||||
<td class="review_table_name">
|
||||
<div class="author-name-email-cell">
|
||||
<div class="author-name-email-cell__name">{{ author.name || '—' }}</div>
|
||||
<div v-if="author.email" class="author-name-email-cell__email">
|
||||
<span class="is-clickable" @click="addContactToTo(author)">{{ author.email }}</span>
|
||||
<span class="is-clickable" @click.stop="addContactToTo(author)">{{ author.email }}</span>
|
||||
<i class="el-icon-document-copy article-copy-icon" :title="$t('mailboxSend.copyText')" @click.stop="copyContactText(author.email)"></i>
|
||||
</div>
|
||||
<div v-else class="author-name-email-cell__email reviewer-no-email">{{ $t('mailboxSend.articleContextNoEmail') }}</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="review_table_role">{{ formatAuthorRoles(author.roles) }}</td>
|
||||
<td class="review_table_role">
|
||||
<div v-if="formatAuthorRoles(author.roles) !== '—'" class="author-role-tags">
|
||||
<el-tag
|
||||
v-if="(author.roles || []).indexOf('firstAuthor') >= 0"
|
||||
size="mini"
|
||||
type="info"
|
||||
effect="plain"
|
||||
>
|
||||
{{ $t('mailboxSend.roleFirstAuthor') }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="(author.roles || []).indexOf('correspondingAuthor') >= 0"
|
||||
size="mini"
|
||||
effect="plain"
|
||||
>
|
||||
{{ $t('mailboxSend.roleCorrespondingAuthor') }}
|
||||
</el-tag>
|
||||
</div>
|
||||
<span v-else>—</span>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
@@ -184,7 +201,12 @@
|
||||
<tr
|
||||
v-for="(row, idx) in articleContext.reviewRows"
|
||||
:key="row.key"
|
||||
:class="{ 'review_table_row--disabled': !hasContactEmail(row) }"
|
||||
class="review_table_row--clickable"
|
||||
:class="{
|
||||
'review_table_row--disabled': !hasContactEmail(row),
|
||||
'review_table_row--selected': isContactSelected('review', row.key)
|
||||
}"
|
||||
@click="handleContactRowClick('review', row, $event)"
|
||||
>
|
||||
<td class="review_table_check">
|
||||
<el-checkbox
|
||||
@@ -243,7 +265,12 @@
|
||||
<tr
|
||||
v-for="(row, idx) in articleContext.finalReviewers"
|
||||
:key="row.key"
|
||||
:class="{ 'review_table_row--disabled': !hasContactEmail(row) }"
|
||||
class="review_table_row--clickable"
|
||||
:class="{
|
||||
'review_table_row--disabled': !hasContactEmail(row),
|
||||
'review_table_row--selected': isContactSelected('final', row.key)
|
||||
}"
|
||||
@click="handleContactRowClick('final', row, $event)"
|
||||
>
|
||||
<td class="review_table_check">
|
||||
<el-checkbox
|
||||
@@ -269,6 +296,15 @@
|
||||
<span class="article-context-selected-tab is-active">
|
||||
{{ $t('mailboxSend.to') }} ({{ articleSelectedTotalCount }})
|
||||
</span>
|
||||
<el-button
|
||||
v-if="articleSelectedTotalCount"
|
||||
type="text"
|
||||
size="mini"
|
||||
class="article-context-selected-clear"
|
||||
@click="clearArticleContacts(articleContactActiveTab)"
|
||||
>
|
||||
{{ $t('mailboxSend.articleContextClearAll') }}
|
||||
</el-button>
|
||||
</div>
|
||||
<div class="article-context-selected-list">
|
||||
<div v-if="!articleSelectedContactList.length" class="article-context-selected-empty">
|
||||
@@ -1180,6 +1216,17 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
isContactSelected(module, key) {
|
||||
return (this.articleContactSelected[module] || []).indexOf(key) >= 0;
|
||||
},
|
||||
handleContactRowClick(module, contact, event) {
|
||||
if (!contact || !this.hasContactEmail(contact)) return;
|
||||
if (
|
||||
event.target.closest(
|
||||
'.el-checkbox, .el-checkbox__input, .article-copy-icon, .is-clickable, a, button, input, label'
|
||||
)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
this.toggleContactSelected(module, contact.key, !this.isContactSelected(module, contact.key));
|
||||
},
|
||||
toggleContactSelected(module, key, checked) {
|
||||
if (checked) {
|
||||
const contact = this.getModuleContactList(module).find(function (item) {
|
||||
@@ -2005,27 +2052,48 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
.article-context-search-bar {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 12px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.article-context-dialog-body {
|
||||
max-height: 60vh;
|
||||
max-height: 62vh;
|
||||
overflow-y: auto;
|
||||
padding-right: 10px;
|
||||
padding-right: 4px;
|
||||
}
|
||||
|
||||
.article-context-loaded-hint {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
flex-wrap: wrap;
|
||||
.article-context-meta-card {
|
||||
margin-bottom: 12px;
|
||||
padding: 10px 12px;
|
||||
background: #f5f7fa;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.article-context-meta-card__row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.article-meta-label {
|
||||
display: inline-block;
|
||||
padding: 0 6px;
|
||||
font-size: 11px;
|
||||
line-height: 20px;
|
||||
color: #909399;
|
||||
background: #fff;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.article-context-meta-card__title {
|
||||
margin-top: 6px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
color: #606266;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.article-template-vars-bar {
|
||||
@@ -2045,14 +2113,7 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
.article-loaded-sn {
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
.article-loaded-title {
|
||||
width: 100%;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.article-contact-empty--hint {
|
||||
@@ -2064,7 +2125,8 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
|
||||
.article-context-layout {
|
||||
display: flex;
|
||||
min-height: 320px;
|
||||
min-height: 360px;
|
||||
max-height: 48vh;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
@@ -2109,13 +2171,13 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
.article-context-main {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
padding: 12px;
|
||||
overflow-x: auto;
|
||||
padding: 0;
|
||||
overflow: auto;
|
||||
border-right: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.article-context-selected {
|
||||
width: 280px;
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -2123,10 +2185,19 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
}
|
||||
|
||||
.article-context-selected-head {
|
||||
padding: 12px 14px 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
padding: 10px 14px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.article-context-selected-clear {
|
||||
padding: 0;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.article-context-selected-tab {
|
||||
display: inline-block;
|
||||
padding-bottom: 10px;
|
||||
@@ -2144,7 +2215,6 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
.article-context-selected-list {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
max-height: 360px;
|
||||
overflow-y: auto;
|
||||
padding: 8px 0;
|
||||
}
|
||||
@@ -2354,22 +2424,30 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
}
|
||||
|
||||
.overflow-x-auto {
|
||||
overflow-x: auto;
|
||||
overflow: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.review_table {
|
||||
width: 100%;
|
||||
table-layout: fixed;
|
||||
border-collapse: collapse;
|
||||
margin-bottom: 20px;
|
||||
margin-bottom: 0;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.review_table thead th {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.review_table th,
|
||||
.review_table td {
|
||||
padding: 8px 12px;
|
||||
padding: 10px 12px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
text-align: left;
|
||||
font-size: 14px;
|
||||
font-size: 13px;
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
vertical-align: top;
|
||||
@@ -2394,6 +2472,22 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.review_table_row--clickable:not(.review_table_row--disabled) {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.review_table_row--selected td {
|
||||
background: #ecf5ff;
|
||||
}
|
||||
|
||||
.review_table_row--clickable:not(.review_table_row--disabled):hover td {
|
||||
background: #f5f7fa;
|
||||
}
|
||||
|
||||
.review_table_row--selected.review_table_row--clickable:not(.review_table_row--disabled):hover td {
|
||||
background: #e6f1fc;
|
||||
}
|
||||
|
||||
.review_table_row--disabled .reviewer-score {
|
||||
color: #d4d7de;
|
||||
}
|
||||
@@ -2416,12 +2510,34 @@ import { applyReviewNotifyTemplateVariables } from '@/utils/mailTemplatePreview'
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.review_table_name {
|
||||
width: auto;
|
||||
min-width: 180px;
|
||||
}
|
||||
|
||||
.review_table_role {
|
||||
min-width: 220px;
|
||||
white-space: nowrap;
|
||||
width: 148px;
|
||||
min-width: 148px;
|
||||
max-width: 148px;
|
||||
word-break: normal;
|
||||
}
|
||||
|
||||
.author-role-tags {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.author-role-tags .el-tag {
|
||||
max-width: 100%;
|
||||
white-space: normal;
|
||||
height: auto;
|
||||
line-height: 1.4;
|
||||
padding-top: 2px;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.reviewer-name-cell {
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user