终审编委信息
This commit is contained in:
@@ -976,6 +976,13 @@ const en = {
|
||||
sendProgressRunning: 'Sending… {done}/{total} completed',
|
||||
sendProgressAllSuccess: 'All sent successfully ({count})',
|
||||
sendProgressDone: 'Done: {ok} sent, {fail} failed',
|
||||
sendPreviewTitle: 'Send preview',
|
||||
sendPreviewSub: 'Review the email each recipient will receive',
|
||||
sendPreviewCount: '{count} email(s)',
|
||||
sendPreviewRecipient: 'To',
|
||||
sendPreviewSubject: 'Subject',
|
||||
sendPreviewConfirm: 'Confirm send',
|
||||
sendPreviewRealHint: 'Each recipient will receive a separate email matching the preview above',
|
||||
backToInbox: 'Back to email list',
|
||||
articleContextId: 'Article ID:',
|
||||
articleContextIdPlaceholder: 'Enter article ID',
|
||||
@@ -1018,8 +1025,26 @@ const en = {
|
||||
articleContextRepeatReviewLine: '{n}nd review: {label}',
|
||||
articleContextFinalReviewLine: 'Final decision: {decision}',
|
||||
articleContextReplaceVars: 'Replace template vars',
|
||||
articleContextReplaceVarsSuccess: 'Replaced {{article_title}} / {{article_sn}}',
|
||||
articleContextReplaceVarsEmpty: 'Subject or body has no {{article_title}} / {{article_sn}} placeholders',
|
||||
articleContextReplaceVarsSuccess: 'Replaced template variables (title, SN, name, article_id, final_r_id, etc.)',
|
||||
articleContextReplaceVarsEmpty: 'Subject or body has no replaceable placeholders (e.g. {{article_title}}, [[name]], {{final_r_id}})',
|
||||
recipientVarsSendHint: 'Keep placeholders in the body; each recipient gets a separate email with values from the table',
|
||||
recipientVarsPanelTitle: 'Recipient variables',
|
||||
recipientVarsPanelSub: 'One email per recipient; placeholders are replaced using the values below when sending',
|
||||
recipientVarsSharedTitle: 'Shared (same for all recipients on this article)',
|
||||
recipientVarsPerRecipientTitle: 'Per recipient',
|
||||
recipientVarsColEmail: 'Recipient',
|
||||
recipientVarsMissingWarn: '{count} recipient(s) missing name or final_r_id — load article and add from Final tab',
|
||||
templateVar_name: 'Name [[name]]',
|
||||
templateVar_article_id: 'Article ID',
|
||||
templateVar_final_r_id: 'final_r_id',
|
||||
templateVar_article_title: 'Article title',
|
||||
templateVar_title: 'Title',
|
||||
templateVar_manuscript_title: 'Manuscript title',
|
||||
templateVar_accept_sn: 'SN',
|
||||
templateVar_article_sn: 'SN',
|
||||
templateVar_sn: 'SN',
|
||||
templateVar_journal_name: 'Journal name',
|
||||
templateVar_journal_url: 'Journal URL',
|
||||
roleFirstAuthor: 'First author',
|
||||
roleCorrespondingAuthor: 'Corresponding author',
|
||||
roleFirstAuthorShort: '1st auth.',
|
||||
|
||||
@@ -961,6 +961,13 @@ const zh = {
|
||||
sendProgressRunning: '正在发送,已完成 {done}/{total}',
|
||||
sendProgressAllSuccess: '全部发送成功,共 {count} 封',
|
||||
sendProgressDone: '发送完成:成功 {ok} 封,失败 {fail} 封',
|
||||
sendPreviewTitle: '发送预览',
|
||||
sendPreviewSub: '请确认每位收件人将收到的邮件内容',
|
||||
sendPreviewCount: '共 {count} 封',
|
||||
sendPreviewRecipient: '收件人',
|
||||
sendPreviewSubject: '主题',
|
||||
sendPreviewConfirm: '确认发送',
|
||||
sendPreviewRealHint: '确认后将按上方预览内容,为每位收件人逐封发送',
|
||||
backToInbox: '返回收件箱',
|
||||
articleContextId: '文章 ID:',
|
||||
articleContextIdPlaceholder: '请输入文章 ID',
|
||||
@@ -1003,8 +1010,26 @@ const zh = {
|
||||
articleContextRepeatReviewLine: '第 {n} 次复审:{label}',
|
||||
articleContextFinalReviewLine: '终审:{decision}',
|
||||
articleContextReplaceVars: '替换模板变量',
|
||||
articleContextReplaceVarsSuccess: '已替换 {{article_title}} / {{article_sn}}',
|
||||
articleContextReplaceVarsEmpty: '主题或正文不含 {{article_title}}、{{article_sn}} 等占位符',
|
||||
articleContextReplaceVarsSuccess: '已替换模板变量(标题、SN、name、article_id、final_r_id 等)',
|
||||
articleContextReplaceVarsEmpty: '主题或正文不含可替换占位符(如 {{article_title}}、[[name]]、{{final_r_id}})',
|
||||
recipientVarsSendHint: '正文保留占位符,发送时按左侧表格为每人单独替换并发送',
|
||||
recipientVarsPanelTitle: '收件人变量',
|
||||
recipientVarsPanelSub: '每人一封邮件,发送时按下列值替换模板中的占位符',
|
||||
recipientVarsSharedTitle: '公共变量(本稿所有人相同)',
|
||||
recipientVarsPerRecipientTitle: '每人不同',
|
||||
recipientVarsColEmail: '收件人',
|
||||
recipientVarsMissingWarn: '有 {count} 位收件人缺少 name 或 final_r_id,请从终审列表添加或先加载稿件',
|
||||
templateVar_name: '姓名 [[name]]',
|
||||
templateVar_article_id: '文章 ID',
|
||||
templateVar_final_r_id: 'final_r_id',
|
||||
templateVar_article_title: '稿件标题',
|
||||
templateVar_title: '标题',
|
||||
templateVar_manuscript_title: '稿件标题',
|
||||
templateVar_accept_sn: 'SN',
|
||||
templateVar_article_sn: 'SN',
|
||||
templateVar_sn: 'SN',
|
||||
templateVar_journal_name: '期刊名',
|
||||
templateVar_journal_url: '期刊网址',
|
||||
roleFirstAuthor: '第一作者',
|
||||
roleCorrespondingAuthor: '通讯作者',
|
||||
roleFirstAuthorShort: '一作',
|
||||
|
||||
@@ -79,8 +79,10 @@
|
||||
v-if="Main_List.length > 0"
|
||||
:content="Main_List"
|
||||
:articleId="articleId"
|
||||
:strippingHeadingNumbers="strippingHeadingNumbers"
|
||||
ref="catalogue"
|
||||
@goToListComment="goToListComment"
|
||||
@stripHeadingNumbers="stripHeadingNumbersFromOutline"
|
||||
style="width: 100%; height: 100%; padding: 0 0px; box-sizing: border-box; background-color: #fff"
|
||||
>
|
||||
</catalogue>
|
||||
@@ -425,6 +427,22 @@
|
||||
</el-form>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button
|
||||
v-if="zyModeEnabled"
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleConvertAddContentSupToBlue"
|
||||
>
|
||||
上标转引用
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="zyModeEnabled"
|
||||
type="warning"
|
||||
plain
|
||||
@click="handleMoveAddContentCitationBeforePeriod"
|
||||
>
|
||||
引用挪到句号前
|
||||
</el-button>
|
||||
<el-button @click="addContentVisible = false"> Cancel </el-button>
|
||||
<el-button type="primary" @click="handleSaveAddContent">
|
||||
<!-- <i class="el-icon-finished" style="margin-right: 5px"></i> -->
|
||||
@@ -447,6 +465,7 @@ import Tinymce from '@/components/page/components/Tinymce';
|
||||
import bottomTinymce from '@/components/page/components/Tinymce';
|
||||
import catalogue from '@/components/page/components/table/catalogue.vue';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
import { collectHeadingNumberStripUpdates } from '@/utils/stripHeadingNumber';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -466,6 +485,7 @@ export default {
|
||||
currentId: null,
|
||||
ManuscirptContent: [],
|
||||
mathFormulasList: [],
|
||||
strippingHeadingNumbers: false,
|
||||
articleId: this.$route.query.id,
|
||||
pArticleId: this.$route.query.p_article_id || '',
|
||||
isShowComment: false,
|
||||
@@ -701,6 +721,34 @@ export default {
|
||||
handleSaveAddContent() {
|
||||
this.$refs.addContent.getTinymceContent('addcontent');
|
||||
},
|
||||
/** 临时:Add Content 内数字上标 → blue 引用标签 */
|
||||
handleConvertAddContentSupToBlue() {
|
||||
const ref = this.$refs.addContent;
|
||||
if (!ref || typeof ref.convertNumericSuperscriptsToBlue !== 'function') {
|
||||
this.$message.warning('编辑器未就绪');
|
||||
return;
|
||||
}
|
||||
const count = ref.convertNumericSuperscriptsToBlue();
|
||||
if (count > 0) {
|
||||
this.$message.success('已转换 ' + count + ' 处数字上标为 blue 引用');
|
||||
} else {
|
||||
this.$message.info('未找到可转换的数字上标(需为上标且内容仅为数字、逗号或连字符)');
|
||||
}
|
||||
},
|
||||
/** 临时:Add Content 内 blue 引用挪到句号前 */
|
||||
handleMoveAddContentCitationBeforePeriod() {
|
||||
const ref = this.$refs.addContent;
|
||||
if (!ref || typeof ref.moveBlueCitationsBeforePeriod !== 'function') {
|
||||
this.$message.warning('编辑器未就绪');
|
||||
return;
|
||||
}
|
||||
const count = ref.moveBlueCitationsBeforePeriod();
|
||||
if (count > 0) {
|
||||
this.$message.success('已调整 ' + count + ' 处参考文献位置');
|
||||
} else {
|
||||
this.$message.info('未找到需要调整的 blue 参考文献(句号已在引用之后则不会重复处理)');
|
||||
}
|
||||
},
|
||||
handleSaveEditProofreadingContent() {
|
||||
this.$refs.addContent.getTinymceContent('addcontent');
|
||||
},
|
||||
@@ -991,6 +1039,65 @@ export default {
|
||||
this.$refs.commonWord.goToComment(mainId);
|
||||
});
|
||||
},
|
||||
async stripHeadingNumbersFromOutline(updates) {
|
||||
const list = updates && updates.length ? updates : collectHeadingNumberStripUpdates(this.Main_List);
|
||||
if (!list.length) {
|
||||
this.$message.info('No heading numbers found to remove.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.$confirm(
|
||||
'Remove numbering from ' + list.length + ' heading(s)? (e.g. "1 Introduction" → "Introduction")',
|
||||
'Remove Heading Numbers',
|
||||
{
|
||||
confirmButtonText: 'Remove',
|
||||
cancelButtonText: 'Cancel',
|
||||
type: 'warning'
|
||||
}
|
||||
);
|
||||
} catch (e) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.strippingHeadingNumbers = true;
|
||||
const loading = this.$loading({
|
||||
lock: true,
|
||||
text: 'Removing heading numbers...',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
});
|
||||
|
||||
let successCount = 0;
|
||||
try {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
const item = list[i];
|
||||
let str = String(item.content || '')
|
||||
.replace(/^<p>\s*(.*?)\s*<\/p>$/, '$1')
|
||||
.trim();
|
||||
str = str.replace(/<br\s*\/?>/gi, '');
|
||||
str = await this.$commonJS.decodeHtml(str);
|
||||
|
||||
const res = await this.$api.post(this.urlList.editContent, {
|
||||
am_id: item.am_id,
|
||||
content: str
|
||||
});
|
||||
if (res && res.code == 0) {
|
||||
successCount++;
|
||||
await this.refreshCurrentContent('content', item.am_id, res.data);
|
||||
} else {
|
||||
throw new Error((res && res.msg) || 'Save failed');
|
||||
}
|
||||
}
|
||||
this.$message.success('Removed numbering from ' + successCount + ' heading(s).');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
this.$message.error((err && err.message) || 'Failed to remove heading numbers.');
|
||||
} finally {
|
||||
loading.close();
|
||||
this.strippingHeadingNumbers = false;
|
||||
}
|
||||
},
|
||||
getTables(tables, html) {
|
||||
this.tables = data.map((e, i) => ({ ...e, p_main_table_id: 2141 + i, p_article_id: 2141 }));
|
||||
this.tablesHtml = html;
|
||||
|
||||
@@ -74,29 +74,46 @@
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="Manuscript :" prop="manuscirpt" label-width="120px">
|
||||
<el-upload
|
||||
:on-progress="handleProgress"
|
||||
ref="uploadFileManuscirpt"
|
||||
class="upload-demo up_newstyle"
|
||||
:action="upload_manuscirpt"
|
||||
accept=".docx"
|
||||
name="manuscirpt"
|
||||
:before-upload="beforeupload_manuscirpt"
|
||||
:on-error="uperr_coverLetter"
|
||||
:on-success="upSuccess_manuscirpt"
|
||||
:limit="1"
|
||||
:on-exceed="alertlimit"
|
||||
:on-remove="removefilemanuscirpt"
|
||||
:file-list="fileL_manuscirpt"
|
||||
:on-preview="dowloadFile"
|
||||
>
|
||||
<div class="el-upload__text" @click="removefilemanuscirpt">
|
||||
<em>Upload</em>
|
||||
<div class="manuscript-upload-wrap">
|
||||
<el-upload
|
||||
:on-progress="handleProgress"
|
||||
ref="uploadFileManuscirpt"
|
||||
class="upload-demo up_newstyle"
|
||||
:action="upload_manuscirpt"
|
||||
accept=".docx"
|
||||
name="manuscirpt"
|
||||
:before-upload="beforeupload_manuscirpt"
|
||||
:on-error="uperr_coverLetter"
|
||||
:on-success="upSuccess_manuscirpt"
|
||||
:limit="1"
|
||||
:on-exceed="alertlimit"
|
||||
:on-remove="removefilemanuscirpt"
|
||||
:file-list="fileL_manuscirpt"
|
||||
:on-preview="dowloadFile"
|
||||
>
|
||||
<div class="el-upload__text" @click="removefilemanuscirpt">
|
||||
<em>Upload</em>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
Only Word and compressed files can be uploaded (file format: .docx).
|
||||
</div>
|
||||
</el-upload>
|
||||
<div class="manuscript-template-notice">
|
||||
<i class="el-icon-info manuscript-template-notice__icon"></i>
|
||||
<p>
|
||||
Kindly prepare and submit your manuscript in DOCX format using our
|
||||
<a
|
||||
href="javascript:void(0)"
|
||||
class="template-download-link"
|
||||
@click.prevent="dowloadFileTemplate"
|
||||
>
|
||||
<i class="el-icon-download"></i> official template
|
||||
</a>.
|
||||
Compliance with the template requirements allows us to process your submission more
|
||||
efficiently and refine the article’s final typesetting to a higher standard.
|
||||
</p>
|
||||
</div>
|
||||
<div class="el-upload__tip" slot="tip">
|
||||
Only Word and compressed files can be uploaded (file format: .docx).
|
||||
</div>
|
||||
</el-upload>
|
||||
</div>
|
||||
</el-form-item>
|
||||
<el-form-item label="Manuscript Title :" prop="title" label-width="160px">
|
||||
<el-input v-model="form.title" type="textarea" rows="2"></el-input>
|
||||
@@ -4365,6 +4382,55 @@ export default {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.manuscript-upload-wrap {
|
||||
position: relative;
|
||||
min-height: 32px;
|
||||
}
|
||||
|
||||
.manuscript-template-notice {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
|
||||
padding: 10px 12px;
|
||||
background: #f5f9fb;
|
||||
border: 1px solid #e3eef3;
|
||||
border-left: 3px solid #006699;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.manuscript-template-notice__icon {
|
||||
flex-shrink: 0;
|
||||
margin-top: 2px;
|
||||
color: #006699;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.manuscript-template-notice p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.65;
|
||||
color: #555;
|
||||
}
|
||||
|
||||
.template-download-link {
|
||||
color: #006699;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.template-download-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.template-download-link .el-icon-download {
|
||||
font-weight: bold;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.el-collapse-item__content {
|
||||
padding-bottom: 8px !important;
|
||||
}
|
||||
|
||||
@@ -154,23 +154,40 @@
|
||||
>
|
||||
|
||||
<el-table-column type="index" label="No." align="center" width="50"></el-table-column>
|
||||
<el-table-column prop="User Message" label="Realname" width="340">
|
||||
<el-table-column prop="User Message" label="Realname" width="320">
|
||||
<template slot-scope="scope">
|
||||
<p style="font-size: 14px; font-weight: bold; margin-top: 2px">{{ scope.row.realname }}</p>
|
||||
<p style="margin: 2px 0 0px 0">
|
||||
<span style="color: #999">Email :</span>
|
||||
{{ scope.row.email }}
|
||||
</p>
|
||||
<p style="margin: 0 0 5px 0" v-if="scope.row.title != ''">
|
||||
<p style="margin: 2px 0 0" v-if="scope.row.type != null && scope.row.type !== ''">
|
||||
<span style="color: #999">Identity : </span>
|
||||
<span v-if="scope.row.type == 0" style="color: #409eff">Editor-in-Chief</span>
|
||||
<span v-if="scope.row.type == 1" style="color: #409eff">Vice Editor-in-Chief</span>
|
||||
<span v-if="scope.row.type == 2" style="color: #409eff">Editorial Board Members</span>
|
||||
</p>
|
||||
<p class="tab_tie_col tab_tie_col--invite tab_tie_col--dialog">
|
||||
<span>Latest invitation: </span>{{ formatInviteTime(scope.row) }}
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="research_areas" label="Research Areas">
|
||||
<el-table-column label="Other Information" min-width="280">
|
||||
<template slot-scope="scope">
|
||||
<p class="tab_tie_col">
|
||||
<span>Research areas: </span>{{ getBoardResearchAreas(scope.row) }}
|
||||
</p>
|
||||
<p class="tab_tie_col">
|
||||
<span>Field: </span>{{ getBoardField(scope.row) }}
|
||||
</p>
|
||||
<p class="tab_tie_col">
|
||||
<span>AI Field: </span>{{ getBoardFieldAi(scope.row) }}
|
||||
</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="Research Areas" width="260">
|
||||
<template slot-scope="scope">
|
||||
<commonMajorTableList
|
||||
:user_id="scope.row.user_id"
|
||||
@@ -363,6 +380,52 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatBoardText(value) {
|
||||
const text = value != null ? String(value).trim() : '';
|
||||
return text || '—';
|
||||
},
|
||||
getBoardField(row) {
|
||||
return this.formatBoardText(row && row.field);
|
||||
},
|
||||
getBoardFieldAi(row) {
|
||||
return this.formatBoardText(row && row.field_ai);
|
||||
},
|
||||
getBoardResearchAreas(row) {
|
||||
return this.formatBoardText(row && row.research_areas);
|
||||
},
|
||||
formatInviteTime(row) {
|
||||
const raw =
|
||||
row &&
|
||||
(row.last_time != null
|
||||
? row.last_time
|
||||
: row.last_invite_time != null
|
||||
? row.last_invite_time
|
||||
: row.latest_invite_time);
|
||||
if (raw == null || raw === '') {
|
||||
return '—';
|
||||
}
|
||||
if (typeof raw === 'string' && /[-/]/.test(raw) && isNaN(Number(raw))) {
|
||||
return raw;
|
||||
}
|
||||
let ts = Number(raw);
|
||||
if (!ts) {
|
||||
return '—';
|
||||
}
|
||||
if (ts < 1e12) {
|
||||
ts *= 1000;
|
||||
}
|
||||
const date = new Date(ts);
|
||||
if (isNaN(date.getTime())) {
|
||||
return String(raw);
|
||||
}
|
||||
const Y = date.getFullYear() + '-';
|
||||
const M = (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1) + '-';
|
||||
const D = date.getDate() < 10 ? '0' + date.getDate() : date.getDate();
|
||||
const h = date.getHours() < 10 ? '0' + date.getHours() : date.getHours();
|
||||
const m = date.getMinutes() < 10 ? '0' + date.getMinutes() : date.getMinutes();
|
||||
const s = date.getSeconds() < 10 ? '0' + date.getSeconds() : date.getSeconds();
|
||||
return Y + M + D + ' ' + h + ':' + m + ':' + s;
|
||||
},
|
||||
selectEditorialBoardType(type) {
|
||||
console.log('type at line 525:', type);
|
||||
this.editorialBoardForm.board_type = type;
|
||||
@@ -745,6 +808,20 @@ export default {
|
||||
margin: 0 5px 0 0;
|
||||
font-size: 13px;
|
||||
}
|
||||
.tab_tie_col--invite {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
}
|
||||
.tab_tie_col--dialog {
|
||||
margin: 4px 0 0;
|
||||
}
|
||||
.tab_tie_col--multiline {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
line-height: 1.5;
|
||||
}
|
||||
::v-deep .editorialBoardDialog .el-dialog__body {
|
||||
padding-top: 10px !important;
|
||||
padding-bottom: 0px !important;
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
<template>
|
||||
<div class="ManuscirptList">
|
||||
<div class="arrlist">
|
||||
<ul class="catalogue-ul">
|
||||
<span v-if="zyModeEnabled" class="outline-format-icon" @click.stop="handleStripHeadingNumbers">
|
||||
<el-tooltip effect="dark" content="Remove heading numbers (e.g. 1, 1.1)" placement="left">
|
||||
<i :class="strippingHeadingNumbers ? 'el-icon-loading' : 'el-icon-brush'"></i>
|
||||
</el-tooltip>
|
||||
</span>
|
||||
<ul class="catalogue-ul" :class="{ 'catalogue-ul--with-toolbar': zyModeEnabled }">
|
||||
<li class="catalogue-li">
|
||||
<div
|
||||
@click="goToListComment(item.am_id, 'content')"
|
||||
@@ -21,8 +26,15 @@
|
||||
|
||||
<script>
|
||||
import { mediaUrl } from '@/common/js/commonJS.js'; // 引入通用逻辑
|
||||
import { collectHeadingNumberStripUpdates } from '@/utils/stripHeadingNumber';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
export default {
|
||||
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content'],
|
||||
props: ['articleId', 'imgWidth', 'imgHeight', 'scale', 'isEdit', 'isShowEdit', 'urlList', 'content', 'strippingHeadingNumbers'],
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
content: {
|
||||
handler(newVal, oldVal) {
|
||||
@@ -102,6 +114,17 @@ export default {
|
||||
|
||||
goToListComment(id, type) {
|
||||
this.$emit('goToListComment', id, type);
|
||||
},
|
||||
handleStripHeadingNumbers() {
|
||||
if (this.strippingHeadingNumbers) {
|
||||
return;
|
||||
}
|
||||
const updates = collectHeadingNumberStripUpdates(this.content);
|
||||
if (!updates.length) {
|
||||
this.$message.info('No heading numbers found to remove.');
|
||||
return;
|
||||
}
|
||||
this.$emit('stripHeadingNumbers', updates);
|
||||
}
|
||||
},
|
||||
async created() {},
|
||||
@@ -159,9 +182,25 @@ export default {
|
||||
.arrlist {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
position: relative;
|
||||
/* padding-top: 8px; */
|
||||
}
|
||||
|
||||
.outline-format-icon {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 2px;
|
||||
z-index: 2;
|
||||
color: #909399;
|
||||
font-size: 16px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.outline-format-icon:hover {
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.catalogue-ul {
|
||||
width: 100%;
|
||||
list-style: none;
|
||||
@@ -169,6 +208,10 @@ export default {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.catalogue-ul--with-toolbar {
|
||||
padding-top: 0px;
|
||||
}
|
||||
|
||||
/* 基础条目样式 - 模拟 Word 悬浮效果 */
|
||||
.catalogue-item {
|
||||
position: relative;
|
||||
|
||||
@@ -37,6 +37,8 @@
|
||||
|
||||
<script>
|
||||
import Tinymce from '@/components/page/components/Tinymce';
|
||||
import { convertNumericSuperscriptsToBlueCitationsHtml } from '@/utils/convertNumericSuperscriptsToBlue';
|
||||
import { moveBlueCitationsBeforePeriodHtml } from '@/utils/moveBlueCitationsBeforePeriod';
|
||||
export default {
|
||||
props: {
|
||||
value: {},
|
||||
@@ -128,6 +130,42 @@ export default {
|
||||
getContent(type, content) {
|
||||
|
||||
this.$emit('getContent', type, content);
|
||||
},
|
||||
/** 临时:数字上标 → blue 参考文献标签 */
|
||||
convertNumericSuperscriptsToBlue() {
|
||||
const tinymceRef = this.$refs.tinymceChild1;
|
||||
if (!tinymceRef || !tinymceRef.tinymceId || typeof window.tinymce === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
const editor = window.tinymce.get(tinymceRef.tinymceId);
|
||||
if (!editor) {
|
||||
return 0;
|
||||
}
|
||||
const raw = editor.getContent({ format: 'raw' });
|
||||
const result = convertNumericSuperscriptsToBlueCitationsHtml(raw);
|
||||
if (result.count > 0) {
|
||||
editor.setContent(result.html);
|
||||
editor.fire('change');
|
||||
}
|
||||
return result.count;
|
||||
},
|
||||
/** 临时:blue 引用挪到句号前,重复句号合并为一个 */
|
||||
moveBlueCitationsBeforePeriod() {
|
||||
const tinymceRef = this.$refs.tinymceChild1;
|
||||
if (!tinymceRef || !tinymceRef.tinymceId || typeof window.tinymce === 'undefined') {
|
||||
return 0;
|
||||
}
|
||||
const editor = window.tinymce.get(tinymceRef.tinymceId);
|
||||
if (!editor) {
|
||||
return 0;
|
||||
}
|
||||
const raw = editor.getContent({ format: 'raw' });
|
||||
const result = moveBlueCitationsBeforePeriodHtml(raw);
|
||||
if (result.count > 0) {
|
||||
editor.setContent(result.html);
|
||||
editor.fire('change');
|
||||
}
|
||||
return result.count;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1179,7 +1179,7 @@ import { TableUtils } from '@/common/js/TableUtils';
|
||||
import { debounce, throttle } from '@/common/js/debounce';
|
||||
import { tableStyle, commonWordStyle } from '@/utils/tinymceStyles';
|
||||
import LatexDataPanel from './LatexDataPanel.vue';
|
||||
import { downloadManuscriptWord, fetchManuscriptReferenceList } from '@/utils/exportManuscriptWord';
|
||||
import { downloadManuscriptWord, fetchManuscriptReferenceList, summarizeManuscriptExportItems } from '@/utils/exportManuscriptWord';
|
||||
import {
|
||||
buildReferencesCopyText,
|
||||
buildReferencesHtmlLabels,
|
||||
@@ -1768,6 +1768,11 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.exportingManuscriptWord = true;
|
||||
console.info('[Word export] 用户点击导出按钮', {
|
||||
articleId: this.articleId,
|
||||
pArticleId: this.pArticleId,
|
||||
summary: summarizeManuscriptExportItems(this.wordList)
|
||||
});
|
||||
try {
|
||||
const hasUploaded =
|
||||
this.uploadedReferenceHtmlItems && this.uploadedReferenceHtmlItems.length;
|
||||
@@ -1786,7 +1791,12 @@ export default {
|
||||
: this.$t('commonTable.exportManuscriptSuccess') || 'Word downloaded.'
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
console.error('[Word export] 导出失败', {
|
||||
articleId: this.articleId,
|
||||
pArticleId: this.pArticleId,
|
||||
summary: summarizeManuscriptExportItems(this.wordList),
|
||||
error: err
|
||||
});
|
||||
if (err && err.message === 'NO_CONTENT') {
|
||||
this.$message.warning(this.$t('commonTable.exportManuscriptEmpty') || 'No content to export.');
|
||||
} else {
|
||||
|
||||
@@ -502,6 +502,40 @@
|
||||
ref="myTableModal"
|
||||
|
||||
/>
|
||||
|
||||
<el-dialog
|
||||
title="导出全部图片 Word"
|
||||
:visible.sync="figureExportDialogVisible"
|
||||
width="420px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<div class="figure-export-dialog">
|
||||
<p class="figure-export-dialog__hint">宽、高只需填一项即可,另一项按原图比例自动计算;两项都填则固定尺寸。图与图之间仅空 1 行。</p>
|
||||
<el-form label-width="88px" size="small" @submit.native.prevent>
|
||||
<el-form-item label="宽度 (cm)">
|
||||
<el-input
|
||||
v-model="figureExportWidthCm"
|
||||
placeholder="如 8,只填宽度时锁定纵横比"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="高度 (cm)">
|
||||
<el-input
|
||||
v-model="figureExportHeightCm"
|
||||
placeholder="如 6,只填高度时锁定纵横比"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
<span slot="footer">
|
||||
<el-button @click="figureExportDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="exportingAllFiguresWord" @click="confirmDownloadAllFiguresWord">
|
||||
导出
|
||||
</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -550,7 +584,10 @@ export default {
|
||||
exportingAllWord: false,
|
||||
exportingTableId: null,
|
||||
exportingAllFiguresWord: false,
|
||||
exportingFigureId: null
|
||||
exportingFigureId: null,
|
||||
figureExportDialogVisible: false,
|
||||
figureExportWidthCm: '',
|
||||
figureExportHeightCm: ''
|
||||
};
|
||||
},
|
||||
components: {
|
||||
@@ -1034,10 +1071,43 @@ if(newData.ami_id) {
|
||||
this.$message.warning('No figures to export.');
|
||||
return;
|
||||
}
|
||||
this.figureExportWidthCm = '';
|
||||
this.figureExportHeightCm = '';
|
||||
this.figureExportDialogVisible = true;
|
||||
},
|
||||
parseFigureExportSizeCm(value) {
|
||||
if (value === undefined || value === null || value === '') {
|
||||
return null;
|
||||
}
|
||||
const num = Number(String(value).trim());
|
||||
if (!Number.isFinite(num) || num <= 0) {
|
||||
return null;
|
||||
}
|
||||
return num;
|
||||
},
|
||||
async confirmDownloadAllFiguresWord() {
|
||||
const widthCm = this.parseFigureExportSizeCm(this.figureExportWidthCm);
|
||||
const heightCm = this.parseFigureExportSizeCm(this.figureExportHeightCm);
|
||||
if (
|
||||
(this.figureExportWidthCm && widthCm == null) ||
|
||||
(this.figureExportHeightCm && heightCm == null)
|
||||
) {
|
||||
this.$message.warning('请输入有效的厘米数值(大于 0)。');
|
||||
return;
|
||||
}
|
||||
|
||||
this.exportingAllFiguresWord = true;
|
||||
try {
|
||||
await exportAllFiguresWordFile(this.images, this.mediaUrl, 'figures');
|
||||
const exportOptions = {};
|
||||
if (widthCm != null || heightCm != null) {
|
||||
exportOptions.figureSize = {
|
||||
widthCm: widthCm,
|
||||
heightCm: heightCm
|
||||
};
|
||||
}
|
||||
await exportAllFiguresWordFile(this.images, this.mediaUrl, 'figures', exportOptions);
|
||||
this.$message.success('Word downloaded.');
|
||||
this.figureExportDialogVisible = false;
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
if (err && err.message === 'NO_FIGURES') {
|
||||
@@ -1370,4 +1440,11 @@ li {
|
||||
}
|
||||
.single {
|
||||
}
|
||||
|
||||
.figure-export-dialog__hint {
|
||||
margin: 0 0 14px;
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -93,8 +93,15 @@
|
||||
</div>
|
||||
|
||||
<!-- 同意弹出框 -->
|
||||
<el-dialog title="Agree User" :visible.sync="agreeVisible" width="700px" :close-on-click-modal="false" class="agree_form_box">
|
||||
<el-form ref="agree_Form" :model="agreeForm" :rules="rules" label-width="140px">
|
||||
<el-dialog
|
||||
title="Agree User"
|
||||
:visible.sync="agreeVisible"
|
||||
width="700px"
|
||||
:close-on-click-modal="false"
|
||||
class="agree_form_box"
|
||||
@opened="handleAgreeDialogOpened"
|
||||
>
|
||||
<el-form ref="agree_Form" :model="agreeForm" :rules="agreeFormRules" label-width="140px">
|
||||
<el-form-item label="Account :" style="margin-bottom: 2px !important">
|
||||
<span>{{ agreeForm.account }}</span>
|
||||
</el-form-item>
|
||||
@@ -188,6 +195,10 @@
|
||||
<script>
|
||||
import commonCv from '../common/cv.vue';
|
||||
import bus from '../common/bus';
|
||||
import { isZyModeEnabled } from '@/utils/zyMode';
|
||||
|
||||
const AGREE_DETAIL_FIELDS = ['realname', 'phone', 'technical', 'country', 'website', 'field', 'introduction', 'company'];
|
||||
|
||||
export default {
|
||||
components: {
|
||||
commonCv
|
||||
@@ -336,6 +347,33 @@ export default {
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
zyModeEnabled() {
|
||||
return isZyModeEnabled(this.$route);
|
||||
},
|
||||
agreeFormRules() {
|
||||
const rules = JSON.parse(JSON.stringify(this.rules));
|
||||
if (this.zyModeEnabled) {
|
||||
rules.phone = [
|
||||
{
|
||||
validator: function (rule, value, callback) {
|
||||
if (!value || String(value).trim() === '') {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
if (/^1[34578]\d{9}$/.test(value) == false) {
|
||||
callback(new Error('Please enter the correct phone format'));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
},
|
||||
trigger: 'blur'
|
||||
}
|
||||
];
|
||||
}
|
||||
return rules;
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getDate();
|
||||
this.getCountry();
|
||||
@@ -406,16 +444,81 @@ export default {
|
||||
|
||||
// 同意
|
||||
agreeApply(e) {
|
||||
this.agreeForm = {};
|
||||
console.log('e at line 405:', e);
|
||||
|
||||
this.agreeForm = Object.assign({}, e);
|
||||
this.applyZyDefaultYear();
|
||||
this.agreeVisible = true;
|
||||
this.agreeForm = e;
|
||||
this.$refs.agree_Form.clearValidate();
|
||||
this.$nextTick(() => {
|
||||
this.applyZyDefaultYear();
|
||||
if (this.$refs.agree_Form) {
|
||||
this.$refs.agree_Form.clearValidate();
|
||||
}
|
||||
});
|
||||
if (this.zyModeEnabled && e.user_id) {
|
||||
this.supplementAgreeFormByUserDetail(e.user_id);
|
||||
}
|
||||
},
|
||||
|
||||
// 提交同意
|
||||
saveAgreee() {
|
||||
applyZyDefaultYear() {
|
||||
if (!this.zyModeEnabled) {
|
||||
return;
|
||||
}
|
||||
this.$set(this.agreeForm, 'year', 1);
|
||||
},
|
||||
|
||||
handleAgreeDialogOpened() {
|
||||
this.applyZyDefaultYear();
|
||||
},
|
||||
|
||||
isAgreeFieldEmpty(value) {
|
||||
return value === undefined || value === null || String(value).trim() === '';
|
||||
},
|
||||
|
||||
mergeAgreeFormFromUserDetail(user) {
|
||||
if (!user) {
|
||||
return;
|
||||
}
|
||||
AGREE_DETAIL_FIELDS.forEach((key) => {
|
||||
if (this.isAgreeFieldEmpty(this.agreeForm[key]) && !this.isAgreeFieldEmpty(user[key])) {
|
||||
this.$set(this.agreeForm, key, user[key]);
|
||||
}
|
||||
});
|
||||
this.applyZyDefaultYear();
|
||||
},
|
||||
|
||||
supplementAgreeFormByUserDetail(userId) {
|
||||
this.$api
|
||||
.post('api/User/getUserDetail', {
|
||||
user_id: userId
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code != 0 || !res.data || !res.data.user) {
|
||||
if (res.msg) {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
return;
|
||||
}
|
||||
this.mergeAgreeFormFromUserDetail(res.data.user);
|
||||
this.$nextTick(() => {
|
||||
this.tryZyModeAutoAgree();
|
||||
});
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
|
||||
tryZyModeAutoAgree() {
|
||||
if (!this.zyModeEnabled || !this.$refs.agree_Form) {
|
||||
return;
|
||||
}
|
||||
this.$refs.agree_Form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.submitAgreeForm();
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
submitAgreeForm() {
|
||||
var data = {
|
||||
ap_yboard_id: this.agreeForm.ap_yboard_id,
|
||||
year: this.agreeForm.year,
|
||||
@@ -428,27 +531,30 @@ export default {
|
||||
introduction: this.agreeForm.introduction,
|
||||
company: this.agreeForm.company
|
||||
};
|
||||
console.log('data at line 419:', data);
|
||||
|
||||
this.$api
|
||||
.post('api/User/agreeYboardApplyNew', {
|
||||
...data
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('Add succeeded!');
|
||||
this.agreeVisible = false;
|
||||
this.getDate();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
},
|
||||
|
||||
// 提交同意
|
||||
saveAgreee() {
|
||||
this.$refs.agree_Form.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$api
|
||||
.post('api/User/agreeYboardApplyNew', {
|
||||
...data
|
||||
})
|
||||
|
||||
.then((res) => {
|
||||
if (res.code == 0) {
|
||||
this.$message.success('Add succeeded!');
|
||||
this.agreeVisible = false;
|
||||
this.getDate();
|
||||
} else {
|
||||
this.$message.error(res.msg);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
console.log(err);
|
||||
});
|
||||
this.submitAgreeForm();
|
||||
} else {
|
||||
this.$message.error('Please complete the information!!');
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user