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 @@ @@ -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') }} + +
+
+ {{ $t('articleListEditor.plagiarismListTitle') }} +
+ + + {{ $t('articleListEditor.plagiarismRefresh') }} + +
+
+
+
+ + + {{ formatPlagiarismSimilarity(row) }} + + {{ formatPlagiarismDate(row) }} + + {{ formatPlagiarismStateLabel(row) }} + + + + {{ $t('articleListEditor.plagiarismPreviewPdf') }} + + + {{ $t('articleListEditor.plagiarismNoPdfLink') }} + +
+
+
+
Manuscript : Save + +
+ +
+ + {{ $t('articleListEditor.plagiarismPreviewClose') }} + + {{ $t('articleListEditor.plagiarismPreviewOpenTab') }} + + +
{ + 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;