Commit 19046586 by Sweet Zhang

调通发邮件

parent 70b9e0d4
......@@ -7,6 +7,8 @@ import type {
Sender,
Variable,
VariableTemplate,
SubTask,
EmailTask,
} from '@/types/index'
import type { ApiResponse } from '@/utils/request'
......@@ -129,6 +131,10 @@ export const importContactApi = {
getEmailContactImportList: (params: EditContactImport): Promise<ApiResponse> => {
return request.post('/emailContactImport/page', params)
},
// 详情会话信息前端展示收件人,抄送人
getEmailContactImportDetail: (id: string): Promise<ApiResponse> => {
return request.get('/emailContactImport/detail/sessionId?sessionId=' + id)
},
}
/**
* 发送邮件
......@@ -142,4 +148,22 @@ export const sendEmailApi = {
testSendEmail: (data: SendEmail): Promise<ApiResponse> => {
return request.post('/email/test/send', data)
},
// 发送任务列表查询
getEmailTaskList: (params: SubTask): Promise<ApiResponse> => {
return request.post('/emailTaskRecipients/page', params)
},
// 主线任务列表查询
getEmailTaskMainList: (params: EmailTask): Promise<ApiResponse> => {
return request.post('/emailTask/page', params)
},
}
/**
* 文件服务接口
*/
export const uploadApi = {
// 上传文件
uploadFile: (data: FormData): Promise<ApiResponse> => {
return request.post('/oss/upload', data)
},
}
......@@ -9,7 +9,7 @@ export interface Pagination<T> {
}
// 联系人类型
export interface Contact extends Pagination<Contact> {
export interface Contact<T> extends Pagination<Contact> {
contactBizId?: string
name?: string
type?: string
......@@ -50,29 +50,6 @@ export interface VariableTemplate extends Pagination<VariableTemplate> {
variableNameEns?: string[]
}
// 邮件类型
export interface Email extends Pagination<Email> {
id?: string
sender?: string
to?: string[]
cc?: string[]
subject?: string
content?: string
sendTime?: string
status?: 'sent' | 'scheduled' | 'draft' | 'failed'
attachments?: { name: string }[]
}
// 邮件表单类型
export interface EmailForm extends Pagination<EmailForm> {
to: string
cc: string
subject: string
content: string
scheduleSend: boolean
sendTime: string
}
// 忘记密码表单类型
export interface ForgotPasswordForm {
email: string
......@@ -81,18 +58,16 @@ export interface ForgotPasswordForm {
}
// 导入记录类型
export interface ImportRecord {
id: string
to: string
cc: string
createdAt: string
updatedAt: string
export interface ImportRecord extends Contact<ImportRecord> {
sessionId?: string
receiveEmailList?: string[]
ccEmailList?: string[]
}
// 选择联系人时,调用接口,获取sessionId
export interface ContactSessionId {
sessionId?: string
apiEmailContactDtoList?: Contact[]
apiEmailContactDtoList?: Contact<unknown>[]
}
// 编辑-邮件联系人导入信息
......@@ -104,11 +79,32 @@ export interface EditContactImport extends Pagination<EditContactImport> {
// 发送邮件
export interface SendEmail {
senderBizId?: string
sendEmail?: string
subject?: string
content?: string
scheduleTime?: string
attachmentPath?: string
variableGroupBizId?: string
sessionId?: string
recipientEmailList?: string[]
ccEmailList?: string[]
bccEmailList?: string[]
receiveEmailList?: string[]
}
export interface EmailForm {
senderBizId?: string
sendEmail?: string
variableGroupBizId?: string
receiveEmail?: string
ccEmailList?: string[]
subject?: string
body?: string
content?: string
attachmentPath?: string
sessionId?: string
ccEmails?: string
scheduleSend?: boolean
scheduleTime?: string
}
//邮件服务商类型
export interface EmailProvider extends Pagination<EmailProvider> {
......@@ -120,3 +116,22 @@ export interface EmailProvider extends Pagination<EmailProvider> {
active?: number
description?: string
}
// 发送任务列表查询参数
export interface SubTask extends Pagination<SubTask> {
taskBizId?: string
receiveEmail?: string
status?: string
}
// 主线任务列表查询参数
export interface EmailTask extends Pagination<EmailTask> {
queryContent?: string
status?: string
taskBizId?: string
taskName?: string
senderBizId?: string
sendEmail?: string
receiveEmails?: string
subject?: string
scheduleTime?: string
sendTime?: string
}
......@@ -17,7 +17,7 @@ const request = axios.create({
'Content-Type': 'application/json',
// Authorization: 'Bearer ' + localStorage.getItem('authToken'),
Authorization:
'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyXzEwMDEiLCJyb2xlcyI6W10sImlhdCI6MTc1ODc4NTY3NCwiZXhwIjoxNzU4ODcyMDc0fQ.tjTO6vdpwLpNjVa1DhxRBdpjZsdhbx6g1TdtpAm7BZBRMwanM_ci7dsnbc8FNXpyfSb-ifXW7ccxwyQbtCaKiQ',
'Bearer eyJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJ1c2VyXzEwMDEiLCJyb2xlcyI6W10sImlhdCI6MTc1ODg3MjMyOSwiZXhwIjoxNzU4OTU4NzI5fQ.McyflIoI_ltve_uy2-mZTjOfxYfBGNMEuOoIVfeEtXdAuoycggGErq8yU3mc15npsIWJy2a8zJ5cNpx_NVtGIw',
},
})
......
......@@ -26,7 +26,7 @@
</select>
<!-- 当选择模版有值时,显示导入数据按钮 -->
<button
@click="showImportContacts = true"
@click="((showImportContacts = true), (importSource = 1))"
class="bg-blue-50 hover:bg-blue-100 text-blue-600 px-4 py-2 rounded-md border border-blue-200 transition-colors flex items-center"
v-if="selectedVariableTemplate"
>
......@@ -38,18 +38,32 @@
<label class="block text-gray-700 mb-2 font-medium">收件人</label>
<div class="flex flex-col sm:flex-row gap-2">
<!-- 多个邮箱用tag的样式展示 -->
<input
v-model="emailForm.to"
type="text"
<div
v-if="emailForm.receiveEmail"
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="输入收件人邮箱,多个邮箱用逗号分隔"
:disabled="!!selectedVariableTemplate"
/>
>
<span
v-for="email in emailForm.receiveEmail.split(',').filter((e) => e.trim())"
:key="email"
class="inline-flex items-center px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium"
>
{{ email.trim() }}
</span>
</div>
<div
v-else
class="flex-1 px-3 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-500 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
>
暂无收件人
</div>
<!-- 当选择模版有值时,不显示选择联系人按钮 -->
<button
@click="showContactSelector = true"
@click="((showContactSelector = true), (importSource = 0))"
class="bg-blue-50 hover:bg-blue-100 text-blue-600 px-4 py-2 rounded-md border border-blue-200 transition-colors flex items-center"
v-if="!selectedVariableTemplate"
v-if="
!selectedVariableTemplate ||
selectedVariableTemplate?.variableGroupBizId == 'email_variable_group_a2Z0lJLQlCuO81ZE'
"
>
<i class="fas fa-address-book mr-1"></i> 选择联系人
</button>
......@@ -65,13 +79,30 @@
<div class="mb-4">
<label class="block text-gray-700 mb-2 font-medium">抄送人</label>
<input
v-model="emailForm.cc"
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="输入抄送人邮箱,多个邮箱用逗号分隔"
:disabled="!!selectedVariableTemplate"
/>
<div
v-if="emailForm.ccEmails"
class="flex flex-wrap gap-2 p-3 border border-gray-300 rounded-md bg-gray-50 min-h-[42px]"
>
<div
v-for="emailGroup in emailForm.ccEmails.split(',').filter((e) => e.trim())"
:key="emailGroup"
class="inline-flex flex-wrap gap-1 px-3 py-1 bg-blue-100 text-blue-800 rounded-full text-sm font-medium"
>
<span
v-for="email in emailGroup.split(';').filter((e) => e.trim())"
:key="email"
class="inline-flex items-center px-2 py-0.5 bg-orange-50 text-yellow-800 rounded-md text-xs"
>
{{ email.trim() }}
</span>
</div>
</div>
<div
v-else
class="flex items-center p-3 border border-gray-300 rounded-md bg-gray-50 text-gray-500 min-h-[42px]"
>
暂无抄送人
</div>
</div>
<div class="mb-4">
......@@ -93,6 +124,19 @@
>
<i class="fas fa-plus"></i> 插入字段
</button>
<!-- 点击插入姓名,正文插入 {{姓名}} -->
<button
@click="insertContent('name')"
class="text-sm bg-orange-50 hover:bg-orange-100 text-orange-600 px-3 py-1 rounded border border-orange-200 transition-colors"
>
<i class="fas fa-plus"></i> 插入姓名
</button>
<button
@click="insertContent('appellation')"
class="text-sm bg-orange-50 hover:bg-orange-100 text-orange-600 px-3 py-1 rounded border border-orange-200 transition-colors"
>
<i class="fas fa-plus"></i> 插入称谓
</button>
</div>
<textarea
v-model="emailForm.content"
......@@ -142,7 +186,7 @@
<div v-if="emailForm.scheduleSend" class="flex items-center">
<input
type="datetime-local"
v-model="emailForm.sendTime"
v-model="emailForm.scheduleTime"
class="px-3 py-1 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
/>
</div>
......@@ -258,12 +302,12 @@ import type {
Contact,
Variable,
VariableTemplate,
Email,
EmailForm,
SendEmail,
ImportRecord,
EmailForm,
} from '../types'
// 引入api接口,获取联系人列表、发件人列表、变量模版列表
import { senderApi, variableGroupApi, contactApi, getContactList } from '../api/api'
import { senderApi, variableGroupApi, contactApi, sendEmailApi, importContactApi } from '../api/api'
// 引入弹窗组件
import CommonModal from '@/components/CommonModal.vue'
// 弹窗提示信息对象
......@@ -328,21 +372,27 @@ const getGroups = () => {
})
}
const importSource = ref(0)
const senders = ref<Sender[]>([])
const contacts = ref<Contact[]>([])
const contacts = ref<Contact<unknown>[]>([])
const groups = ref<VariableTemplate[]>([])
const variables = ref<Variable[]>([])
// 状态
const currentSender = ref<Sender | null>(senders.value.length > 0 ? senders.value[0] : null)
const emailForm = ref<EmailForm>({
to: '',
cc: '',
senderBizId: '',
sendEmail: '',
variableGroupBizId: '',
receiveEmail: '',
ccEmailList: [],
subject: '',
content: '',
scheduleSend: false,
sendTime: '',
attachmentPath: '',
sessionId: '',
ccEmails: '',
})
const selectedVariableTemplate = ref<VariableTemplate | null>(null)
const attachments = ref<File[]>([])
const showContactSelector = ref(false)
......@@ -354,12 +404,12 @@ const importRecords = ref<ImportRecord[]>([])
// 监听收件人变化,自动匹配抄送人
watch(
() => emailForm.value.to,
() => emailForm.value.receiveEmail,
(newTo) => {
if (newTo) {
const matchedRecord = importRecords.value.find((record) => record.to === newTo)
if (matchedRecord && matchedRecord.cc) {
emailForm.value.cc = matchedRecord.cc
const matchedRecord = importRecords.value.find((record) => record.receiveEmail === newTo)
if (matchedRecord && matchedRecord.ccEmailList) {
emailForm.value.ccEmailList = matchedRecord.ccEmailList
}
}
},
......@@ -410,7 +460,7 @@ const insertVariable = (variable: Variable) => {
if (textarea && document.activeElement === textarea) {
const start = textarea.selectionStart
const end = textarea.selectionEnd
const currentValue = emailForm.value.content
const currentValue = emailForm.value.content || ''
emailForm.value.content =
currentValue.substring(0, start) + variableText + currentValue.substring(end)
......@@ -427,34 +477,31 @@ const insertVariable = (variable: Variable) => {
showVariableSelector.value = false
}
const confirmContactSelection = (selected: { to: string; cc: string }) => {
emailForm.value.to = selected.to
emailForm.value.cc = selected.cc
showContactSelector.value = false
// 保存导入记录
saveImportRecord(selected.to, selected.cc)
const insertContent = (i: string) => {
emailForm.value.content += `${variablePrefix}${i}${variableNextfix}`
}
// 保存导入记录
const saveImportRecord = (to: string, cc: string) => {
const existingRecord = importRecords.value.find((record) => record.to === to)
if (existingRecord) {
// 更新现有记录
existingRecord.cc = cc
existingRecord.updatedAt = new Date().toISOString()
} else {
// 添加新记录
const newRecord: ImportRecord = {
id: Date.now().toString(),
to,
cc,
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
}
importRecords.value.push(newRecord)
const confirmContactSelection = (selected) => {
emailForm.value.ccEmailList = selected.cc
const params = {
sessionId: '',
apiEmailContactDtoList: selected,
}
showContactSelector.value = false
importContactApi.getEmailContactSessionId(params).then((res) => {
if (res.code === 200) {
emailForm.value.sessionId = res.data?.sessionId || ''
if (res.data?.sessionId) {
importContactApi.getEmailContactImportDetail(res.data?.sessionId || '').then((res) => {
if (res.code === 200) {
emailForm.value.receiveEmail = res.data?.receiveEmails || ''
emailForm.value.ccEmails = res.data?.ccEmails || ''
}
})
}
getImportedContacts(emailForm.value.sessionId || '')
}
})
}
// 更新导入记录
......@@ -478,7 +525,6 @@ const handleImportContacts = (event: { file: File; content: string }) => {
lines.forEach((line) => {
const [to, cc] = line.split(',')
if (to && to.includes('@')) {
saveImportRecord(to.trim(), cc ? cc.trim() : '')
}
})
}
......@@ -487,8 +533,8 @@ const saveAsDraft = () => {
alert('请添加并选择发件人')
return
}
const draft: Email = {
id: Date.now().toString(),
const draft: SendEmail = {
senderBizId: currentSender.value.senderBizId || '',
sender: currentSender.value.email || '',
to: emailForm.value.to ? emailForm.value.to.split(',') : [],
cc: emailForm.value.cc ? emailForm.value.cc.split(',') : [],
......@@ -503,6 +549,21 @@ const saveAsDraft = () => {
const sendEmail = () => {
showPreview.value = true
const params = {
...emailForm.value,
variableGroupBizId: selectedVariableTemplate.value?.variableGroupBizId || '',
senderBizId: currentSender.value?.senderBizId,
sendEmail: currentSender.value?.email || '',
}
console.log(params)
// 确认发送邮件
sendEmailApi.sendEmail(params).then((res) => {
if (res.code === 200) {
alert('邮件发送成功')
} else {
alert('邮件发送失败')
}
})
}
const confirmSendEmail = () => {
......@@ -537,4 +598,30 @@ const confirmSendEmail = () => {
showPreview.value = false
alert(emailForm.value.scheduleSend ? '邮件已安排定时发送' : '邮件发送成功')
}
// 通过sessionId获取导入的联系人
const getImportedContacts = (sessionId: string) => {
const params = {
sessionId: sessionId || '',
source: importSource.value,
}
importContactApi.getEmailContactImportList(params).then((res) => {
if (res.code === 200) {
console.log('导入的联系人:', res.data)
importRecords.value = res.data.records || []
}
})
}
// 编辑数据
const editImportRecord = (record: ImportRecord) => {
console.log('编辑导入记录:', record)
// 这里可以添加编辑逻辑,例如打开编辑弹窗
importContactApi.editEmailContactImport(record).then((res) => {
if (res.code === 200) {
console.log('编辑导入记录成功:', res.data)
updateImportRecord(res.data)
}
})
}
</script>
......@@ -99,12 +99,6 @@ const confirmSelection = () => {
const selected = props.contacts.filter((contact) =>
selectedContacts.value.includes(contact.contactBizId || ''),
)
const to = selected.map((contact) => contact.email).join(',')
const cc = selected
.map((contact) => contact.ccEmailList?.join(',') || '')
.filter((email) => email)
.join(',')
emits('confirm-selection', { to, cc })
emits('confirm-selection', selected)
}
</script>
......@@ -65,9 +65,9 @@
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="email in filteredEmails" :key="email.id">
<td class="px-6 py-4 whitespace-nowrap">{{ email.sender }}</td>
<td class="px-6 py-4 whitespace-nowrap max-w-xs truncate">{{ email.to }}</td>
<tr v-for="email in emails" :key="email.taskBizId">
<td class="px-6 py-4 whitespace-nowrap">{{ email.sendEmail }}</td>
<td class="px-6 py-4 whitespace-nowrap max-w-xs truncate">{{ email.receiveEmails }}</td>
<td class="px-6 py-4 max-w-xs truncate">{{ email.subject }}</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ formatDate(email.sendTime) }}
......@@ -111,7 +111,7 @@
</tbody>
</table>
</div>
<div v-if="filteredEmails.length === 0" class="p-8 text-center text-gray-500">
<div v-if="emails.length === 0" class="p-8 text-center text-gray-500">
<i class="fas fa-history text-4xl mb-3 opacity-30"></i>
<p>暂无邮件发送记录</p>
</div>
......@@ -119,27 +119,17 @@
</template>
<script setup lang="ts">
import { ref, computed, defineProps, defineEmits } from 'vue'
import type { Email } from '@/types/index'
import { ref, computed, onMounted } from 'vue'
import type { EmailTask } from '@/types/index'
import { sendEmailApi } from '@/api/api'
// 状态
const emails = ref<Email[]>([])
const emails = ref<EmailTask[]>([])
const searchTerm = ref('')
const filterStatus = ref('')
// 计算属性
const filteredEmails = computed(() => {
return emails.value
.filter((email) => {
const matchesSearch =
email.subject.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
email.to.toLowerCase().includes(searchTerm.value.toLowerCase())
const matchesStatus = !filterStatus.value || email.status === filterStatus.value
return matchesSearch && matchesStatus
})
.sort((a, b) => new Date(b.sendTime).getTime() - new Date(a.sendTime).getTime())
onMounted(() => {
getEmailTaskMainList()
})
// 方法
......@@ -148,7 +138,7 @@ const formatDate = (dateString: string) => {
return date.toLocaleString()
}
const viewEmailDetail = (email: Email) => {
const viewEmailDetail = (email: EmailTask) => {
// 显示邮件详情
alert(
`邮件主题: ${email.subject || '无'}\n收件人: ${email.to || '无'}\n发送时间: ${formatDate(email.sendTime || '')}`,
......@@ -156,7 +146,19 @@ const viewEmailDetail = (email: Email) => {
// 实际项目中可以打开详情弹窗
}
const reuseEmailContent = (email: Email) => {
const reuseEmailContent = (email: EmailTask) => {
// 触发复用邮件内容事件
}
// 发送任务列表查询
const getEmailTaskMainList = async () => {
const params: EmailTask = {
pageNum: 1,
pageSize: 100,
}
const res = await sendEmailApi.getEmailTaskMainList(params)
if (res.code === 200) {
emails.value = res.data.records || []
}
}
</script>
......@@ -30,73 +30,127 @@
<p>未找到匹配的导入记录</p>
</div>
<div v-else class="space-y-3">
<div
v-for="record in filteredRecords"
:key="record.id"
class="border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow"
>
<div class="flex justify-between items-start mb-3">
<div class="flex-1">
<div class="font-medium text-gray-900">收件人: {{ record.to }}</div>
<div class="text-sm text-gray-600 mt-1">抄送人: {{ record.cc || '无' }}</div>
<div class="text-xs text-gray-400 mt-2">
创建时间: {{ formatDate(record.createdAt) }}
</div>
</div>
<div class="flex gap-2">
<button
@click="editRecord(record)"
class="px-3 py-1 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 transition-colors text-sm"
>
编辑
</button>
<button
@click="deleteRecord(record.id)"
class="px-3 py-1 bg-red-100 text-red-700 rounded-md hover:bg-red-200 transition-colors text-sm"
<div v-else class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
删除
</button>
</div>
</div>
<div v-if="editingRecordId === record.id" class="mt-3 p-3 bg-gray-50 rounded-md">
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">收件人</label>
<input
v-model="editingRecord.to"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="收件人邮箱"
/>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-1">抄送人</label>
<input
v-model="editingRecord.cc"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="抄送人邮箱,多个用逗号分隔"
/>
</div>
</div>
<div class="flex justify-end gap-2 mt-3">
<button
@click="cancelEdit"
class="px-3 py-1 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors text-sm"
收件人
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
取消
</button>
<button
@click="saveEdit"
class="px-3 py-1 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors text-sm"
抄送人
</th>
<th
class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider"
>
保存
</button>
</div>
</div>
</div>
操作
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr
v-for="record in filteredRecords"
:key="record.importBizId"
class="hover:bg-gray-50"
>
<td class="px-6 py-4 whitespace-nowrap">
<div v-if="editingRecordId === record.importBizId" class="w-full">
<input
v-model="editingRecord.receiveEmail"
type="text"
class="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="收件人邮箱"
/>
</div>
<div v-else class="text-sm text-gray-900">
{{ record.receiveEmail || '无' }}
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div v-if="editingRecordId === record.importBizId" class="w-full">
<!-- 抄送人tag输入区域 -->
<div class="flex flex-wrap gap-1 mb-2">
<span
v-for="(tag, index) in ccTags"
:key="index"
class="inline-flex items-center px-2 py-1 bg-blue-100 text-blue-800 text-xs rounded-full"
>
{{ tag }}
<button
@click="removeCcTag(index)"
class="ml-1 text-blue-600 hover:text-blue-800"
>
×
</button>
</span>
</div>
<!-- 抄送人输入框 -->
<div class="flex gap-2">
<input
v-model="newCcTag"
type="text"
@keydown="handleCcInputKeydown"
class="flex-1 px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
placeholder="输入抄送人邮箱后按回车"
/>
<button
@click="addCcTag"
class="px-3 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors text-sm"
>
添加
</button>
</div>
</div>
<div v-else class="text-sm text-gray-900">
<div v-if="record.ccEmail" class="flex flex-wrap gap-1">
<span
v-for="(tag, index) in parseCcTags(record.ccEmail)"
:key="index"
class="inline-flex items-center px-2 py-1 bg-gray-100 text-gray-800 text-xs rounded-full"
>
{{ tag }}
</span>
</div>
<span v-else></span>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
<div v-if="editingRecordId === record.id" class="flex gap-2">
<button
@click="cancelEdit"
class="px-3 py-1 border border-gray-300 rounded-md hover:bg-gray-50 transition-colors text-sm"
>
取消
</button>
<button
@click="saveEdit"
class="px-3 py-1 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors text-sm"
>
保存
</button>
</div>
<div v-else class="flex gap-2">
<button
@click="editRecord(record)"
class="px-3 py-1 bg-blue-100 text-blue-700 rounded-md hover:bg-blue-200 transition-colors text-sm"
>
编辑
</button>
<button
@click="deleteRecord(record.id)"
class="px-3 py-1 bg-red-100 text-red-700 rounded-md hover:bg-red-200 transition-colors text-sm"
>
删除
</button>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</div>
......@@ -128,40 +182,91 @@ const emits = defineEmits(['update-record', 'delete-record', 'close'])
const searchTerm = ref('')
const editingRecordId = ref<string | null>(null)
const editingRecord = ref<Partial<ImportRecord>>({})
const ccTags = ref<string[]>([])
const newCcTag = ref('')
const filteredRecords = computed(() => {
if (!searchTerm.value) return props.records
return props.records.filter(
(record) =>
record.to.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
record.cc.toLowerCase().includes(searchTerm.value.toLowerCase()),
record.receiveEmail?.toLowerCase().includes(searchTerm.value.toLowerCase()) ||
record.ccEmail?.toLowerCase().includes(searchTerm.value.toLowerCase()),
)
})
// 将分号分隔的字符串转换为数组
const parseCcTags = (ccString: string) => {
if (!ccString) return []
return ccString
.split(';')
.filter((tag) => tag.trim())
.map((tag) => tag.trim())
}
// 将数组转换为分号分隔的字符串
const joinCcTags = (tags: string[]) => {
return tags.join(';')
}
// 添加新的抄送人tag
const addCcTag = () => {
if (newCcTag.value.trim()) {
ccTags.value.push(newCcTag.value.trim())
newCcTag.value = ''
updateEditingRecordCc()
}
}
// 删除抄送人tag
const removeCcTag = (index: number) => {
ccTags.value.splice(index, 1)
updateEditingRecordCc()
}
// 更新编辑记录中的抄送人字段
const updateEditingRecordCc = () => {
editingRecord.value.ccEmail = joinCcTags(ccTags.value)
}
// 处理输入框回车事件
const handleCcInputKeydown = (event: KeyboardEvent) => {
if (event.key === 'Enter') {
event.preventDefault()
addCcTag()
}
}
const formatDate = (dateString: string) => {
return new Date(dateString).toLocaleString('zh-CN')
}
const editRecord = (record: ImportRecord) => {
editingRecordId.value = record.id
editingRecordId.value = record.importBizId
editingRecord.value = { ...record }
// 初始化抄送人tag数组
ccTags.value = parseCcTags(record.ccEmail || '')
newCcTag.value = ''
}
const cancelEdit = () => {
editingRecordId.value = null
editingRecord.value = {}
ccTags.value = []
newCcTag.value = ''
}
const saveEdit = () => {
if (editingRecordId.value && editingRecord.value.to) {
if (editingRecordId.value && editingRecord.value.receiveEmail) {
emits('update-record', {
id: editingRecordId.value,
to: editingRecord.value.to,
cc: editingRecord.value.cc || '',
updatedAt: new Date().toISOString(),
importBizId: editingRecordId.value,
receiveEmail: editingRecord.value.receiveEmail,
ccEmail: editingRecord.value.ccEmail || '',
updateTime: new Date().toISOString(),
})
editingRecordId.value = null
editingRecord.value = {}
ccTags.value = []
newCcTag.value = ''
}
}
......
......@@ -148,7 +148,7 @@
</template>
<script setup lang="ts">
import { ref, defineProps, defineEmits, onMounted } from 'vue'
import { ref, onMounted } from 'vue'
import { emailProviderApi, senderApi, getEmailSenderConfigList } from '@/api/api'
import type { Sender } from '@/types/index'
// 引入弹窗组件
......
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