Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
frontend-yd-email
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Sweet Zhang
frontend-yd-email
Commits
cde5c983
Commit
cde5c983
authored
Sep 29, 2025
by
Sweet Zhang
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
增加搜索条件
parent
0fd0cb01
Show whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
249 additions
and
36 deletions
+249
-36
src/components/EmailDetailModal.vue
+1
-1
src/components/FileUploadComponent.vue
+5
-4
src/views/ComposeEmail.vue
+66
-14
src/views/ContactManagement.vue
+44
-9
src/views/EmailManagement.vue
+20
-1
src/views/ImportDialog.vue
+6
-2
src/views/SenderManagement.vue
+57
-4
src/views/VariableManagement.vue
+50
-1
No files found.
src/components/EmailDetailModal.vue
View file @
cde5c983
...
...
@@ -91,7 +91,7 @@
class=
"px-3 py-1 inline-flex text-xs leading-5 font-semibold rounded-full"
:class=
"getStatusClass(record.status)"
>
{{
getStatusLabel
(
record
.
status
)
}}
{{
record
.
statusLabel
||
'未知状态'
}}
</span>
</td>
<td
class=
"px-6 py-4 whitespace-nowrap text-sm text-gray-500"
>
...
...
src/components/FileUploadComponent.vue
View file @
cde5c983
...
...
@@ -241,13 +241,14 @@ const formatStatus = (status: string): string => {
// 上传所有文件方法
const
handleUploadAll
=
async
()
=>
{
try
{
const
results
=
await
uploadAllFiles
()
if
(
results
[
0
].
data
.
code
===
200
)
{
console
.
log
(
'上传成功:'
,
results
)
emit
(
'success'
,
results
)
return
results
}
catch
(
error
)
{
emit
(
'error'
,
error
as
Error
)
throw
error
}
else
{
console
.
log
(
'上传失败:'
,
results
)
emit
(
'error'
,
results
[
0
].
data
.
msg
as
Error
)
}
}
</
script
>
...
...
src/views/ComposeEmail.vue
View file @
cde5c983
<
template
>
<div
class=
"bg-white rounded-lg shadow-md p-6"
>
<div
class=
"bg-white rounded-lg shadow-md p-6"
v-loading=
"loading"
>
<div
class=
"mb-4"
>
<label
class=
"block text-gray-700 mb-2 font-medium"
>
发件人
</label>
<select
...
...
@@ -190,7 +190,7 @@
发送
</button>
</div>
<!--
导入记录
管理弹窗 -->
<!--
编辑数据
管理弹窗 -->
<ImportRecordManager
v-if=
"showImportRecordManager"
:records=
"importRecords"
...
...
@@ -205,6 +205,7 @@
title=
"导入数据"
accept=
".csv,.txt,.xlsx"
@
file-selected=
"handleImportContacts"
@
error=
"handleImportError"
/>
<!-- 联系人选择弹窗 -->
<ContactSelector
...
...
@@ -221,11 +222,30 @@
@
insert-variable=
"insertVariable"
@
close=
"showVariableSelector = false"
/>
<!-- 发送邮件弹窗 -->
<el-dialog
v-model=
"dialogVisible"
title=
"提示"
width=
"500"
:before-close=
"handleClose"
>
<span>
邮件发送任务已提交,请前往邮件记录中查看发送状态
</span>
<template
#
footer
>
<div
class=
"dialog-footer"
>
<el-button
@
click=
"dialogVisible = false"
>
暂不前往
</el-button>
<!-- 路由导航到邮件记录 -->
<el-button
type=
"primary"
@
click=
"navigateToEmailRecords"
>
前往邮件记录
</el-button>
</div>
</
template
>
</el-dialog>
</div>
</template>
<
script
setup
lang=
"ts"
>
import
{
ref
,
watch
,
onMounted
}
from
'vue'
import
{
ElMessageBox
}
from
'element-plus'
import
{
useRouter
}
from
'vue-router'
const
router
=
useRouter
()
const
navigateToEmailRecords
=
()
=>
{
router
.
push
({
name
:
'EmailRecords'
})
}
import
ContactSelector
from
'./ContactSelector.vue'
import
VariableSelector
from
'./VariableSelector.vue'
import
ImportRecordManager
from
'./ImportRecordManager.vue'
...
...
@@ -233,7 +253,17 @@ import ImportDialog from './ImportDialog.vue'
import
type
{
Sender
,
Contact
,
Variable
,
VariableTemplate
,
ImportRecord
,
EmailForm
}
from
'../types'
// 引入api接口,获取联系人列表、发件人列表、变量模版列表
import
{
senderApi
,
variableGroupApi
,
contactApi
,
sendEmailApi
,
importContactApi
}
from
'../api/api'
const
dialogVisible
=
ref
(
false
)
const
handleClose
=
(
done
:
()
=>
void
)
=>
{
ElMessageBox
.
confirm
(
'邮件发送任务已提交,请前往邮件记录中查看发送状态'
)
.
then
(()
=>
{
done
()
})
.
catch
(()
=>
{
// catch error
})
}
// 初始化数据
const
getSenders
=
()
=>
{
senderApi
...
...
@@ -315,7 +345,7 @@ onMounted(() => {
getContacts
()
getGroups
()
})
const
loading
=
ref
(
false
)
const
variablePrefix
=
'{{'
const
variableNextfix
=
'}}'
...
...
@@ -378,12 +408,15 @@ const confirmContactSelection = (selected: Contact<unknown>[]) => {
}
})
}
getImportedContacts
(
emailForm
.
value
.
sessionId
||
''
)
getImportedContacts
(
emailForm
.
value
.
sessionId
||
''
,
''
)
}
})
}
const
handleImportError
=
(
error
:
string
)
=>
{
ElMessage
.
error
(
error
)
}
//
更新导入
记录
//
编辑数据
记录
const
updateImportRecord
=
(
updatedRecord
:
ImportRecord
)
=>
{
const
params
=
{
...
updatedRecord
,
...
...
@@ -426,11 +459,22 @@ const deleteImportRecord = (importBizId: string) => {
})
}
//
修改handleImportContacts方法
//
导入数据
const
handleImportContacts
=
(
results
)
=>
{
console
.
log
(
results
)
console
.
log
(
'导入数据:'
,
results
)
if
(
results
.
data
)
{
emailForm
.
value
.
sessionId
=
results
.
data
.
sessionId
||
''
emailForm
.
value
.
sessionId
=
results
.
data
.
data
.
sessionId
||
''
importContactApi
.
getEmailContactImportDetail
(
emailForm
.
value
.
sessionId
||
''
).
then
((
res
)
=>
{
if
(
res
.
code
===
200
)
{
emailForm
.
value
.
receiveEmail
=
res
.
data
?.
receiveEmails
||
''
emailForm
.
value
.
ccEmails
=
res
.
data
?.
ccEmails
||
''
}
else
{
ElMessage
({
message
:
res
.
data
.
msg
||
'导入数据失败'
,
type
:
'error'
,
})
}
})
}
}
...
...
@@ -442,14 +486,14 @@ const sendEmail = () => {
senderBizId
:
currentSender
.
value
?.
senderBizId
,
sendEmail
:
currentSender
.
value
?.
email
||
''
,
}
console
.
log
(
params
)
console
.
log
(
'发送邮件参数:'
,
params
)
loading
.
value
=
true
try
{
// 确认发送邮件
sendEmailApi
.
sendEmail
(
params
).
then
((
res
)
=>
{
loading
.
value
=
false
if
(
res
.
code
===
200
)
{
ElMessage
({
message
:
'邮件发送成功'
,
type
:
'success'
,
})
dialogVisible
.
value
=
true
// 发送成功之后,清空表单
emailForm
.
value
=
{
receiveEmail
:
''
,
...
...
@@ -460,11 +504,18 @@ const sendEmail = () => {
}
}
else
{
ElMessage
({
message
:
'邮件发送失败
'
,
message
:
'邮件发送失败,请重新提交
'
,
type
:
'error'
,
})
}
})
}
catch
(
error
)
{
loading
.
value
=
false
ElMessage
({
message
:
'邮件发送失败,请重新提交'
,
type
:
'error'
,
})
}
}
// 通过sessionId获取导入的联系人
...
...
@@ -497,6 +548,7 @@ import { ElMessage } from 'element-plus'
import
FileUploadComponent
from
'@/components/FileUploadComponent.vue'
import
{
UploadResult
}
from
'@/utils/fileUpload'
import
{
get
}
from
'http'
import
{
ca
,
lo
}
from
'element-plus/es/locales.mjs'
// 上传成功的文件
const
uploadedFiles
=
ref
<
any
[]
>
([])
...
...
src/views/ContactManagement.vue
View file @
cde5c983
...
...
@@ -26,25 +26,49 @@
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery"
v-model=
"searchQuery
.name
"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"搜索联系人姓名、公司或邮箱..."
@
keyup
.
enter=
"searchContacts"
placeholder=
"搜索联系人姓名..."
/>
</div>
<!-- 搜索公司 -->
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.companyName"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"搜索公司..."
/>
</div>
<!-- 搜索邮箱 -->
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.email"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"搜索邮箱..."
/>
</div>
<div
class=
"flex gap-3 w-full md:w-auto"
>
<select
v-model=
"sortBy"
class=
"w-full md:w-auto px-4 py-2 border border-gray-300 rounded-lg"
@
change=
"searchContacts"
>
<option
value=
"name"
>
按姓名排序
</option>
<option
value=
"company"
>
按公司排序
</option>
<option
value=
"
addedAt"
>
按添加时间
排序
</option>
<option
value=
"company
Name
"
>
按公司排序
</option>
<option
value=
"
email"
>
按邮箱
排序
</option>
</select>
<button
@
click=
"resetSearch"
class=
"btn-outline px-4"
>
重置
</button>
</div>
<button
@
click=
"resetSearch"
class=
"btn-outline px-4"
>
<i
class=
"fas fa-sync mr-2"
></i>
重置
</button>
<!-- 搜索按钮 -->
<button
@
click=
"searchContacts"
class=
"btn-primary px-4 flex items-center"
>
<i
class=
"fas fa-search mr-2"
></i>
搜索
</button>
</div>
</div>
...
...
@@ -407,7 +431,11 @@ const handleCancel = (triggerKey: string) => {
const
editingSenderId
=
ref
(
''
)
// 页面状态
const
contacts
=
ref
<
Contact
[]
>
([])
const
searchQuery
=
ref
(
''
)
const
searchQuery
=
ref
({
name
:
''
,
companyName
:
''
,
email
:
''
,
})
const
sortBy
=
ref
(
'name'
)
const
isLoading
=
ref
(
false
)
// 模态框状态
...
...
@@ -465,6 +493,9 @@ const fetchContacts = async () => {
try
{
isLoading
.
value
=
true
const
data
=
await
contactApi
.
getContactList
({
companyName
:
searchQuery
.
value
.
companyName
,
name
:
searchQuery
.
value
.
name
,
email
:
searchQuery
.
value
.
email
,
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
sortField
:
sortBy
.
value
,
...
...
@@ -493,7 +524,11 @@ const searchContacts = () => {
// 重置搜索
const
resetSearch
=
()
=>
{
searchQuery
.
value
=
''
searchQuery
.
value
=
{
name
:
''
,
companyName
:
''
,
email
:
''
,
}
sortBy
.
value
=
'name'
currentPage
.
value
=
1
fetchContacts
()
...
...
src/views/EmailManagement.vue
View file @
cde5c983
...
...
@@ -18,13 +18,26 @@
<select
v-model=
"filterStatus"
class=
"px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
@
change=
"getEmailTaskMainList"
>
<option
value=
""
>
全部状态
</option>
<option
v-for=
"item in statusOptions"
:key=
"item.itemValue"
:value=
"item.itemValue"
>
{{
item
.
itemLabel
}}
</option>
</select>
<!-- 重置按钮 -->
<button
@
click=
"resetSearch"
class=
"px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-400 transition-colors"
>
<i
class=
"fas fa-sync mr-2"
></i>
重置
</button>
<!-- 搜索按钮 -->
<button
@
click=
"getEmailTaskMainList"
class=
"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2"
>
<i
class=
"fas fa-search mr-2"
></i>
搜索
</button>
</div>
</div>
</div>
...
...
@@ -148,6 +161,11 @@ import Pagination from '@/components/Pagination.vue'
const
total
=
ref
(
0
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
10
)
const
resetSearch
=
()
=>
{
searchTerm
.
value
=
''
filterStatus
.
value
=
''
getEmailTaskMainList
()
}
// 处理分页变化
const
handlePageChange
=
(
page
:
number
,
size
:
number
)
=>
{
...
...
@@ -261,6 +279,7 @@ const getEmailTaskMainList = async () => {
try
{
isEmailListLoading
.
value
=
true
const
params
:
EmailTask
=
{
queryContent
:
searchTerm
.
value
,
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
status
:
filterStatus
.
value
,
...
...
src/views/ImportDialog.vue
View file @
cde5c983
...
...
@@ -9,6 +9,7 @@
v-model=
"uploadedFiles"
:config=
"uploadConfig"
@
success=
"handleDocumentUploadSuccess"
@
error=
"handleDocumentUploadError"
/>
<div
class=
"flex justify-end gap-3"
>
<button
...
...
@@ -43,7 +44,6 @@ const props = defineProps({
* 文件上传配置
*/
// 上传附件
import
{
ElMessage
}
from
'element-plus'
import
FileUploadComponent
from
'@/components/FileUploadComponent.vue'
import
{
UploadResult
,
UploadConfig
}
from
'@/utils/fileUpload'
...
...
@@ -62,7 +62,11 @@ const handleDocumentUploadSuccess = (results: UploadResult[]) => {
emit
(
'update:visible'
,
false
)
emit
(
'file-selected'
,
results
[
0
])
}
const
emit
=
defineEmits
([
'update:visible'
,
'file-selected'
])
// 处理文档上传失败
const
handleDocumentUploadError
=
(
error
:
string
)
=>
{
emit
(
'error'
,
error
)
}
const
emit
=
defineEmits
([
'update:visible'
,
'file-selected'
,
'error'
])
const
handleCancel
=
()
=>
{
emit
(
'update:visible'
,
false
)
...
...
src/views/SenderManagement.vue
View file @
cde5c983
...
...
@@ -18,12 +18,12 @@
/>
</div>
<div>
<label
class=
"block text-gray-700 mb-1 text-sm"
>
密码/
授权码 *
</label>
<label
class=
"block text-gray-700 mb-1 text-sm"
>
授权码 *
</label>
<input
v-model=
"formData.password"
type=
"password"
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=
"邮箱
密码或
授权码"
placeholder=
"邮箱授权码"
/>
</div>
<div>
...
...
@@ -66,6 +66,45 @@
<div
class=
"p-6 border-b border-gray-200"
>
<h3
class=
"text-lg font-semibold"
>
发件人列表
</h3>
</div>
<!-- 搜索发件人 -->
<div
class=
"p-6 border-b border-gray-200 flex flex-col md:flex-row gap-4"
>
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.displayName"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"搜索发件人姓名..."
/>
</div>
<!-- 搜索发件人邮箱 -->
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.email"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"搜索发件人邮箱..."
/>
</div>
<!-- 搜索和重置按钮在右侧 -->
<div
class=
"flex gap-3 w-full md:w-auto"
>
<!-- 重置和搜索 -->
<button
@
click=
"resetSearch"
class=
"px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-400 transition-colors mr-2"
>
<i
class=
"fas fa-sync mr-2"
></i>
重置
</button>
<button
@
click=
"getSenders"
class=
"btn-outline px-4 py-2 border border-blue-500 text-blue-500 rounded-md hover:bg-blue-50 transition-colors"
>
<i
class=
"fas fa-search mr-2"
></i>
搜索
</button>
</div>
</div>
<div
class=
"overflow-x-auto"
>
<table
class=
"min-w-full divide-y divide-gray-200"
>
<thead
class=
"bg-gray-50"
>
...
...
@@ -167,6 +206,18 @@ import Pagination from '@/components/Pagination.vue'
const
total
=
ref
(
0
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
10
)
const
searchQuery
=
ref
({
displayName
:
''
,
email
:
''
,
})
const
resetSearch
=
()
=>
{
searchQuery
.
value
=
{
displayName
:
''
,
email
:
''
,
}
getSenders
()
}
// 处理分页变化
const
handlePageChange
=
(
page
:
number
,
size
:
number
)
=>
{
...
...
@@ -185,6 +236,7 @@ const handlePageSizeUpdate = (size: number) => {
}
// 引入弹窗组件
import
CommonModal
from
'@/components/CommonModal.vue'
import
{
get
}
from
'http'
// 弹窗提示信息对象
const
modalVisible
=
ref
(
false
)
const
modalConfig
=
ref
({
...
...
@@ -272,7 +324,8 @@ const getProviders = () => {
const
getSenders
=
()
=>
{
senderApi
.
getEmailSenderConfigList
({
emailSenderConfigName
:
''
,
displayName
:
searchQuery
.
value
.
displayName
,
email
:
searchQuery
.
value
.
email
,
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
})
...
...
@@ -286,7 +339,7 @@ const saveSender = () => {
if
(
!
formData
.
value
.
email
||
!
formData
.
value
.
password
||
!
formData
.
value
.
providerBizId
)
{
openModal
({
title
:
'添加失败'
,
message
:
'请填写完整邮箱
、密
码和服务商'
,
message
:
'请填写完整邮箱
授权
码和服务商'
,
})
return
}
...
...
src/views/VariableManagement.vue
View file @
cde5c983
...
...
@@ -134,6 +134,44 @@
<div
class=
"p-6 border-b border-gray-200"
>
<h3
class=
"text-lg font-semibold"
>
变量列表
</h3>
</div>
<!-- 增加搜索条件 -->
<!-- 通过变量中文名和英文名搜索 -->
<div
class=
"bg-white rounded-lg shadow-sm p-4 mb-6"
>
<div
class=
"flex flex-col md:flex-row gap-4"
>
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.variableNameCn"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"输入变量中文名"
/>
</div>
<div
class=
"relative flex-1"
>
<i
class=
"fas fa-search absolute left-3 top-1/2 -translate-y-1/2 text-gray-400"
></i>
<input
v-model=
"searchQuery.variableNameEn"
type=
"text"
class=
"w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg"
placeholder=
"输入变量英文名"
/>
</div>
<!-- 搜索按钮 -->
<button
@
click=
"resetSearch"
class=
"px-4 py-2 bg-gray-100 text-gray-700 rounded-md hover:bg-gray-400 transition-colors mr-2"
>
<i
class=
"fas fa-sync mr-2"
></i>
重置
</button>
<button
@
click=
"fetchVariables"
class=
"px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-700 transition-colors text-sm"
>
<i
class=
"fas fa-search mr-2"
></i>
搜索
</button>
</div>
</div>
<div
class=
"overflow-x-auto"
>
<table
class=
"min-w-full divide-y divide-gray-200"
>
<thead
class=
"bg-gray-50"
>
...
...
@@ -310,7 +348,17 @@ import Pagination from '@/components/Pagination.vue'
const
total
=
ref
(
0
)
const
currentPage
=
ref
(
1
)
const
pageSize
=
ref
(
10
)
const
searchQuery
=
ref
({
variableNameCn
:
''
,
variableNameEn
:
''
,
})
const
resetSearch
=
()
=>
{
searchQuery
.
value
=
{
variableNameCn
:
''
,
variableNameEn
:
''
,
}
fetchVariables
()
}
// 处理分页变化
const
handlePageChange
=
(
page
:
number
,
size
:
number
)
=>
{
console
.
log
(
'分页变化:'
,
page
,
size
)
...
...
@@ -509,6 +557,7 @@ const deleteVariable = (id: string, type?: string) => {
// 查询变量列表
const
fetchVariables
=
()
=>
{
const
params
=
{
...
searchQuery
.
value
,
pageNo
:
currentPage
.
value
,
pageSize
:
pageSize
.
value
,
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment