提交
This commit is contained in:
@@ -2,14 +2,14 @@
|
|||||||
//记得切换
|
//记得切换
|
||||||
|
|
||||||
//正式
|
//正式
|
||||||
const mediaUrl = '/public/';
|
// const mediaUrl = '/public/';
|
||||||
const baseUrl = '/';
|
// const baseUrl = '/';
|
||||||
|
|
||||||
//正式环境
|
//正式环境
|
||||||
|
|
||||||
// const mediaUrl = 'https://submission.tmrjournals.com/public/';
|
const mediaUrl = 'https://submission.tmrjournals.com/public/';
|
||||||
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
// const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
|
||||||
// const baseUrl = '/api'
|
const baseUrl = '/api'
|
||||||
|
|
||||||
//测试环境
|
//测试环境
|
||||||
|
|
||||||
|
|||||||
@@ -657,6 +657,15 @@ colTitle: 'Template title',
|
|||||||
printBtn: 'Print',
|
printBtn: 'Print',
|
||||||
previewNotSupported: 'This file format cannot be previewed online',
|
previewNotSupported: 'This file format cannot be previewed online',
|
||||||
downloadToView: 'Download to view locally',
|
downloadToView: 'Download to view locally',
|
||||||
|
registerAuthorBtn: 'Create author account',
|
||||||
|
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.',
|
||||||
|
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.',
|
||||||
},
|
},
|
||||||
crawlerKeywords: {
|
crawlerKeywords: {
|
||||||
pageTitle: 'Keyword Configuration',
|
pageTitle: 'Keyword Configuration',
|
||||||
|
|||||||
@@ -646,6 +646,15 @@ const zh = {
|
|||||||
printBtn: '打印',
|
printBtn: '打印',
|
||||||
previewNotSupported: '该文件格式无法在线预览',
|
previewNotSupported: '该文件格式无法在线预览',
|
||||||
downloadToView: '下载到本地查看',
|
downloadToView: '下载到本地查看',
|
||||||
|
registerAuthorBtn: '创建作者账号',
|
||||||
|
registerAuthorConfirm:
|
||||||
|
'将使用推广后台「添加用户」接口创建账号:登录名「{account}」,显示名「{realname}」,邮箱「{email}」,初始密码 123456qwe(无需验证码)。是否继续?',
|
||||||
|
registerAuthorSuccess: '作者账号已创建。',
|
||||||
|
registerAuthorFail: '创建失败,请稍后重试或到用户管理中手动添加。',
|
||||||
|
registerAuthorExistsEmail: '该邮箱已被注册。',
|
||||||
|
registerAuthorExistsAccount: '该登录名已被占用,请人工处理或修改发件人显示名后重试。',
|
||||||
|
registerAuthorNeedEmail: '缺少发件人邮箱,无法创建账号。',
|
||||||
|
registerAuthorNoQq: '本站不支持 QQ 邮箱作为作者账号,请在用户管理中手动添加。',
|
||||||
},
|
},
|
||||||
crawlerKeywords: {
|
crawlerKeywords: {
|
||||||
pageTitle: '关键词配置',
|
pageTitle: '关键词配置',
|
||||||
|
|||||||
@@ -5,6 +5,18 @@
|
|||||||
<h1 class="mail-subject-top">{{ $t('mailboxCollect.subject') }}:{{ mailData.subject }}</h1>
|
<h1 class="mail-subject-top">{{ $t('mailboxCollect.subject') }}:{{ mailData.subject }}</h1>
|
||||||
</div>
|
</div>
|
||||||
<div class="toolbar-right">
|
<div class="toolbar-right">
|
||||||
|
<el-button
|
||||||
|
v-if="hasWordAttachment"
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
plain
|
||||||
|
icon="el-icon-user-solid"
|
||||||
|
:loading="registerAuthorLoading"
|
||||||
|
class="register-author-btn"
|
||||||
|
@click="registerAuthorFromMail"
|
||||||
|
>
|
||||||
|
{{ $t('mailboxCollect.registerAuthorBtn') }}
|
||||||
|
</el-button>
|
||||||
<!-- <i class="el-icon-star-off action-icon"></i> -->
|
<!-- <i class="el-icon-star-off action-icon"></i> -->
|
||||||
<i class="el-icon-close action-icon" @click="$emit('close')"></i>
|
<i class="el-icon-close action-icon" @click="$emit('close')"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -70,6 +82,18 @@
|
|||||||
<el-link type="primary" :underline="false" @click="scrollToAttachments" class="jump-link">
|
<el-link type="primary" :underline="false" @click="scrollToAttachments" class="jump-link">
|
||||||
{{ $t('mailboxCollect.viewAttachments') }}
|
{{ $t('mailboxCollect.viewAttachments') }}
|
||||||
</el-link>
|
</el-link>
|
||||||
|
<el-button
|
||||||
|
v-if="hasWordAttachment"
|
||||||
|
type="primary"
|
||||||
|
size="mini"
|
||||||
|
plain
|
||||||
|
icon="el-icon-user-solid"
|
||||||
|
:loading="registerAuthorLoading"
|
||||||
|
class="register-author-btn-inline"
|
||||||
|
@click="registerAuthorFromMail"
|
||||||
|
>
|
||||||
|
{{ $t('mailboxCollect.registerAuthorBtn') }}
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -152,10 +176,20 @@ export default {
|
|||||||
mediaUrl: Common.mediaUrl,
|
mediaUrl: Common.mediaUrl,
|
||||||
isDetailExpanded: false,
|
isDetailExpanded: false,
|
||||||
downloadingIndex: -1,
|
downloadingIndex: -1,
|
||||||
packingAll: false
|
packingAll: false,
|
||||||
|
registerAuthorLoading: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
/** 存在 Word 附件(.doc / .docx)时显示「创建作者账号」 */
|
||||||
|
hasWordAttachment() {
|
||||||
|
const att = (this.mailData && this.mailData.attachments) || [];
|
||||||
|
if (!att.length) return false;
|
||||||
|
return att.some((f) => {
|
||||||
|
const n = (f && f.name) || '';
|
||||||
|
return /\.(doc|docx)$/i.test(n);
|
||||||
|
});
|
||||||
|
},
|
||||||
totalAttachmentSize() {
|
totalAttachmentSize() {
|
||||||
if (!this.mailData.attachments || !this.mailData.attachments.length) return '0B';
|
if (!this.mailData.attachments || !this.mailData.attachments.length) return '0B';
|
||||||
const total = this.mailData.attachments.reduce((sum, f) => sum + (Number(f.size) || 0), 0);
|
const total = this.mailData.attachments.reduce((sum, f) => sum + (Number(f.size) || 0), 0);
|
||||||
@@ -175,6 +209,127 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
stripHtml(html) {
|
||||||
|
if (html == null || html === '') return '';
|
||||||
|
const s = String(html);
|
||||||
|
const d = typeof document !== 'undefined' ? document.createElement('div') : null;
|
||||||
|
if (d) {
|
||||||
|
d.innerHTML = s;
|
||||||
|
return (d.textContent || d.innerText || '').trim();
|
||||||
|
}
|
||||||
|
return s.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
|
||||||
|
},
|
||||||
|
/** 与 partyList 添加用户一致:从正文尝试提取手机号(国内 11 位或含 + 的国际号) */
|
||||||
|
extractPhoneFromMailBody() {
|
||||||
|
const m = this.mailData || {};
|
||||||
|
let blob = '';
|
||||||
|
['content_text', 'content', 'body', 'content_html', 'html', 'body_html'].forEach((k) => {
|
||||||
|
const v = m[k];
|
||||||
|
if (v != null && String(v).trim() !== '') blob += `\n${String(v)}`;
|
||||||
|
});
|
||||||
|
const text = this.stripHtml(blob);
|
||||||
|
const cn = text.match(/(?:^|\D)(1[3-9]\d{9})(?:\D|$)/);
|
||||||
|
if (cn) return cn[1];
|
||||||
|
const intl = text.match(/\+\d{1,3}[\s\-]?\d[\d\s\-]{8,18}\d/);
|
||||||
|
if (intl) return intl[0].replace(/\s+/g, ' ').trim().slice(0, 32);
|
||||||
|
const labeled = text.match(/(?:Tel|Phone|Mobile|MW)[\s::]*([+()\d][\d\s\-().]{10,40})/i);
|
||||||
|
if (labeled) return labeled[1].trim().slice(0, 32);
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
buildAuthorRegisterPayload() {
|
||||||
|
const m = this.mailData || {};
|
||||||
|
const email = String(m.from_email || '')
|
||||||
|
.trim()
|
||||||
|
.toLowerCase();
|
||||||
|
const localPart = email.split('@')[0] || 'user';
|
||||||
|
let rawName = String(m.from_name || '').trim();
|
||||||
|
let realname = rawName || localPart;
|
||||||
|
if (this.$validateString && !this.$validateString(realname)) {
|
||||||
|
realname = localPart;
|
||||||
|
if (this.$validateString && !this.$validateString(realname)) {
|
||||||
|
realname = 'Author';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let account = rawName.replace(/[^a-zA-Z0-9_-]/g, '');
|
||||||
|
if (!account || account.length < 2) {
|
||||||
|
account = localPart.replace(/[^a-zA-Z0-9_-]/g, '');
|
||||||
|
}
|
||||||
|
if (!account || account.length < 2) {
|
||||||
|
account = `u${email.replace(/[^a-zA-Z0-9]/g, '').slice(0, 12) || 'ser'}`;
|
||||||
|
}
|
||||||
|
const phone = this.extractPhoneFromMailBody() || '';
|
||||||
|
return { email, account, realname, phone };
|
||||||
|
},
|
||||||
|
async registerAuthorFromMail() {
|
||||||
|
if (!this.hasWordAttachment) return;
|
||||||
|
const { email, account, realname, phone } = this.buildAuthorRegisterPayload();
|
||||||
|
if (!email) {
|
||||||
|
this.$message.warning(this.$t('mailboxCollect.registerAuthorNeedEmail'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (email.endsWith('@qq.com')) {
|
||||||
|
this.$message.warning(this.$t('mailboxCollect.registerAuthorNoQq'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const pwd = '123456qwe';
|
||||||
|
try {
|
||||||
|
await this.$confirm(
|
||||||
|
this.$t('mailboxCollect.registerAuthorConfirm', { email, account, realname }),
|
||||||
|
this.$t('mailboxCollect.registerAuthorBtn'),
|
||||||
|
{ type: 'warning', distinguishCancelAndClose: true }
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.registerAuthorLoading = true;
|
||||||
|
try {
|
||||||
|
const p = this.buildAuthorRegisterPayload();
|
||||||
|
const email2 = p.email;
|
||||||
|
const account2 = p.account;
|
||||||
|
const realname2 = p.realname;
|
||||||
|
const phone2 = p.phone;
|
||||||
|
const ef = { email: email2, account: account2 };
|
||||||
|
const r1 = await this.$api.post('api/User/checkUserByEmail', ef);
|
||||||
|
if (!r1 || Number(r1.code) !== 0) {
|
||||||
|
this.$message.error((r1 && r1.msg) || this.$t('mailboxCollect.registerAuthorFail'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const r2 = await this.$api.post('api/User/checkUserByAccount', ef);
|
||||||
|
if (!r2 || Number(r2.code) !== 0) {
|
||||||
|
this.$message.error((r2 && r2.msg) || this.$t('mailboxCollect.registerAuthorFail'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const hasEmail = r1.data && Number(r1.data.has) === 1;
|
||||||
|
const hasAccount = r2.data && Number(r2.data.has) === 1;
|
||||||
|
if (hasEmail) {
|
||||||
|
this.$message.warning(this.$t('mailboxCollect.registerAuthorExistsEmail'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (hasAccount) {
|
||||||
|
this.$message.warning(this.$t('mailboxCollect.registerAuthorExistsAccount'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const addForm = {
|
||||||
|
email: email2,
|
||||||
|
account: account2,
|
||||||
|
password: pwd,
|
||||||
|
repassword: pwd,
|
||||||
|
realname: realname2,
|
||||||
|
phone: phone2 || ''
|
||||||
|
};
|
||||||
|
const res = await this.$api.post('api/User/addUser', addForm);
|
||||||
|
if (res && Number(res.code) === 0) {
|
||||||
|
this.$message.success(this.$t('mailboxCollect.registerAuthorSuccess'));
|
||||||
|
} else {
|
||||||
|
this.$message.error((res && res.msg) || this.$t('mailboxCollect.registerAuthorFail'));
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err);
|
||||||
|
this.$message.error(this.$t('mailboxCollect.registerAuthorFail'));
|
||||||
|
} finally {
|
||||||
|
this.registerAuthorLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
escapeHtml(text) {
|
escapeHtml(text) {
|
||||||
if (text == null) return '';
|
if (text == null) return '';
|
||||||
return String(text)
|
return String(text)
|
||||||
@@ -383,6 +538,19 @@ const res = await this.$api.post('api/email_client/getAttachment', {
|
|||||||
color: #606266;
|
color: #606266;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
.toolbar-right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.register-author-btn {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.register-author-btn-inline {
|
||||||
|
margin-left: 10px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
.action-icon:hover {
|
.action-icon:hover {
|
||||||
color: #409eff;
|
color: #409eff;
|
||||||
}
|
}
|
||||||
@@ -665,7 +833,9 @@ const res = await this.$api.post('api/email_client/getAttachment', {
|
|||||||
}
|
}
|
||||||
.attachment-brief-bar {
|
.attachment-brief-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
gap: 8px 12px;
|
||||||
padding: 8px 0;
|
padding: 8px 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
color: #606266;
|
color: #606266;
|
||||||
@@ -681,7 +851,7 @@ const res = await this.$api.post('api/email_client/getAttachment', {
|
|||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
.jump-link {
|
.jump-link {
|
||||||
margin-left: 15px;
|
margin-left: 0;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
.attachment-section {
|
.attachment-section {
|
||||||
|
|||||||
Reference in New Issue
Block a user