This commit is contained in:
2026-03-11 10:14:57 +08:00
parent 68f52bed67
commit 2c2ef4e6c4
10 changed files with 1694 additions and 231 deletions

View File

@@ -1,78 +1,143 @@
<template>
<div>
<div class="crumbs">
<el-button type="text" icon="el-icon-arrow-left" @click="goBackInbox" class="back-inbox-btn">
{{ $t('mailboxSend.backToInbox') }}
</el-button>
<el-breadcrumb separator="/">
<el-breadcrumb-item>
<i class="el-icon-message"></i> Mailbox Send
<i class="el-icon-message"></i> {{ $t('mailboxSend.title') }}
</el-breadcrumb-item>
</el-breadcrumb>
</div>
<div class="container">
<div class="mail_shuru" style="position: relative;">
<span class="mail_tit">Addressee :</span>
<span class="sel_liby" @click="handleSetLibrary">
<i class="el-icon-plus"></i>Select from library
</span>
<!-- <el-input v-model="queryMail.sendname" class="mail_inp"></el-input> -->
<multi-items-input v-model="queryMail.sendnamelist" placeholder="" separator="" @select="selectHandle" @delete="deleteHandle"
:selection-only="false" class="dxk_inp" @blur="blueHandle"></multi-items-input>
</div>
<div class="mail_shuru">
<span class="mail_tit">Mail Subject :</span>
<el-input v-model="queryMail.sendtitle" class="mail_inp"></el-input>
</div>
<div class="mail_shuru">
<span class="mail_tit">CC :</span>
<el-input v-model="queryMail.sendcc" class="mail_inp"></el-input>
<!-- <multi-items-input v-model="queryMail.sendnamelist" placeholder="" separator="" @select="selectHandle" @delete="deleteHandle"
:selection-only="false" class="dxk_inp" @blur="blueHandle"></multi-items-input> -->
</div>
<div class="mail_shuru" style="border: 0;">
<span class="mail_tit">Attachments :</span>
<el-upload class="upload-demo up_newstyle" :action="this.baseUrl + 'api/Article/up_file/type/attr'" accept=".jpg, .png, .rar, .zip"
name="atta" :before-upload="beforeupload_atta" :on-error="uperr_atta" :on-success="upSuccess_atta" :limit="30"
:on-exceed="alertlimit" :on-remove="removefile_atta" :file-list="fileL_atta">
<div class="el-upload__text">
<em>Upload</em>
</div>
<!-- <div class="el-upload__tip" slot="tip">Only compressed files can be uploaded(.rar,.zip)</div> -->
</el-upload>
<div class="mail_shuru" style="position: relative; display: flex; align-items: center;">
<span class="mail_tit">{{ $t('mailboxSend.to') }}</span>
<div style="flex: 1; display: flex; flex-wrap: wrap; align-items: center;">
<div class="selected-tags" v-if="queryMail.sendnamelist && queryMail.sendnamelist.length" style="display: inline-block;">
<el-tag
v-for="(item, index) in queryMail.sendnamelist"
:key="item._uid != null ? item._uid : 'to-' + index"
closable
@close="removeRecipient(index)"
type="info"
size="small"
style="margin-right: 6px;"
>
{{ item.name }}
</el-tag>
</div>
<el-autocomplete
ref="autocompleteTo"
class="mail_inp"
v-model="toInput"
:fetch-suggestions="fetchUserSuggestions"
:trigger-on-focus="false"
placeholder=""
@select="handleSelectUser"
@blur="handleToBlur"
style="width: 200px; flex: 1;"
>
<!-- <i slot="suffix" class="el-icon-plus" @click="handleSetLibrary" style="cursor: pointer; color: #006699; font-weight: bold; line-height: 40px; font-size: 16px;"></i> -->
<template slot-scope="{ item }">
<div class="name">
{{ item.value }} <span style="padding: 4px; color: #ccc;">|</span> {{ item.realname }}
</div>
</template>
</el-autocomplete>
</div>
<span class="sel_liby" @click="handleSetLibrary" style="position: static; margin-left: 10px;">
<i class="el-icon-plus"></i>{{ $t('mailboxSend.selectFromLibrary') }}
</span>
</div>
<div class="mail_shuru">
<span class="mail_tit">{{ $t('mailboxSend.subject') }}</span>
<el-input v-model="queryMail.sendtitle" class="mail_inp" ></el-input>
</div>
<p class="sel_moud" @click="handleSetMoudle">
<i class="el-icon-brush"></i>Template selection
</p>
<div class="mail_shuru" style="position: relative; display: flex; align-items: center;">
<span class="mail_tit">{{ $t('mailboxSend.cc') }}</span>
<div style="flex: 1; display: flex; flex-wrap: wrap; align-items: center;">
<div class="selected-tags" v-if="ccList && ccList.length" style="display: inline-block;">
<el-tag
v-for="(item, index) in ccList"
:key="item._uid != null ? item._uid : 'cc-' + index"
closable
@close="removeCc(index)"
type="info"
size="small"
style="margin-right: 6px;"
>
{{ item.name }}
</el-tag>
</div>
<el-autocomplete
ref="autocompleteCc"
class="mail_inp"
v-model="ccInput"
:fetch-suggestions="fetchUserSuggestions"
:trigger-on-focus="false"
placeholder=""
@select="handleSelectCc"
@blur="handleCcBlur"
style="width: 200px; flex: 1;"
>
<template slot-scope="{ item }">
<div class="name">
{{ item.value }} <span style="padding: 4px; color: #ccc;">|</span> {{ item.realname }}
</div>
</template>
</el-autocomplete>
</div>
</div>
<div style="margin: 20px 0 0 0;">
<quill-editor ref="myTextEditor" v-model="queryMail.content" :options="editorOption"></quill-editor>
<el-upload class="avatar-uploader-mail" :action="baseUrl+'api/Suggest/upImg'" name="img" :show-file-list="false"
:on-success="uploadSuccess">
</el-upload>
<p style="margin-top: 10px;font-size: 14px;">
Sender :<span style="color: #006699;margin: 0 0 0 15px;">{{userMes.realname}} &lt {{userMes.email}} &gt</span>
</p>
</div>
<div style="margin: 20px 0 0 0;">
<el-button type="primary" icon="el-icon-s-promotion" @click="handleSend" style="">Send</el-button>
</div>
<div class="mail-footer-bar" :style="{ left: footerBarLeft }">
<div class="sender-info">
<span class="sender-label">{{ $t('mailboxSend.sender') }}</span>
<span class="sender-content">
{{ userMes.realname }} &lt;{{ userMes.email }}&gt;
</span>
</div>
<div class="action-buttons">
<el-button type="primary" icon="el-icon-s-promotion" :loading="sendLoading" :disabled="sendLoading" @click="handleSend">
{{ $t('mailboxSend.send') }}
</el-button>
<el-button size="medium" :loading="saveDraftLoading" :disabled="saveDraftLoading" @click="handleSaveDraft">{{ $t('mailboxSend.saveDraft') }}</el-button>
</div>
</div>
</div>
<!-- 选择通讯录 -->
<el-dialog title="Select User" :visible.sync="Librarybox" width="900px" :close-on-click-modal="false">
<el-button type="primary" style="margin: 0 0 15px 0;" icon="el-icon-plus" @click="mailLiyAll()" v-if="LibrarySelection!=''">Batch
Selection</el-button>
<el-table :data="mail_List" border ref="multipleTable" header-cell-class-name="table-header" @selection-change="handleSelectionChange" empty-text="New messages (0)">
<el-dialog :title="$t('mailboxSend.selectUser')" :visible.sync="Librarybox" width="900px" :close-on-click-modal="false">
<el-button type="primary" style="margin: 0 0 15px 0;" icon="el-icon-plus" @click="mailLiyAll()" v-if="LibrarySelection!=''">
{{ $t('mailboxSend.batchSelection') }}
</el-button>
<el-table :data="mail_List" border ref="multipleTable" header-cell-class-name="table-header" @selection-change="handleSelectionChange" :empty-text="$t('mailboxSend.emptyText')">
<el-table-column type="selection" width="40" align="center" :selectable='checkboxSelect'></el-table-column>
<el-table-column label="Email">
<el-table-column :label="$t('mailboxSend.email')">
<template slot-scope="scope">
<b class="el-icon-check" v-if="scope.row.select_mark==1" style="color:#f74c4c;margin-right: 5px;font-weight: bold;"></b>
{{scope.row.email}}
</template>
</el-table-column>
<el-table-column prop="account" label="Account"></el-table-column>
<el-table-column label=" " width="110" align="center">
<el-table-column prop="account" :label="$t('mailboxSend.account')"></el-table-column>
<el-table-column :label="$t('mailboxSend.operation')" width="110" align="center">
<template slot-scope="scope">
<el-button plain type="primary" icon="el-icon-plus" @click="mailLibAdd(scope.row)">Select</el-button>
<el-button plain type="primary" icon="el-icon-plus" @click="mailLibAdd(scope.row)">
{{ $t('mailboxSend.selectBtn') }}
</el-button>
</template>
</el-table-column>
</el-table>
@@ -83,22 +148,22 @@
</el-dialog>
<!-- 选择模板 -->
<el-dialog title="Select template" :visible.sync="Templatebox" width="620px" :close-on-click-modal="false">
<el-dialog :title="$t('mailboxSend.selectTemplate')" :visible.sync="Templatebox" width="620px" :close-on-click-modal="false">
<el-form ref="Tempform" :model="TempForm" label-width="225px">
<el-form-item label="Choose Template :">
<el-select v-model="TempForm.board" placeholder=" " @change="select_tem($event)" style="width: 220px;">
<el-option :key="0" label="None" :value="0"></el-option>
<el-form-item :label="$t('mailboxSend.chooseTemplate')">
<el-select v-model="TempForm.board" :placeholder="$t('mailboxSend.chooseTemplatePlaceholder')" @change="select_tem($event)" style="width: 220px;">
<el-option :key="0" :label="$t('mailboxSend.none') " :value="0"></el-option>
<el-option v-for="item in fol_low" :key="item.value" :label="item.label" :value="item.value">
</el-option>
</el-select>
</el-form-item>
<el-form-item label="Preview Formatted Document :">
<el-form-item :label="$t('mailboxSend.previewTemplate')">
<img src="../../assets/img/img.jpg" alt="" style="width: 250px;">
</el-form-item>
</el-form>
<span slot="footer" class="dialog-footer">
<el-button @click="Templatebox = false">Cancel</el-button>
<el-button type="primary" @click="saveTemplate">Save</el-button>
<el-button @click="Templatebox = false">{{ $t('mailboxSend.cancel') }}</el-button>
<el-button type="primary" @click="saveTemplate">{{ $t('mailboxSend.save') }}</el-button>
</span>
</el-dialog>
</div>
@@ -118,6 +183,7 @@
// Quill.register('modules/imageResize', ImageResize)
import 'multi-items-input'
import 'multi-items-input/dist/multi-items-input.css'
import bus from '../common/bus'
export default {
data() {
return {
@@ -127,13 +193,7 @@
userMes: {},
queryMail: {
sendname: [],
sendnamelist: [{
id: null,
name: "atr@tmrjournals.com"
}, {
id: null,
name: "tmrtheory@tmrjournals.com"
}],
sendnamelist: [],
sendtitle: '',
sendcc: '',
content: ''
@@ -158,7 +218,7 @@
Librarybox: false,
link_TotalLibry: 0,
editorOption: {
placeholder: 'Please enter...',
placeholder: this.$t('mailboxSend.editorPlaceholder'),
modules: {
toolbar: {
container: [
@@ -229,46 +289,215 @@
// }
}
},
toInput: '',
toSelecting: false,
nextToUid: 1,
ccList: [],
ccInput: '',
ccSelecting: false,
nextCcUid: 1,
collapseValue: localStorage.getItem('collapse'),
sendLoading: false,
saveDraftLoading: false,
};
},
computed: {
footerBarLeft() {
const collapsed = this.collapseValue === true || this.collapseValue === 'true';
return (collapsed ? 64: 260) + 'px';
},
},
watch: {
collapseValue: {
handler() {
// 监听 collapseValue 变化footerBarLeft 通过 computed 自动更新
},
immediate: true,
},
},
created() {
this.getDate();
this.getLibary();
},
// components: {
// quillEditor
// },
computed: {
mounted() {
bus.$on('collapse-content', (msg) => {
this.collapseValue = msg;
});
},
beforeDestroy() {
bus.$off('collapse-content');
},
methods: {
// 获取初始用户数据
getDate() {
// 返回收件箱(邮箱列表),带上 journal_id 和 j_email_id
goBackInbox() {
const q = this.$route.query;
const query = {};
if (q.journal_id) query.journal_id = q.journal_id;
if (q.j_email_id) query.j_email_id = q.j_email_id;
this.$router.push({
path: '/mailboxCollect',
query,
});
},
// 收件人自动补全 - 搜索用户
fetchUserSuggestions(queryString, cb) {
if (!queryString) {
cb([]);
return;
}
this.$api
.post('api/User/getUserDetail', {
'user_id': localStorage.getItem('U_id')
.post('api/Reviewer/researchUser', {
keywords: queryString
})
.then(res => {
if (res.code == 0) {
this.userMes = res.data.user
if (res && res.code === 0 && res.data && res.data.list) {
const list = res.data.list.map(u => ({
...u,
value: u.email,
realname: u.realname || u.username || ''
}));
cb(list);
} else {
this.$message.error(res.msg);
cb([]);
}
})
.catch(err => {
this.$message.error(err);
.catch(() => cb([]));
},
// 收件人输入框失焦:输入内容失去焦点则自动成为一条收件人数据(排除点击下拉项时)
handleToBlur(e) {
// 若失焦是因为点击了下拉建议区域,不执行任何逻辑,让 click 触发 select否则点击会选不中
const related = e && e.relatedTarget;
if (related && typeof related.closest === 'function' && related.closest('.el-autocomplete-suggestion')) {
return;
}
const value = (this.toInput || '').trim();
setTimeout(() => {
if (this.toSelecting) {
this.toSelecting = false;
return;
}
if (!value) return;
if (value.indexOf('@') === -1) {
this.toInput = '';
return;
}
const exists = (this.queryMail.sendnamelist || []).some(item => item.name === value);
if (!exists) {
this.queryMail.sendnamelist.push({
id: null,
name: value,
_uid: this.nextToUid++,
});
this.queryMail.sendname = (this.queryMail.sendnamelist || []).map(i => i.name);
}
this.toInput = '';
}, 150);
},
// 选中某个用户作为收件人
handleSelectUser(user) {
this.toSelecting = true;
if (!user || !user.email) return;
const email = user.email;
// 去重
const exists = (this.queryMail.sendnamelist || []).some(item => item.name === email);
if (!exists) {
this.queryMail.sendnamelist.push({
id: user.user_id || user.id || null,
name: email,
_uid: this.nextToUid++,
});
// 同步简单数组
this.queryMail.sendname = (this.queryMail.sendnamelist || []).map(i => i.name);
}
// 清空输入框,便于继续输入下一个
this.toInput = '';
},
// 删除已选收件人
removeRecipient(index) {
if (index < 0) return;
this.queryMail.sendnamelist.splice(index, 1);
this.queryMail.sendname = (this.queryMail.sendnamelist || []).map(i => i.name);
},
// CC 输入框失焦:与 To 相同逻辑,输入失去焦点则自动成为一条数据(排除点击下拉项时)
handleCcBlur(e) {
const related = e && e.relatedTarget;
if (related && typeof related.closest === 'function' && related.closest('.el-autocomplete-suggestion')) {
return;
}
const value = (this.ccInput || '').trim();
setTimeout(() => {
if (this.ccSelecting) {
this.ccSelecting = false;
return;
}
if (!value) return;
if (value.indexOf('@') === -1) {
this.ccInput = '';
return;
}
const exists = (this.ccList || []).some(item => item.name === value);
if (!exists) {
this.ccList.push({
id: null,
name: value,
_uid: this.nextCcUid++,
});
}
this.ccInput = '';
}, 150);
},
// 选中某个用户作为 CC与 To 相同逻辑)
handleSelectCc(user) {
this.ccSelecting = true;
if (!user || !user.email) return;
const email = user.email;
const exists = (this.ccList || []).some(item => item.name === email);
if (!exists) {
this.ccList.push({
id: user.user_id || user.id || null,
name: email,
_uid: this.nextCcUid++,
});
}
this.ccInput = '';
},
// 删除已选 CC
removeCc(index) {
if (index < 0) return;
this.ccList.splice(index, 1);
},
// 根据路由 j_email_id 获取发件邮箱信息,用于展示发件人
getDate() {
const jEmailId = this.$route.query.j_email_id;
if (!jEmailId) {
this.userMes = {};
return;
}
this.$api
.post('api/email_client/getOneEmail', { j_email_id: jEmailId })
.then(res => {
if (res && res.code === 0 && res.data && res.data.email) {
const email = res.data.email;
this.userMes = {
realname: email.smtp_from_name || email.smtp_user || '',
email: email.smtp_user || '',
};
} else {
this.userMes = {};
}
})
.catch(() => {
this.userMes = {};
});
},
// 获取通讯录数据
getLibary() {
this.$api
.post('api/User/getAllUser', this.queryLibry)
.post('api/Reviewer/researchUser', {keywords: this.queryLibry.username})
.then(res => {
if (res.code == 0) {
this.mail_List = res.data.users;
this.link_TotalLibry = res.data.count || 0;
this.mail_List = res.data.list;
this.link_TotalLibry = res.data.list.length || 0;
for (let i = 0; i < this.mail_List.length; i++) {
this.mail_List[i].select_mark = 0
for (let j = 0; j < this.queryMail.sendnamelist.length; j++) {
@@ -286,9 +515,60 @@
});
},
// 发送邮件
// 发送邮件api/email_client/sendOneto_email 为多邮箱字符串拼接(逗号分隔),发送成功后关闭当前页并跳转收件箱
handleSend() {
console.log(this.queryMail)
if (this.sendLoading) return;
const toList = (this.queryMail.sendnamelist || []).map((item) => item.name).filter(Boolean);
if (!toList.length) {
this.$message.warning(this.$t('mailboxSend.validateTo'));
return;
}
if (!this.queryMail.sendtitle) {
this.$message.warning(this.$t('mailboxSend.validateSubject'));
return;
}
const journalId = this.$route.query.journal_id;
if (!journalId) {
this.$message.warning(this.$t('mailboxSend.needAccount'));
return;
}
this.sendLoading = true;
const params = {
journal_id: journalId,
to_email: toList.join(','),
subject: this.queryMail.sendtitle,
content: this.queryMail.content || '',
};
const self = this;
this.$api.post('api/email_client/sendOne', params).then((res) => {
if (res && res.code === 0) {
self.$message.success(self.$t('mailboxSend.sendSuccess'));
self.queryMail.sendnamelist = [];
self.queryMail.sendtitle = '';
self.queryMail.sendcc = '';
self.ccList = [];
self.queryMail.content = '';
self.fileL_atta = [];
self.goBackInbox();
} else {
self.$message.error((res && res.msg) || self.$t('mailboxSend.sendFail'));
}
}).catch(() => {
self.$message.error(self.$t('mailboxSend.sendFail'));
}).finally(() => {
self.sendLoading = false;
});
},
// 保存草稿(防抖:请求中禁用按钮)
handleSaveDraft() {
if (this.saveDraftLoading) return;
this.saveDraftLoading = true;
// TODO: 调用保存草稿接口,此处仅防抖占位
this.$nextTick(() => {
setTimeout(() => {
this.saveDraftLoading = false;
}, 300);
});
},
// 选择通讯录-弹出框
@@ -320,7 +600,8 @@
this.queryMail.sendname.push(e.email)
this.queryMail.sendnamelist.push({
id: null,
name: e.email
name: e.email,
_uid: this.nextToUid++,
})
this.getLibary();
},
@@ -331,7 +612,8 @@
this.queryMail.sendname.push(this.LibrarySelection[i].email)
this.queryMail.sendnamelist.push({
id: null,
name: this.LibrarySelection[i].email
name: this.LibrarySelection[i].email,
_uid: this.nextToUid++,
})
}
this.getLibary();
@@ -433,7 +715,16 @@
};
</script>
<style>
<style scoped>
.crumbs {
display: flex;
align-items: center;
margin-bottom: 16px;
}
.back-inbox-btn {
margin-right: 12px;
padding-left: 0;
}
.mail_shuru {
font-size: 14px;
padding: 0 0 5px 0;
@@ -442,18 +733,18 @@
}
.mail_tit {
width: 90px;
width: 60px;
display: inline-block;
}
.mail_inp {
display: inline-block;
width: 550px;
width: 100%;
margin: 0 15px 0 0;
border: 0;
}
.mail_inp .el-input__inner {
.mail_inp ::v-deep .el-input__inner {
border: 1px solid #fff !important;
}
@@ -463,14 +754,13 @@
vertical-align: middle;
}
.up_newstyle .el-upload--text {
.up_newstyle ::v-deep .el-upload--text {
background-color: #006699;
border: 1px solid #006699;
padding: 5px 15px;
/* margin-left: 10px; */
}
.up_newstyle .el-upload__text em {
.up_newstyle ::v-deep .el-upload__text em {
color: #fff !important;
font-size: 12px;
}
@@ -545,8 +835,75 @@
height: 0;
}
.avatar-uploader-mail .el-upload--text {
.avatar-uploader-mail ::v-deep .el-upload--text {
height: 0 !important;
border: 0;
}
/* 让输入框的边框在平时不可见,只有在交互时体现,对齐邮件系统风格 */
.mail_inp ::v-deep .el-input__inner {
border: none !important;
border-bottom: 0px solid #fff !important;
padding-right: 30px;
}
.mail_inp ::v-deep .el-input__suffix {
right: 5px;
transition: all .3s;
}
.mail_inp ::v-deep .el-input__suffix:hover {
transform: scale(1.2);
}
/* 调整容器对齐 */
.mail_shuru {
display: flex;
align-items: flex-start; /* 考虑到多行标签,对齐顶部 */
min-height: 40px;
line-height: 40px;
}
/* 底部操作栏容器 */
.mail-footer-bar {
margin-top: 20px;
padding: 10px 15px;
background-color: #f8f9fa; /* 浅灰色背景类似图1 */
border: 1px solid #ddd;
border-radius: 4px;
display: flex;
justify-content: space-between; /* 左右分布 */
align-items: center;
}
/* 发件人信息样式 */
.sender-info {
font-size: 14px;
color: #666;
}
.sender-label {
font-weight: bold;
margin-right: 10px;
}
.sender-content {
color: #006699; /* 保持你代码中的蓝色 */
}
/* 按钮组样式 */
.action-buttons {
display: flex;
gap: 10px;
}
.mail-footer-bar {
position: fixed;
bottom: 0;
right: 0;
z-index: 10;
box-shadow: 0 -2px 10px rgba(0,0,0,0.05);
}
</style>