Commit 4de2993b by Sweet Zhang

联系人管理、发件人管理对接

parent d86fbaed
...@@ -34,19 +34,7 @@ ...@@ -34,19 +34,7 @@
</header> </header>
<!-- 使用router-view显示当前路由组件 --> <!-- 使用router-view显示当前路由组件 -->
<router-view <router-view />
:senders="senders"
:contacts="contacts"
:variables="variables"
:variable-templates="variableTemplates"
:emails="emails"
@save-email="saveEmail"
@update-contacts="updateContacts"
@update-senders="updateSenders"
@update-variables="updateVariables"
@update-variable-templates="updateVariableTemplates"
@reuse-email="reuseEmail"
/>
</main> </main>
</div> </div>
</div> </div>
...@@ -59,7 +47,6 @@ import { useRoute, useRouter } from 'vue-router' ...@@ -59,7 +47,6 @@ import { useRoute, useRouter } from 'vue-router'
import LoginPage from './views/LoginPage.vue' import LoginPage from './views/LoginPage.vue'
import Sidebar from './views/Sidebar.vue' import Sidebar from './views/Sidebar.vue'
import MobileSidebar from './views/MobileSidebar.vue' import MobileSidebar from './views/MobileSidebar.vue'
import type { Contact, Sender, Variable, VariableTemplate, Email } from './types'
const route = useRoute() const route = useRoute()
const router = useRouter() const router = useRouter()
...@@ -80,13 +67,6 @@ watch( ...@@ -80,13 +67,6 @@ watch(
}, },
) )
// 数据存储(保持不变)
const contacts = ref<Contact[]>([])
const senders = ref<Sender[]>([])
const variables = ref<Variable[]>([])
const variableTemplates = ref<VariableTemplate[]>([])
const emails = ref<Email[]>([])
// 页面标题映射(保持不变) // 页面标题映射(保持不变)
const pageTitles = { const pageTitles = {
compose: '写邮件', compose: '写邮件',
...@@ -100,7 +80,6 @@ const pageTitles = { ...@@ -100,7 +80,6 @@ const pageTitles = {
const handleLogin = () => { const handleLogin = () => {
isAuthenticated.value = true isAuthenticated.value = true
isLoginPage.value = false isLoginPage.value = false
loadInitialData()
router.push('/compose') // 登录后跳转到写邮件页面 router.push('/compose') // 登录后跳转到写邮件页面
} }
...@@ -110,64 +89,10 @@ const handleLogout = () => { ...@@ -110,64 +89,10 @@ const handleLogout = () => {
router.push('/login') // 退出后跳转到登录页面 router.push('/login') // 退出后跳转到登录页面
} }
const handlePageChange = (page: string) => {
currentPage.value = page as 'compose' | 'contacts' | 'senders' | 'variables' | 'emails'
}
const handleMobilePageChange = (page: string) => {
currentPage.value = page as 'compose' | 'contacts' | 'senders' | 'variables' | 'emails'
showMobileMenu.value = false
}
const updateContacts = (newContacts: Contact[]) => {
contacts.value = newContacts
}
const updateSenders = (newSenders: Sender[]) => {
senders.value = newSenders
}
const updateVariables = (newVariables: Variable[]) => {
variables.value = newVariables
}
const updateVariableTemplates = (newTemplates: VariableTemplate[]) => {
variableTemplates.value = newTemplates
}
const saveEmail = (email: Email) => {
emails.value.push(email)
}
const reuseEmail = (emailData: Email) => {
currentPage.value = 'compose'
// 这里可以传递需要复用的邮件数据到ComposeEmail组件
// 实际实现中可以使用状态管理或props
}
const loadInitialData = () => {
// 模拟加载初始数据
// 联系人
contacts.value = []
// 发件人
senders.value = []
// 变量
variables.value = []
// 变量模板
variableTemplates.value = []
// 邮件记录
emails.value = []
}
// 初始化 // 初始化
onMounted(() => { onMounted(() => {
// 检查是否已登录(实际项目中应该检查本地存储或令牌) // 检查是否已登录(实际项目中应该检查本地存储或令牌)
if (isAuthenticated.value) { if (isAuthenticated.value) {
loadInitialData()
} }
}) })
</script> </script>
...@@ -35,7 +35,7 @@ export const editContact = (data) => { ...@@ -35,7 +35,7 @@ export const editContact = (data) => {
* @returns * @returns
*/ */
export const deleteContact = (id: number) => { export const deleteContact = (id: number) => {
return request.delete('/emailContact/del?contactBizId=' + id, { params: { contactBizId: id } }) return request.delete('/emailContact/del?contactBizId=' + id)
} }
// 获取联系人详情 // 获取联系人详情
...@@ -74,6 +74,28 @@ export const getContactList = (params: { ...@@ -74,6 +74,28 @@ export const getContactList = (params: {
return request.post('/emailContact/page', params) return request.post('/emailContact/page', params)
} }
/**服务商列表 */
/**
*
* @param params {
"providerName": "", //邮箱服务商名称
"pageNo": 1,
"pageSize": 1,
"sortField": "",
"sortOrder": ""
}
* @returns
*/
export const getEmailProviderList = (params: {
providerName?: string
pageNo?: number
pageSize?: number
sortField?: string
sortOrder?: string
}) => {
return request.post('/emailProviderConfig/page', params)
}
// 新增发送配置 // 新增发送配置
/** /**
* *
...@@ -81,13 +103,11 @@ export const getContactList = (params: { ...@@ -81,13 +103,11 @@ export const getContactList = (params: {
* @returns * @returns
*/ */
export const addEmailSenderConfig = (data: { export const addEmailSenderConfig = (data: {
emailSenderConfigBizId?: number email?: number
emailSenderConfigName?: string password?: string
emailSenderConfigEmail?: string providerBizId?: string
emailSenderConfigType?: string displayName?: string
emailSenderConfigAppellation?: string active?: boolean
emailSenderConfigOther?: string
emailSenderConfigCcEmailList?: string[]
}) => { }) => {
return request.post('/emailSenderConfig/add', data) return request.post('/emailSenderConfig/add', data)
} }
...@@ -99,13 +119,12 @@ export const addEmailSenderConfig = (data: { ...@@ -99,13 +119,12 @@ export const addEmailSenderConfig = (data: {
* @returns * @returns
*/ */
export const editEmailSenderConfig = (data: { export const editEmailSenderConfig = (data: {
emailSenderConfigBizId?: number senderBizId?: number
emailSenderConfigName?: string email?: string
emailSenderConfigEmail?: string password?: string
emailSenderConfigType?: string providerBizId?: string
emailSenderConfigAppellation?: string displayName?: string
emailSenderConfigOther?: string active?: number
emailSenderConfigCcEmailList?: string[]
}) => { }) => {
return request.put('/emailSenderConfig/edit', data) return request.put('/emailSenderConfig/edit', data)
} }
...@@ -116,8 +135,8 @@ export const editEmailSenderConfig = (data: { ...@@ -116,8 +135,8 @@ export const editEmailSenderConfig = (data: {
* @param id 发送配置id * @param id 发送配置id
* @returns * @returns
*/ */
export const deleteEmailSenderConfig = (id: number) => { export const deleteEmailSenderConfig = (id: string) => {
return request.delete('/emailSenderConfig/delete', { params: { emailSenderConfigBizId: id } }) return request.delete('/emailSenderConfig/del?senderBizId=' + id)
} }
// 获取发送配置详情 // 获取发送配置详情
...@@ -126,8 +145,8 @@ export const deleteEmailSenderConfig = (id: number) => { ...@@ -126,8 +145,8 @@ export const deleteEmailSenderConfig = (id: number) => {
* @param id 发送配置id * @param id 发送配置id
* @returns * @returns
*/ */
export const getEmailSenderConfigDetail = (id: number) => { export const getEmailSenderConfigDetail = (id: string) => {
return request.get('/emailSenderConfig/detail', { params: { emailSenderConfigBizId: id } }) return request.get('/emailSenderConfig/detail', { params: { senderBizId: id } })
} }
// 获取发送配置列表 // 获取发送配置列表
......
...@@ -6,7 +6,7 @@ const router = createRouter({ ...@@ -6,7 +6,7 @@ const router = createRouter({
routes: [ routes: [
{ {
path: '/', path: '/',
redirect: '/compose', redirect: '/login',
}, },
{ {
path: '/compose', path: '/compose',
......
// 联系人类型 // 联系人类型
export interface Contact { export interface Contact {
id: string contactBizId?: string
name: string name?: string
type: string type?: string
companyName: string companyName?: string
email: string email?: string
ccEmailList: string[] ccEmailList?: string[]
other: string other?: string
appellation: string appellation?: string
} }
// 发件人类型 // 发件人类型
export interface Sender { export interface Sender {
id: string senderBizId?: string
email: string email?: string
password: string password?: string
smtpServer: string displayName?: string
smtpPort: string providerBizId?: string
active?: number
} }
// 变量类型 // 变量类型
......
...@@ -8,7 +8,7 @@ const request = axios.create({ ...@@ -8,7 +8,7 @@ const request = axios.create({
'Content-Type': 'application/json', 'Content-Type': 'application/json',
// Authorization: 'Bearer ' + localStorage.getItem('authToken'), // Authorization: 'Bearer ' + localStorage.getItem('authToken'),
Authorization: Authorization:
'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyXzEwMDEiLCJyb2xlcyI6W10sImlhdCI6MTc1ODY5OTA3NywiZXhwIjoxNzU4Nzg1NDc3fQ.LR2fGy0aO6EHsHe9Que8rzCaJ0TSAB9KtJndYMSYvvKOSeNvGawCmjE8kgDeRmyFFOFJ2kt0sk-fGaExgzQHSw', 'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyXzEwMDEiLCJyb2xlcyI6W10sImlhdCI6MTc1ODc4NTY3NCwiZXhwIjoxNzU4ODcyMDc0fQ.tjTO6vdpwLpNjVa1DhxRBdpjZsdhbx6g1TdtpAm7BZBRMwanM_ci7dsnbc8FNXpyfSb-ifXW7ccxwyQbtCaKiQ',
}, },
}) })
......
...@@ -250,33 +250,14 @@ import { ...@@ -250,33 +250,14 @@ import {
ImportRecord, ImportRecord,
} from '../types' } from '../types'
const props = defineProps({ const senders = ref<Sender[]>([])
senders: { const contacts = ref<Contact[]>([])
type: Array as () => Sender[], const variables = ref<Variable[]>([])
required: true, const variableTemplates = ref<VariableTemplate[]>([])
}, const emails = ref<Email[]>([])
contacts: {
type: Array as () => Contact[],
required: true,
},
variables: {
type: Array as () => Variable[],
required: true,
},
variableTemplates: {
type: Array as () => VariableTemplate[],
required: true,
},
emails: {
type: Array as () => Email[],
required: true,
},
})
const emits = defineEmits(['save-email', 'save-import-record'])
// 状态 // 状态
const currentSender = ref<Sender | null>(props.senders.length > 0 ? props.senders[0] : null) const currentSender = ref<Sender | null>(senders.value.length > 0 ? senders.value[0] : null)
const emailForm = ref<EmailForm>({ const emailForm = ref<EmailForm>({
to: '', to: '',
cc: '', cc: '',
...@@ -307,8 +288,8 @@ watch( ...@@ -307,8 +288,8 @@ watch(
}, },
) )
// 计算属性
const variablePrefix = '{{' const variablePrefix = '{{'
const variableNextfix = '}}'
// 方法 // 方法
const handleFileUpload = (e: Event) => { const handleFileUpload = (e: Event) => {
...@@ -325,16 +306,19 @@ const removeAttachment = (index: number) => { ...@@ -325,16 +306,19 @@ const removeAttachment = (index: number) => {
const applyVariableTemplate = () => { const applyVariableTemplate = () => {
if (!selectedVariableTemplate.value) return if (!selectedVariableTemplate.value) return
const variableKeys = selectedVariableTemplate.value.variableIds.map((id) => { const variableKeys =
const variable = props.variables.find((v) => v.id === id) selectedVariableTemplate.value.variableBizIdList.map((id) => {
return variable ? variable.key : '' const variable = variables.value.find((v) => v.variableBizId === id)
}) return variable ? variable.variableNameEn : ''
const variablesText = variableKeys.map((key) => `${variablePrefix}${key}}`).join(', ') }) || []
const variablesText = variableKeys
.map((key) => `${variablePrefix}${key}${variableNextfix}`)
.join(', ')
emailForm.value.content = `【使用了模板变量:${variablesText}】\n\n${emailForm.value.content}` emailForm.value.content = `【使用了模板变量:${variablesText}】\n\n${emailForm.value.content}`
} }
const insertVariable = (variable: Variable) => { const insertVariable = (variable: Variable) => {
emailForm.value.content += `${variablePrefix}${variable.key}}` emailForm.value.content += `${variablePrefix}${variable.variableNameEn}${variableNextfix}`
showVariableSelector.value = false showVariableSelector.value = false
} }
...@@ -366,8 +350,6 @@ const saveImportRecord = (to: string, cc: string) => { ...@@ -366,8 +350,6 @@ const saveImportRecord = (to: string, cc: string) => {
} }
importRecords.value.push(newRecord) importRecords.value.push(newRecord)
} }
emits('save-import-record', importRecords.value)
} }
// 更新导入记录 // 更新导入记录
...@@ -375,14 +357,12 @@ const updateImportRecord = (updatedRecord: ImportRecord) => { ...@@ -375,14 +357,12 @@ const updateImportRecord = (updatedRecord: ImportRecord) => {
const index = importRecords.value.findIndex((record) => record.id === updatedRecord.id) const index = importRecords.value.findIndex((record) => record.id === updatedRecord.id)
if (index !== -1) { if (index !== -1) {
importRecords.value[index] = { ...updatedRecord } importRecords.value[index] = { ...updatedRecord }
emits('save-import-record', importRecords.value)
} }
} }
// 删除导入记录 // 删除导入记录
const deleteImportRecord = (id: string) => { const deleteImportRecord = (id: string) => {
importRecords.value = importRecords.value.filter((record) => record.id !== id) importRecords.value = importRecords.value.filter((record) => record.id !== id)
emits('save-import-record', importRecords.value)
} }
// 修改handleImportContacts方法 // 修改handleImportContacts方法
...@@ -413,7 +393,6 @@ const saveAsDraft = () => { ...@@ -413,7 +393,6 @@ const saveAsDraft = () => {
status: 'draft', status: 'draft',
attachments: attachments.value.map((file) => ({ name: file.name })), attachments: attachments.value.map((file) => ({ name: file.name })),
} }
emits('save-email', draft)
alert('草稿已保存') alert('草稿已保存')
} }
...@@ -447,7 +426,6 @@ const confirmSendEmail = () => { ...@@ -447,7 +426,6 @@ const confirmSendEmail = () => {
status: emailForm.value.scheduleSend ? 'scheduled' : 'sent', status: emailForm.value.scheduleSend ? 'scheduled' : 'sent',
attachments: attachments.value.map((file) => ({ name: file.name })), attachments: attachments.value.map((file) => ({ name: file.name })),
} }
emits('save-email', email)
emailForm.value = { to: '', cc: '', subject: '', content: '', scheduleSend: false, sendTime: '' } emailForm.value = { to: '', cc: '', subject: '', content: '', scheduleSend: false, sendTime: '' }
attachments.value = [] attachments.value = []
selectedVariableTemplate.value = null selectedVariableTemplate.value = null
......
<template> <template>
<!-- 模板内容与之前保持一致 --> <div>
<div class="p-4 md:p-6"> <div class="bg-white rounded-lg shadow-md p-6 mb-6">
<div class="max-w-7xl mx-auto"> <div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6 gap-4">
<h3 class="text-lg font-semibold">添加联系人</h3>
<button v-if="editingSenderId" @click="resetForm" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i> 取消编辑
</button>
</div>
<!-- 页面标题和操作按钮 --> <!-- 页面标题和操作按钮 -->
<div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6 gap-4"> <div class="flex flex-col md:flex-row md:items-center md:justify-between mb-6 gap-4">
<div>
<h1 class="text-2xl font-bold text-gray-800">联系人管理</h1>
<p class="text-gray-500 mt-1">管理您的所有联系人信息</p>
</div>
<div class="flex gap-3"> <div class="flex gap-3">
<button @click="showImportModal = true" class="btn-outline flex items-center"> <button @click="showImportModal = true" class="btn-outline flex items-center">
<i class="fas fa-upload mr-2"></i> 批量导入 <i class="fas fa-upload mr-2"></i> 批量导入
...@@ -18,171 +18,367 @@ ...@@ -18,171 +18,367 @@
</button> </button>
</div> </div>
</div> </div>
</div>
<!-- 搜索和筛选区域 --> <!-- 搜索和筛选区域 -->
<div class="bg-white rounded-lg shadow-sm p-4 mb-6"> <div class="bg-white rounded-lg shadow-sm p-4 mb-6">
<div class="flex flex-col md:flex-row gap-4"> <div class="flex flex-col md:flex-row gap-4">
<div class="relative flex-1"> <div class="relative flex-1">
<i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i> <i class="fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"></i>
<input <input
v-model="searchQuery" v-model="searchQuery"
type="text" type="text"
class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg" class="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder="搜索联系人姓名、公司或邮箱..." placeholder="搜索联系人姓名、公司或邮箱..."
@keyup.enter="searchContacts" @keyup.enter="searchContacts"
/> />
</div> </div>
<div class="flex gap-3 w-full md:w-auto"> <div class="flex gap-3 w-full md:w-auto">
<select <select
v-model="sortBy" v-model="sortBy"
class="w-full md:w-auto px-4 py-2 border border-gray-300 rounded-lg" class="w-full md:w-auto px-4 py-2 border border-gray-300 rounded-lg"
@change="searchContacts" @change="searchContacts"
> >
<option value="name">按姓名排序</option> <option value="name">按姓名排序</option>
<option value="company">按公司排序</option> <option value="company">按公司排序</option>
<option value="addedAt">按添加时间排序</option> <option value="addedAt">按添加时间排序</option>
</select> </select>
<button @click="resetSearch" class="btn-outline px-4">重置</button> <button @click="resetSearch" class="btn-outline px-4">重置</button>
</div>
</div> </div>
</div> </div>
</div>
<!-- 联系人列表 --> <!-- 联系人列表 -->
<div class="bg-white rounded-lg shadow-sm overflow-hidden"> <div class="bg-white rounded-lg shadow-sm overflow-hidden">
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<table class="w-full"> <table class="w-full">
<thead> <thead>
<tr class="bg-gray-50 border-b border-gray-200"> <tr class="bg-gray-50 border-b border-gray-200">
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
姓名
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
称谓
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
公司
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
收件人邮箱
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
抄送人邮箱
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
其他信息
</th>
<th
class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"
>
操作
</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr
v-for="contact in filteredContacts"
:key="contact.id"
class="hover:bg-gray-50 transition-colors"
> >
<td class="px-6 py-4 whitespace-nowrap"> 姓名
<div class="flex items-center"> </th>
<div <th
class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-3" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
> >
<span class="text-sm font-medium">{{ contact.name.charAt(0) }}</span> 称谓
</div> </th>
<div> <th
<div class="text-sm font-medium text-gray-900">{{ contact.name }}</div> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
</div> >
</div> 公司
</td> </th>
<td class="px-6 py-4 whitespace-nowrap"> <th
<div class="text-sm text-gray-500">{{ contact.title || '-' }}</div> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
</td> >
<td class="px-6 py-4 whitespace-nowrap"> 收件人邮箱
<div class="text-sm text-gray-500">{{ contact.company || '-' }}</div> </th>
</td> <th
<td class="px-6 py-4 whitespace-nowrap"> class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
<div class="text-sm text-gray-500">{{ contact.toEmail }}</div> >
</td> 抄送人邮箱
<td class="px-6 py-4"> </th>
<div class="flex flex-wrap gap-1"> <th
<span class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
v-for="(email, index) in contact.ccEmails" >
:key="index" 其他信息
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800" </th>
> <th
{{ email }} class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider"
</span> >
<span v-if="contact.ccEmails.length === 0" class="text-sm text-gray-500" 操作
>-</span </th>
> </tr>
</thead>
<tbody class="divide-y divide-gray-200">
<tr
v-for="contact in filteredContacts"
:key="contact.contactBizId"
class="hover:bg-gray-50 transition-colors"
>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div
class="w-8 h-8 rounded-full bg-blue-100 flex items-center justify-center text-blue-600 mr-3"
>
<span class="text-sm font-medium">{{ contact.name?.charAt(0) || '-' }}</span>
</div> </div>
</td> <div>
<td class="px-6 py-4"> <div class="text-sm font-medium text-gray-900">{{ contact.name || '-' }}</div>
<div class="text-sm text-gray-500 line-clamp-2">
{{ contact.otherInfo || '-' }}
</div> </div>
</td> </div>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium"> </td>
<button <td class="px-6 py-4 whitespace-nowrap">
@click="editContact(contact)" <div class="text-sm text-gray-500">{{ contact.appellation || '-' }}</div>
class="text-blue-600 hover:text-blue-900 mr-4" </td>
title="编辑" <td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500">{{ contact.companyName || '-' }}</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm text-gray-500">{{ contact.email || '-' }}</div>
</td>
<td class="px-6 py-4">
<div class="flex flex-wrap gap-1">
<span
v-for="(email, index) in contact.ccEmailList || []"
:key="index"
class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800"
> >
<i class="fas fa-edit"></i> {{ email }}
</button> </span>
<span v-if="contact.ccEmailList?.length === 0" class="text-sm text-gray-500"
>-</span
>
</div>
</td>
<td class="px-6 py-4">
<div class="text-sm text-gray-500 line-clamp-2">
{{ contact.other || '-' }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<button
@click="editContactModal(contact)"
class="text-blue-600 hover:text-blue-900 mr-4"
title="编辑"
>
<i class="fas fa-edit"></i>
</button>
<button
@click="deleteContactModal(contact)"
class="text-red-600 hover:text-red-900"
title="删除"
>
<i class="fas fa-trash"></i>
</button>
</td>
</tr>
</tbody>
</table>
</div>
<!-- 空状态、加载状态和分页组件与之前保持一致 -->
</div>
<!-- 联系人模态框 -->
<div
v-if="showContactModal"
class="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4"
>
<div class="bg-white rounded-lg shadow-xl w-full max-w-2xl max-h-[80vh] flex flex-col">
<div class="p-4 border-b border-gray-200 flex justify-between items-center">
<h3 class="text-lg font-semibold">
{{ isEditing ? '编辑联系人' : '新建联系人' }}
</h3>
<button @click="closeContactModal" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="p-4 flex-1 overflow-y-auto">
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<!-- 姓名 -->
<div class="md:col-span-2">
<label class="block text-gray-700 mb-1 text-sm">姓名 *</label>
<input
v-model="form.name"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入联系人姓名"
/>
<p v-if="errors.name" class="text-red-500 text-xs mt-1">{{ errors.name }}</p>
</div>
<!-- 联系人类型 -->
<div>
<label class="block text-gray-700 mb-1 text-sm">联系人类型</label>
<select
v-model="form.type"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
<option value="">请选择类型</option>
<option value="customer">客户</option>
<option value="partner">合作伙伴</option>
<option value="supplier">供应商</option>
<option value="internal">内部员工</option>
<option value="other">其他</option>
</select>
</div>
<!-- 称谓 -->
<div>
<label class="block text-gray-700 mb-1 text-sm">称谓</label>
<input
v-model="form.appellation"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="例如:经理、总监"
/>
</div>
<!-- 公司 -->
<div class="md:col-span-2">
<label class="block text-gray-700 mb-1 text-sm">公司</label>
<input
v-model="form.companyName"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入公司名称"
/>
</div>
<!-- 收件人邮箱 -->
<div class="md:col-span-2">
<label class="block text-gray-700 mb-1 text-sm">收件人邮箱 *</label>
<input
v-model="form.email"
type="email"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="example@company.com"
/>
<p v-if="errors.email" class="text-red-500 text-xs mt-1">{{ errors.email }}</p>
</div>
<!-- 抄送人邮箱 -->
<div class="md:col-span-2">
<label class="block text-gray-700 mb-1 text-sm">抄送人邮箱</label>
<div class="flex gap-2 mb-2">
<input
v-model="newCcEmail"
type="email"
class="flex-1 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="添加抄送邮箱"
@keyup.enter="addCcEmail"
/>
<button
@click="addCcEmail"
class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-700"
>
添加
</button>
</div>
<div class="flex flex-wrap gap-2">
<span
v-for="(email, index) in form.ccEmailList"
:key="index"
class="inline-flex items-center px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm"
>
{{ email }}
<button <button
@click="deleteContact(contact.id)" @click="removeCcEmail(index)"
class="text-red-600 hover:text-red-900" class="ml-2 text-blue-600 hover:text-blue-900"
title="删除"
> >
<i class="fas fa-trash"></i> <i class="fas fa-times"></i>
</button> </button>
</td> </span>
</tr> </div>
</tbody> </div>
</table>
<!-- 其他信息 -->
<div class="md:col-span-2">
<label class="block text-gray-700 mb-1 text-sm">其他信息</label>
<textarea
v-model="form.other"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
rows="3"
placeholder="备注信息"
></textarea>
</div>
</div>
</div> </div>
<!-- 空状态、加载状态和分页组件与之前保持一致 --> <div class="p-4 border-t border-gray-200 flex justify-end gap-3">
<button
@click="closeContactModal"
class="px-6 py-2 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors"
>
取消
</button>
<button
@click="saveContact"
:disabled="isSubmitting"
class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-700 transition-colors"
>
{{ isSubmitting ? '保存中...' : isEditing ? '更新' : '保存' }}
</button>
</div>
</div> </div>
</div> </div>
<CommonModal
<!-- 模态框组件与之前保持一致 --> v-model:visible="modalVisible"
:trigger-key="modalConfig.triggerKey"
:title="modalConfig.title"
type="confirm"
:message="modalConfig.message"
:show-cancel-button="modalConfig.showCancel"
@confirm="handleConfirm"
@cancel="handleCancel"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
// 引入我们创建的api拦截器 // 引入我们创建的api拦截器
import { editContact } from '@/api/api' import {
addContact,
editContact,
deleteContact,
getContactDetail,
getContactList,
getEmailSenderConfigList,
} from '@/api/api'
import { Contact, Sender } from '@/types/index'
// 引入弹窗组件
import CommonModal from '@/components/CommonModal.vue'
import { isTypeOnlyImportOrExportDeclaration } from 'typescript'
// 弹窗提示信息对象
const modalVisible = ref(false)
const modalConfig = ref({
showCancel: false,
title: '操作确认',
message: '确定要执行此操作吗?',
triggerKey: 'templateModal',
})
// 联系人数据结构 const openModal = (
interface Contact { config: { triggerKey?: string; showCancel?: boolean; title?: string; message?: string } = {},
id: number ) => {
name: string modalConfig.value = {
title?: string showCancel: config.showCancel ?? false,
company?: string title: config.title ?? '操作确认',
toEmail: string message: config.message ?? '确定要执行此操作吗?',
ccEmailList: string[] triggerKey: config.triggerKey ?? modalConfig.value.triggerKey,
other?: string }
appellation?: string modalVisible.value = true
addedAt: string }
const handleConfirm = (triggerKey: string) => {
modalVisible.value = false
console.log('用户确认操作', triggerKey)
if (triggerKey === 'deleteContactModal') {
deleteContact(form.value.contactBizId).then((res) => {
if (res.code === 200) {
fetchContacts()
openModal({
triggerKey: '',
title: '成功',
message: '联系人删除成功',
})
} else {
openModal({
triggerKey: '',
title: '错误',
message: res.message || '联系人删除失败',
})
}
})
}
} }
const handleCancel = (triggerKey: string) => {
modalVisible.value = false
console.log('用户取消操作', triggerKey)
}
// 导入错误数据结构 // 导入错误数据结构
interface ImportError { interface ImportError {
row: number row: number
...@@ -209,7 +405,6 @@ const currentPage = ref(1) ...@@ -209,7 +405,6 @@ const currentPage = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
const totalContacts = ref(0) const totalContacts = ref(0)
const isLoading = ref(false) const isLoading = ref(false)
// 模态框状态 // 模态框状态
const showContactModal = ref(false) const showContactModal = ref(false)
const isEditing = ref(false) const isEditing = ref(false)
...@@ -218,20 +413,20 @@ const showImportResultModal = ref(false) ...@@ -218,20 +413,20 @@ const showImportResultModal = ref(false)
// 表单数据 // 表单数据
const form = ref<Partial<Contact>>({ const form = ref<Partial<Contact>>({
id: 0,
name: '', name: '',
title: '', type: '',
company: '', companyName: '',
toEmail: '', email: '',
ccEmails: [], ccEmailList: [],
otherInfo: '', other: '',
appellation: '',
}) })
const newCcEmail = ref('') const newCcEmail = ref('')
// 表单错误 // 表单错误
const errors = ref({ const errors = ref({
name: '', name: '',
toEmail: '', email: '',
}) })
// 导入相关状态 // 导入相关状态
...@@ -264,21 +459,24 @@ const totalPages = computed(() => { ...@@ -264,21 +459,24 @@ const totalPages = computed(() => {
return Math.ceil(totalContacts.value / pageSize.value) return Math.ceil(totalContacts.value / pageSize.value)
}) })
// 获取联系人列表 - 使用api拦截器 // 获取联系人列表
const fetchContacts = async () => { const fetchContacts = async () => {
try { try {
isLoading.value = true isLoading.value = true
const data = await getContactList({
// 使用我们的api实例,会自动添加Authorization头 pageNo: currentPage.value,
const data = await api.get( pageSize: pageSize.value,
`/contacts?page=${currentPage.value}&size=${pageSize.value}&search=${searchQuery.value}&sort=${sortBy.value}`, sortField: sortBy.value,
) sortOrder: 'asc',
})
contacts.value = data.items contacts.value = data.data.records
totalContacts.value = data.total totalContacts.value = data.data.total
} catch (error) { } catch (error) {
console.error('获取联系人失败:', error) console.error('获取联系人失败:', error)
showError('获取联系人失败,请稍后重试') openModal({
title: '获取联系人失败',
message: '获取联系人失败,请稍后重试',
})
} finally { } finally {
isLoading.value = false isLoading.value = false
} }
...@@ -309,14 +507,15 @@ const changePage = (page: number) => { ...@@ -309,14 +507,15 @@ const changePage = (page: number) => {
const openAddContactModal = () => { const openAddContactModal = () => {
form.value = { form.value = {
name: '', name: '',
title: '', type: '',
company: '', companyName: '',
toEmail: '', email: '',
ccEmails: [], ccEmailList: [],
otherInfo: '', other: '',
appellation: '',
} }
newCcEmail.value = '' newCcEmail.value = ''
errors.value = { name: '', toEmail: '' } errors.value = { name: '', email: '' }
isEditing.value = false isEditing.value = false
showContactModal.value = true showContactModal.value = true
} }
...@@ -327,10 +526,10 @@ const closeContactModal = () => { ...@@ -327,10 +526,10 @@ const closeContactModal = () => {
} }
// 编辑联系人 // 编辑联系人
const editContact = (contact: Contact) => { const editContactModal = (contact: Contact) => {
form.value = { ...contact } form.value = { ...contact }
newCcEmail.value = '' newCcEmail.value = ''
errors.value = { name: '', toEmail: '' } errors.value = { name: '', email: '' }
isEditing.value = true isEditing.value = true
showContactModal.value = true showContactModal.value = true
} }
...@@ -338,18 +537,18 @@ const editContact = (contact: Contact) => { ...@@ -338,18 +537,18 @@ const editContact = (contact: Contact) => {
// 验证表单 // 验证表单
const validateForm = (): boolean => { const validateForm = (): boolean => {
let isValid = true let isValid = true
errors.value = { name: '', toEmail: '' } errors.value = { name: '', email: '' }
if (!form.value.name?.trim()) { if (!form.value.name?.trim()) {
errors.value.name = '请输入联系人姓名' errors.value.name = '请输入联系人姓名'
isValid = false isValid = false
} }
if (!form.value.toEmail?.trim()) { if (!form.value.email?.trim()) {
errors.value.toEmail = '请输入收件人邮箱' errors.value.email = '请输入收件人邮箱'
isValid = false isValid = false
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.value.toEmail)) { } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(form.value.email)) {
errors.value.toEmail = '请输入有效的邮箱地址' errors.value.email = '请输入有效的邮箱地址'
isValid = false isValid = false
} }
...@@ -367,50 +566,74 @@ const addCcEmail = () => { ...@@ -367,50 +566,74 @@ const addCcEmail = () => {
} }
// 检查是否已存在 // 检查是否已存在
if (form.value.ccEmails?.includes(email)) { if (form.value.ccEmailList?.includes(email)) {
showError('该邮箱已在抄送人列表中') showError('该邮箱已在抄送人列表中')
return return
} }
form.value.ccEmails = [...(form.value.ccEmails || []), email] form.value.ccEmailList = [...(form.value.ccEmailList || []), email]
newCcEmail.value = '' newCcEmail.value = ''
} }
// 移除抄送人邮箱 // 移除抄送人邮箱
const removeCcEmail = (index: number) => { const removeCcEmail = (index: number) => {
if (form.value.ccEmails) { if (form.value.ccEmailList) {
form.value.ccEmails.splice(index, 1) form.value.ccEmailList.splice(index, 1)
} }
} }
// 保存联系人 - 使用api拦截器 // 保存联系人
const saveContact = async () => { const saveContact = async () => {
if (!validateForm()) return if (!validateForm()) return
try { try {
isSubmitting.value = true isSubmitting.value = true
const contactData = { const contactData = {
contactBizId: form.value.contactBizId || undefined,
type: form.value.type,
name: form.value.name, name: form.value.name,
title: form.value.title, companyName: form.value.companyName,
company: form.value.company, email: form.value.email,
toEmail: form.value.toEmail, ccEmailList: form.value.ccEmailList,
ccEmails: form.value.ccEmails, other: form.value.other,
otherInfo: form.value.otherInfo, appellation: form.value.appellation,
} }
if (isEditing.value && form.value.id) { if (isEditing.value && form.value.contactBizId) {
// 更新现有联系人 // 更新现有联系人
await api.put(`/contacts/${form.value.id}`, contactData) editContact({ ...contactData }).then((res) => {
if (res.code === 200) {
openModal({
title: '更新联系人成功',
message: '联系人已更新',
})
closeContactModal()
fetchContacts()
} else {
openModal({
title: '更新联系人失败',
message: res.msg || '保存联系人失败',
})
}
})
} else { } else {
// 添加新联系人 // 添加新联系人
await api.post('/contacts', contactData) await addContact(contactData).then((res) => {
if (res.code === 200) {
openModal({
title: '添加联系人成功',
message: '联系人已添加',
})
closeContactModal()
fetchContacts()
} else {
openModal({
title: '添加联系人失败',
message: res.msg || '保存联系人失败',
})
}
})
} }
// 保存成功,刷新列表并关闭模态框
showSuccess(isEditing.value ? '联系人已更新' : '联系人已添加')
closeContactModal()
fetchContacts()
} catch (error) { } catch (error) {
console.error('保存联系人失败:', error) console.error('保存联系人失败:', error)
showError(error instanceof Error ? error.message : '保存联系人失败,请稍后重试') showError(error instanceof Error ? error.message : '保存联系人失败,请稍后重试')
...@@ -420,19 +643,14 @@ const saveContact = async () => { ...@@ -420,19 +643,14 @@ const saveContact = async () => {
} }
// 删除联系人 - 使用api拦截器 // 删除联系人 - 使用api拦截器
const deleteContact = async (id: number) => { const deleteContactModal = async (contact: Contact) => {
if (!confirm('确定要删除这个联系人吗?此操作不可撤销。')) { form.value = contact
return if (form.value) {
} openModal({
triggerKey: 'deleteContactModal',
try { title: '删除联系人',
await api.delete(`/contacts/${id}`) message: `确定要删除联系人 ${form.value.name} 吗?`,
})
showSuccess('联系人已删除')
fetchContacts()
} catch (error) {
console.error('删除联系人失败:', error)
showError('删除联系人失败,请稍后重试')
} }
} }
......
...@@ -122,22 +122,14 @@ ...@@ -122,22 +122,14 @@
import { ref, computed, defineProps, defineEmits } from 'vue' import { ref, computed, defineProps, defineEmits } from 'vue'
import { Email } from '../types' import { Email } from '../types'
const props = defineProps({
emails: {
type: Array as () => Email[],
required: true,
},
})
const emits = defineEmits(['reuse-email'])
// 状态 // 状态
const emails = ref<Email[]>([])
const searchTerm = ref('') const searchTerm = ref('')
const filterStatus = ref('') const filterStatus = ref('')
// 计算属性 // 计算属性
const filteredEmails = computed(() => { const filteredEmails = computed(() => {
return props.emails return emails.value
.filter((email) => { .filter((email) => {
const matchesSearch = const matchesSearch =
email.subject.toLowerCase().includes(searchTerm.value.toLowerCase()) || email.subject.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
...@@ -164,9 +156,5 @@ const viewEmailDetail = (email: Email) => { ...@@ -164,9 +156,5 @@ const viewEmailDetail = (email: Email) => {
const reuseEmailContent = (email: Email) => { const reuseEmailContent = (email: Email) => {
// 触发复用邮件内容事件 // 触发复用邮件内容事件
emits('reuse-email', {
subject: email.subject,
content: email.content,
})
} }
</script> </script>
...@@ -27,31 +27,35 @@ ...@@ -27,31 +27,35 @@
/> />
</div> </div>
<div> <div>
<label class="block text-gray-700 mb-1 text-sm">SMTP服务器 *</label> <label class="block text-gray-700 mb-1 text-sm">发件人姓名</label>
<input <input
v-model="formData.smtpServer" v-model="formData.displayName"
type="text" type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="例如:smtp.example.com" placeholder="例如:XX公司"
/> />
</div> </div>
<div> <div>
<label class="block text-gray-700 mb-1 text-sm">SMTP端口 *</label> <label class="block text-gray-700 mb-1 text-sm">邮件服务商 *</label>
<input <select
v-model="formData.smtpPort" v-model="formData.providerBizId"
type="number"
class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="例如:587" >
/> <option
v-for="provider in providers"
:value="provider.providerBizId"
:key="provider.providerBizId"
>
{{ provider.providerName }}
</option>
</select>
</div> </div>
</div> </div>
<div class="mt-4 flex justify-end"> <div class="mt-4 flex justify-end">
<button <button
@click="saveSender" @click="saveSender"
class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-700 transition-colors" class="px-6 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-700 transition-colors"
:disabled=" :disabled="!formData.email || !formData.password || !formData.providerBizId"
!formData.email || !formData.password || !formData.smtpServer || !formData.smtpPort
"
> >
{{ editingSenderId ? '更新发件人' : '添加发件人' }} {{ editingSenderId ? '更新发件人' : '添加发件人' }}
</button> </button>
...@@ -74,12 +78,12 @@ ...@@ -74,12 +78,12 @@
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
> >
SMTP服务器 邮箱服务商
</th> </th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
> >
SMTP端口 发件人姓名
</th> </th>
<th <th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
...@@ -96,8 +100,13 @@ ...@@ -96,8 +100,13 @@
<tbody class="bg-white divide-y divide-gray-200"> <tbody class="bg-white divide-y divide-gray-200">
<tr v-for="sender in senders" :key="sender.id"> <tr v-for="sender in senders" :key="sender.id">
<td class="px-6 py-4 whitespace-nowrap">{{ sender.email }}</td> <td class="px-6 py-4 whitespace-nowrap">{{ sender.email }}</td>
<td class="px-6 py-4 whitespace-nowrap">{{ sender.smtpServer }}</td> <td class="px-6 py-4 whitespace-nowrap">
<td class="px-6 py-4 whitespace-nowrap">{{ sender.smtpPort }}</td> {{
providers.find((provider) => provider.providerBizId === sender.providerBizId)
?.providerName || '未知'
}}
</td>
<td class="px-6 py-4 whitespace-nowrap">{{ sender.displayName }}</td>
<td class="px-6 py-4 whitespace-nowrap"> <td class="px-6 py-4 whitespace-nowrap">
<span <span
class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800" class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800"
...@@ -109,7 +118,10 @@ ...@@ -109,7 +118,10 @@
<button @click="editSender(sender)" class="text-blue-600 hover:text-blue-900 mr-3"> <button @click="editSender(sender)" class="text-blue-600 hover:text-blue-900 mr-3">
编辑 编辑
</button> </button>
<button @click="deleteSender(sender.id)" class="text-red-600 hover:text-red-900"> <button
@click="deleteSender(sender.senderBizId)"
class="text-red-600 hover:text-red-900"
>
删除 删除
</button> </button>
</td> </td>
...@@ -122,31 +134,94 @@ ...@@ -122,31 +134,94 @@
<p>暂无发件人邮箱,请添加发件人</p> <p>暂无发件人邮箱,请添加发件人</p>
</div> </div>
</div> </div>
<CommonModal
v-model:visible="modalVisible"
:trigger-key="modalConfig.triggerKey"
:title="modalConfig.title"
type="confirm"
:message="modalConfig.message"
:show-cancel-button="modalConfig.showCancel"
@confirm="handleConfirm"
@cancel="handleCancel"
/>
</div> </div>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { ref, defineProps, defineEmits } from 'vue' import { ref, defineProps, defineEmits, onMounted } from 'vue'
import { Sender } from '../types' import {
getEmailSenderConfigList,
const props = defineProps({ getEmailProviderList,
senders: { deleteEmailSenderConfig,
type: Array as () => Sender[], addEmailSenderConfig,
required: true, editEmailSenderConfig,
}, } from '@/api/api'
import { Provider, Sender } from '@/types'
// 引入弹窗组件
import CommonModal from '@/components/CommonModal.vue'
// 弹窗提示信息对象
const modalVisible = ref(false)
const modalConfig = ref({
showCancel: false,
title: '操作确认',
message: '确定要执行此操作吗?',
triggerKey: 'templateModal',
}) })
const emits = defineEmits(['update-senders']) const openModal = (
config: { triggerKey?: string; showCancel?: boolean; title?: string; message?: string } = {},
) => {
modalConfig.value = {
showCancel: config.showCancel ?? false,
title: config.title ?? '操作确认',
message: config.message ?? '确定要执行此操作吗?',
triggerKey: config.triggerKey ?? modalConfig.value.triggerKey,
}
modalVisible.value = true
}
const handleConfirm = (triggerKey: string) => {
modalVisible.value = false
console.log('用户确认操作', triggerKey)
if (triggerKey === 'deleteModal') {
deleteEmailSenderConfig(editingSenderId.value).then((res) => {
if (res.code === 200) {
openModal({
title: '删除确认',
message: '发件人删除成功',
triggerKey: '',
})
getSenders()
resetForm()
} else {
openModal({
title: '删除失败',
message: res.msg || '删除失败',
triggerKey: '',
})
return
}
})
}
}
const handleCancel = (triggerKey: string) => {
modalVisible.value = false
console.log('用户取消操作', triggerKey)
}
// 状态 // 状态
const senders = ref<Sender[]>([...props.senders]) const senders = ref<Sender[]>([])
const editingSenderId = ref('') const editingSenderId = ref('')
const formData = ref<Partial<Sender>>({ const formData = ref<Partial<Sender>>({
email: '', email: '',
password: '', password: '',
smtpServer: '', providerBizId: '',
smtpPort: '', displayName: '',
active: 1,
}) })
// 服务商列表
const providers = ref<Provider[]>([])
// 方法 // 方法
const resetForm = () => { const resetForm = () => {
...@@ -154,58 +229,108 @@ const resetForm = () => { ...@@ -154,58 +229,108 @@ const resetForm = () => {
formData.value = { formData.value = {
email: '', email: '',
password: '', password: '',
smtpServer: '', providerBizId: '',
smtpPort: '', displayName: '',
active: 1,
} }
} }
const getProviders = () => {
getEmailProviderList({
providerName: '',
pageNo: 1,
pageSize: 100,
}).then((res) => {
providers.value = res.data?.records || []
})
}
const getSenders = () => {
getEmailSenderConfigList({
emailSenderConfigName: '',
pageNo: 1,
pageSize: 100,
}).then((res) => {
senders.value = res.data?.records || []
})
}
const saveSender = () => { const saveSender = () => {
if ( console.log(formData.value)
!formData.value.email || if (!formData.value.email || !formData.value.password || !formData.value.providerBizId) {
!formData.value.password || openModal({
!formData.value.smtpServer || title: '添加失败',
!formData.value.smtpPort message: '请填写完整邮箱、密码和服务商',
) })
return return
}
if (editingSenderId.value) { if (editingSenderId.value) {
// 更新现有发件人 // 更新现有发件人
const index = senders.value.findIndex((s) => s.id === editingSenderId.value) editEmailSenderConfig({
if (index > -1) { senderBizId: editingSenderId.value,
senders.value[index] = { ...formData.value,
...senders.value[index], }).then((res) => {
...formData.value, if (res.code === 200) {
} as Sender getSenders()
emits('update-senders', [...senders.value]) openModal({
alert('发件人更新成功') title: '更新确认',
} message: '发件人更新成功',
})
} else {
openModal({
title: '更新失败',
message: res.msg || '更新失败',
})
}
})
} else { } else {
// 添加新发件人 // 添加新发件人
const newSender: Sender = { const newSender: Sender = {
id: Date.now().toString(),
email: formData.value.email || '', email: formData.value.email || '',
password: formData.value.password || '', password: formData.value.password || '',
smtpServer: formData.value.smtpServer || '', providerBizId: formData.value.providerBizId || '',
smtpPort: formData.value.smtpPort || '', displayName: formData.value.displayName || '',
active: formData.value.active ?? 1,
} }
addEmailSenderConfig(newSender).then((res) => {
senders.value.push(newSender) // 补充异常处理
emits('update-senders', [...senders.value]) if (res.code === 200) {
alert('发件人添加成功') getSenders()
openModal({
title: '添加确认',
message: '发件人添加成功',
})
} else {
openModal({
title: '添加失败',
message: res.msg || '添加失败',
})
}
})
} }
resetForm() resetForm()
} }
const editSender = (sender: Sender) => { const editSender = (sender: Sender) => {
editingSenderId.value = sender.id console.log('编辑发件人', sender)
editingSenderId.value = sender.senderBizId || ''
formData.value = { ...sender } formData.value = { ...sender }
} }
const deleteSender = (id: string) => { const deleteSender = (id: string) => {
if (confirm('确定要删除这个发件人吗?')) { console.log('删除发件人', id)
senders.value = senders.value.filter((sender) => sender.id !== id) editingSenderId.value = id
emits('update-senders', [...senders.value]) openModal({
} showCancel: true,
title: '删除确认',
message: '确定要删除这个发件人吗?',
triggerKey: 'deleteModal',
})
} }
onMounted(() => {
// 初始化服务商列表
getProviders()
// 初始化发件人列表
getSenders()
})
</script> </script>
...@@ -340,22 +340,10 @@ const handleCancel = (triggerKey: string) => { ...@@ -340,22 +340,10 @@ const handleCancel = (triggerKey: string) => {
console.log('用户取消操作', triggerKey) console.log('用户取消操作', triggerKey)
} }
const props = defineProps({ const variables = ref<Variable[]>([])
variables: { const variableTemplates = ref<VariableTemplate[]>([])
type: Array as () => Variable[],
required: true,
},
variableTemplates: {
type: Array as () => VariableTemplate[],
required: true,
},
})
const emits = defineEmits(['update-variables', 'update-variable-templates'])
// 状态 // 状态
const variables = ref<Variable[]>([...props.variables])
const variableTemplates = ref<VariableTemplate[]>([...props.variableTemplates])
const variablePrefix = '{{' const variablePrefix = '{{'
const variableNextfix = '}}' const variableNextfix = '}}'
...@@ -405,13 +393,6 @@ const saveVariable = () => { ...@@ -405,13 +393,6 @@ const saveVariable = () => {
description: variableForm.value.description, description: variableForm.value.description,
}).then(() => { }).then(() => {
// 更新本地变量列表 // 更新本地变量列表
const index = variables.value.findIndex((v) => v.id === editingVariableId.value)
if (index > -1) {
variables.value[index] = {
...variables.value[index],
...variableForm.value,
}
}
fetchVariables() fetchVariables()
openModal({ openModal({
title: '成功', title: '成功',
...@@ -638,7 +619,6 @@ const saveVariableTemplate = () => { ...@@ -638,7 +619,6 @@ const saveVariableTemplate = () => {
.then(() => { .then(() => {
// 刷新变量模版列表 // 刷新变量模版列表
fetchVariableTemplates() fetchVariableTemplates()
emits('update-variable-templates', [...variableTemplates.value])
}) })
.catch((error) => { .catch((error) => {
console.error('更新变量模版失败:', error) console.error('更新变量模版失败:', error)
...@@ -664,7 +644,6 @@ const saveVariableTemplate = () => { ...@@ -664,7 +644,6 @@ const saveVariableTemplate = () => {
.then(() => { .then(() => {
// 刷新变量模版列表 // 刷新变量模版列表
fetchVariableTemplates() fetchVariableTemplates()
emits('update-variable-templates', [...variableTemplates.value])
}) })
.catch((error) => { .catch((error) => {
console.error('创建变量模版失败:', error) console.error('创建变量模版失败:', error)
...@@ -692,7 +671,6 @@ const deleteVariableTemplate = (id: string, type?: string) => { ...@@ -692,7 +671,6 @@ const deleteVariableTemplate = (id: string, type?: string) => {
.then(() => { .then(() => {
// 刷新变量模版列表 // 刷新变量模版列表
fetchVariableTemplates() fetchVariableTemplates()
emits('update-variable-templates', [...variableTemplates.value])
}) })
.catch((error) => { .catch((error) => {
console.error('删除变量模版失败:', error) console.error('删除变量模版失败:', error)
...@@ -710,7 +688,7 @@ const deleteVariableTemplate = (id: string, type?: string) => { ...@@ -710,7 +688,7 @@ const deleteVariableTemplate = (id: string, type?: string) => {
}) })
} else { } else {
openModal({ openModal({
showClose: true, showCancel: true,
title: '确认删除', title: '确认删除',
message: '确定删除此变量模版吗?', message: '确定删除此变量模版吗?',
triggerKey: 'templeteDelete', triggerKey: 'templeteDelete',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment