This commit is contained in:
2026-05-13 13:25:05 +08:00
parent b10de50fdf
commit 723ec0d190
4 changed files with 195 additions and 7 deletions

View File

@@ -2,14 +2,14 @@
//记得切换
//正式
const mediaUrl = '/public/';
const baseUrl = '/';
// const mediaUrl = '/public/';
// const baseUrl = '/';
//正式环境
// const mediaUrl = 'https://submission.tmrjournals.com/public/';
// // const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
// const baseUrl = '/api'
const mediaUrl = 'https://submission.tmrjournals.com/public/';
// const mediaUrl = 'http://zmzm.tougao.dev.com/public/';
const baseUrl = '/api'
//测试环境

View File

@@ -657,6 +657,15 @@ colTitle: 'Template title',
printBtn: 'Print',
previewNotSupported: 'This file format cannot be previewed online',
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: {
pageTitle: 'Keyword Configuration',

View File

@@ -646,6 +646,15 @@ const zh = {
printBtn: '打印',
previewNotSupported: '该文件格式无法在线预览',
downloadToView: '下载到本地查看',
registerAuthorBtn: '创建作者账号',
registerAuthorConfirm:
'将使用推广后台「添加用户」接口创建账号:登录名「{account}」,显示名「{realname}」,邮箱「{email}」,初始密码 123456qwe无需验证码。是否继续',
registerAuthorSuccess: '作者账号已创建。',
registerAuthorFail: '创建失败,请稍后重试或到用户管理中手动添加。',
registerAuthorExistsEmail: '该邮箱已被注册。',
registerAuthorExistsAccount: '该登录名已被占用,请人工处理或修改发件人显示名后重试。',
registerAuthorNeedEmail: '缺少发件人邮箱,无法创建账号。',
registerAuthorNoQq: '本站不支持 QQ 邮箱作为作者账号,请在用户管理中手动添加。',
},
crawlerKeywords: {
pageTitle: '关键词配置',

View File

@@ -5,6 +5,18 @@
<h1 class="mail-subject-top">{{ $t('mailboxCollect.subject') }}{{ mailData.subject }}</h1>
</div>
<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-close action-icon" @click="$emit('close')"></i>
</div>
@@ -70,6 +82,18 @@
<el-link type="primary" :underline="false" @click="scrollToAttachments" class="jump-link">
{{ $t('mailboxCollect.viewAttachments') }}
</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>
@@ -152,10 +176,20 @@ export default {
mediaUrl: Common.mediaUrl,
isDetailExpanded: false,
downloadingIndex: -1,
packingAll: false
packingAll: false,
registerAuthorLoading: false
};
},
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() {
if (!this.mailData.attachments || !this.mailData.attachments.length) return '0B';
const total = this.mailData.attachments.reduce((sum, f) => sum + (Number(f.size) || 0), 0);
@@ -175,6 +209,127 @@ export default {
}
},
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) {
if (text == null) return '';
return String(text)
@@ -383,6 +538,19 @@ const res = await this.$api.post('api/email_client/getAttachment', {
color: #606266;
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 {
color: #409eff;
}
@@ -665,7 +833,9 @@ const res = await this.$api.post('api/email_client/getAttachment', {
}
.attachment-brief-bar {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 8px 12px;
padding: 8px 0;
font-size: 13px;
color: #606266;
@@ -681,7 +851,7 @@ const res = await this.$api.post('api/email_client/getAttachment', {
margin-left: 4px;
}
.jump-link {
margin-left: 15px;
margin-left: 0;
font-size: 13px;
}
.attachment-section {