feat: mail config and mail send

This commit is contained in:
zchengo
2023-01-24 20:03:35 +08:00
parent a9e33f2bcc
commit 35886fde4f
17 changed files with 731 additions and 8 deletions
+37
View File
@@ -0,0 +1,37 @@
import request from '../axios/index'
// 保存邮件服务配置
export function saveMailConfig(param) {
return request({
url: '/config/save',
method: 'put',
data: param,
})
}
// 开启或关闭邮件服务
export function updateMailConfigStmpStatus(param) {
return request({
url: '/config/status',
method: 'put',
data: param,
})
}
// 获取邮件服务配置信息
export function getMailConfig(param) {
return request({
url: '/config/info',
method: 'get',
data: param,
})
}
// 检查邮件配置的有效性
export function checkMailConfig(param) {
return request({
url: '/config/check',
method: 'get',
data: param,
})
}
+9
View File
@@ -18,6 +18,15 @@ export function updateCustomer(param) {
})
}
// 发送邮件给客户
export function sendMailToCustomer(param) {
return request({
url: '/customer/send',
method: 'post',
data: param,
})
}
// 删除客户
export function deleteCustomer(param) {
return request({
+6
View File
@@ -11,6 +11,7 @@ import Dashboard from '../views/Dashboard.vue'
import Customer from '../views/Customer.vue'
import Contract from '../views/Contract.vue'
import Product from '../views/Product.vue'
import Config from '../views/Config.vue'
import Subscribe from '../views/Subscribe.vue'
import Result from '../views/Result.vue'
@@ -59,6 +60,11 @@ const routes = [
name: 'product',
component: Product,
},
{
path: '/config',
name: 'config',
component: Config,
},
{
path: '/subscribe',
name: 'subscribe',
+217
View File
@@ -0,0 +1,217 @@
<template>
<div :style="{ padding: '0 20px 12px 20px' }">
<a-tabs v-model:activeKey="activeKey">
<a-tab-pane key="1" tab="邮件服务配置">
<a-card size="small" title="配置STMP服务" :headStyle="{ padding: '0 0' }" :bodyStyle="{ padding: '10px 0' }"
:bordered="false">
<template #extra><a-switch v-model:checked="mailConfig.status" :checkedValue="1" :unCheckedValue="2"
@change="onSwitch" /></template>
<a-alert message="您需要配置SMTP服务器才能使用邮件发送功能,目前仅支持企鹅邮箱或网易邮箱提供的免费SMTP服务器。" type="info" show-icon /><br />
<a-form ref="mailConfigRef" :model="mailConfig" layout="vertical" name="mailConfig" :rules="rules"
@finish="onSave">
<a-row :gutter="50">
<a-col :span="12">
<a-form-item label="STMP服务器" name="stmp">
<a-input v-model:value="mailConfig.stmp" placeholder="请输入域名">
<template #suffix>
<a-tooltip title="如 smtp.qq.com">
<info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="端口号" name="port">
<a-input-number v-model:value="mailConfig.port" placeholder="请输入端口号"
:controls="false" style="width: 100%;">
<template #suffix>
<a-tooltip title="STMP服务对应的端口号">
<info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input-number>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="授权码" name="authCode">
<a-input v-model:value="mailConfig.authCode" placeholder="请输入授权码">
<template #suffix>
<a-tooltip title="从STMP服务器提供商获得的授权码">
<info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="邮箱账号" name="email">
<a-input v-model:value="mailConfig.email" placeholder="请输入邮箱">
<template #suffix>
<a-tooltip title="开启STMP服务时所使用的邮箱账号">
<info-circle-outlined style="color: rgba(0, 0, 0, 0.45)" />
</a-tooltip>
</template>
</a-input>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="服务有效性检测" name="usability">
<a-select ref="select" v-model:value="usability" @focus="focus"
@change="handleChange" placeholder="自动检测服务有效性" disabled>
<a-select-option :value="1">
<Spot type="info" title="正在检测,请勿离开页面!" />
</a-select-option>
<a-select-option :value="2">
<Spot type="success" title="服务配置有效" />
</a-select-option>
<a-select-option :value="3">
<Spot type="danger" title="配置无效,请检查STMP服务器、端口号、授权码和邮箱账号是否匹配。" />
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="服务状态" name="status">
<a-select ref="select" v-model:value="mailConfig.status" placeholder="自动检测服务有效性"
disabled>
<a-select-option :value="0">
<Spot type="warning" title="服务未配置" />
</a-select-option>
<a-select-option :value="1">
<Spot type="success" title="服务已开启" />
</a-select-option>
<a-select-option :value="2">
<Spot type="danger" title="服务已关闭" />
</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item><br />
<a-button type="primary" html-type="submit">保存</a-button>
<a-button style="margin-left: 20px;" @click="reset">重置</a-button>
</a-form-item>
</a-col>
</a-row>
</a-form>
</a-card>
</a-tab-pane>
</a-tabs>
</div>
</template>
<script setup>
import { reactive, ref, onBeforeMount } from 'vue';
import { InfoCircleOutlined } from '@ant-design/icons-vue';
import Spot from '../components/Spot.vue';
import { saveMailConfig, updateMailConfigStmpStatus, getMailConfig, checkMailConfig } from '../api/config';
import { message, Modal } from 'ant-design-vue';
// 初始化
onBeforeMount(() => {
getMailConfigInfo()
checkConfig()
})
// 邮件服务配置
const mailConfig = reactive({
id: undefined,
stmp: undefined,
port: undefined,
authCode: undefined,
email: undefined,
status: undefined
})
const mailConfigRef = ref()
const usability = ref(1)
// 表单校验
const rules = {
stmp: [{
pattern: /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/,
message: '域名格式不正确',
trigger: 'blur',
}],
port: [{
pattern: /^([0-9]|[1-9]\d{1,3}|[1-5]\d{4}|6[0-4]\d{4}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])$/,
message: '端口号格式不正确',
trigger: 'blur',
}],
authCode: [{ message: '产品编码格式不正确', trigger: 'blur' }],
email: [{
pattern: /^([a-zA-Z]|[0-9])(\w|\-)+@[a-zA-Z0-9]+\.([a-zA-Z]{2,4})$/,
message: '邮箱格式不正确',
trigger: 'blur',
}]
};
// 保存邮件服务配置
const onSave = () => {
saveMailConfig(mailConfig).then((res) => {
if (res.data.code == 0) {
setData(res.data.data)
message.success("保存成功")
checkConfig()
} else {
message.error("保存失败")
}
})
}
// 保存邮件服务配置
const onSwitch = () => {
updateMailConfigStmpStatus({
id: mailConfig.id,
status: mailConfig.status
}).then((res) => {
if (res.data.code == 0) {
if (mailConfig.status == 1) {
message.success("服务已开启")
} else {
message.warning("服务已关闭")
}
}
})
}
// 获取邮件服务配置
const getMailConfigInfo = () => {
getMailConfig().then((res) => {
if (res.data.code == 0) {
setData(res.data.data)
}
})
}
// 检查邮件服务配置
const checkConfig = () => {
usability.value = 1
checkMailConfig().then((res) => {
if (res.data.code == 0) {
usability.value = 2
} else {
usability.value = 3
}
})
}
// 重置
const reset = () => {
mailConfigRef.value.resetFields()
}
// 数据赋值
const setData = data => {
mailConfig.id = data.id
mailConfig.stmp = data.stmp
mailConfig.port = data.port
if (data.port === 0) {
mailConfig.port = undefined
}
mailConfig.authCode = data.authCode
mailConfig.email = data.email
mailConfig.status = data.status
mailConfig.usability = data.usability
}
</script>
+87 -5
View File
@@ -1,5 +1,5 @@
<template>
<div>
<div :style="{ padding: '20px 20px 12px 20px' }">
<div style="display: flex;justify-content: space-between;margin-bottom: 20px;">
<a-space>
<a-input v-model:value="keyWord" placeholder="客户名称" style="width: 280px; margin-right: 50px;">
@@ -27,6 +27,11 @@
<template v-if="column.dataIndex === 'name'">
<a @click="onEdit(record)">{{ text }}</a>
</template>
<template v-if="column.dataIndex === 'operation'">
<a-button type="link"><template #icon>
<MailTwoTone two-tone-color="#476FFF" @click="onMail(record.name, record.email)" />
</template></a-button>
</template>
<template v-if="column.dataIndex === 'status'">
<a-tag v-if="text == 1" color="green">已成交</a-tag>
<a-tag v-if="text == 2" color="blue">未成交</a-tag>
@@ -121,21 +126,53 @@
</a-form>
</div>
</a-modal>
<!-- 发送邮件对话框 -->
<a-modal v-model:visible="visibleMail" title="发送邮件" @ok="onSend" @cancel="onCancel" cancelText="取消" okText="发送"
width="800px" :centered="true">
<div style="height: 55vh;overflow-y: scroll;padding: 0 15px;">
<a-form :model="mail" layout="vertical">
<a-row :gutter="16">
<a-col :span="12">
<a-form-item label="客户名称" name="customerName">
<a-input v-model:value="mail.customerName" disabled />
</a-form-item>
</a-col>
<a-col :span="12">
<a-form-item label="收件人" name="receiver">
<a-input v-model:value="mail.receiver" disabled />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="邮件主题" name="subject">
<a-input v-model:value="mail.subject" />
</a-form-item>
</a-col>
<a-col :span="24">
<a-form-item label="邮件内容" name="content">
<a-textarea v-model:value="mail.content" placeholder="邮件正文支持HTML语法"
:auto-size="{ minRows: 6, maxRows: 100 }" />
</a-form-item>
</a-col>
</a-row>
</a-form>
</div>
</a-modal>
</div>
</template>
<script>
import { ref, reactive, onMounted, createVNode } from 'vue';
import { SearchOutlined, ExclamationCircleOutlined, ExportOutlined } from '@ant-design/icons-vue';
import { SearchOutlined, ExclamationCircleOutlined, ExportOutlined, MailTwoTone } from '@ant-design/icons-vue';
import moment from 'moment'
import { createCustomer, updateCustomer, queryCustomerList, queryCustomerInfo, deleteCustomer, customerExport } from '../api/customer';
import { createCustomer, updateCustomer, sendMailToCustomer, queryCustomerList, queryCustomerInfo, deleteCustomer, customerExport } from '../api/customer';
import { message, Modal } from 'ant-design-vue';
import regionData from '../assets/region';
export default {
components: {
SearchOutlined,
ExportOutlined
ExportOutlined,
MailTwoTone
},
setup() {
const columns = [{
@@ -192,6 +229,12 @@ export default {
customRender: text => {
return text.value == 0 ? '' : moment(text.value * 1000).format('YYYY-MM-DD HH:mm:ss')
}
}, {
title: '操作',
dataIndex: 'operation',
width: 65,
fixed: 'right',
ellipsis: true,
}];
// 表单校验
@@ -263,6 +306,7 @@ export default {
const operation = ref(0);
const customerFormRef = ref();
const keyWord = ref('')
const visibleMail = ref(false)
// 点击新建客户
const onCreate = () => {
@@ -375,7 +419,7 @@ export default {
// 导出表格
const onExport = () => {
customerExport().then((res) => {
if (res.data.type == 'application/json'){
if (res.data.type == 'application/json') {
message.error('导出错误!')
} else {
let blob = new Blob([res.data], {
@@ -390,6 +434,40 @@ export default {
})
}
const mail = reactive({
customerName: '',
receiver: '',
subject: '',
content: ''
})
// 点击邮件
const onMail = (cname, email) => {
mail.customerName = cname
mail.receiver = email
visibleMail.value = true
}
// 点击发送邮件
const onSend = () => {
let param = {
receiver: mail.receiver,
subject: mail.subject,
content: mail.content
}
sendMailToCustomer(param).then((res) => {
if (res.data.code == 0) {
message.success("邮件已发送")
}
if (res.data.code == 50002) {
message.error("邮件发送失败")
}
if (res.data.code == 50003) {
message.warn("邮件服务未开启")
}
})
}
// 点击取消按钮
const onCancel = () => {
customerFormRef.value.resetFields()
@@ -429,6 +507,10 @@ export default {
onPagination,
pagination,
selectedOptions,
mail,
visibleMail,
onMail,
onSend
};
},
}
+8 -2
View File
@@ -88,7 +88,7 @@
</a-modal>
</a-layout-header>
<a-layout-content
:style="{ margin: '10px', padding: '20px 20px 12px 20px', background: '#fff', overflow: 'initial', borderRadius: '5px' }">
:style="{ margin: '10px', background: '#fff', overflow: 'initial', borderRadius: '5px' }">
<transition name="fade">
<router-view v-slot="{ Component }">
<component :is="Component" />
@@ -106,7 +106,7 @@ import { useStore } from '../store/index';
import { message } from 'ant-design-vue';
import { getUserInfo, getVerifyCode, userDelete } from '../api/user';
import { updateNotice, getNoticeCount, getNoticeList, deleteNotice } from '../api/notice';
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, CrownOutlined } from '@ant-design/icons-vue';
import { DashboardOutlined, SmileOutlined, MehOutlined, ShoppingOutlined, ProfileOutlined, CrownOutlined } from '@ant-design/icons-vue';
import { QuestionCircleFilled, BellFilled, ExclamationCircleOutlined, LogoutOutlined } from '@ant-design/icons-vue';
import moment from 'moment'
@@ -116,6 +116,7 @@ export default {
SmileOutlined,
MehOutlined,
ShoppingOutlined,
ProfileOutlined,
CrownOutlined,
QuestionCircleFilled,
BellFilled,
@@ -144,6 +145,11 @@ export default {
to: "/product",
icon: "shopping-outlined",
name: "产品"
}, {
key: "config",
to: "/config",
icon: "profile-outlined",
name: "配置"
}, {
key: "subscribe",
to: "/subscribe",