feat: mail config and mail send
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"crm/models"
|
||||
"crm/response"
|
||||
"crm/service"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
type MailConfigApi struct {
|
||||
mailConfigService *service.MailConfigService
|
||||
}
|
||||
|
||||
func NewMailConfigApi() *MailConfigApi {
|
||||
return &MailConfigApi{
|
||||
mailConfigService: service.NewMailConfigService(),
|
||||
}
|
||||
}
|
||||
|
||||
// 保存邮件服务配置
|
||||
func (m *MailConfigApi) Save(context *gin.Context) {
|
||||
var param models.MailConfigSaveParam
|
||||
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||
err := context.ShouldBind(¶m)
|
||||
if uid <= 0 || err != nil {
|
||||
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
param.Creator = int64(uid)
|
||||
errCode := m.mailConfigService.Save(¶m)
|
||||
if errCode == 0 {
|
||||
mailConfig, errCode := m.mailConfigService.GetInfo(int64(uid))
|
||||
response.Result(errCode, mailConfig, context)
|
||||
return
|
||||
}
|
||||
response.Result(errCode, nil, context)
|
||||
}
|
||||
|
||||
// 开启或关闭邮件服务
|
||||
func (m *MailConfigApi) UpdateStatus(context *gin.Context) {
|
||||
var param models.MailConfigStatusParam
|
||||
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||
err := context.ShouldBind(¶m)
|
||||
if uid <= 0 || err != nil {
|
||||
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
param.Creator = int64(uid)
|
||||
errCode := m.mailConfigService.UpdateStatus(¶m)
|
||||
response.Result(errCode, nil, context)
|
||||
}
|
||||
|
||||
// 获取邮件服务配置信息
|
||||
func (m *MailConfigApi) GetInfo(context *gin.Context) {
|
||||
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||
if uid <= 0 {
|
||||
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||
return
|
||||
}
|
||||
mailConfig, errCode := m.mailConfigService.GetInfo(int64(uid))
|
||||
response.Result(errCode, mailConfig, context)
|
||||
}
|
||||
|
||||
// 检查邮件配置的有效性
|
||||
func (m *MailConfigApi) Check(context *gin.Context) {
|
||||
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||
if uid <= 0 {
|
||||
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||
return
|
||||
}
|
||||
errCode := m.mailConfigService.Check(int64(uid))
|
||||
response.Result(errCode, nil, context)
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"crm/models"
|
||||
"crm/response"
|
||||
"crm/service"
|
||||
"log"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -45,6 +46,21 @@ func (c *CustomerApi) Update(context *gin.Context) {
|
||||
response.Result(errCode, nil, context)
|
||||
}
|
||||
|
||||
// 发送邮件给客户
|
||||
func (c *CustomerApi) SendMail(context *gin.Context) {
|
||||
var param models.CustomerSendMailParam
|
||||
uid, _ := strconv.Atoi(context.Request.Header.Get("uid"))
|
||||
err := context.ShouldBind(¶m)
|
||||
if uid <= 0 || err != nil {
|
||||
response.Result(response.ErrCodeParamInvalid, nil, context)
|
||||
log.Println(err)
|
||||
return
|
||||
}
|
||||
param.Uid = int64(uid)
|
||||
errCode := c.customerService.SendMail(¶m)
|
||||
response.Result(errCode, nil, context)
|
||||
}
|
||||
|
||||
// 删除客户
|
||||
func (c *CustomerApi) Delete(context *gin.Context) {
|
||||
var param models.CustomerDeleteParam
|
||||
|
||||
+27
-1
@@ -2,13 +2,15 @@ package common
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"gopkg.in/gomail.v2"
|
||||
)
|
||||
|
||||
// 发送邮件
|
||||
// 发送邮件(系统级别)
|
||||
// QQ邮箱:SMTP 服务器地址:smtp.qq.com(SSL协议端口:465/994 | 非SSL协议端口:25)
|
||||
// 163邮箱:SMTP 服务器地址:smtp.163.com(端口:25)
|
||||
func SendMail(email, content string) error {
|
||||
@@ -31,3 +33,27 @@ func SendMail(email, content string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 发送邮件给客户(客户级别)
|
||||
func SendMailToCustomer(mp models.MailParam) error {
|
||||
m := gomail.NewMessage()
|
||||
m.SetHeader("From", mp.Sender)
|
||||
m.SetHeader("To", mp.Receiver)
|
||||
m.SetHeader("Subject", mp.Subject)
|
||||
m.SetBody("text/html", mp.Content)
|
||||
d := gomail.NewDialer(mp.Smtp, mp.Port, mp.Sender, mp.AuthCode)
|
||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
if err := d.DialAndSend(m); err != nil {
|
||||
log.Printf("send mail to customer error : %s", err)
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 检测STMP服务是否可连接
|
||||
func DialMail(smtp string, port int, sender, authCode string) error {
|
||||
d := gomail.NewDialer(smtp, port, sender, authCode)
|
||||
d.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
_, err := d.Dial()
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package dao
|
||||
|
||||
import (
|
||||
"crm/global"
|
||||
"crm/models"
|
||||
"time"
|
||||
)
|
||||
|
||||
type MailConfigDao struct {
|
||||
}
|
||||
|
||||
func NewMailConfigDao() *MailConfigDao {
|
||||
return &MailConfigDao{}
|
||||
}
|
||||
|
||||
func (m *MailConfigDao) Save(param *models.MailConfigSaveParam) error {
|
||||
if !isExists(param.Creator) {
|
||||
return create(param)
|
||||
}
|
||||
return update(param)
|
||||
}
|
||||
|
||||
func (m *MailConfigDao) GetInfo(uid int64) (*models.MailConfig, error) {
|
||||
var mc models.MailConfig
|
||||
err := global.Db.Table(MAIL_CONFIG).Where("creator = ?", uid).First(&mc).Error
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mc, nil
|
||||
}
|
||||
|
||||
func (m *MailConfigDao) UpdateStatus(param *models.MailConfigStatusParam) error {
|
||||
mailConfig := models.MailConfig{
|
||||
Status: param.Status,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
db := global.Db.Model(&mailConfig).Where("id = ? and creator = ?", param.Id, param.Creator)
|
||||
if err := db.Updates(&mailConfig).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func isExists(uid int64) bool {
|
||||
var mc models.MailConfig
|
||||
db := global.Db.Table(MAIL_CONFIG).Where("creator = ?", uid).First(&mc)
|
||||
return db.RowsAffected != NumberNull
|
||||
}
|
||||
|
||||
func create(param *models.MailConfigSaveParam) error {
|
||||
mailConfig := models.MailConfig{
|
||||
Stmp: param.Stmp,
|
||||
Port: param.Port,
|
||||
AuthCode: param.AuthCode,
|
||||
Email: param.Email,
|
||||
Status: 1,
|
||||
Creator: param.Creator,
|
||||
Created: time.Now().Unix(),
|
||||
}
|
||||
if err := global.Db.Create(&mailConfig).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func update(param *models.MailConfigSaveParam) error {
|
||||
mailConfig := models.MailConfig{
|
||||
Id: param.Id,
|
||||
Stmp: param.Stmp,
|
||||
Port: param.Port,
|
||||
AuthCode: param.AuthCode,
|
||||
Email: param.Email,
|
||||
Status: param.Status,
|
||||
Creator: param.Creator,
|
||||
Updated: time.Now().Unix(),
|
||||
}
|
||||
db := global.Db.Model(&mailConfig).Select("*").Omit("id", "creator", "created")
|
||||
if err := db.Updates(&mailConfig).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -39,6 +39,7 @@ func Router() {
|
||||
route.GET("/customer/option", api.NewCustomerApi().QueryOption)
|
||||
route.GET("/customer/export", api.NewCustomerApi().Export)
|
||||
route.POST("/customer/create", api.NewCustomerApi().Create)
|
||||
route.POST("/customer/send", api.NewCustomerApi().SendMail)
|
||||
route.PUT("/customer/update", api.NewCustomerApi().Update)
|
||||
route.DELETE("/customer/delete", api.NewCustomerApi().Delete)
|
||||
|
||||
@@ -62,6 +63,12 @@ func Router() {
|
||||
// 仪表盘模块
|
||||
route.GET("/dashboard/sum", api.NewDashboardApi().Summary)
|
||||
|
||||
// 配置模块
|
||||
route.GET("/config/info", api.NewMailConfigApi().GetInfo)
|
||||
route.GET("/config/check", api.NewMailConfigApi().Check)
|
||||
route.PUT("/config/save", api.NewMailConfigApi().Save)
|
||||
route.PUT("/config/status", api.NewMailConfigApi().UpdateStatus)
|
||||
|
||||
// 订阅模块
|
||||
route.GET("/subscribe/info", api.NewSubscribeApi().GetInfo)
|
||||
route.POST("/subscribe/pay", api.NewSubscribeApi().Pay)
|
||||
|
||||
@@ -5,3 +5,14 @@ type Page struct {
|
||||
PageNum int `form:"pageNum" json:"pageNum"`
|
||||
PageSize int `form:"pageSize" json:"pageSize"`
|
||||
}
|
||||
|
||||
// 发送邮件参数模型
|
||||
type MailParam struct {
|
||||
Smtp string
|
||||
Port int
|
||||
AuthCode string
|
||||
Sender string
|
||||
Subject string
|
||||
Content string
|
||||
Receiver string
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package models
|
||||
|
||||
type MailConfig struct {
|
||||
Id int64 `gorm:"primaryKey"`
|
||||
Stmp string `gorm:"stmp"`
|
||||
Port int `gorm:"port"`
|
||||
AuthCode string `gorm:"auth_code"`
|
||||
Email string `gorm:"email"`
|
||||
Status int `gorm:"status"`
|
||||
Creator int64 `gorm:"creator"`
|
||||
Created int64 `gorm:"created"`
|
||||
Updated int64 `gorm:"updated"`
|
||||
}
|
||||
|
||||
type MailConfigSaveParam struct {
|
||||
Id int64 `json:"id" binding:"omitempty,gt=0"`
|
||||
Stmp string `json:"stmp" binding:"omitempty,ip|hostname"`
|
||||
Port int `json:"port" binding:"omitempty,gt=0"`
|
||||
AuthCode string `json:"authCode" binding:"omitempty,gt=0"`
|
||||
Email string `json:"email" binding:"omitempty,email"`
|
||||
Status int `json:"status" binding:"omitempty,oneof=1 2"`
|
||||
Creator int64 `json:"creator" binding:"omitempty"`
|
||||
}
|
||||
|
||||
type MailConfigStatusParam struct {
|
||||
Id int64 `json:"id" binding:"required,gt=0"`
|
||||
Status int `json:"status" binding:"required,oneof=1 2"`
|
||||
Creator int64 `json:"creator" binding:"omitempty"`
|
||||
}
|
||||
|
||||
type MailConfigInfo struct {
|
||||
Id int64 `json:"id"`
|
||||
Stmp string `json:"stmp"`
|
||||
Port int `json:"port"`
|
||||
AuthCode string `json:"authCode"`
|
||||
Email string `json:"email"`
|
||||
Status int `json:"status"`
|
||||
Usability int `json:"usability"`
|
||||
}
|
||||
@@ -45,6 +45,13 @@ type CustomerUpdateParam struct {
|
||||
Status int `json:"status" binding:"-"`
|
||||
}
|
||||
|
||||
type CustomerSendMailParam struct {
|
||||
Uid int64 `json:"uid" binding:"-"`
|
||||
Receiver string `json:"receiver" binding:"required,email"`
|
||||
Subject string `json:"subject" binding:"omitempty,gt=0"`
|
||||
Content string `json:"content" binding:"required,gt=0"`
|
||||
}
|
||||
|
||||
type CustomerDeleteParam struct {
|
||||
Ids []int64 `json:"ids" binding:"required"`
|
||||
}
|
||||
|
||||
@@ -23,6 +23,10 @@ const (
|
||||
ErrCodePayFailed = 20001 // 支付宝支付失败
|
||||
|
||||
ErrCodeFileExportFailed = 30001 // 文件导出失败
|
||||
|
||||
ErrCodeMailConfigInvalid = 50001 // 邮件服务配置无效
|
||||
ErrCodeMailSendFailed = 50002 // 邮件发送失败
|
||||
ErrCodeMailSendUnEnable = 50003 // 邮件服务未开启
|
||||
)
|
||||
|
||||
var msg = map[int]string{
|
||||
@@ -42,4 +46,7 @@ var msg = map[int]string{
|
||||
ErrCodeEmailFormatInvalid: "email format invalid",
|
||||
ErrCodeUserPassResetFailed: "user password reset failed",
|
||||
ErrCodeFileExportFailed: "file export failed",
|
||||
ErrCodeMailConfigInvalid: "mail config invalid",
|
||||
ErrCodeMailSendFailed: "mail send failed",
|
||||
ErrCodeMailSendUnEnable: "mail send server unEnable",
|
||||
}
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"crm/common"
|
||||
"crm/dao"
|
||||
"crm/models"
|
||||
"crm/response"
|
||||
)
|
||||
|
||||
type MailConfigService struct {
|
||||
mailConfigDao *dao.MailConfigDao
|
||||
}
|
||||
|
||||
func NewMailConfigService() *MailConfigService {
|
||||
return &MailConfigService{
|
||||
mailConfigDao: dao.NewMailConfigDao(),
|
||||
}
|
||||
}
|
||||
|
||||
// 保存邮件服务配置
|
||||
func (m *MailConfigService) Save(param *models.MailConfigSaveParam) int {
|
||||
if err := m.mailConfigDao.Save(param); err != nil {
|
||||
return response.ErrCodeFailed
|
||||
}
|
||||
return response.ErrCodeSuccess
|
||||
}
|
||||
|
||||
// 开启或关闭邮件服务
|
||||
func (m *MailConfigService) UpdateStatus(param *models.MailConfigStatusParam) int {
|
||||
if err := m.mailConfigDao.UpdateStatus(param); err != nil {
|
||||
return response.ErrCodeFailed
|
||||
}
|
||||
return response.ErrCodeSuccess
|
||||
}
|
||||
|
||||
// 获取邮件配置信息
|
||||
func (m *MailConfigService) GetInfo(uid int64) (*models.MailConfigInfo, int) {
|
||||
mc, err := m.mailConfigDao.GetInfo(uid)
|
||||
if err != nil {
|
||||
return nil, response.ErrCodeFailed
|
||||
}
|
||||
mailConfig := models.MailConfigInfo{
|
||||
Id: mc.Id,
|
||||
Stmp: mc.Stmp,
|
||||
Port: mc.Port,
|
||||
AuthCode: mc.AuthCode,
|
||||
Email: mc.Email,
|
||||
Status: mc.Status,
|
||||
}
|
||||
return &mailConfig, response.ErrCodeSuccess
|
||||
}
|
||||
|
||||
// 检查邮件配置的有效性
|
||||
func (m *MailConfigService) Check(uid int64) int {
|
||||
mc, err := m.mailConfigDao.GetInfo(uid)
|
||||
if err != nil {
|
||||
return response.ErrCodeMailConfigInvalid
|
||||
}
|
||||
if err = common.DialMail(mc.Stmp, mc.Port, mc.Email, mc.AuthCode); err != nil {
|
||||
return response.ErrCodeMailConfigInvalid
|
||||
}
|
||||
return response.ErrCodeSuccess
|
||||
}
|
||||
@@ -9,6 +9,11 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
Open = 1
|
||||
Close = 2
|
||||
)
|
||||
|
||||
type CustomerService struct {
|
||||
}
|
||||
|
||||
@@ -57,6 +62,31 @@ func (c *CustomerService) Update(param *models.CustomerUpdateParam) int {
|
||||
return response.ErrCodeSuccess
|
||||
}
|
||||
|
||||
// 发送邮件给客户
|
||||
func (c *CustomerService) SendMail(param *models.CustomerSendMailParam) int {
|
||||
var mc models.MailConfig
|
||||
err := global.Db.Model(&models.MailConfig{}).Where("creator = ?", param.Uid).First(&mc).Error
|
||||
if err != nil {
|
||||
return response.ErrCodeMailSendFailed
|
||||
}
|
||||
if mc.Status == Close {
|
||||
return response.ErrCodeMailSendUnEnable
|
||||
}
|
||||
mailParam := models.MailParam{
|
||||
Smtp: mc.Stmp,
|
||||
Port: mc.Port,
|
||||
AuthCode: mc.AuthCode,
|
||||
Sender: mc.Email,
|
||||
Subject: param.Subject,
|
||||
Content: param.Content,
|
||||
Receiver: param.Receiver,
|
||||
}
|
||||
if err := common.SendMailToCustomer(mailParam); err != nil {
|
||||
return response.ErrCodeMailSendFailed
|
||||
}
|
||||
return response.ErrCodeSuccess
|
||||
}
|
||||
|
||||
// 删除客户
|
||||
func (c *CustomerService) Delete(param *models.CustomerDeleteParam) int {
|
||||
if err := global.Db.Delete(&models.Customer{}, param.Ids).Error; err != nil {
|
||||
|
||||
@@ -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,
|
||||
})
|
||||
}
|
||||
@@ -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({
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user