From 00d58f8d566a134dce4475015bb0408c39651d54 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com>
Date: Tue, 19 May 2026 10:09:15 +0800
Subject: [PATCH 1/4] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E6=B8=B2?=
=?UTF-8?q?=E6=9F=93=E5=8F=98=E9=87=8F=E6=98=BE=E7=A4=BA?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/api/index.js | 4 ++--
.../components/autoPromotion/PromotionFactoryTaskDialog.vue | 2 +-
src/components/page/components/email/TmrEmailEditor.vue | 2 +-
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/api/index.js b/src/api/index.js
index 525753d..487fb66 100644
--- a/src/api/index.js
+++ b/src/api/index.js
@@ -19,8 +19,8 @@ const service = axios.create({
// baseURL: 'https://submission.tmrjournals.com/', //正式 记得切换
// baseURL: 'http://www.tougao.com/', //测试本地 记得切换
// baseURL: 'http://192.168.110.110/tougao/public/index.php/',
- // baseURL: '/api', //本地
- baseURL: '/', //正式
+ baseURL: '/api', //本地
+ // baseURL: '/', //正式
});
diff --git a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
index 31470a8..af9e782 100644
--- a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
+++ b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
@@ -1584,7 +1584,7 @@
.account-grid-layout {
display: grid;
/* 如果希望固定 3 个一行:repeat(3, 1fr);如果自适应:repeat(auto-fill, minmax(280px, 1fr)) */
- grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 8px;
}
diff --git a/src/components/page/components/email/TmrEmailEditor.vue b/src/components/page/components/email/TmrEmailEditor.vue
index ac2e1af..2574353 100644
--- a/src/components/page/components/email/TmrEmailEditor.vue
+++ b/src/components/page/components/email/TmrEmailEditor.vue
@@ -90,7 +90,7 @@ export default {
expert_title: "Prof", // 专家职称 (如 Prof./Dr.)
expert_name: "John Doe", // 专家姓名
expert_field: "Biomedical Engineering", // 专家研究领域
- representative_work_title: "Advanced Applications of AI in Medical Imaging", // 专家代表作标题
+ representative_work_title: "Advanced Applications of AI in Medical Imaging.", // 专家代表作标题
ai_content_analysis: "【AI分析文章,一句话总结】", // AI solicitation rationale
ai_advised_topics: "Based on your research expertise, we would particularly welcome submissions on topics such as 【这里是AI针对学者领域给特定约稿主题】, or other closely related areas that align with your work.", // AI suggested directions
From 2a7b9a0ec2fbe0fed4dc276ce691e4d9ae8b160a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com>
Date: Tue, 19 May 2026 11:28:33 +0800
Subject: [PATCH 2/4] =?UTF-8?q?=E8=87=AA=E5=8A=A8=E5=8C=96=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E5=AE=A1=E7=A8=BF=E4=BA=BA=E7=B1=BB=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/langs/en.js | 142 +-
src/components/common/langs/zh.js | 136 ++
src/components/page/autoPromotion.vue | 1477 ++++++++++++++---
src/components/page/autoPromotionLogs.vue | 1 +
.../PromotionFactoryTaskDialog.vue | 10 +-
5 files changed, 1527 insertions(+), 239 deletions(-)
diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js
index 72de6d1..2893300 100644
--- a/src/components/common/langs/en.js
+++ b/src/components/common/langs/en.js
@@ -47,6 +47,13 @@ const en = {
status: 'Status',
delete: 'Delete',
deleteInfo: 'Are you sure you want to delete this journal installment?',
+ plagiarismNotChecked: 'Not checked',
+ plagiarismChecking: 'Checking…',
+ plagiarismRecheck: 'Re-check',
+ plagiarismCheckFailed: 'Failed to start plagiarism check.',
+ plagiarismStatusFailed: 'Failed to load plagiarism status.',
+ plagiarismNoReportUrl: 'Report link is not available yet.',
+ plagiarismReportDetailFailed: 'Could not load manuscript details. Please try again.',
},
menu: {
main: 'Personal Center',
@@ -503,7 +510,7 @@ const en = {
languagePlaceholder: 'Language',
searchBtn: 'Search',
createTemplate: 'Create Template',
-colTitle: 'Template title',
+ colTitle: 'Template title',
colSubject: 'Email subject',
colScene: 'Scene',
colLanguage: 'Language',
@@ -519,6 +526,23 @@ colTitle: 'Template title',
deleteFail: 'Delete failed',
previewTitle: 'Template preview',
previewClose: 'Close',
+ batchImportBtn: 'Batch import',
+ batchImportTitle: 'Batch import templates (JSON)',
+ batchImportHint:
+ 'Paste a JSON array. Each item is saved via the same API as the editor (omit template_id to create; include template_id to update). Fields: title, subject, scene, language (or lang), version, body_html (or body), variables_json (or variables), is_active.',
+ batchImportCommonTip: 'Journal ID is set in the field below; when non-empty it overrides journal_id / journalId on every row.',
+ batchImportJournalId: 'Journal ID',
+ batchImportJournalPlaceholder: 'Match list filter or type manually',
+ batchImportRun: 'Run import',
+ batchImportBadJson: 'Invalid JSON',
+ batchImportEmpty: 'Array must contain at least one object',
+ batchImportMissingJournal: 'Row {index}: missing journal_id (use the input above or put journal_id in JSON)',
+ batchImportMissingField: 'Row {index}: missing {field}',
+ batchImportRowFail: 'Row {index} failed: {msg}',
+ batchImportRowNetwork: 'Row {index}: request error',
+ batchImportDone: 'Done: {ok} succeeded, {fail} failed',
+ batchImportErrorsTitle: 'Errors (first 8)',
+ batchImportSaveFail: 'Save failed',
},
mailboxStyle: {
title: 'Email Styles',
@@ -650,6 +674,72 @@ colTitle: 'Template title',
printBtn: 'Print',
previewNotSupported: 'This file format cannot be previewed online',
downloadToView: 'Download to view locally',
+ registerAuthorBtn: 'Auto submit',
+ registerAuthorConfirm:
+ 'Create an account via the same admin API as User Management: login name "{account}", display name "{realname}", email "{email}", initial password 123456qwe (no captcha). Continue?',
+ registerAuthorSuccess: 'Author account created.',
+ registerAuthorSuccessWithEmail: 'Created: {email}, password: 123456qwe',
+ registerAuthorFail: 'Creation failed. Try again later or add the user manually in User Management.',
+ registerAuthorExistsEmail: 'This email is already registered.',
+ registerAuthorExistsAccount: 'This login name is already taken. Edit the sender display name or add the user manually.',
+ registerAuthorNeedEmail: 'Sender email is missing; cannot create an account.',
+ registerAuthorNoQq: 'QQ Mail is not supported for author accounts. Please add the user manually.',
+ registerAuthorConfirmShort: 'Email: {email}\nPassword: {password}',
+ registerAuthorPickEmailFail: 'Could not allocate an available email after several attempts. Try again later or add the user manually.',
+ autoSubmitBtn: 'Submit first .docx as manuscript',
+ autoSubmitTitle: 'Log in as author and upload the first .docx attachment',
+ autoSubmitSessionTip:
+ 'Author checkLogin does not change your U_* local account. The server cookie is the author during upload. After finishing, enter your editor password below and log in again to restore the editor server session.',
+ autoSubmitUsername: 'Username',
+ autoSubmitPassword: 'Password',
+ autoSubmitSenderEmailLabel: 'Sender email',
+ autoSubmitSenderEmailPlaceholder: '(No sender email)',
+ autoSubmitCode: 'Captcha',
+ autoSubmitCodePh: 'Fill only if the server requires captcha; often leave empty',
+ autoSubmitCancel: 'Cancel',
+ autoSubmitDialogTitle: 'Auto submit',
+ autoSubmitConfirm: 'Auto submit',
+ autoSubmitExistingAccountTip:
+ 'This sender email may already have an account. Enter your password. Re-selecting a local file replaces the previous one.',
+ autoSubmitNotifyMailSubject: '[{journal}] Please complete your submission',
+ autoSubmitNotifyMailFail: 'Could not send the notification email. You can resend from the compose page.',
+ autoSubmitNotifyMailSkipped: 'No sender mailbox (j_email_id) found; skipped automatic email.',
+ autoSubmitNoDocx: 'No .docx attachment in this message (only .docx is supported, same as new submission).',
+ autoSubmitDownloadFail: 'Could not download the attachment. Try again later.',
+ autoSubmitSuccessTitle: 'Manuscript created',
+ autoSubmitDialogClose: 'Close',
+ autoSubmitSuccessLineAccount: 'Account: {account}',
+ autoSubmitSuccessLinePassword: 'Password: {password}',
+ autoSubmitSuccessLineDraft: 'Manuscript ID: {id}. Draft created in staging.',
+ autoSubmitSuccessLineLinkPrefix: 'Submission link: ',
+ autoSubmitSuccessMailSent: 'Notification email sent',
+ autoSubmitSuccessMailSkipped: 'No notification email (journal sender mailbox not configured).',
+ autoSubmitSuccessMailSkippedRecipient: 'No notification email (no valid From address on this message).',
+ autoSubmitSuccessMailFailed: 'Notification email was not sent; try again from the compose page.',
+ autoSubmitSuccessBodyLocalOnly:
+ 'Article ID: {id}. The UI still shows your editor account; the server session may still be the author after author login. Refresh or log in again as editor.',
+ autoSubmitSuccessBodyServerRestored:
+ 'Article ID: {id}. Logged in again as editor; both local storage and server session should match your editor account.',
+ autoSubmitSuccessNotify:
+ 'Article ID: {id}
Open articleAdd',
+ autoSubmitEditorRestorePwd: 'Editor password (restore session)',
+ autoSubmitEditorRestorePwdPh:
+ 'Optional: password for the current editor account shown in the header, used after success to restore the server session',
+ autoSubmitEditorReloginFail:
+ 'Could not restore the editor server session; local values were restored where possible. Refresh the page or log in again as editor.',
+ autoSubmitFail: 'Submission failed. Check credentials or network and try again.',
+ autoSubmitUsernameRequired: 'Username is required',
+ autoSubmitPasswordRequired: 'Password is required',
+ autoSubmitJournalLabel: 'Journal',
+ autoSubmitJournalUnknown: 'No journal (switch to a mailbox account that is bound to a journal)',
+ autoSubmitNeedJournal:
+ 'This mailbox has no journal ID; staging backfill cannot match the submission page. Switch mailbox account first.',
+ autoSubmitFailPartial: '(If contribute succeeded, article ID may be {id}; please verify in admin.)',
+ autoSubmitManuscriptSource: 'Manuscript file',
+ autoSubmitPickLocalDocx: 'Upload .docx from disk',
+ autoSubmitSourceHint: 'Optional: upload a file; otherwise the first .docx in the email is used. Choosing again replaces the current local file.',
+ autoSubmitLocalPicked: 'Local file: {name}',
+ autoSubmitNeedDocxSource: 'Upload a .docx file, or ensure the email has a .docx attachment.',
},
crawlerKeywords: {
pageTitle: 'Keyword Configuration',
@@ -1171,6 +1261,55 @@ colTitle: 'Template title',
onlySaveConfig: 'Save configuration only',
enableNowNextDay: 'Enable auto promotion now (starts next day)',
factoryCreateBtn: 'Create automated promotion task',
+ factoryBatchImportBtn: 'Batch import (JSON)',
+ factoryBatchImportTitle: 'Batch create tasks (JSON)',
+ factoryBatchImportHintShort: 'Submit a JSON array; non-empty fields merge into each row. You can still edit JSON.',
+ factoryBatchImportHint:
+ 'Paste a JSON array… Pick journal (getAllJournal) or type ID; template/style/fetch_ids below override JSON when non-empty. Load promotion fields to tick IDs or type comma-separated fetch_ids; load accounts for j_email_id. expert_type "5" needs partitions/countries. Shorthand: zones, countries, email_id_list.',
+ factoryBatchImportCommonTip: 'Journal from cover or ID field; non-empty template ID, style ID, or fetch_ids override JSON on every row.',
+ factoryBatchImportJournalId: 'Journal ID',
+ factoryBatchImportJournalPick: 'Journal',
+ factoryBatchImportJournalEmpty: 'No journals returned. Check api/Journal/getAllJournal.',
+ factoryBatchImportJournalManualPlaceholder: 'Filled when you pick a cover, or type manually',
+ factoryBatchImportTemplateId: 'Template ID',
+ factoryBatchImportStyleId: 'Style ID',
+ factoryBatchImportJournalPlaceholder: 'Merged into payload',
+ factoryBatchImportTemplatePlaceholder: 'Merged into payload',
+ factoryBatchImportStylePlaceholder: 'Merged into payload',
+ factoryBatchImportFetchIdsLabel: 'Promotion fields (fetch_ids)',
+ factoryBatchImportLoadFields: 'Load available fields',
+ factoryBatchImportFetchTip: 'Uses current journal: pick journal, then load. Search by name or ID (multiple tokens: space or comma). “Select all” selects the filtered list when search is set, otherwise all fields. Checkboxes sync with the comma text; when non-empty, overrides fetch_ids on every row.',
+ factoryBatchImportFetchIdsManual: 'Merged IDs (comma-separated, editable)',
+ factoryBatchImportFetchIdsPlaceholder: 'e.g. 1,2,3 or use checkboxes',
+ factoryBatchImportNeedJournalForFields: 'Select or enter journal ID first',
+ factoryBatchImportLoadAccounts: 'Load accounts for journal',
+ factoryBatchImportAccountsApiTip: 'POST api/email_client/getAccounts with journal_id',
+ factoryBatchImportColEmailId: 'j_email_id',
+ factoryBatchImportColAddress: 'Sender address',
+ factoryBatchImportColQuota: 'Remaining / daily limit',
+ factoryBatchImportCopyEmailIds: 'Copy email_ids (comma)',
+ factoryBatchImportNeedJournalForAccounts: 'Enter journal ID first',
+ factoryBatchImportNoAccounts: 'No mailbox accounts for this journal',
+ factoryBatchImportAccountsFail: 'Failed to load accounts',
+ factoryBatchImportCopyEmailIdsEmpty: 'Load the account list first',
+ factoryBatchImportIdsCopied: 'Copied j_email_id list to clipboard',
+ factoryBatchImportCopyFail: 'Copy failed; select and copy manually',
+ factoryBatchImportSyncToJson: 'Apply top form to JSON',
+ factoryBatchImportSyncFromJson: 'Load first row into form',
+ factoryBatchImportSyncIncludeEmails: 'When applying, set each row email_ids from loaded accounts',
+ factoryBatchImportSyncTip: 'You can still edit JSON manually; non-empty top fields are merged again on submit.',
+ factoryBatchImportJsonFromUiOk: 'JSON updated from the form',
+ factoryBatchImportUiFromJsonOk: 'Form updated from the first JSON row',
+ factoryBatchImportRun: 'Run batch create',
+ factoryBatchImportBadJson: 'Invalid JSON; check brackets and quotes',
+ factoryBatchImportEmpty: 'Array must contain at least one object',
+ factoryBatchImportMissing: 'Row {index}: missing field {field}',
+ factoryBatchImportNeedFetch: 'Row {index}: expert database requires fetch_ids',
+ factoryBatchImportNeedZone: 'Row {index}: expert database requires target_partitions or target_country_ids',
+ factoryBatchImportRowFail: 'Row {index} failed: {msg}',
+ factoryBatchImportRowNetwork: 'Row {index}: request error',
+ factoryBatchImportDone: 'Done: {ok} succeeded, {fail} failed',
+ factoryBatchImportErrorsTitle: 'Errors (first 8)',
factoryDialogTitle: 'Create task',
factoryJournal: 'Journal',
factoryJournalPlaceholder: 'Select a journal',
@@ -1224,6 +1363,7 @@ colTitle: 'Template title',
factoryExpertYoungBoard: 'Young editorial board',
factoryExpertAuthor: 'Author',
factoryExpertDb: 'Expert database',
+ factoryExpertReviewer: 'Reviewer',
factoryExpertJump: 'View',
factoryOfficialEmailTip: 'For this type, the system uses the official sender email by default. No account selection is required.',
factoryScenario: 'Scenario',
diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js
index 9a557f5..df7475c 100644
--- a/src/components/common/langs/zh.js
+++ b/src/components/common/langs/zh.js
@@ -45,6 +45,13 @@ const zh = {
status: '状态',
delete: '删除',
deleteInfo: '您确定要删除该期刊分期吗?',
+ plagiarismNotChecked: '未检测',
+ plagiarismChecking: '正在检测…',
+ plagiarismRecheck: '重新查重',
+ plagiarismCheckFailed: '查重任务启动失败。',
+ plagiarismStatusFailed: '获取查重状态失败。',
+ plagiarismNoReportUrl: '报告链接暂不可用。',
+ plagiarismReportDetailFailed: '获取稿件详情失败,请稍后重试。',
},
menu: {
main: '个人中心',
@@ -508,6 +515,23 @@ const zh = {
deleteFail: '删除失败',
previewTitle: '模板预览',
previewClose: '关闭',
+ batchImportBtn: '批量导入',
+ batchImportTitle: '批量导入邮件模板(JSON)',
+ batchImportHint:
+ '粘贴 JSON 数组,每条对应一次保存接口(新建不传 template_id;更新可带 template_id)。字段与编辑页一致:title、subject、scene、language(可用 lang)、version、body_html(可用 body)、variables_json(可用 variables)、is_active。',
+ batchImportCommonTip: '期刊 ID 在下方单独填写;若填写非空,会覆盖每条 JSON 中的 journal_id / journalId。',
+ batchImportJournalId: '期刊 ID',
+ batchImportJournalPlaceholder: '可与列表筛选一致,或手填',
+ batchImportRun: '开始导入',
+ batchImportBadJson: 'JSON 解析失败',
+ batchImportEmpty: '请至少包含一条对象',
+ batchImportMissingJournal: '第 {index} 条:缺少期刊 ID(请填写上方输入框或在 JSON 中提供 journal_id)',
+ batchImportMissingField: '第 {index} 条:缺少字段 {field}',
+ batchImportRowFail: '第 {index} 条保存失败:{msg}',
+ batchImportRowNetwork: '第 {index} 条请求异常',
+ batchImportDone: '完成:成功 {ok},失败 {fail}',
+ batchImportErrorsTitle: '失败明细(最多 8 条)',
+ batchImportSaveFail: '保存失败',
},
mailboxStyle: {
title: '邮件风格',
@@ -639,6 +663,68 @@ const zh = {
printBtn: '打印',
previewNotSupported: '该文件格式无法在线预览',
downloadToView: '下载到本地查看',
+ registerAuthorBtn: '自动投稿',
+ registerAuthorConfirm:
+ '将使用推广后台「添加用户」接口创建账号:登录名「{account}」,显示名「{realname}」,邮箱「{email}」,初始密码 123456qwe(无需验证码)。是否继续?',
+ registerAuthorSuccess: '作者账号已创建。',
+ registerAuthorSuccessWithEmail: '已创建:{email},密码:123456qwe',
+ registerAuthorFail: '创建失败,请稍后重试或到用户管理中手动添加。',
+ registerAuthorExistsEmail: '该邮箱已被注册。',
+ registerAuthorExistsAccount: '该登录名已被占用,请人工处理或修改发件人显示名后重试。',
+ registerAuthorNeedEmail: '缺少发件人邮箱,无法创建账号。',
+ registerAuthorNoQq: '本站不支持 QQ 邮箱作为作者账号,请在用户管理中手动添加。',
+ registerAuthorConfirmShort: '邮箱:{email}\n密码:{password}',
+ registerAuthorPickEmailFail: '多次尝试后仍无法分配到可用邮箱,请稍后重试或手动添加用户。',
+ autoSubmitBtn: '附件一键建稿',
+ autoSubmitTitle: '用作者账号登录并上传首份 .docx 附件',
+ autoSubmitSessionTip:
+ '作者 checkLogin 不会修改顶部 U_* 本地账号;上传期间服务端 Cookie 为作者。完成后请填写「编辑密码」再登录一次以恢复编辑服务端会话。',
+ autoSubmitUsername: '登录名',
+ autoSubmitPassword: '密码',
+ autoSubmitSenderEmailLabel: '发件人邮箱',
+ autoSubmitSenderEmailPlaceholder: '(未识别发件人邮箱)',
+ autoSubmitCode: '验证码',
+ autoSubmitCodePh: '若后台要求验证码则填写,一般可留空',
+ autoSubmitCancel: '取消',
+ autoSubmitDialogTitle: '自动投稿',
+ autoSubmitConfirm: '自动投稿',
+ autoSubmitExistingAccountTip:
+ '该发件邮箱可能已有账号。请填写密码;本地文件可多次重选,新文件会替换上一份。',
+ autoSubmitNotifyMailSubject: '【{journal}】投稿完善提醒',
+ autoSubmitNotifyMailFail: '通知邮件发送失败,可稍后在发件页手动补发。',
+ autoSubmitNotifyMailSkipped: '未识别发件邮箱账号,已跳过自动发信。',
+ autoSubmitNoDocx: '邮件中无 .docx 附件(仅支持 docx,与新增稿件一致)。',
+ autoSubmitDownloadFail: '无法下载附件,请稍后重试。',
+ autoSubmitSuccessTitle: '建稿成功',
+ autoSubmitDialogClose: '关闭',
+ autoSubmitSuccessLineAccount: '账号{account}',
+ autoSubmitSuccessLinePassword: '密码{password}',
+ autoSubmitSuccessLineDraft: '稿号id:{id} 已创建草稿箱',
+ autoSubmitSuccessLineLinkPrefix: '稿件链接地址是:',
+ autoSubmitSuccessMailSent: '邮件通知已发送',
+ autoSubmitSuccessMailSkipped: '未发送通知邮件(未配置期刊发件邮箱)',
+ autoSubmitSuccessMailSkippedRecipient: '未发送通知邮件(邮件中缺少有效发件人邮箱)',
+ autoSubmitSuccessMailFailed: '邮件通知未发送,请稍后在发件页补发',
+ autoSubmitSuccessBodyLocalOnly:
+ '文章 ID:{id}。界面仍为当前编辑账号;服务端在作者登录后可能仍为作者会话,请刷新或重新登录编辑账号。',
+ autoSubmitSuccessBodyServerRestored: '文章 ID:{id}。已通过编辑账号重新登录,本地与服务端会话均已恢复为编辑。',
+ autoSubmitSuccessNotify:
+ '文章 ID:{id}
打开 articleAdd 继续编辑',
+ autoSubmitEditorRestorePwd: '编辑密码(恢复会话)',
+ autoSubmitEditorRestorePwdPh: '选填:与顶部当前登录名对应的编辑密码,用于上传成功后恢复服务端会话',
+ autoSubmitEditorReloginFail: '恢复编辑服务端会话失败,已尽量恢复本地信息,请刷新页面或重新登录。',
+ autoSubmitFail: '建稿失败,请检查账号密码或网络后重试。',
+ autoSubmitUsernameRequired: '请填写登录名',
+ autoSubmitPasswordRequired: '请填写密码',
+ autoSubmitJournalLabel: '目标期刊',
+ autoSubmitJournalUnknown: '未识别期刊(请先在邮件列表切换绑定期刊的邮箱账号)',
+ autoSubmitNeedJournal: '当前邮箱账号没有期刊信息,无法与投稿页一致回填。请通过「切换邮箱账号」选择绑定期刊的账号。',
+ autoSubmitFailPartial: '(若 contribute 已成功,文章 ID 可能为:{id},请到后台核对)',
+ autoSubmitManuscriptSource: '稿件文件',
+ autoSubmitPickLocalDocx: '本地上传 .docx',
+ autoSubmitSourceHint: '可选本地上传;否则使用邮件中第一份 .docx。再次选择会替换当前本机文件。',
+ autoSubmitLocalPicked: '当前本机文件:{name}',
+ autoSubmitNeedDocxSource: '请在本地上传 .docx,或确保邮件中带有 .docx 附件。',
},
crawlerKeywords: {
pageTitle: '关键词配置',
@@ -1156,6 +1242,55 @@ const zh = {
onlySaveConfig: '仅保存配置',
enableNowNextDay: '立即激活自动推广(次日开始自动推广)',
factoryCreateBtn: '创建自动化推广任务',
+ factoryBatchImportBtn: '临时批量导入',
+ factoryBatchImportTitle: '批量创建推广任务(JSON)',
+ factoryBatchImportHintShort: '数组提交;上方非空项会合并进每条请求,仍可直接改 JSON。',
+ factoryBatchImportHint:
+ '粘贴 JSON 数组…期刊用封面(getAllJournal)或手填 ID;模板/样式/推广领域 fetch_ids 在下方非空则覆盖 JSON。推广领域可「加载可选领域」勾选或手改逗号 ID;填期刊后可查邮箱 j_email_id(getAccounts)。expert_type 为 5 时须分区或国家。简写:zones、countries、email_id_list。',
+ factoryBatchImportCommonTip: '期刊以封面或下方 ID 为准;模板 ID、样式 ID、推广领域 fetch_ids 任一非空则覆盖每条 JSON 中对应字段后再请求接口。',
+ factoryBatchImportJournalId: '期刊 ID',
+ factoryBatchImportJournalPick: '选择期刊',
+ factoryBatchImportJournalEmpty: '未获取到期刊列表,请检查接口或稍后重试',
+ factoryBatchImportJournalManualPlaceholder: '点击上方封面自动填入,也可手改',
+ factoryBatchImportTemplateId: '模板 ID',
+ factoryBatchImportStyleId: '样式 ID',
+ factoryBatchImportJournalPlaceholder: '与 JSON 合并',
+ factoryBatchImportTemplatePlaceholder: '与 JSON 合并',
+ factoryBatchImportStylePlaceholder: '与 JSON 合并',
+ factoryBatchImportFetchIdsLabel: '推广领域 fetch_ids',
+ factoryBatchImportLoadFields: '加载可选领域',
+ factoryBatchImportFetchTip: '依赖当前期刊:先选期刊再加载。上方可搜索名称或 ID(多关键词用空格或逗号);有搜索时「全选」勾选当前筛选结果,无搜索时「全选」为全部。勾选与下方逗号文本同步,非空则覆盖每条 JSON 的 fetch_ids。',
+ factoryBatchImportFetchIdsManual: '合并用 ID(逗号分隔,可手改)',
+ factoryBatchImportFetchIdsPlaceholder: '例:1,2,3;或与勾选联动',
+ factoryBatchImportNeedJournalForFields: '请先选择或填写期刊 ID',
+ factoryBatchImportLoadAccounts: '查询该期刊邮箱',
+ factoryBatchImportAccountsApiTip: 'POST api/email_client/getAccounts,参数 journal_id',
+ factoryBatchImportColEmailId: 'j_email_id',
+ factoryBatchImportColAddress: '发件地址',
+ factoryBatchImportColQuota: '今日剩余 / 日上限',
+ factoryBatchImportCopyEmailIds: '复制 email_ids(逗号)',
+ factoryBatchImportNeedJournalForAccounts: '请先填写期刊 ID',
+ factoryBatchImportNoAccounts: '该期刊下暂无邮箱账号',
+ factoryBatchImportAccountsFail: '拉取邮箱列表失败',
+ factoryBatchImportCopyEmailIdsEmpty: '请先查询出账号列表',
+ factoryBatchImportIdsCopied: '已复制 j_email_id 列表到剪贴板',
+ factoryBatchImportCopyFail: '复制失败,请手动选中复制',
+ factoryBatchImportSyncToJson: '上方选项写入 JSON',
+ factoryBatchImportSyncFromJson: '首条 JSON 回显到上方',
+ factoryBatchImportSyncIncludeEmails: '写入时用当前邮箱列表覆盖每条 email_ids',
+ factoryBatchImportSyncTip: '写入后仍可单独改 JSON;提交时若上方输入框非空仍会再合并覆盖。',
+ factoryBatchImportJsonFromUiOk: '已根据上方选项更新 JSON',
+ factoryBatchImportUiFromJsonOk: '已用首条 JSON 更新上方表单',
+ factoryBatchImportRun: '开始批量创建',
+ factoryBatchImportBadJson: 'JSON 解析失败,请检查括号与引号',
+ factoryBatchImportEmpty: '请至少包含一条对象',
+ factoryBatchImportMissing: '第 {index} 条缺少字段:{field}',
+ factoryBatchImportNeedFetch: '第 {index} 条:专家库需填写 fetch_ids(推广领域)',
+ factoryBatchImportNeedZone: '第 {index} 条:专家库需至少填写分区或国家(target_partitions / target_country_ids)',
+ factoryBatchImportRowFail: '第 {index} 条创建失败:{msg}',
+ factoryBatchImportRowNetwork: '第 {index} 条请求异常',
+ factoryBatchImportDone: '完成:成功 {ok},失败 {fail}',
+ factoryBatchImportErrorsTitle: '失败明细(最多显示 8 条)',
factoryDialogTitle: '创建任务',
factoryJournal: '期刊',
factoryJournalPlaceholder: '请选择期刊',
@@ -1209,6 +1344,7 @@ const zh = {
factoryExpertYoungBoard: '青年编委',
factoryExpertAuthor: '作者',
factoryExpertDb: 'expert库',
+ factoryExpertReviewer: '审稿人',
factoryExpertJump: '查看',
factoryOfficialEmailTip: '此类型默认使用系统官方邮箱发送,无需选择邮箱账号。',
factoryScenario: '场景',
diff --git a/src/components/page/autoPromotion.vue b/src/components/page/autoPromotion.vue
index aebbe41..a79edb8 100644
--- a/src/components/page/autoPromotion.vue
+++ b/src/components/page/autoPromotion.vue
@@ -1,20 +1,23 @@
-
+ />
+
@@ -81,13 +84,13 @@
>Template :
{{ taskCard.templateName || 'No Template Configured' }}
-
+
Type :
{{ taskCard.expertTypeLabel || '-' }}
-
+
-
+
@@ -128,9 +131,9 @@
"
>
-
+
{{ getFactoryTaskActionText(taskCard) }}
-
+
+
+
@@ -155,47 +158,47 @@
{{ $t('autoPromotion.factoryCreateNow') }}
-
-
+
+
-
+
-
+ @cancel="showWizardDialog = false"
+ @confirm="saveWizardConfig"
+ />
-
+
-
+
+
+
+
+
+
{{ $t('autoPromotion.factoryBatchImportHintShort') }}
+
+
+ {{ $t('autoPromotion.factoryBatchImportTemplateId') }}
+
+
+
+ {{ $t('autoPromotion.factoryBatchImportStyleId') }}
+
+
+
+
+
+ {{ $t('autoPromotion.factoryBatchImportFetchIdsLabel') }}
+ {{ $t('autoPromotion.selectedCount', { count: batchImportSelectedFieldIds.length }) }}
+
+ {{ $t('autoPromotion.factoryBatchImportLoadFields') }}
+
+
+
+
+ {{ $t('autoPromotion.selectAll') }}
+ {{ $t('autoPromotion.clearAll') }}
+
+
+
+
+ {{ f.label }} ({{ f.id }})
+
+
+
+ {{ $t('autoPromotion.noFieldMatch') }}
+
+
+
{{ $t('autoPromotion.factoryBatchImportFetchIdsManual') }}
+
+
+
+
+
+ {{ batchImportAccountDisplay(scope.row) }}
+
+
+
+ {{
+ $t('autoPromotion.factoryBatchImportSyncToJson')
+ }}
+ {{ $t('autoPromotion.factoryBatchImportSyncFromJson') }}
+ {{
+ $t('autoPromotion.factoryBatchImportSyncIncludeEmails')
+ }}
+
+
+
+
+
+
+
@@ -1158,6 +1799,368 @@ export default {
border-color: #409eff;
}
+/* 临时批量导入 JSON 对话框 */
+.factory-batch-import-dialog .el-dialog__body {
+ padding-top: 12px;
+}
+
+.batch-import-layout {
+ display: flex;
+ align-items: flex-start;
+ gap: 16px;
+ min-height: 0;
+}
+
+.batch-import-journal-rail {
+ flex: 0 0 74px;
+ width: 74px;
+ padding: 8px 8px 8px 0;
+ border-right: 1px solid #ebeef5;
+ align-self: stretch;
+ max-height: 72vh;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+}
+
+.batch-import-journal-scroll {
+ flex: 1;
+ min-height: 0;
+ overflow-x: hidden;
+ overflow-y: auto;
+ -webkit-overflow-scrolling: touch;
+}
+
+.batch-import-rail-title {
+ font-size: 11px;
+ font-weight: 600;
+ color: #606266;
+ margin-bottom: 6px;
+ text-align: center;
+ line-height: 1.25;
+ flex-shrink: 0;
+}
+
+.batch-import-journal-rail .batch-import-journal-empty {
+ text-align: center;
+ font-size: 10px;
+ color: #909399;
+ padding: 4px 0 8px;
+ line-height: 1.3;
+}
+
+.batch-import-journal-rail .batch-import-journal-grid--rail {
+ flex-direction: column;
+ flex-wrap: nowrap;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 0;
+ flex: none;
+ min-height: 0;
+ overflow: visible;
+ padding-right: 0;
+}
+
+.batch-import-journal-rail .batch-import-journal-card {
+ width: 35px;
+}
+
+.batch-import-journal-rail .batch-import-mini-cover-wrapper {
+ width: 35px;
+ height: 45px;
+}
+
+.batch-import-journal-rail .batch-import-mini-badge {
+ width: 14px;
+ height: 14px;
+ font-size: 8px;
+ top: -3px;
+ right: -3px;
+}
+
+.batch-import-journal-rail .batch-import-mini-abbr {
+ font-size: 9px;
+ margin-top: 4px;
+ max-width: 35px;
+}
+
+.batch-import-journal-rail .batch-import-journal-card.is-active .batch-import-mini-cover {
+ transform: translateY(-1px);
+}
+
+.batch-import-journal-id-row--rail {
+ flex-direction: column;
+ align-items: stretch;
+ gap: 6px;
+ padding-top: 4px;
+ border-top: 1px solid #ebeef5;
+}
+
+.batch-import-journal-id-row--rail .batch-import-id-label {
+ font-size: 11px;
+}
+
+.batch-import-journal-id-row--rail .batch-import-journal-id-input {
+ max-width: none;
+ width: 100%;
+ min-width: 0;
+}
+
+.batch-import-main {
+ flex: 1;
+ min-width: 0;
+}
+
+.batch-import-hint-compact {
+ font-size: 12px;
+ line-height: 1.45;
+ color: #909399;
+ margin: 0 0 12px;
+}
+
+.batch-import-hint {
+ font-size: 12px;
+ line-height: 1.55;
+ color: #606266;
+ margin: 0 0 10px;
+ white-space: pre-wrap;
+}
+
+.batch-import-common-tip {
+ font-size: 12px;
+ line-height: 1.5;
+ color: #909399;
+ margin: 0 0 10px;
+}
+
+.batch-import-common-fields {
+ margin-bottom: 12px;
+}
+
+.batch-import-journal-block {
+ margin-bottom: 14px;
+}
+
+.batch-import-journal-panel {
+ background: #f8fafc;
+ border: 1px solid #ebf0f5;
+ border-radius: 8px;
+ padding: 12px;
+}
+
+.batch-import-journal-empty {
+ font-size: 12px;
+ color: #909399;
+ padding: 8px 0;
+}
+
+.batch-import-journal-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ margin-bottom: 12px;
+}
+
+.batch-import-journal-card {
+ width: 70px;
+ text-align: center;
+ cursor: pointer;
+}
+
+.batch-import-mini-cover-wrapper {
+ position: relative;
+ width: 70px;
+ height: 90px;
+}
+
+.batch-import-mini-cover {
+ width: 100%;
+ height: 100%;
+ border-radius: 4px;
+ border: 2px solid transparent;
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
+ transition: 0.25s;
+}
+
+.batch-import-journal-card.is-active .batch-import-mini-cover {
+ border-color: #409eff;
+ transform: translateY(-2px);
+}
+
+.batch-import-mini-badge {
+ position: absolute;
+ top: -6px;
+ right: -6px;
+ background: #67c23a;
+ color: #fff;
+ width: 18px;
+ height: 18px;
+ border-radius: 50%;
+ font-size: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 2;
+}
+
+.batch-import-image-slot {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ background: #f5f7fa;
+ color: #c0c4cc;
+}
+
+.batch-import-mini-abbr {
+ font-size: 11px;
+ margin-top: 6px;
+ color: #606266;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.batch-import-mini-abbr.is-active {
+ color: #409eff;
+ font-weight: 600;
+}
+
+.batch-import-journal-id-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-wrap: wrap;
+}
+
+.batch-import-id-label {
+ font-size: 12px;
+ color: #606266;
+ font-weight: 600;
+ flex-shrink: 0;
+}
+
+.batch-import-journal-id-input {
+ flex: 1;
+ min-width: 160px;
+ max-width: 360px;
+}
+
+.batch-import-fetch-block {
+ margin-bottom: 12px;
+ padding: 10px 12px;
+ background: #fafafa;
+ border: 1px solid #eef0f3;
+ border-radius: 6px;
+}
+
+.batch-import-field-row-head {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 6px;
+}
+
+.batch-import-fetch-count {
+ font-size: 12px;
+ color: #909399;
+ flex: 1;
+ min-width: 80px;
+}
+
+.batch-import-fetch-tip {
+ font-size: 12px;
+ color: #c27a00;
+ margin: 0 0 8px;
+ line-height: 1.45;
+}
+
+.batch-import-field-toolbar {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 8px;
+ margin-bottom: 8px;
+}
+
+.batch-import-field-search {
+ flex: 1;
+ min-width: 180px;
+ max-width: 100%;
+}
+
+.batch-import-field-empty {
+ font-size: 12px;
+ color: #909399;
+ padding: 8px 4px;
+}
+
+.batch-import-field-checkbox-wrap {
+ max-height: 160px;
+ overflow: auto;
+ margin-bottom: 8px;
+ padding: 8px;
+ background: #fff;
+ border: 1px solid #ebeef5;
+ border-radius: 4px;
+}
+
+.batch-import-field-checkbox-wrap .el-checkbox {
+ display: block;
+ margin-right: 0;
+ margin-bottom: 6px;
+}
+
+.batch-import-fetch-text-label {
+ margin-top: 4px;
+ margin-bottom: 4px;
+}
+
+.factory-batch-import-dialog .batch-import-fetch-textarea >>> textarea {
+ font-family: Consolas, 'Courier New', monospace;
+ font-size: 12px;
+ line-height: 1.45;
+}
+
+.batch-import-field-label {
+ font-size: 12px;
+ color: #606266;
+ margin-bottom: 4px;
+ font-weight: 600;
+}
+
+.batch-import-accounts-table {
+ margin-bottom: 12px;
+}
+
+.batch-import-json-toolbar {
+ display: flex;
+ align-items: center;
+ flex-wrap: wrap;
+ gap: 10px;
+ margin-bottom: 10px;
+}
+
+.batch-import-sync-email-check {
+ margin-right: 0;
+}
+
+.batch-import-json-toolbar-tip {
+ font-size: 12px;
+ color: #909399;
+ flex: 1;
+ min-width: 200px;
+ line-height: 1.45;
+}
+
+.factory-batch-import-dialog .batch-import-textarea >>> textarea {
+ font-family: Consolas, 'Courier New', monospace;
+ font-size: 12px;
+ line-height: 1.45;
+}
+
/* 响应式 */
@media (max-width: 1200px) {
.cards-grid {
diff --git a/src/components/page/autoPromotionLogs.vue b/src/components/page/autoPromotionLogs.vue
index 19f5b41..11c6561 100644
--- a/src/components/page/autoPromotionLogs.vue
+++ b/src/components/page/autoPromotionLogs.vue
@@ -490,6 +490,7 @@ export default {
if (t === '3') return this.$t('autoPromotion.factoryExpertYoungBoard');
if (t === '4') return this.$t('autoPromotion.factoryExpertAuthor');
if (t === '5') return this.$t('autoPromotion.factoryExpertDb');
+ if (t === '6') return this.$t('autoPromotion.factoryExpertReviewer');
return '-';
},
getStatusType(status) {
diff --git a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
index af9e782..1b68bf9 100644
--- a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
+++ b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
@@ -447,6 +447,14 @@
icon: 'el-icon-collection',
count: counts.expertDb,
jump: { path: '/expertDatabase', queryKey: 'journal_id' }
+ },
+ {
+ value: '6',
+ label: this.$t('autoPromotion.factoryExpertReviewer'),
+ desc: this.$t('autoPromotion.factoryExpertReviewer'),
+ icon: 'el-icon-s-check',
+ count: counts.reviewer,
+ jump: { path: '/reviewerList', queryKey: 'journal_id' }
}
];
},
@@ -1584,7 +1592,7 @@
.account-grid-layout {
display: grid;
/* 如果希望固定 3 个一行:repeat(3, 1fr);如果自适应:repeat(auto-fill, minmax(280px, 1fr)) */
- grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 8px;
}
From 7570e0a1eb0fa3453e05d395cbd948913f1aaf48 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com>
Date: Tue, 19 May 2026 14:17:28 +0800
Subject: [PATCH 3/4] tijiao
---
src/components/common/langs/en.js | 12 +
src/components/common/langs/zh.js | 12 +
src/components/page/articleDetailEditor.vue | 463 +++++++++++++++++-
.../PromotionFactoryTaskDialog.vue | 16 +-
4 files changed, 494 insertions(+), 9 deletions(-)
diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js
index 2893300..4af9fc9 100644
--- a/src/components/common/langs/en.js
+++ b/src/components/common/langs/en.js
@@ -50,10 +50,22 @@ const en = {
plagiarismNotChecked: 'Not checked',
plagiarismChecking: 'Checking…',
plagiarismRecheck: 'Re-check',
+ plagiarismDuplicateCheck: 'Re-check',
plagiarismCheckFailed: 'Failed to start plagiarism check.',
plagiarismStatusFailed: 'Failed to load plagiarism status.',
plagiarismNoReportUrl: 'Report link is not available yet.',
plagiarismReportDetailFailed: 'Could not load manuscript details. Please try again.',
+ plagiarismListTitle: 'Plagiarism check history',
+ plagiarismAutoCheck: 'Auto plagiarism check',
+ plagiarismRefresh: 'Refresh',
+ plagiarismEmptyList: 'No plagiarism checks yet',
+ plagiarismSimilarity: 'Similarity',
+ plagiarismFile: 'File',
+ plagiarismPreviewPdf: 'Preview report',
+ plagiarismReportLink: 'Report',
+ plagiarismNoPdfLink: 'No link',
+ plagiarismPreviewClose: 'Close',
+ plagiarismPreviewOpenTab: 'Open in new tab',
},
menu: {
main: 'Personal Center',
diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js
index df7475c..d76edac 100644
--- a/src/components/common/langs/zh.js
+++ b/src/components/common/langs/zh.js
@@ -48,10 +48,22 @@ const zh = {
plagiarismNotChecked: '未检测',
plagiarismChecking: '正在检测…',
plagiarismRecheck: '重新查重',
+ plagiarismDuplicateCheck: '重复检查',
plagiarismCheckFailed: '查重任务启动失败。',
plagiarismStatusFailed: '获取查重状态失败。',
plagiarismNoReportUrl: '报告链接暂不可用。',
plagiarismReportDetailFailed: '获取稿件详情失败,请稍后重试。',
+ plagiarismListTitle: '自动查重记录',
+ plagiarismAutoCheck: '自动查重',
+ plagiarismRefresh: '刷新',
+ plagiarismEmptyList: '暂无查重记录',
+ plagiarismSimilarity: '相似度',
+ plagiarismFile: '文件',
+ plagiarismPreviewPdf: '预览报告',
+ plagiarismReportLink: '报告',
+ plagiarismNoPdfLink: '无链接',
+ plagiarismPreviewClose: '关闭',
+ plagiarismPreviewOpenTab: '新窗口打开',
},
menu: {
main: '个人中心',
diff --git a/src/components/page/articleDetailEditor.vue b/src/components/page/articleDetailEditor.vue
index 386334b..e88aefc 100644
--- a/src/components/page/articleDetailEditor.vue
+++ b/src/components/page/articleDetailEditor.vue
@@ -947,6 +947,77 @@
Change
+
+
+ {{ $t('articleListEditor.plagiarismAutoCheck') }}
+
+
+
Manuscript :
Save
+
+
+
+
+
+
{
+ this.fetchPlagiarismList(false);
+ }, 3 * 60 * 1000);
+ },
+ stopPlagiarismPolling() {
+ if (this.plagiarismPollTimer) {
+ clearInterval(this.plagiarismPollTimer);
+ this.plagiarismPollTimer = null;
+ }
+ },
+ async submitPlagiarismCheck() {
+ const articleId = String((this.editform && this.editform.articleId) || this.$route.query.id || '').trim();
+ if (!articleId) {
+ this.$message.warning(this.$t('articleListEditor.plagiarismReportDetailFailed'));
+ return;
+ }
+ this.plagiarismSubmitLoading = true;
+ try {
+ const res = await this.$api.post('api/Plagiarism/submit', { article_id: articleId });
+ if (res && Number(res.code) === 0) {
+ this.$message.success((res && res.msg) || this.$t('articleListEditor.plagiarismChecking'));
+ await this.fetchPlagiarismList(true);
+ } else {
+ this.$message.error((res && res.msg) || this.$t('articleListEditor.plagiarismCheckFailed'));
+ }
+ } catch (e) {
+ this.$message.error(this.$t('articleListEditor.plagiarismCheckFailed'));
+ } finally {
+ this.plagiarismSubmitLoading = false;
+ }
+ },
+ async fetchPlagiarismList(manual) {
+ const articleId = String((this.editform && this.editform.articleId) || this.$route.query.id || '').trim();
+ if (!articleId) {
+ this.plagiarismListReady = true;
+ return;
+ }
+ if (manual && this.plagiarismList.length) {
+ this.plagiarismListLoading = true;
+ }
+ try {
+ const res = await this.$api.post('api/Plagiarism/getList', { article_id: articleId });
+ if (res && Number(res.code) === 0) {
+ const payload = res.data || {};
+ const list = Array.isArray(payload.list) ? payload.list : Array.isArray(payload) ? payload : [];
+ this.plagiarismList = list;
+ } else if (manual) {
+ this.$message.error((res && res.msg) || this.$t('articleListEditor.plagiarismStatusFailed'));
+ }
+ } catch (e) {
+ if (manual) {
+ this.$message.error(this.$t('articleListEditor.plagiarismStatusFailed'));
+ }
+ } finally {
+ this.plagiarismListLoading = false;
+ this.plagiarismListReady = true;
+ }
+ },
+ formatPlagiarismState(state) {
+ const s = String(state != null ? state : '').trim();
+ if (!s) return '-';
+ return s;
+ },
+ formatPlagiarismStateLabel(row) {
+ if (!row || typeof row !== 'object') return '—';
+ const label = String(row.state_label || row.stateLabel || '').trim();
+ if (label) return label;
+ const s = row.state;
+ if (s == null || String(s).trim() === '') return '—';
+ return this.formatPlagiarismState(s);
+ },
+ getPlagiarismStateClass(row) {
+ const s = Number(row && row.state);
+ if (s === 1) return 'state-uploading';
+ if (s === 2 || s === 3) return 'state-done';
+ if (s === 4 || s === 5) return 'state-fail';
+ return '';
+ },
+ getPlagiarismSimilarityScore(row) {
+ if (!row || typeof row !== 'object') return null;
+ const raw = row.similarity_score != null ? row.similarity_score : row.similarity;
+ if (raw == null || String(raw).trim() === '') return null;
+ const n = Number(raw);
+ return isNaN(n) ? null : n;
+ },
+ formatPlagiarismSimilarity(row) {
+ const n = this.getPlagiarismSimilarityScore(row);
+ return n == null ? '—' : n + '%';
+ },
+ /** Crossref 相似度色块:0 绿、1–29 蓝、30+ 橙 */
+ getPlagiarismSimilarityLevel(row) {
+ const n = this.getPlagiarismSimilarityScore(row);
+ if (n == null) return 'sim-unknown';
+ if (n <= 0) return 'sim-zero';
+ if (n < 30) return 'sim-low';
+ return 'sim-high';
+ },
+ formatPlagiarismDate(row) {
+ if (!row || typeof row !== 'object') return '—';
+ const raw =
+ row.finish_time ||
+ row.finished_at ||
+ row.update_time ||
+ row.ctime ||
+ row.create_time ||
+ row.created_at ||
+ '';
+ if (raw == null || String(raw).trim() === '') return '—';
+ const s = String(raw).trim();
+ if (/^\d+$/.test(s)) {
+ const num = Number(s);
+ const ts = num > 1e12 ? num : num * 1000;
+ try {
+ return this.formatDate(Math.floor(ts / 1000));
+ } catch (e) {
+ return s;
+ }
+ }
+ const d = new Date(s.replace(/-/g, '/'));
+ if (!isNaN(d.getTime())) {
+ const pad = (v) => String(v).padStart(2, '0');
+ return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
+ }
+ return s.length > 10 ? s.slice(0, 10) : s;
+ },
+ resolvePlagiarismPdfUrl(row) {
+ if (!row || typeof row !== 'object') return '';
+ const raw = String(row.viewer_url || row.local_pdf_url || row.localPdfUrl || '').trim();
+ if (!raw) return '';
+ if (/^https?:\/\//i.test(raw)) return raw;
+
+ let path = raw.replace(/^\/+/, '');
+ if (!/^public\//i.test(path)) {
+ const media = String(this.mediaUrl || '/public/').replace(/\/+$/, '');
+ if (/^https?:\/\//i.test(media)) {
+ return media + '/' + path;
+ }
+ path = (media.startsWith('/') ? media : '/' + media) + '/' + path;
+ } else {
+ path = '/' + path;
+ }
+ path = path.replace(/\/+/g, '/');
+
+ if (typeof window !== 'undefined' && window.location && window.location.origin && path.startsWith('/')) {
+ return window.location.origin + path;
+ }
+ return path;
+ },
+ hasPlagiarismPdf(row) {
+ return !!this.resolvePlagiarismPdfUrl(row);
+ },
+ openPlagiarismReportPage(row) {
+ const url = this.resolvePlagiarismPdfUrl(row);
+ if (!url) return;
+ window.open(url, '_blank', 'noopener,noreferrer');
+ },
+ openPlagiarismPdfPreview(row) {
+ const url = this.resolvePlagiarismPdfUrl(row);
+ if (!url) return;
+ const name = row && row.source_file_name ? String(row.source_file_name) : 'report.pdf';
+ const id = row && row.check_id != null ? row.check_id : '';
+ this.plagiarismPdfPreviewTitle =
+ this.$t('articleListEditor.plagiarismPreviewPdf') + (id ? ' #' + id : '') + ' - ' + name;
+ this.plagiarismPdfPreviewUrl = url;
+ this.plagiarismPdfPreviewLoading = true;
+ this.plagiarismPdfPreviewVisible = true;
+ },
+ onPlagiarismPdfPreviewClosed() {
+ this.plagiarismPdfPreviewUrl = '';
+ this.plagiarismPdfPreviewTitle = '';
+ this.plagiarismPdfPreviewLoading = false;
+ },
+ openPlagiarismPdfInNewTab() {
+ if (!this.plagiarismPdfPreviewUrl) return;
+ window.open(this.plagiarismPdfPreviewUrl, '_blank', 'noopener');
}
},
mounted() {
@@ -2921,6 +3219,169 @@ export default {
text-decoration: underline;
}
+.plagiarism-check-shell {
+ margin: 0 0 10px 0;
+}
+.plagiarism-check-block >>> .el-loading-mask {
+ background-color: transparent !important;
+}
+.plagiarism-auto-check-btn.el-button {
+ display: block;
+ width: 200px;
+ border: none;
+ color: #fff;
+ font-size: 13px;
+ margin-left: 75px;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+ padding: 11px 20px;
+ border-radius: 4px;
+ background: linear-gradient(135deg, #2ec4b6 0%, #0d9b8f 45%, #0a7f76 100%);
+ box-shadow: 0 2px 8px rgba(13, 155, 143, 0.35);
+ transition: opacity 0.2s ease, box-shadow 0.2s ease;
+}
+.plagiarism-auto-check-btn.el-button:hover,
+.plagiarism-auto-check-btn.el-button:focus {
+ color: #fff;
+ background: linear-gradient(135deg, #3dd4c6 0%, #14b0a3 45%, #0e948a 100%);
+ box-shadow: 0 4px 12px rgba(13, 155, 143, 0.45);
+}
+.plagiarism-auto-check-btn.el-button.is-loading {
+ background: linear-gradient(135deg, #2ec4b6 0%, #0d9b8f 45%, #0a7f76 100%);
+}
+.plagiarism-check-block {
+ margin: 0 0 10px 0;
+ padding: 10px 12px;
+ background: #f8fafc;
+ border: 1px solid #e8edf3;
+ border-radius: 4px;
+}
+.plagiarism-check-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 8px;
+}
+.plagiarism-check-actions {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+.plagiarism-check-actions .el-button {
+ padding: 0 6px;
+}
+.plagiarism-check-title {
+ font-size: 13px;
+ color: #606266;
+ font-weight: 500;
+}
+.plagiarism-check-list-wrap {
+ min-height: 32px;
+}
+.plagiarism-check-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 6px 0;
+ border-top: 1px solid #eef1f5;
+ font-size: 12px;
+ line-height: 20px;
+}
+.plagiarism-check-row:first-child {
+ border-top: none;
+ padding-top: 0;
+}
+.plagiarism-sim-dot {
+ flex-shrink: 0;
+ width: 10px;
+ height: 10px;
+ border-radius: 1px;
+}
+.plagiarism-sim-dot.sim-zero {
+ background: #00a99d;
+}
+.plagiarism-sim-dot.sim-low {
+ background: #0070c0;
+}
+.plagiarism-sim-dot.sim-high {
+ background: #c87f0a;
+}
+.plagiarism-sim-dot.sim-unknown {
+ background: #c0c4cc;
+}
+.plagiarism-sim-pct {
+ flex-shrink: 0;
+ min-width: 36px;
+ font-weight: 600;
+}
+.plagiarism-sim-pct.sim-zero {
+ color: #00a99d;
+}
+.plagiarism-sim-pct.sim-low {
+ color: #0070c0;
+}
+.plagiarism-sim-pct.sim-high {
+ color: #c87f0a;
+}
+.plagiarism-sim-pct.sim-unknown {
+ color: #909399;
+}
+.plagiarism-sim-date {
+ flex-shrink: 0;
+ color: #909399;
+}
+.plagiarism-sim-state {
+ flex: 1;
+ min-width: 48px;
+ color: #909399;
+ text-align: center;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.plagiarism-sim-state.state-uploading {
+ color: #e6a23c;
+}
+.plagiarism-sim-state.state-done {
+ color: #67c23a;
+}
+.plagiarism-sim-state.state-fail {
+ color: #f56c6c;
+}
+.plagiarism-sim-report {
+ flex-shrink: 0;
+ display: inline-flex;
+ align-items: center;
+ justify-content: flex-end;
+ gap: 6px;
+}
+.plagiarism-report-preview {
+ color: #409eff;
+ text-decoration: none;
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+}
+.plagiarism-report-preview:hover {
+ text-decoration: underline;
+ color: #66b1ff;
+}
+.plagiarism-report-preview .el-icon-link {
+ font-size: 14px;
+}
+.plagiarism-check-no-pdf {
+ color: #f56c6c;
+ font-size: 12px;
+}
+.plagiarism-pdf-preview-body {
+ min-height: 70vh;
+}
+.plagiarism-pdf-preview-iframe {
+ width: 100%;
+ height: 70vh;
+ border: none;
+ background: #f5f7fa;
+}
.el-upload__tip {
display: inline-block;
line-height: 32px;
diff --git a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
index 1b68bf9..e2aa124 100644
--- a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
+++ b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
@@ -432,6 +432,14 @@
count: counts.young,
jump: { path: '/youthList', queryKey: 'journal_id' }
},
+ {
+ value: '6',
+ label: this.$t('autoPromotion.factoryExpertReviewer'),
+ desc: this.$t('autoPromotion.factoryExpertReviewer'),
+ icon: 'el-icon-s-check',
+ count: counts.reviewer,
+ jump: { path: '/reviewerList', queryKey: 'journal_id' }
+ },
{
value: '4',
label: this.$t('autoPromotion.factoryExpertAuthor'),
@@ -447,14 +455,6 @@
icon: 'el-icon-collection',
count: counts.expertDb,
jump: { path: '/expertDatabase', queryKey: 'journal_id' }
- },
- {
- value: '6',
- label: this.$t('autoPromotion.factoryExpertReviewer'),
- desc: this.$t('autoPromotion.factoryExpertReviewer'),
- icon: 'el-icon-s-check',
- count: counts.reviewer,
- jump: { path: '/reviewerList', queryKey: 'journal_id' }
}
];
},
From 7542d724ce0f885ea43903639febf499c1c08220 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=A7=8B=E4=BA=8E=E5=88=9D=E8=A7=81?= <752204717@qq.com>
Date: Wed, 20 May 2026 09:10:01 +0800
Subject: [PATCH 4/4] =?UTF-8?q?=E7=B1=BB=E5=9E=8B=206=20=E9=9D=92=E5=B9=B4?=
=?UTF-8?q?=E7=BC=96=E5=A7=942025=E5=89=8D=207=20=E4=BD=9C=E8=80=852025?=
=?UTF-8?q?=E5=B9=B4=E4=B9=8B=E5=89=8D=E7=9A=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/langs/en.js | 3 +-
src/components/common/langs/zh.js | 3 +-
src/components/page/autoPromotion.vue | 3 +-
src/components/page/autoPromotionLogs.vue | 3 +-
.../PromotionFactoryTaskDialog.vue | 95 +++++++++++++------
5 files changed, 75 insertions(+), 32 deletions(-)
diff --git a/src/components/common/langs/en.js b/src/components/common/langs/en.js
index 4af9fc9..8b91dc4 100644
--- a/src/components/common/langs/en.js
+++ b/src/components/common/langs/en.js
@@ -1375,7 +1375,8 @@ const en = {
factoryExpertYoungBoard: 'Young editorial board',
factoryExpertAuthor: 'Author',
factoryExpertDb: 'Expert database',
- factoryExpertReviewer: 'Reviewer',
+ factoryExpertYoungBoardBefore2025: 'Young board (before 2025)',
+ factoryExpertAuthorBefore2025: 'Author (before 2025)',
factoryExpertJump: 'View',
factoryOfficialEmailTip: 'For this type, the system uses the official sender email by default. No account selection is required.',
factoryScenario: 'Scenario',
diff --git a/src/components/common/langs/zh.js b/src/components/common/langs/zh.js
index d76edac..181d2a4 100644
--- a/src/components/common/langs/zh.js
+++ b/src/components/common/langs/zh.js
@@ -1356,7 +1356,8 @@ const zh = {
factoryExpertYoungBoard: '青年编委',
factoryExpertAuthor: '作者',
factoryExpertDb: 'expert库',
- factoryExpertReviewer: '审稿人',
+ factoryExpertYoungBoardBefore2025: '2025前青年编委',
+ factoryExpertAuthorBefore2025: '2025前作者',
factoryExpertJump: '查看',
factoryOfficialEmailTip: '此类型默认使用系统官方邮箱发送,无需选择邮箱账号。',
factoryScenario: '场景',
diff --git a/src/components/page/autoPromotion.vue b/src/components/page/autoPromotion.vue
index a79edb8..c884b68 100644
--- a/src/components/page/autoPromotion.vue
+++ b/src/components/page/autoPromotion.vue
@@ -1221,7 +1221,8 @@ export default {
if (t === '3') return this.$t('autoPromotion.factoryExpertYoungBoard');
if (t === '4') return this.$t('autoPromotion.factoryExpertAuthor');
if (t === '5') return this.$t('autoPromotion.factoryExpertDb');
- if (t === '6') return this.$t('autoPromotion.factoryExpertReviewer');
+ if (t === '6') return this.$t('autoPromotion.factoryExpertYoungBoardBefore2025');
+ if (t === '7') return this.$t('autoPromotion.factoryExpertAuthorBefore2025');
return '-';
},
getJournalDisplayTasks(journal) {
diff --git a/src/components/page/autoPromotionLogs.vue b/src/components/page/autoPromotionLogs.vue
index 11c6561..8b27c46 100644
--- a/src/components/page/autoPromotionLogs.vue
+++ b/src/components/page/autoPromotionLogs.vue
@@ -490,7 +490,8 @@ export default {
if (t === '3') return this.$t('autoPromotion.factoryExpertYoungBoard');
if (t === '4') return this.$t('autoPromotion.factoryExpertAuthor');
if (t === '5') return this.$t('autoPromotion.factoryExpertDb');
- if (t === '6') return this.$t('autoPromotion.factoryExpertReviewer');
+ if (t === '6') return this.$t('autoPromotion.factoryExpertYoungBoardBefore2025');
+ if (t === '7') return this.$t('autoPromotion.factoryExpertAuthorBefore2025');
return '-';
},
getStatusType(status) {
diff --git a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
index e2aa124..4cac74a 100644
--- a/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
+++ b/src/components/page/components/autoPromotion/PromotionFactoryTaskDialog.vue
@@ -354,8 +354,9 @@
chief: null,
board: null,
young: null,
+ youngBefore2025: null,
+ authorBefore2025: null,
author: null,
- reviewer: null,
expertDb: null
},
expertTypeCountsLoading: false,
@@ -434,11 +435,19 @@
},
{
value: '6',
- label: this.$t('autoPromotion.factoryExpertReviewer'),
- desc: this.$t('autoPromotion.factoryExpertReviewer'),
- icon: 'el-icon-s-check',
- count: counts.reviewer,
- jump: { path: '/reviewerList', queryKey: 'journal_id' }
+ label: this.$t('autoPromotion.factoryExpertYoungBoardBefore2025'),
+ desc: this.$t('autoPromotion.factoryExpertYoungBoardBefore2025'),
+ icon: 'el-icon-time',
+ count: counts.youngBefore2025,
+ jump: { path: '/youthList', queryKey: 'journal_id' }
+ },
+ {
+ value: '7',
+ label: this.$t('autoPromotion.factoryExpertAuthorBefore2025'),
+ desc: this.$t('autoPromotion.factoryExpertAuthorBefore2025'),
+ icon: 'el-icon-document',
+ count: counts.authorBefore2025,
+ jump: { path: '/partyListCorr', queryKey: 'journal_id' }
},
{
value: '4',
@@ -892,12 +901,28 @@
this.availableFields = [];
this.factoryFieldIds = [];
this.selectedEmailIds = [];
- this.expertTypeCounts = { chief: null, board: null, young: null, author: null, reviewer: null, expertDb: null };
+ this.expertTypeCounts = {
+ chief: null,
+ board: null,
+ young: null,
+ youngBefore2025: null,
+ authorBefore2025: null,
+ author: null,
+ expertDb: null
+ };
this.syncActiveStep();
return;
}
// 切换期刊时:先清空旧人数并立即触发 loading
- this.expertTypeCounts = { chief: null, board: null, young: null, author: null, reviewer: null, expertDb: null };
+ this.expertTypeCounts = {
+ chief: null,
+ board: null,
+ young: null,
+ youngBefore2025: null,
+ authorBefore2025: null,
+ author: null,
+ expertDb: null
+ };
this.fetchExpertTypeCounts();
await this.loadAccounts(val);
await Promise.all([
@@ -915,6 +940,7 @@
const payload = {
from: 'promotionFactory',
journal_id: journalId != null && journalId !== '' ? String(journalId) : '',
+ expert_type: opt && opt.value != null ? String(opt.value) : '',
ts: Date.now(),
targetPath: String(jump.path || '')
};
@@ -947,19 +973,21 @@
return sum;
};
- const [boardRes, youngRes, authorRes, reviewerRes, expertDbRes] = await Promise.all([
+ const yboardBase = {
+ journal_id: journalId,
+ pageIndex: 1,
+ pageSize: 1,
+ keywords: '',
+ fieldkey: '',
+ order_remark: 0
+ };
+ const [boardRes, youngRes, youngBefore2025Res, authorRes, authorBefore2025Res, expertDbRes] = await Promise.all([
this.$api.post('api/Board/getBoards', { journal_id: journalId }).catch(() => null),
this.$api
- .post('api/User/getYboardlist', {
- journal_id: journalId,
- type: 1,
- year: 0,
- pageIndex: 1,
- pageSize: 1,
- keywords: '',
- fieldkey: '',
- order_remark: 0
- })
+ .post('api/User/getYboardlist', Object.assign({}, yboardBase, { type: 1, year: 0 }))
+ .catch(() => null),
+ this.$api
+ .post('api/User/getYboardlist', Object.assign({}, yboardBase, { type: 6, year: 0 }))
.catch(() => null),
this.$api
.post('api/User/authorDatabase', {
@@ -970,12 +998,12 @@
})
.catch(() => null),
this.$api
- .post('api/Reviewer/getReviewerListByJournal', {
- username: localStorage.getItem('U_name'),
- journalId: journalId,
- class: 0,
- pageIndex: 1,
- pageSize: 1
+ .post('api/User/authorDatabase', {
+ journal_id: journalId,
+ type: 7,
+ keywords: '',
+ page: 1,
+ limit: 1
})
.catch(() => null),
this.$api
@@ -986,7 +1014,15 @@
.catch(() => null)
]);
- const next = { chief: null, board: null, young: null, author: null, reviewer: null, expertDb: null };
+ const next = {
+ chief: null,
+ board: null,
+ young: null,
+ youngBefore2025: null,
+ authorBefore2025: null,
+ author: null,
+ expertDb: null
+ };
if (boardRes && boardRes.code === 0 && boardRes.data && boardRes.data.boards) {
const b = boardRes.data.boards || {};
@@ -999,11 +1035,14 @@
if (youngRes && youngRes.code === 0) {
next.young = safeCount(youngRes.data && youngRes.data.count);
}
+ if (youngBefore2025Res && youngBefore2025Res.code === 0) {
+ next.youngBefore2025 = safeCount(youngBefore2025Res.data && youngBefore2025Res.data.count);
+ }
if (authorRes && authorRes.code === 0) {
next.author = safeCount(authorRes.data && authorRes.data.count);
}
- if (reviewerRes && reviewerRes.code === 0) {
- next.reviewer = safeCount(reviewerRes.total);
+ if (authorBefore2025Res && authorBefore2025Res.code === 0) {
+ next.authorBefore2025 = safeCount(authorBefore2025Res.data && authorBefore2025Res.data.count);
}
if (expertDbRes && expertDbRes.code === 0) {
const total = (expertDbRes.data && (expertDbRes.data.total || expertDbRes.data.count)) || expertDbRes.total;