Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
df3b381d
authored
Apr 12, 2023
by
dhb52
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
refactor: mp模块ts重构
parent
ebbf47f5
Hide whitespace changes
Inline
Side-by-side
Showing
12 changed files
with
938 additions
and
598 deletions
+938
-598
src/views/mp/autoReply/index.vue
+167
-110
src/views/mp/components/WxMpSelect.vue
+10
-12
src/views/mp/components/wx-editor/WxEditor.vue
+10
-105
src/views/mp/draft/index.vue
+231
-238
src/views/mp/freePublish/index.vue
+24
-9
src/views/mp/material/index.vue
+41
-47
src/views/mp/menu/index.vue
+38
-38
src/views/mp/tag/TagForm.vue
+12
-7
src/views/mp/tag/index.vue
+30
-15
src/views/mp/user/index.vue
+69
-17
src/views/pay/order/orderForm.vue
+152
-0
src/views/pay/refund/refundForm.vue
+154
-0
No files found.
src/views/mp/autoReply/index.vue
View file @
df3b381d
...
...
@@ -3,13 +3,16 @@
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"accountChanged"
/>
<el-form
class=
"-mb-15px"
:model=
"queryParams"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
</el-form>
</ContentWrap>
<!-- tab 切换 -->
<ContentWrap>
<el-tabs
v-model=
"
t
ype"
@
tab-change=
"handleTabChange"
>
<el-tabs
v-model=
"
msgT
ype"
@
tab-change=
"handleTabChange"
>
<!-- 操作工具栏 -->
<el-row
:gutter=
"10"
class=
"mb8"
>
<el-col
:span=
"1.5"
>
...
...
@@ -18,26 +21,26 @@
plain
@
click=
"handleAdd"
v-hasPermi=
"['mp:auto-reply:create']"
v-if=
"
type !== '1'
|| list.length
<
=
0
"
v-if=
"
msgType !== MsgType.Follow
|| list.length
<
=
0
"
>
<Icon
icon=
"ep:plus"
/>
新增
</el-button>
</el-col>
</el-row>
<!-- tab 项 -->
<el-tab-pane
name=
"1
"
>
<el-tab-pane
:name=
"MsgType.Follow
"
>
<template
#
label
>
<span><Icon
icon=
"ep:star
-off
"
/>
关注时回复
</span>
<span><Icon
icon=
"ep:star"
/>
关注时回复
</span>
</
template
>
</el-tab-pane>
<el-tab-pane
name=
"2
"
>
<el-tab-pane
:name=
"MsgType.Message
"
>
<
template
#
label
>
<span><Icon
icon=
"ep:chat-line-round"
/>
消息回复
</span>
</
template
>
</el-tab-pane>
<el-tab-pane
name=
"3
"
>
<el-tab-pane
:name=
"MsgType.Keyword
"
>
<
template
#
label
>
<span><Icon
icon=
"
ep:news
"
/>
关键词回复
</span>
<span><Icon
icon=
"
fa:newspaper-o
"
/>
关键词回复
</span>
</
template
>
</el-tab-pane>
</el-tabs>
...
...
@@ -47,10 +50,20 @@
label=
"请求消息类型"
align=
"center"
prop=
"requestMessageType"
v-if=
"
type === '2'
"
v-if=
"
msgType === MsgType.Message
"
/>
<el-table-column
label=
"关键词"
align=
"center"
prop=
"requestKeyword"
v-if=
"type === '3'"
/>
<el-table-column
label=
"匹配类型"
align=
"center"
prop=
"requestMatch"
v-if=
"type === '3'"
>
<el-table-column
label=
"关键词"
align=
"center"
prop=
"requestKeyword"
v-if=
"msgType === MsgType.Keyword"
/>
<el-table-column
label=
"匹配类型"
align=
"center"
prop=
"requestMatch"
v-if=
"msgType === MsgType.Keyword"
>
<
template
#
default=
"scope"
>
<dict-tag
:type=
"DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH"
:value=
"scope.row.requestMatch"
/>
</
template
>
...
...
@@ -64,7 +77,7 @@
<
template
#
default=
"scope"
>
<div
v-if=
"scope.row.responseMessageType === 'text'"
>
{{
scope
.
row
.
responseContent
}}
</div>
<div
v-else-if=
"scope.row.responseMessageType === 'voice'"
>
<WxVoicePlayer
:url=
"scope.row.responseMediaUrl"
/>
<WxVoicePlayer
v-if=
"scope.row.responseMediaUrl"
:url=
"scope.row.responseMediaUrl"
/>
</div>
<div
v-else-if=
"scope.row.responseMessageType === 'image'"
>
<a
target=
"_blank"
:href=
"scope.row.responseMediaUrl"
>
...
...
@@ -77,7 +90,11 @@
scope.row.responseMessageType === 'shortvideo'
"
>
<WxVideoPlayer
:url=
"scope.row.responseMediaUrl"
style=
"margin-top: 10px"
/>
<WxVideoPlayer
v-if=
"scope.row.responseMediaUrl"
:url=
"scope.row.responseMediaUrl"
style=
"margin-top: 10px"
/>
</div>
<div
v-else-if=
"scope.row.responseMessageType === 'news'"
>
<WxNews
:articles=
"scope.row.responseArticles"
/>
...
...
@@ -123,21 +140,21 @@
</el-table>
<!-- 添加或修改自动回复的对话框 -->
<el-dialog
:title=
"title"
v-model=
"
open
"
width=
"800px"
append-to-body
>
<el-form
ref=
"formRef"
:model=
"
f
orm"
:rules=
"rules"
label-width=
"80px"
>
<el-form-item
label=
"消息类型"
prop=
"requestMessageType"
v-if=
"
type === '2'
"
>
<el-select
v-model=
"
f
orm.requestMessageType"
placeholder=
"请选择"
>
<el-dialog
:title=
"title"
v-model=
"
showReplyFormDialog
"
width=
"800px"
append-to-body
>
<el-form
ref=
"formRef"
:model=
"
replyF
orm"
:rules=
"rules"
label-width=
"80px"
>
<el-form-item
label=
"消息类型"
prop=
"requestMessageType"
v-if=
"
msgType === MsgType.Message
"
>
<el-select
v-model=
"
replyF
orm.requestMessageType"
placeholder=
"请选择"
>
<
template
v-for=
"dict in getDictOptions(DICT_TYPE.MP_MESSAGE_TYPE)"
:key=
"dict.value"
>
<el-option
v-if=
"
r
equestMessageTypes.includes(dict.value)"
v-if=
"
R
equestMessageTypes.includes(dict.value)"
:label=
"dict.label"
:value=
"dict.value"
/>
</
template
>
</el-select>
</el-form-item>
<el-form-item
label=
"匹配类型"
prop=
"requestMatch"
v-if=
"
type === '3'
"
>
<el-select
v-model=
"
f
orm.requestMatch"
placeholder=
"请选择匹配类型"
clearable
>
<el-form-item
label=
"匹配类型"
prop=
"requestMatch"
v-if=
"
msgType === MsgType.Keyword
"
>
<el-select
v-model=
"
replyF
orm.requestMatch"
placeholder=
"请选择匹配类型"
clearable
>
<el-option
v-for=
"dict in getDictOptions(DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH)"
:key=
"dict.value"
...
...
@@ -146,8 +163,8 @@
/>
</el-select>
</el-form-item>
<el-form-item
label=
"关键词"
prop=
"requestKeyword"
v-if=
"
type === '3'
"
>
<el-input
v-model=
"
f
orm.requestKeyword"
placeholder=
"请输入内容"
clearable
/>
<el-form-item
label=
"关键词"
prop=
"requestKeyword"
v-if=
"
msgType === MsgType.Keyword
"
>
<el-input
v-model=
"
replyF
orm.requestKeyword"
placeholder=
"请输入内容"
clearable
/>
</el-form-item>
<el-form-item
label=
"回复消息"
>
<WxReplySelect
:objData=
"objData"
v-if=
"hackResetWxReplySelect"
/>
...
...
@@ -160,38 +177,47 @@
</el-dialog>
</ContentWrap>
</template>
<
script
setup
name=
"MpAutoReply"
>
<
script
setup
lang=
"ts"
name=
"MpAutoReply"
>
import
WxVideoPlayer
from
'@/views/mp/components/wx-video-play/main.vue'
import
WxVoicePlayer
from
'@/views/mp/components/wx-voice-play/main.vue'
import
WxMusic
from
'@/views/mp/components/wx-music/main.vue'
import
WxNews
from
'@/views/mp/components/wx-news/main.vue'
import
WxReplySelect
from
'@/views/mp/components/wx-reply/main.vue'
import
Wx
AccountSelect
from
'@/views/mp/components/wx-account-select/main
.vue'
import
Wx
MpSelect
from
'@/views/mp/components/WxMpSelect
.vue'
import
*
as
MpAutoReplyApi
from
'@/api/mp/autoReply'
import
{
DICT_TYPE
,
getDictOptions
}
from
'@/utils/dict'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
{
ContentWrap
}
from
'@/components/ContentWrap'
import
{
TabPaneName
}
from
'element-plus'
const
message
=
useMessage
()
// const queryFormRef = ref()
const
formRef
=
ref
()
// tab 类型(1、关注时回复;2、消息回复;3、关键词回复)
const
type
=
ref
(
'3'
)
// 消息类型(Follow: 关注时回复;Message: 消息回复;Keyword: 关键词回复)
// 作为tab.name
enum
MsgType
{
Follow
=
1
,
Message
=
2
,
Keyword
=
3
}
const
msgType
=
ref
<
MsgType
>
(
MsgType
.
Keyword
)
// 允许选择的请求消息类型
const
r
equestMessageTypes
=
[
'text'
,
'image'
,
'voice'
,
'video'
,
'shortvideo'
,
'location'
,
'link'
]
const
R
equestMessageTypes
=
[
'text'
,
'image'
,
'voice'
,
'video'
,
'shortvideo'
,
'location'
,
'link'
]
// 遮罩层
const
loading
=
ref
(
true
)
// 显示搜索条件
// const showSearch = ref(true)
// 总条数
const
total
=
ref
(
0
)
// 自动回复列表
const
list
=
ref
([])
const
list
=
ref
<
any
[]
>
([])
// 查询参数
const
queryParams
=
reactive
({
interface
QueryParams
{
pageNo
:
number
pageSize
:
number
accountId
?:
number
}
const
queryParams
:
QueryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
accountId
:
undefined
...
...
@@ -200,12 +226,50 @@ const queryParams = reactive({
// 弹出层标题
const
title
=
ref
(
''
)
// 是否显示弹出层
const
open
=
ref
(
false
)
const
showReplyFormDialog
=
ref
(
false
)
// 表单参数
const
form
=
ref
({})
type
ReplyType
=
'text'
|
'image'
|
'voice'
|
'video'
|
'shortvideo'
|
'location'
|
'link'
interface
ReplyForm
{
// relation:
id
?:
number
accountId
?:
number
type
?:
MsgType
// request:
requestMessageType
?:
ReplyType
requestMatch
?:
number
requestKeyword
?:
string
// response:
responseMessageType
?:
ReplyType
responseContent
?:
string
responseMediaId
?:
number
responseMediaUrl
?:
string
responseTitle
?:
string
responseDescription
?:
number
responseThumbMediaId
?:
string
responseThumbMediaUrl
?:
string
responseArticles
?:
any
[]
responseMusicUrl
?:
string
responseHqMusicUrl
?:
string
}
interface
ObjData
{
type
:
ReplyType
accountId
?:
number
content
?:
string
mediaId
?:
number
url
?:
string
title
?:
string
description
?:
string
thumbMediaId
?:
number
thumbMediaUrl
?:
string
articles
?:
any
[]
musicUrl
?:
string
hqMusicUrl
?:
string
}
const
replyForm
=
ref
<
ReplyForm
>
({})
// 回复消息
const
objData
=
ref
({
type
:
'text'
const
objData
=
ref
<
ObjData
>
({
type
:
'text'
,
accountId
:
undefined
})
// 表单校验
const
rules
=
{
...
...
@@ -216,8 +280,8 @@ const rules = {
// 重置 WxReplySelect 组件,解决无法清除的问题
const
hackResetWxReplySelect
=
ref
(
false
)
const
accountChanged
=
(
accountId
)
=>
{
queryParams
.
accountId
=
accountI
d
const
onAccountChanged
=
(
id
?:
number
)
=>
{
queryParams
.
accountId
=
i
d
getList
()
}
...
...
@@ -227,7 +291,7 @@ const getList = async () => {
try
{
const
data
=
await
MpAutoReplyApi
.
getAutoReplyPage
({
...
queryParams
,
type
:
t
ype
.
value
type
:
msgT
ype
.
value
})
list
.
value
=
data
.
list
total
.
value
=
data
.
total
...
...
@@ -242,8 +306,8 @@ const handleQuery = () => {
getList
()
}
const
handleTabChange
=
(
tabName
)
=>
{
type
.
value
=
tabNam
e
const
handleTabChange
=
(
tabName
:
TabPaneName
)
=>
{
msgType
.
value
=
tabName
as
MsgTyp
e
handleQuery
()
}
...
...
@@ -252,94 +316,87 @@ const handleAdd = () => {
reset
()
resetEditor
()
// 打开表单,并设置初始化
open
.
value
=
true
title
.
value
=
'新增自动回复'
objData
.
value
=
{
type
:
'text'
,
accountId
:
queryParams
.
accountId
}
title
.
value
=
'新增自动回复'
showReplyFormDialog
.
value
=
true
}
/** 修改按钮操作 */
const
handleUpdate
=
(
row
)
=>
{
const
handleUpdate
=
async
(
row
:
any
)
=>
{
reset
()
resetEditor
()
console
.
log
(
row
)
MpAutoReplyApi
.
getAutoReply
(
row
.
id
).
then
((
data
)
=>
{
// 设置属性
f
orm
.
value
=
{
...
data
}
delete
f
orm
.
value
[
'responseMessageType'
]
delete
f
orm
.
value
[
'responseContent'
]
delete
f
orm
.
value
[
'responseMediaId'
]
delete
f
orm
.
value
[
'responseMediaUrl'
]
delete
f
orm
.
value
[
'responseDescription'
]
delete
f
orm
.
value
[
'responseArticles'
]
objData
.
value
=
{
type
:
data
.
responseMessageType
,
accountId
:
queryParams
.
accountId
,
content
:
data
.
responseContent
,
mediaId
:
data
.
responseMediaId
,
url
:
data
.
responseMediaUrl
,
title
:
data
.
responseTitle
,
description
:
data
.
responseDescription
,
thumbMediaId
:
data
.
responseThumbMediaId
,
thumbMediaUrl
:
data
.
responseThumbMediaUrl
,
articles
:
data
.
responseArticles
,
musicUrl
:
data
.
responseMusicUrl
,
hqMusicUrl
:
data
.
responseHqMusicUrl
}
const
data
=
await
MpAutoReplyApi
.
getAutoReply
(
row
.
id
)
// 设置属性
replyF
orm
.
value
=
{
...
data
}
delete
replyF
orm
.
value
[
'responseMessageType'
]
delete
replyF
orm
.
value
[
'responseContent'
]
delete
replyF
orm
.
value
[
'responseMediaId'
]
delete
replyF
orm
.
value
[
'responseMediaUrl'
]
delete
replyF
orm
.
value
[
'responseDescription'
]
delete
replyF
orm
.
value
[
'responseArticles'
]
objData
.
value
=
{
type
:
data
.
responseMessageType
,
accountId
:
queryParams
.
accountId
,
content
:
data
.
responseContent
,
mediaId
:
data
.
responseMediaId
,
url
:
data
.
responseMediaUrl
,
title
:
data
.
responseTitle
,
description
:
data
.
responseDescription
,
thumbMediaId
:
data
.
responseThumbMediaId
,
thumbMediaUrl
:
data
.
responseThumbMediaUrl
,
articles
:
data
.
responseArticles
,
musicUrl
:
data
.
responseMusicUrl
,
hqMusicUrl
:
data
.
responseHqMusicUrl
}
// 打开表单
open
.
value
=
true
title
.
value
=
'修改自动回复'
})
// 打开表单
title
.
value
=
'修改自动回复'
showReplyFormDialog
.
value
=
true
}
const
handleSubmit
=
()
=>
{
formRef
.
value
?.
validate
((
valid
)
=>
{
if
(
!
valid
)
{
return
}
const
handleSubmit
=
async
()
=>
{
const
valid
=
await
formRef
.
value
?.
validate
()
if
(
!
valid
)
return
// 处理回复消息
const
form
=
{
...
f
orm
.
value
}
f
orm
.
responseMessageType
=
objData
.
value
.
type
f
orm
.
responseContent
=
objData
.
value
.
content
f
orm
.
responseMediaId
=
objData
.
value
.
mediaId
f
orm
.
responseMediaUrl
=
objData
.
value
.
url
f
orm
.
responseTitle
=
objData
.
value
.
title
f
orm
.
responseDescription
=
objData
.
value
.
description
f
orm
.
responseThumbMediaId
=
objData
.
value
.
thumbMediaId
f
orm
.
responseThumbMediaUrl
=
objData
.
value
.
thumbMediaUrl
f
orm
.
responseArticles
=
objData
.
value
.
articles
f
orm
.
responseMusicUrl
=
objData
.
value
.
musicUrl
f
orm
.
responseHqMusicUrl
=
objData
.
value
.
hqMusicUrl
// 处理回复消息
const
submitForm
:
any
=
{
...
replyF
orm
.
value
}
submitF
orm
.
responseMessageType
=
objData
.
value
.
type
submitF
orm
.
responseContent
=
objData
.
value
.
content
submitF
orm
.
responseMediaId
=
objData
.
value
.
mediaId
submitF
orm
.
responseMediaUrl
=
objData
.
value
.
url
submitF
orm
.
responseTitle
=
objData
.
value
.
title
submitF
orm
.
responseDescription
=
objData
.
value
.
description
submitF
orm
.
responseThumbMediaId
=
objData
.
value
.
thumbMediaId
submitF
orm
.
responseThumbMediaUrl
=
objData
.
value
.
thumbMediaUrl
submitF
orm
.
responseArticles
=
objData
.
value
.
articles
submitF
orm
.
responseMusicUrl
=
objData
.
value
.
musicUrl
submitF
orm
.
responseHqMusicUrl
=
objData
.
value
.
hqMusicUrl
if
(
form
.
value
.
id
!==
undefined
)
{
MpAutoReplyApi
.
updateAutoReply
(
form
).
then
(()
=>
{
message
.
success
(
'修改成功'
)
open
.
value
=
false
getList
()
})
}
else
{
MpAutoReplyApi
.
createAutoReply
(
form
).
then
(()
=>
{
message
.
success
(
'新增成功'
)
open
.
value
=
false
getList
()
})
}
})
if
(
replyForm
.
value
.
id
!==
undefined
)
{
await
MpAutoReplyApi
.
updateAutoReply
(
submitForm
)
message
.
success
(
'修改成功'
)
}
else
{
await
MpAutoReplyApi
.
createAutoReply
(
submitForm
)
message
.
success
(
'新增成功'
)
}
showReplyFormDialog
.
value
=
false
getList
()
}
// 表单重置
const
reset
=
()
=>
{
f
orm
.
value
=
{
replyF
orm
.
value
=
{
id
:
undefined
,
accountId
:
queryParams
.
accountId
,
type
:
t
ype
.
value
,
type
:
msgT
ype
.
value
,
requestKeyword
:
undefined
,
requestMatch
:
type
.
value
===
'3'
?
1
:
undefined
,
requestMatch
:
msgType
.
value
===
MsgType
.
Keyword
?
1
:
undefined
,
requestMessageType
:
undefined
}
formRef
.
value
?.
resetFields
()
...
...
@@ -347,7 +404,7 @@ const reset = () => {
// 取消按钮
const
cancel
=
()
=>
{
open
.
value
=
false
showReplyFormDialog
.
value
=
false
reset
()
}
...
...
src/views/mp/components/WxMpSelect.vue
View file @
df3b381d
<
template
>
<el-select
v-model=
"accountId"
placeholder=
"请选择公众号"
class=
"!w-240px"
@
change=
"accountChanged"
>
<el-select
v-model=
"account.id"
placeholder=
"请选择公众号"
class=
"!w-240px"
@
change=
"onChanged"
>
<el-option
v-for=
"item in accountList"
:key=
"item.id"
:label=
"item.name"
:value=
"item.id"
/>
</el-select>
</
template
>
...
...
@@ -12,11 +7,14 @@
<
script
lang=
"ts"
setup
name=
"WxMpSelect"
>
import
*
as
MpAccountApi
from
'@/api/mp/account'
const
accountId
:
Ref
<
number
|
undefined
>
=
ref
()
const
account
:
MpAccountApi
.
AccountVO
=
reactive
({
id
:
undefined
,
name
:
''
})
const
accountList
:
Ref
<
MpAccountApi
.
AccountVO
[]
>
=
ref
([])
const
emit
=
defineEmits
<
{
(
e
:
'change'
,
id
:
number
|
undefined
):
void
(
e
:
'change'
,
id
?:
number
,
name
?:
string
):
void
}
>
()
onMounted
(()
=>
{
...
...
@@ -27,12 +25,12 @@ const handleQuery = async () => {
accountList
.
value
=
await
MpAccountApi
.
getSimpleAccountList
()
// 默认选中第一个
if
(
accountList
.
value
.
length
>
0
)
{
account
Id
.
value
=
accountList
.
value
[
0
].
id
emit
(
'change'
,
account
Id
.
valu
e
)
account
.
id
=
accountList
.
value
[
0
].
id
emit
(
'change'
,
account
.
id
,
account
.
nam
e
)
}
}
const
account
Changed
=
()
=>
{
emit
(
'change'
,
account
Id
.
valu
e
)
const
on
Changed
=
()
=>
{
emit
(
'change'
,
account
.
id
,
account
.
nam
e
)
}
</
script
>
src/views/mp/components/wx-editor/WxEditor.vue
View file @
df3b381d
<
script
setup
>
import
{
ref
,
reactive
}
from
'vue'
import
{
QuillEditor
}
from
'@vueup/vue-quill'
import
'@vueup/vue-quill/dist/vue-quill.snow.css'
import
{
getAccessToken
}
from
'@/utils/auth'
import
editorOptions
from
'./quill-options
'
import
{
Editor
}
from
'@/components/Editor
'
const
BASE_URL
=
import
.
meta
.
env
.
VITE_BASE_URL
const
actionUrl
=
BASE_URL
+
'/admin-api/mp/material/upload-news-image'
// 这里写你要上传的图片服务器地址
const
headers
=
{
Authorization
:
'Bearer '
+
getAccessToken
()
}
// 设置上传的请求头部
const
message
=
useMessage
()
...
...
@@ -30,21 +30,16 @@ const props = defineProps({
const
emit
=
defineEmits
([
'input'
])
const
myQuillEditorRef
=
ref
()
const
content
=
ref
(
props
.
value
.
replace
(
/data-src/g
,
'src'
))
const
loading
=
ref
(
false
)
// 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示
const
actionUrl
=
ref
(
BASE_URL
+
'/admin-api/mp/material/upload-news-image'
)
// 这里写你要上传的图片服务器地址
const
headers
=
ref
({
Authorization
:
'Bearer '
+
getAccessToken
()
})
// 设置上传的请求头部
const
uploadData
=
reactive
({
type
:
'image'
,
// TODO 芋艿:试试要不要换成 thumb
accountId
:
props
.
accountId
})
const
onEditorChange
=
()
=>
{
const
onEditorChange
=
(
text
)
=>
{
//内容改变事件
emit
(
'input'
,
content
.
value
)
emit
(
'input'
,
text
)
}
// 富文本图片上传前
...
...
@@ -98,104 +93,14 @@ const uploadError = () => {
:on-error=
"uploadError"
:before-upload=
"beforeUpload"
/>
<QuillEditor
class=
"editor"
v-model=
"content"
<Editor
editor-id=
"wxEditor"
ref=
"quillEditorRef"
:
options=
"editorOptions
"
@
change=
"
onEditorChange($event
)"
:
modelValue=
"content
"
@
change=
"
(editor) => onEditorChange(editor.getText()
)"
/>
</div>
</div>
</
template
>
<
style
>
.editor
{
line-height
:
normal
!important
;
height
:
500px
;
}
.ql-snow
.ql-tooltip
[
data-mode
=
'link'
]
::before
{
content
:
'请输入链接地址:'
;
}
.ql-snow
.ql-tooltip.ql-editing
a
.ql-action
::after
{
border-right
:
0
;
content
:
'保存'
;
padding-right
:
0
;
}
.ql-snow
.ql-tooltip
[
data-mode
=
'video'
]
::before
{
content
:
'请输入视频地址:'
;
}
.ql-snow
.ql-picker.ql-size
.ql-picker-label
::before
,
.ql-snow
.ql-picker.ql-size
.ql-picker-item
::before
{
content
:
'14px'
;
}
.ql-snow
.ql-picker.ql-size
.ql-picker-label
[
data-value
=
'small'
]
::before
,
.ql-snow
.ql-picker.ql-size
.ql-picker-item
[
data-value
=
'small'
]
::before
{
content
:
'10px'
;
}
.ql-snow
.ql-picker.ql-size
.ql-picker-label
[
data-value
=
'large'
]
::before
,
.ql-snow
.ql-picker.ql-size
.ql-picker-item
[
data-value
=
'large'
]
::before
{
content
:
'18px'
;
}
.ql-snow
.ql-picker.ql-size
.ql-picker-label
[
data-value
=
'huge'
]
::before
,
.ql-snow
.ql-picker.ql-size
.ql-picker-item
[
data-value
=
'huge'
]
::before
{
content
:
'32px'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
::before
{
content
:
'文本'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'1'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'1'
]
::before
{
content
:
'标题1'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'2'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'2'
]
::before
{
content
:
'标题2'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'3'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'3'
]
::before
{
content
:
'标题3'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'4'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'4'
]
::before
{
content
:
'标题4'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'5'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'5'
]
::before
{
content
:
'标题5'
;
}
.ql-snow
.ql-picker.ql-header
.ql-picker-label
[
data-value
=
'6'
]
::before
,
.ql-snow
.ql-picker.ql-header
.ql-picker-item
[
data-value
=
'6'
]
::before
{
content
:
'标题6'
;
}
.ql-snow
.ql-picker.ql-font
.ql-picker-label
::before
,
.ql-snow
.ql-picker.ql-font
.ql-picker-item
::before
{
content
:
'标准字体'
;
}
.ql-snow
.ql-picker.ql-font
.ql-picker-label
[
data-value
=
'serif'
]
::before
,
.ql-snow
.ql-picker.ql-font
.ql-picker-item
[
data-value
=
'serif'
]
::before
{
content
:
'衬线字体'
;
}
.ql-snow
.ql-picker.ql-font
.ql-picker-label
[
data-value
=
'monospace'
]
::before
,
.ql-snow
.ql-picker.ql-font
.ql-picker-item
[
data-value
=
'monospace'
]
::before
{
content
:
'等宽字体'
;
}
</
style
>
<
style
></
style
>
src/views/mp/draft/index.vue
View file @
df3b381d
...
...
@@ -3,14 +3,22 @@
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"accountChanged"
>
<template
#
actions
>
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
<el-form-item>
<el-button
type=
"primary"
plain
@
click=
"handleAdd"
v-hasPermi=
"['mp:draft:create']"
>
<Icon
icon=
"ep:plus"
/>
新增
</el-button>
</
template
>
</
WxAccountSelect
>
</
el-form-item
>
</
el-form
>
</ContentWrap>
<!-- 列表 -->
...
...
@@ -58,214 +66,221 @@
/>
</ContentWrap>
<!-- TODO @Dhb52:迁移成独立路由 -->
<div
class=
"app-container"
>
<!-- 添加或修改草稿对话框 -->
<Teleport
to=
"body"
>
<el-dialog
:title=
"operateMaterial === 'add' ? '新建图文' : '修改图文'"
width=
"80%"
top=
"20px"
v-model=
"dialogNewsVisible"
:before-close=
"dialogNewsClose"
:close-on-click-modal=
"false"
>
<div
class=
"left"
>
<div
class=
"select-item"
>
<div
v-for=
"(news, index) in articlesAdd"
:key=
"news.id"
>
<div
class=
"news-main father"
v-if=
"index === 0"
:class=
"{ activeAddNews: isActiveAddNews === index }"
@
click=
"activeNews(index)"
>
<div
class=
"news-content"
>
<img
class=
"material-img"
v-if=
"news.thumbUrl"
:src=
"news.thumbUrl"
/>
<div
class=
"news-content-title"
>
{{ news.title }}
</div>
</div>
<div
class=
"child"
v-if=
"articlesAdd.length > 1"
>
<el-button
size=
"small"
@
click=
"downNews(index)"
><Icon
icon=
"ep:sort-down"
/>
下移
</el-button
>
<el-button
v-if=
"operateMaterial === 'add'"
size=
"small"
@
click=
"minusNews(index)"
><Icon
icon=
"ep:delete"
/>
删除
</el-button>
</div>
<el-dialog
:title=
"operateMaterial === 'add' ? '新建图文' : '修改图文'"
width=
"80%"
top=
"20px"
v-model=
"dialogNewsVisible"
:before-close=
"dialogNewsClose"
:close-on-click-modal=
"false"
>
<div
class=
"left"
>
<div
class=
"select-item"
>
<div
v-for=
"(news, index) in articlesAdd"
:key=
"news.id"
>
<div
class=
"news-main father"
v-if=
"index === 0"
:class=
"{ activeAddNews: isActiveAddNews === index }"
@
click=
"activeNews(index)"
>
<div
class=
"news-content"
>
<img
class=
"material-img"
v-if=
"news.thumbUrl"
:src=
"news.thumbUrl"
/>
<div
class=
"news-content-title"
>
{{ news.title }}
</div>
</div>
<div
class=
"news-main-item father"
v-if=
"index > 0"
:class=
"{ activeAddNews: isActiveAddNews === index }"
@
click=
"activeNews(index)"
>
<div
class=
"news-content-item"
>
<div
class=
"news-content-item-title"
>
{{ news.title }}
</div>
<div
class=
"news-content-item-img"
>
<img
class=
"material-img"
v-if=
"news.thumbUrl"
:src=
"news.thumbUrl"
height=
"100%"
/>
</div>
</div>
<div
class=
"child"
>
<el-button
v-if=
"articlesAdd.length > index + 1"
size=
"small"
@
click=
"downNews(index)"
><Icon
icon=
"ep:sort-down"
/>
下移
</el-button>
<el-button
size=
"small"
@
click=
"upNews(index)"
><Icon
icon=
"ep:sort-up"
/>
上移
</el-button
>
<el-button
v-if=
"operateMaterial === 'add'"
type=
"danger"
size=
"small"
@
click=
"minusNews(index)"
><Icon
icon=
"ep:delete"
/>
删除
</el-button>
<div
class=
"child"
v-if=
"articlesAdd.length > 1"
>
<el-button
size=
"small"
@
click=
"downNews(index)"
><Icon
icon=
"ep:sort-down"
/>
下移
</el-button
>
<el-button
v-if=
"operateMaterial === 'add'"
size=
"small"
@
click=
"minusNews(index)"
><Icon
icon=
"ep:delete"
/>
删除
</el-button>
</div>
</div>
<div
class=
"news-main-item father"
v-if=
"index > 0"
:class=
"{ activeAddNews: isActiveAddNews === index }"
@
click=
"activeNews(index)"
>
<div
class=
"news-content-item"
>
<div
class=
"news-content-item-title"
>
{{ news.title }}
</div>
<div
class=
"news-content-item-img"
>
<img
class=
"material-img"
v-if=
"news.thumbUrl"
:src=
"news.thumbUrl"
height=
"100%"
/>
</div>
</div>
<div
class=
"child"
>
<el-button
v-if=
"articlesAdd.length > index + 1"
size=
"small"
@
click=
"downNews(index)"
><Icon
icon=
"ep:sort-down"
/>
下移
</el-button>
<el-button
size=
"small"
@
click=
"upNews(index)"
><Icon
icon=
"ep:sort-up"
/>
上移
</el-button
>
<el-button
v-if=
"operateMaterial === 'add'"
type=
"danger"
size=
"small"
@
click=
"minusNews(index)"
><Icon
icon=
"ep:delete"
/>
删除
</el-button>
</div>
</div>
<el-row
justify=
"center"
class=
"ope-row"
>
<el-button
type=
"primary"
circle
@
click=
"plusNews(item)"
v-if=
"articlesAdd.length < 8 && operateMaterial === 'add'"
>
<Icon
icon=
"ep:plus"
/>
</el-button>
</el-row>
</div>
<el-row
justify=
"center"
class=
"ope-row"
>
<el-button
type=
"primary"
circle
@
click=
"plusNews"
v-if=
"articlesAdd.length < 8 && operateMaterial === 'add'"
>
<Icon
icon=
"ep:plus"
/>
</el-button>
</el-row>
</div>
<div
class=
"right"
v-loading=
"addMaterialLoading"
v-if=
"articlesAdd.length > 0"
>
<br
/>
<br
/>
<br
/>
<br
/>
<!-- 标题、作者、原文地址 -->
<el-input
v-model=
"articlesAdd[isActiveAddNews].title"
placeholder=
"请输入标题(必填)"
/>
<el-input
v-model=
"articlesAdd[isActiveAddNews].author"
placeholder=
"请输入作者"
style=
"margin-top: 5px"
/>
<el-input
v-model=
"articlesAdd[isActiveAddNews].contentSourceUrl"
placeholder=
"请输入原文地址"
style=
"margin-top: 5px"
/>
<!-- 封面和摘要 -->
<div
class=
"input-tt"
>
封面和摘要:
</div>
<div>
<div
class=
"thumb-div"
>
<img
class=
"material-img"
v-if=
"articlesAdd[isActiveAddNews].thumbUrl"
:src=
"articlesAdd[isActiveAddNews].thumbUrl"
:class=
"isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
/>
<Icon
v-else
icon=
"ep:plus"
class=
"avatar-uploader-icon"
:class=
"isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
/>
<div
class=
"thumb-but"
>
<el-upload
:action=
"actionUrl"
:headers=
"headers"
multiple
:limit=
"1"
:file-list=
"fileList"
:data=
"uploadData"
:before-upload=
"beforeThumbImageUpload"
:on-success=
"handleUploadSuccess"
</div>
<div
class=
"right"
v-loading=
"addMaterialLoading"
v-if=
"articlesAdd.length > 0"
>
<br
/>
<br
/>
<br
/>
<br
/>
<!-- 标题、作者、原文地址 -->
<el-input
v-model=
"articlesAdd[isActiveAddNews].title"
placeholder=
"请输入标题(必填)"
/>
<el-input
v-model=
"articlesAdd[isActiveAddNews].author"
placeholder=
"请输入作者"
style=
"margin-top: 5px"
/>
<el-input
v-model=
"articlesAdd[isActiveAddNews].contentSourceUrl"
placeholder=
"请输入原文地址"
style=
"margin-top: 5px"
/>
<!-- 封面和摘要 -->
<div
class=
"input-tt"
>
封面和摘要:
</div>
<div>
<div
class=
"thumb-div"
>
<img
class=
"material-img"
v-if=
"articlesAdd[isActiveAddNews].thumbUrl"
:src=
"articlesAdd[isActiveAddNews].thumbUrl"
:class=
"isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
/>
<Icon
v-else
icon=
"ep:plus"
class=
"avatar-uploader-icon"
:class=
"isActiveAddNews === 0 ? 'avatar' : 'avatar1'"
/>
<div
class=
"thumb-but"
>
<el-upload
:action=
"uploadUrl"
:headers=
"headers"
multiple
:limit=
"1"
:file-list=
"fileList"
:data=
"uploadData"
:before-upload=
"beforeThumbImageUpload"
:on-success=
"handleUploadSuccess"
>
<
template
#
trigger
>
<el-button
size=
"small"
type=
"primary"
>
本地上传
</el-button>
</
template
>
<el-button
size=
"small"
type=
"primary"
@
click=
"openMaterial"
style=
"margin-left: 5px"
>
素材库选择
</el-button
>
<
template
#
trigger
>
<el-button
size=
"small"
type=
"primary"
>
本地上传
</el-button>
</
template
>
<el-button
size=
"small"
type=
"primary"
@
click=
"openMaterial"
style=
"margin-left: 5px"
>
素材库选择
</el-button
>
<
template
#
tip
>
<div
class=
"el-upload__tip"
>
支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M
</div>
</
template
>
</el-upload>
</div>
<Teleport
to=
"body"
>
<el-dialog
title=
"选择图片"
v-model=
"dialogImageVisible"
width=
"80%"
>
<WxMaterialSelect
ref=
"materialSelectRef"
:objData=
"{ type: 'image', accountId: queryParams.accountId }"
@
select-material=
"selectMaterial"
/>
</el-dialog>
</Teleport>
<
template
#
tip
>
<div
class=
"el-upload__tip"
>
支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M
</div>
</
template
>
</el-upload>
</div>
<el-input
:rows=
"8"
type=
"textarea"
v-model=
"articlesAdd[isActiveAddNews].digest"
placeholder=
"请输入摘要"
class=
"digest"
maxlength=
"120"
style=
"float: right"
/>
<el-dialog
title=
"选择图片"
v-model=
"dialogImageVisible"
width=
"80%"
append-to-body
>
<WxMaterialSelect
ref=
"materialSelectRef"
:objData=
"{ type: 'image', accountId: queryParams.accountId }"
@
select-material=
"selectMaterial"
/>
</el-dialog>
</div>
<!--富文本编辑器组件-->
<el-row>
<WxEditor
v-model=
"articlesAdd[isActiveAddNews].content"
:account-id=
"uploadData.accountId"
v-if=
"hackResetEditor"
/>
</el-row>
<el-input
:rows=
"8"
type=
"textarea"
v-model=
"articlesAdd[isActiveAddNews].digest"
placeholder=
"请输入摘要"
class=
"digest"
maxlength=
"120"
style=
"float: right"
/>
</div>
<
template
#
footer
>
<el-button
@
click=
"dialogNewsVisible = false"
>
取 消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm"
>
提 交
</el-button>
</
template
>
</el-dialog>
</Teleport>
<!--富文本编辑器组件-->
<el-row>
<WxEditor
v-model=
"articlesAdd[isActiveAddNews].content"
:account-id=
"uploadData.accountId"
v-if=
"hackResetEditor"
/>
</el-row>
</div>
<
template
#
footer
>
<el-button
@
click=
"dialogNewsVisible = false"
>
取 消
</el-button>
<el-button
type=
"primary"
@
click=
"submitForm"
>
提 交
</el-button>
</
template
>
</el-dialog>
</div>
</template>
<
script
setup
name=
"MpDraft"
>
<
script
setup
lang=
"ts"
name=
"MpDraft"
>
import
WxEditor
from
'@/views/mp/components/wx-editor/WxEditor.vue'
import
WxNews
from
'@/views/mp/components/wx-news/main.vue'
import
WxMaterialSelect
from
'@/views/mp/components/wx-material-select/main.vue'
import
Wx
AccountSelect
from
'@/views/mp/components/wx-account-select/main
.vue'
import
Wx
MpSelect
from
'@/views/mp/components/WxMpSelect
.vue'
import
{
getAccessToken
}
from
'@/utils/auth'
import
*
as
MpDraftApi
from
'@/api/mp/draft'
import
*
as
MpFreePublishApi
from
'@/api/mp/freePublish'
import
{
UploadFiles
,
UploadProps
,
UploadRawFile
}
from
'element-plus'
// 可以用改本地数据模拟,避免API调用超限
// import drafts from './mock'
const
message
=
useMessage
()
// 消息
const
loading
=
ref
(
true
)
// 列表的加载中
const
list
=
ref
<
any
[]
>
([])
// 列表的数据
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryParams
=
reactive
({
interface
QueryParams
{
pageNo
:
number
pageSize
:
number
accountId
?:
number
}
const
queryParams
:
QueryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
accountId
:
undefined
})
// ========== 文件上传 ==========
const
materialSelectRef
=
ref
()
const
BASE_URL
=
import
.
meta
.
env
.
VITE_BASE_URL
const
actionUrl
=
ref
(
BASE_URL
+
'/admin-api/mp/material/upload-permanent'
)
// 上传永久素材的地址
const
headers
=
ref
({
Authorization
:
'Bearer '
+
getAccessToken
()
})
// 设置上传的请求头部
const
fileList
=
ref
([])
const
uploadData
=
reactive
({
const
uploadUrl
=
BASE_URL
+
'/admin-api/mp/material/upload-permanent'
// 上传永久素材的地址
const
headers
=
{
Authorization
:
'Bearer '
+
getAccessToken
()
}
// 设置上传的请求头部
const
materialSelectRef
=
ref
<
InstanceType
<
typeof
WxMaterialSelect
>
|
null
>
(
null
)
const
fileList
=
ref
<
UploadFiles
>
([])
interface
UploadData
{
type
:
'image'
|
'video'
|
'audio'
accountId
?:
number
}
const
uploadData
:
UploadData
=
reactive
({
type
:
'image'
,
accountId
:
1
})
...
...
@@ -273,34 +288,28 @@ const uploadData = reactive({
// ========== 草稿新建 or 修改 ==========
const
dialogNewsVisible
=
ref
(
false
)
const
addMaterialLoading
=
ref
(
false
)
// 添加草稿的 loading 标识
const
articlesAdd
=
ref
([])
const
articlesAdd
=
ref
<
any
[]
>
([])
const
isActiveAddNews
=
ref
(
0
)
const
dialogImageVisible
=
ref
(
false
)
const
operateMaterial
=
ref
(
'add'
)
const
operateMaterial
=
ref
<
'add'
|
'edit'
>
(
'add'
)
const
articlesMediaId
=
ref
(
''
)
const
hackResetEditor
=
ref
(
false
)
/** 侦听公众号变化 **/
const
accountChanged
=
(
accountId
)
=>
{
setAccountId
(
accountI
d
)
const
onAccountChanged
=
(
id
?:
number
)
=>
{
setAccountId
(
i
d
)
getList
()
}
// ======================== 列表查询 ========================
/** 设置账号编号 */
const
setAccountId
=
(
accountId
)
=>
{
queryParams
.
accountId
=
accountI
d
uploadData
.
accountId
=
accountI
d
const
setAccountId
=
(
id
?:
number
)
=>
{
queryParams
.
accountId
=
i
d
uploadData
.
accountId
=
i
d
}
/** 查询列表 */
const
getList
=
async
()
=>
{
// 如果没有选中公众号账号,则进行提示。
if
(
!
queryParams
.
accountId
)
{
message
.
error
(
'未选中公众号,无法查询草稿箱'
)
return
false
}
loading
.
value
=
true
try
{
const
drafts
=
await
MpDraftApi
.
getDraftPage
(
queryParams
)
...
...
@@ -329,7 +338,7 @@ const handleAdd = () => {
}
/** 更新按钮操作 */
const
handleUpdate
=
(
item
)
=>
{
const
handleUpdate
=
(
item
:
any
)
=>
{
resetEditor
()
reset
()
articlesMediaId
.
value
=
item
.
mediaId
...
...
@@ -340,39 +349,30 @@ const handleUpdate = (item) => {
}
/** 提交按钮 */
const
submitForm
=
()
=>
{
// TODO @Dhb52: 参考别的模块写法,改成 await 方式
const
submitForm
=
async
()
=>
{
addMaterialLoading
.
value
=
true
if
(
operateMaterial
.
value
===
'add'
)
{
MpDraftApi
.
createDraft
(
queryParams
.
accountId
,
articlesAdd
.
value
)
.
then
(()
=>
{
message
.
notifySuccess
(
'新增成功'
)
dialogNewsVisible
.
value
=
false
getList
()
})
.
finally
(()
=>
{
addMaterialLoading
.
value
=
false
})
}
else
{
MpDraftApi
.
updateDraft
(
queryParams
.
accountId
,
articlesMediaId
.
value
,
articlesAdd
.
value
)
.
then
(()
=>
{
message
.
notifySuccess
(
'更新成功'
)
dialogNewsVisible
.
value
=
false
getList
()
})
.
finally
(()
=>
{
addMaterialLoading
.
value
=
false
})
try
{
if
(
operateMaterial
.
value
===
'add'
)
{
await
MpDraftApi
.
createDraft
(
queryParams
.
accountId
,
articlesAdd
.
value
)
message
.
notifySuccess
(
'新增成功'
)
}
else
{
await
MpDraftApi
.
updateDraft
(
queryParams
.
accountId
,
articlesMediaId
.
value
,
articlesAdd
.
value
)
message
.
notifySuccess
(
'更新成功'
)
}
}
finally
{
dialogNewsVisible
.
value
=
false
addMaterialLoading
.
value
=
false
getList
()
}
}
// 关闭弹窗
const
dialogNewsClose
=
async
(
done
)
=>
{
const
dialogNewsClose
=
async
(
onDone
:
()
=>
{}
)
=>
{
try
{
await
message
.
confirm
(
'修改内容可能还未保存,确定关闭吗?'
)
reset
()
resetEditor
()
d
one
()
onD
one
()
}
catch
{}
}
...
...
@@ -391,7 +391,7 @@ const resetEditor = () => {
}
// 将图文向下移动
const
downNews
=
(
index
)
=>
{
const
downNews
=
(
index
:
number
)
=>
{
let
temp
=
articlesAdd
.
value
[
index
]
articlesAdd
.
value
[
index
]
=
articlesAdd
.
value
[
index
+
1
]
articlesAdd
.
value
[
index
+
1
]
=
temp
...
...
@@ -407,13 +407,13 @@ const upNews = (index) => {
}
// 选中指定 index 的图文
const
activeNews
=
(
index
)
=>
{
const
activeNews
=
(
index
:
number
)
=>
{
resetEditor
()
isActiveAddNews
.
value
=
index
}
// 删除指定 index 的图文
const
minusNews
=
async
(
index
)
=>
{
const
minusNews
=
async
(
index
:
number
)
=>
{
try
{
await
message
.
confirm
(
'确定删除该图文吗?'
)
articlesAdd
.
value
.
splice
(
index
,
1
)
...
...
@@ -446,20 +446,17 @@ const buildEmptyArticle = () => {
}
// ======================== 文件上传 ========================
const
beforeThumbImageUpload
=
(
f
ile
)
=>
{
const
beforeThumbImageUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawF
ile
)
=>
{
addMaterialLoading
.
value
=
true
const
isType
=
file
.
type
===
'image/jpeg'
||
file
.
type
===
'image/png'
||
file
.
type
===
'image/gif'
||
file
.
type
===
'image/bmp'
||
file
.
type
===
'image/jpg'
const
isType
=
[
'image/jpeg'
,
'image/png'
,
'image/gif'
,
'image/bmp'
,
'image/jpg'
].
includes
(
rawFile
.
type
)
if
(
!
isType
)
{
message
.
error
(
'上传图片格式不对!'
)
addMaterialLoading
.
value
=
false
return
false
}
const
isLt
=
f
ile
.
size
/
1024
/
1024
<
2
const
isLt
=
rawF
ile
.
size
/
1024
/
1024
<
2
if
(
!
isLt
)
{
message
.
error
(
'上传图片大小不能超过 2M!'
)
addMaterialLoading
.
value
=
false
...
...
@@ -469,7 +466,7 @@ const beforeThumbImageUpload = (file) => {
return
true
}
const
handleUploadSuccess
=
(
response
,
file
,
fileList
)
=>
{
const
handleUploadSuccess
:
UploadProps
[
'onSuccess'
]
=
(
response
:
any
)
=>
{
addMaterialLoading
.
value
=
false
if
(
response
.
code
!==
0
)
{
message
.
error
(
'上传出错:'
+
response
.
msg
)
...
...
@@ -485,7 +482,7 @@ const handleUploadSuccess = (response, file, fileList) => {
}
// 选择 or 上传完素材,设置回草稿
const
selectMaterial
=
(
item
)
=>
{
const
selectMaterial
=
(
item
:
any
)
=>
{
dialogImageVisible
.
value
=
false
articlesAdd
.
value
[
isActiveAddNews
.
value
].
thumbMediaId
=
item
.
mediaId
articlesAdd
.
value
[
isActiveAddNews
.
value
].
thumbUrl
=
item
.
url
...
...
@@ -494,14 +491,10 @@ const selectMaterial = (item) => {
// 打开素材选择
const
openMaterial
=
()
=>
{
dialogImageVisible
.
value
=
true
try
{
materialSelectRef
.
value
.
queryParams
.
accountId
=
queryParams
.
accountId
// 强制设置下 accountId,避免二次查询不对
materialSelectRef
.
value
.
handleQuery
()
// 刷新列表,失败也无所谓
}
catch
(
e
)
{}
}
// ======================== 草稿箱发布 ========================
const
handlePublish
=
async
(
item
)
=>
{
const
handlePublish
=
async
(
item
:
any
)
=>
{
const
accountId
=
queryParams
.
accountId
const
mediaId
=
item
.
mediaId
const
content
=
...
...
@@ -510,19 +503,19 @@ const handlePublish = async (item) => {
await
message
.
confirm
(
content
)
await
MpFreePublishApi
.
submitFreePublish
(
accountId
,
mediaId
)
message
.
notifySuccess
(
'发布成功'
)
await
getList
()
getList
()
}
catch
{}
}
/** 删除按钮操作 */
const
handleDelete
=
async
(
item
)
=>
{
const
handleDelete
=
async
(
item
:
any
)
=>
{
const
accountId
=
queryParams
.
accountId
const
mediaId
=
item
.
mediaId
try
{
await
message
.
confirm
(
'此操作将永久删除该草稿, 是否继续?'
)
await
MpDraftApi
.
deleteDraft
(
accountId
,
mediaId
)
message
.
notifySuccess
(
'删除成功'
)
await
getList
()
getList
()
}
catch
{}
}
</
script
>
...
...
src/views/mp/freePublish/index.vue
View file @
df3b381d
...
...
@@ -3,8 +3,17 @@
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"(accountId) => accountChanged(accountId)"
/>
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
...
...
@@ -39,26 +48,32 @@
</ContentWrap>
</
template
>
<
script
setup
name=
"MpFreePublish"
>
<
script
lang=
"ts"
setup
name=
"MpFreePublish"
>
import
*
as
FreePublishApi
from
'@/api/mp/freePublish'
import
WxNews
from
'@/views/mp/components/wx-news/main.vue'
import
Wx
AccountSelect
from
'@/views/mp/components/wx-account-select/main
.vue'
import
Wx
MpSelect
from
'@/views/mp/components/WxMpSelect
.vue'
const
message
=
useMessage
()
// 消息弹窗
const
{
t
}
=
useI18n
()
// 国际化
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryParams
=
reactive
({
const
list
=
ref
<
any
[]
>
([])
// 列表的数据
interface
QueryParams
{
pageNo
:
number
pageSize
:
number
accountId
?:
number
}
const
queryParams
:
QueryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
accountId
:
undefined
})
/** 侦听公众号变化 **/
const
accountChanged
=
(
accountI
d
)
=>
{
queryParams
.
accountId
=
accountI
d
const
onAccountChanged
=
(
id
:
number
|
undefine
d
)
=>
{
queryParams
.
accountId
=
i
d
getList
()
}
...
...
@@ -75,7 +90,7 @@ const getList = async () => {
}
/** 删除按钮操作 */
const
handleDelete
=
async
(
item
)
=>
{
const
handleDelete
=
async
(
item
:
any
)
=>
{
try
{
// 删除的二次确认
await
message
.
delConfirm
(
'删除后用户将无法访问此页面,确定删除?'
)
...
...
src/views/mp/material/index.vue
View file @
df3b381d
...
...
@@ -4,13 +4,13 @@
<ContentWrap>
<el-form
class=
"-mb-15px"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"
accountChange
"
/>
<WxMpSelect
@
change=
"
onAccountChanged
"
/>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<el-tabs
v-model=
"type"
@
tab-change=
"
handle
TabChange"
>
<el-tabs
v-model=
"type"
@
tab-change=
"
on
TabChange"
>
<!-- tab 1:图片 -->
<el-tab-pane
name=
"image"
>
<template
#
label
>
...
...
@@ -93,7 +93,7 @@
<el-table-column
label=
"文件名"
align=
"center"
prop=
"name"
/>
<el-table-column
label=
"语音"
align=
"center"
>
<
template
#
default=
"scope"
>
<WxVoicePlayer
:url=
"scope.row.url"
/>
<WxVoicePlayer
v-if=
"scope.row.url"
:url=
"scope.row.url"
/>
</
template
>
</el-table-column>
<el-table-column
...
...
@@ -188,10 +188,8 @@
</el-row>
</el-form>
<
template
#
footer
>
<!--
<span
class=
"dialog-footer"
>
-->
<el-button
@
click=
"cancelVideo"
>
取 消
</el-button>
<el-button
type=
"primary"
@
click=
"submitVideo"
>
提 交
</el-button>
<!--
</span>
-->
</
template
>
</el-dialog>
...
...
@@ -203,7 +201,7 @@
<el-table-column
label=
"介绍"
align=
"center"
prop=
"introduction"
/>
<el-table-column
label=
"视频"
align=
"center"
>
<
template
#
default=
"scope"
>
<WxVideoPlayer
:url=
"scope.row.url"
/>
<WxVideoPlayer
v-if=
"scope.row.url"
:url=
"scope.row.url"
/>
</
template
>
</el-table-column>
<el-table-column
...
...
@@ -284,9 +282,9 @@ const type = ref<MatertialType>('image')
// 遮罩层
const
loading
=
ref
(
false
)
// 总条数
const
total
=
ref
(
0
)
// 数据列表
const
list
=
ref
([])
const
list
=
ref
<
any
[]
>
([])
const
total
=
ref
(
0
)
// 查询参数
interface
QueryParams
{
pageNo
:
number
...
...
@@ -319,8 +317,8 @@ const dialogVideoVisible = ref(false)
const
addMaterialLoading
=
ref
(
false
)
/** 侦听公众号变化 **/
const
accountChange
=
(
accountId
:
number
|
undefined
)
=>
{
queryParams
.
accountId
=
accountI
d
const
onAccountChanged
=
(
id
?:
number
)
=>
{
queryParams
.
accountId
=
i
d
getList
()
}
...
...
@@ -346,7 +344,7 @@ const handleQuery = () => {
getList
()
}
const
handle
TabChange
=
(
tabName
:
TabPaneName
)
=>
{
const
on
TabChange
=
(
tabName
:
TabPaneName
)
=>
{
// 设置 type
uploadData
.
type
=
tabName
as
MatertialType
...
...
@@ -359,54 +357,49 @@ const handleTabChange = (tabName: TabPaneName) => {
}
// ======================== 文件上传 ========================
const
beforeImageUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
{
const
isType
=
[
'image/jpeg'
,
'image/png'
,
'image/gif'
,
'image/bmp'
,
'image/jpg'
].
includes
(
rawFile
.
type
)
if
(
!
isType
)
{
message
.
error
(
'上传图片格式不对!'
)
return
false
const
beforeUpload
=
(
rawFile
:
UploadRawFile
,
category
:
'image'
|
'audio'
|
'video'
):
boolean
=>
{
let
allowTypes
:
string
[]
=
[]
let
maxSizeMB
=
0
let
name
=
''
switch
(
category
)
{
case
'image'
:
allowTypes
=
[
'image/jpeg'
,
'image/png'
,
'image/gif'
,
'image/bmp'
,
'image/jpg'
]
maxSizeMB
=
2
name
=
'图片'
break
case
'audio'
:
allowTypes
=
[
'audio/mp3'
,
'audio/mpeg'
,
'audio/wma'
,
'audio/wav'
,
'audio/amr'
]
maxSizeMB
=
2
name
=
'图片'
break
case
'video'
:
allowTypes
=
[
'video/mp4'
]
maxSizeMB
=
10
name
=
'视频'
}
const
isLt
=
rawFile
.
size
/
1024
/
1024
<
2
if
(
!
isLt
)
{
message
.
error
(
'上传图片大小不能超过 2M!'
)
return
false
}
loading
.
value
=
true
return
true
}
const
beforeVoiceUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
{
const
isType
=
[
'audio/mp3'
,
'audio/wma'
,
'audio/wav'
,
'audio/amr'
].
includes
(
file
.
type
)
const
isLt
=
rawFile
.
size
/
1024
/
1024
<
2
if
(
!
isType
)
{
message
.
error
(
'上传语音格式不对!'
)
if
(
!
allowTypes
.
includes
(
rawFile
.
type
))
{
message
.
error
(
`上传
${
name
}
格式不对!`
)
return
false
}
if
(
!
isLt
)
{
message
.
error
(
'上传语音大小不能超过 2M!'
)
// 校验大小
if
(
rawFile
.
size
/
1024
/
1024
>
maxSizeMB
)
{
message
.
error
(
`上传
${
name
}
大小不能超过
${
maxSizeMB
}
M!`
)
return
false
}
loading
.
value
=
true
return
true
}
const
beforeVideoUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
{
const
isType
=
rawFile
.
type
===
'video/mp4'
if
(
!
isType
)
{
message
.
error
(
'上传视频格式不对!'
)
return
false
}
const
beforeImageUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
beforeUpload
(
rawFile
,
'image'
)
const
isLt
=
rawFile
.
size
/
1024
/
1024
<
10
if
(
!
isLt
)
{
message
.
error
(
'上传视频大小不能超过 10M!'
)
return
false
}
const
beforeVoiceUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
beforeUpload
(
rawFile
,
'audio'
)
addMaterialLoading
.
value
=
true
return
true
}
const
beforeVideoUpload
:
UploadProps
[
'beforeUpload'
]
=
(
rawFile
:
UploadRawFile
)
=>
beforeUpload
(
rawFile
,
'video'
)
const
handleUploadSuccess
:
UploadProps
[
'onSuccess'
]
=
(
response
:
any
)
=>
{
loading
.
value
=
false
...
...
@@ -441,6 +434,7 @@ const submitVideo = () => {
})
}
// 弹出 video 新建的表单
const
handleAddVideo
=
()
=>
{
resetVideo
()
dialogVideoVisible
.
value
=
true
...
...
src/views/mp/menu/index.vue
View file @
df3b381d
...
...
@@ -2,8 +2,11 @@
<doc-alert
title=
"公众号菜单"
url=
"https://doc.iocoder.cn/mp/menu/"
/>
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"accountChanged"
/>
<el-form
class=
"-mb-15px"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
...
...
@@ -12,7 +15,7 @@
<!--左边配置菜单-->
<div
class=
"left"
>
<div
class=
"weixin-hd"
>
<div
class=
"weixin-title"
>
{{
n
ame
}}
</div>
<div
class=
"weixin-title"
>
{{
accountN
ame
}}
</div>
</div>
<div
class=
"weixin-menu menu_main clearfix"
>
<div
class=
"menu_bottom"
v-for=
"(item, i) of menuList"
:key=
"i"
>
...
...
@@ -68,7 +71,7 @@
<div
v-if=
"showRightFlag"
class=
"right"
>
<div
class=
"configure_page"
>
<div
class=
"delete_btn"
>
<el-button
size=
"small"
type=
"danger"
@
click=
"handleDeleteMenu
(tempObj)
"
>
<el-button
size=
"small"
type=
"danger"
@
click=
"handleDeleteMenu"
>
删除当前菜单
<Icon
icon=
"ep:delete"
/>
</el-button>
</div>
...
...
@@ -155,7 +158,7 @@
<div
v-else
>
<el-row
justify=
"center"
>
<el-col
:span=
"24"
style=
"text-align: center"
>
<el-button
type=
"success"
@
click=
"
openMaterial
"
>
<el-button
type=
"success"
@
click=
"
dialogNewsVisible = true
"
>
素材库选择
<Icon
icon=
"ep:circle-check"
/>
</el-button>
</el-col>
...
...
@@ -185,24 +188,26 @@
</div>
</ContentWrap>
</
template
>
<
script
setup
name=
"MpMenu"
>
import
{
handleTree
}
from
'@/utils/tree'
<
script
lang=
"ts"
setup
name=
"MpMenu"
>
import
WxReplySelect
from
'@/views/mp/components/wx-reply/main.vue'
import
WxNews
from
'@/views/mp/components/wx-news/main.vue'
import
WxMaterialSelect
from
'@/views/mp/components/wx-material-select/main.vue'
import
Wx
AccountSelect
from
'@/views/mp/components/wx-account-select/main
.vue'
import
Wx
MpSelect
from
'@/views/mp/components/WxMpSelect
.vue'
import
*
as
MpMenuApi
from
'@/api/mp/menu'
import
{
handleTree
}
from
'@/utils/tree'
import
menuOptions
from
'./menuOptions'
const
message
=
useMessage
()
// 消息
// ======================== 列表查询 ========================
const
loading
=
ref
(
true
)
// 遮罩层
const
accountId
=
ref
(
undefined
)
// 公众号Id
const
menuList
=
ref
({
children
:
[]
})
const
loading
=
ref
(
false
)
// 遮罩层
const
accountId
=
ref
<
number
|
undefined
>
()
const
accountName
=
ref
<
string
|
undefined
>
(
''
)
const
menuList
=
ref
<
any
>
({
children
:
[]
})
// ======================== 菜单操作 ========================
const
isActive
=
ref
(
-
1
)
// 一级菜单点中样式
const
isSubMenuActive
=
ref
(
-
1
)
// 一级菜单点中样式
const
isSubMenuActive
=
ref
<
string
|
number
>
(
-
1
)
// 一级菜单点中样式
const
isSubMenuFlag
=
ref
(
-
1
)
// 二级菜单显示标志
// ======================== 菜单编辑 ========================
...
...
@@ -210,15 +215,16 @@ const showRightFlag = ref(false) // 右边配置显示默认详情还是配置
const
nameMaxLength
=
ref
(
0
)
// 菜单名称最大长度;1 级是 4 字符;2 级是 7 字符;
const
showConfigureContent
=
ref
(
true
)
// 是否展示配置内容;如果有子菜单,就不显示配置内容
const
hackResetWxReplySelect
=
ref
(
false
)
// 重置 WxReplySelect 组件
const
tempObj
=
ref
({})
// 右边临时变量,作为中间值牵引关系
const
tempObj
=
ref
<
any
>
({})
// 右边临时变量,作为中间值牵引关系
// 一些临时值放在这里进行判断,如果放在 tempObj,由于引用关系,menu 也会多了多余的参数
const
tempSelfObj
=
ref
({})
const
tempSelfObj
=
ref
<
any
>
({})
const
dialogNewsVisible
=
ref
(
false
)
// 跳转图文时的素材选择弹窗
/** 侦听公众号变化 **/
const
accountChanged
=
(
id
)
=>
{
const
onAccountChanged
=
(
id
?:
number
,
name
?:
string
)
=>
{
accountId
.
value
=
id
accountName
.
value
=
name
getList
()
}
...
...
@@ -241,10 +247,10 @@ const handleQuery = () => {
}
// 将后端返回的 menuList,转换成前端的 menuList
const
convertMenuList
=
(
list
)
=>
{
const
convertMenuList
=
(
list
:
any
[]
)
=>
{
if
(
!
list
)
return
[]
const
menuList
=
[]
const
result
:
any
[]
=
[]
list
.
forEach
((
item
)
=>
{
const
menu
=
{
...
item
...
...
@@ -271,9 +277,9 @@ const convertMenuList = (list) => {
hqMusicUrl
:
item
.
replyHqMusicUrl
}
}
menuLis
t
.
push
(
menu
)
resul
t
.
push
(
menu
)
})
return
menuLis
t
return
resul
t
}
// 重置表单,清空表单数据
...
...
@@ -286,7 +292,7 @@ const resetForm = () => {
// 菜单编辑
showRightFlag
.
value
=
false
nameMaxLength
.
value
=
0
showConfigureContent
.
value
=
0
showConfigureContent
.
value
=
false
hackResetWxReplySelect
.
value
=
false
tempObj
.
value
=
{}
tempSelfObj
.
value
=
{}
...
...
@@ -295,7 +301,7 @@ const resetForm = () => {
// ======================== 菜单操作 ========================
// 一级菜单点击事件
const
menuClick
=
(
i
,
item
)
=>
{
const
menuClick
=
(
i
:
number
,
item
:
any
)
=>
{
// 右侧的表单相关
resetEditor
()
showRightFlag
.
value
=
true
// 右边菜单
...
...
@@ -312,11 +318,10 @@ const menuClick = (i, item) => {
}
// 二级菜单点击事件
const
subMenuClick
=
(
subItem
,
index
,
k
)
=>
{
const
subMenuClick
=
(
subItem
:
any
,
index
:
number
,
k
:
number
)
=>
{
// 右侧的表单相关
resetEditor
()
showRightFlag
.
value
=
true
// 右边菜单
console
.
log
(
subItem
)
tempObj
.
value
=
subItem
// 将点击的数据放到临时变量,对象有引用作用
tempSelfObj
.
value
.
grand
=
'2'
// 表示二级菜单
tempSelfObj
.
value
.
index
=
index
// 表示一级菜单索引
...
...
@@ -331,7 +336,7 @@ const subMenuClick = (subItem, index, k) => {
// 添加横向一级菜单
const
addMenu
=
()
=>
{
const
menuKeyLength
=
menuList
.
value
.
length
const
menuKeyLength
:
number
=
menuList
.
value
.
length
const
addButton
=
{
name
:
'菜单名称'
,
children
:
[],
...
...
@@ -342,10 +347,10 @@ const addMenu = () => {
}
}
menuList
.
value
[
menuKeyLength
]
=
addButton
menuClick
(
menuKeyLength
.
value
-
1
,
addButton
)
menuClick
(
menuKeyLength
-
1
,
addButton
)
}
// 添加横向二级菜单;item 表示要操作的父菜单
const
addSubMenu
=
(
i
,
item
)
=>
{
const
addSubMenu
=
(
i
:
number
,
item
:
any
)
=>
{
// 清空父菜单的属性,因为它只需要 name 属性即可
if
(
!
item
.
children
||
item
.
children
.
length
<=
0
)
{
item
.
children
=
[]
...
...
@@ -361,8 +366,8 @@ const addSubMenu = (i, item) => {
showConfigureContent
.
value
=
false
}
le
t
subMenuKeyLength
=
item
.
children
.
length
// 获取二级菜单key长度
le
t
addButton
=
{
cons
t
subMenuKeyLength
=
item
.
children
.
length
// 获取二级菜单key长度
cons
t
addButton
=
{
name
:
'子菜单名称'
,
reply
:
{
// 用于存储回复内容
...
...
@@ -399,7 +404,7 @@ const handleDeleteMenu = async () => {
// ======================== 菜单编辑 ========================
const
handleSave
=
async
()
=>
{
try
{
await
message
.
confirm
(
'确定要
删除
吗?'
)
await
message
.
confirm
(
'确定要
保存
吗?'
)
loading
.
value
=
true
await
MpMenuApi
.
saveMenu
(
accountId
.
value
,
convertMenuFormList
())
getList
()
...
...
@@ -413,7 +418,6 @@ const handleSave = async () => {
const
resetEditor
=
()
=>
{
hackResetWxReplySelect
.
value
=
false
// 销毁组件
nextTick
(()
=>
{
console
.
log
(
'nextTick'
)
hackResetWxReplySelect
.
value
=
true
// 重建组件
})
}
...
...
@@ -432,9 +436,9 @@ const handleDelete = async () => {
// 将前端的 menuList,转换成后端接收的 menuList
const
convertMenuFormList
=
()
=>
{
const
result
=
[]
const
result
:
any
[]
=
[]
menuList
.
value
.
forEach
((
item
)
=>
{
le
t
menu
=
convertMenuForm
(
item
)
cons
t
menu
=
convertMenuForm
(
item
)
result
.
push
(
menu
)
// 处理子菜单
...
...
@@ -450,7 +454,7 @@ const convertMenuFormList = () => {
}
// 将前端的 menu,转换成后端接收的 menu
const
convertMenuForm
=
(
menu
)
=>
{
const
convertMenuForm
=
(
menu
:
any
)
=>
{
let
result
=
{
...
menu
,
children
:
undefined
,
// 不处理子节点
...
...
@@ -473,11 +477,7 @@ const convertMenuForm = (menu) => {
}
// ======================== 菜单编辑(素材选择) ========================
const
openMaterial
=
()
=>
{
dialogNewsVisible
.
value
=
true
}
const
selectMaterial
=
(
item
)
=>
{
const
selectMaterial
=
(
item
:
any
)
=>
{
const
articleId
=
item
.
articleId
const
articles
=
item
.
content
.
newsItem
// 提示,针对多图文
...
...
src/views/mp/tag/TagForm.vue
View file @
df3b381d
...
...
@@ -19,24 +19,30 @@
</template>
<
script
setup
lang=
"ts"
>
import
*
as
MpTagApi
from
'@/api/mp/tag'
import
{
FormInstance
,
FormRules
}
from
'element-plus'
const
{
t
}
=
useI18n
()
// 国际化
const
message
=
useMessage
()
// 消息弹窗
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
''
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
formType
=
ref
(
''
)
// 表单的类型:create - 新增;update - 修改
const
formType
=
ref
<
'create'
|
'update'
|
''
>
(
''
)
// 表单的类型:create - 新增;update - 修改
const
formData
=
ref
({
accountId
:
-
1
,
name
:
''
})
const
formRules
=
reactive
(
{
const
formRules
:
FormRules
=
{
name
:
[{
required
:
true
,
message
:
'请输入标签名称'
,
trigger
:
'blur'
}]
})
const
formRef
=
ref
()
// 表单 Ref
}
const
formRef
=
ref
<
FormInstance
|
null
>
(
null
)
// 表单 Ref
const
emit
=
defineEmits
<
{
(
e
:
'success'
):
void
}
>
()
/** 打开弹窗 */
const
open
=
async
(
type
:
string
,
accountId
:
number
,
id
?:
number
)
=>
{
const
open
=
async
(
type
:
'create'
|
'update'
,
accountId
:
number
,
id
?:
number
)
=>
{
dialogVisible
.
value
=
true
dialogTitle
.
value
=
t
(
'action.'
+
type
)
formType
.
value
=
type
...
...
@@ -55,11 +61,10 @@ const open = async (type: string, accountId: number, id?: number) => {
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
/** 提交表单 */
const
emit
=
defineEmits
([
'success'
])
// 定义 success 事件,用于操作成功后的回调
const
submitForm
=
async
()
=>
{
// 校验表单
if
(
!
formRef
)
return
const
valid
=
await
formRef
.
value
.
validate
()
const
valid
=
await
formRef
.
value
?
.
validate
()
if
(
!
valid
)
return
// 提交请求
formLoading
.
value
=
true
...
...
src/views/mp/tag/index.vue
View file @
df3b381d
...
...
@@ -3,17 +3,25 @@
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"accountChanged"
>
<template
#
actions
>
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
<el-form-item>
<el-button
type=
"primary"
plain
@
click=
"openForm('create')"
v-hasPermi=
"['mp:tag:create']"
>
<Icon
icon=
"ep:plus"
class=
"mr-5px"
/>
新增
</el-button>
<el-button
type=
"success"
plain
@
click=
"handleSync"
v-hasPermi=
"['mp:tag:sync']"
>
<Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
同步
</el-button>
</
template
>
</
WxAccountSelect
>
</
el-form-item
>
</
el-form
>
</ContentWrap>
<!-- 列表 -->
...
...
@@ -63,26 +71,35 @@
<TagForm
ref=
"formRef"
@
success=
"getList"
/>
</template>
<
script
setup
lang=
"ts"
name=
"MpTag"
>
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
WxAccountSelect
from
'@/views/mp/components/wx-account-select/main.vue'
import
*
as
MpTagApi
from
'@/api/mp/tag'
import
TagForm
from
'./TagForm.vue'
import
WxMpSelect
from
'@/views/mp/components/WxMpSelect.vue'
import
{
dateFormatter
}
from
'@/utils/formatTime'
const
message
=
useMessage
()
// 消息弹窗
const
{
t
}
=
useI18n
()
// 国际化
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryParams
=
reactive
({
const
list
=
ref
<
any
>
([])
// 列表的数据
interface
QueryParams
{
pageNo
:
number
pageSize
:
number
accountId
?:
number
}
const
queryParams
:
QueryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
accountId
:
undefined
})
const
formRef
=
ref
<
InstanceType
<
typeof
TagForm
>
|
null
>
(
null
)
/** 侦听公众号变化 **/
const
accountChanged
=
(
accountId
)
=>
{
const
onAccountChanged
=
(
id
?:
number
)
=>
{
queryParams
.
pageNo
=
1
queryParams
.
accountId
=
accountI
d
queryParams
.
accountId
=
i
d
getList
()
}
...
...
@@ -99,9 +116,8 @@ const getList = async () => {
}
/** 添加/修改操作 */
const
formRef
=
ref
()
const
openForm
=
(
type
:
string
,
id
?:
number
)
=>
{
formRef
.
value
.
open
(
type
,
queryParams
.
accountId
,
id
)
formRef
.
value
?.
open
(
type
,
queryParams
.
accountId
as
number
,
id
)
}
/** 删除按钮操作 */
...
...
@@ -121,8 +137,7 @@ const handleDelete = async (id: number) => {
const
handleSync
=
async
()
=>
{
try
{
await
message
.
confirm
(
'是否确认同步标签?'
)
// @ts-ignore
await
MpTagApi
.
syncTag
(
queryParams
.
accountId
)
await
MpTagApi
.
syncTag
(
queryParams
.
accountId
as
number
)
message
.
success
(
'同步标签成功'
)
await
getList
()
}
catch
{}
...
...
src/views/mp/user/index.vue
View file @
df3b381d
...
...
@@ -3,14 +3,42 @@
<!-- 搜索工作栏 -->
<ContentWrap>
<!-- TODO @芋艿:调整成 el-form 和 WxAccountSelect -->
<WxAccountSelect
@
change=
"(accountId) => accountChanged(accountId)"
>
<template
#
actions
>
<el-form
class=
"-mb-15px"
:model=
"queryParams"
ref=
"queryFormRef"
:inline=
"true"
label-width=
"68px"
>
<el-form-item
label=
"公众号"
prop=
"accountId"
>
<WxMpSelect
@
change=
"onAccountChanged"
/>
</el-form-item>
<el-form-item
label=
"用户标识"
prop=
"openid"
>
<el-input
v-model=
"queryParams.openid"
placeholder=
"请输入用户标识"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item
label=
"昵称"
prop=
"nickname"
>
<el-input
v-model=
"queryParams.nickname"
placeholder=
"请输入昵称"
clearable
@
keyup
.
enter=
"handleQuery"
class=
"!w-240px"
/>
</el-form-item>
<el-form-item>
<el-button
@
click=
"handleQuery"
>
<Icon
icon=
"ep:search"
/>
搜索
</el-button>
<el-button
@
click=
"resetQuery"
>
<Icon
icon=
"ep:refresh"
/>
重置
</el-button>
<el-button
type=
"success"
plain
@
click=
"handleSync"
v-hasPermi=
"['mp:user:sync']"
>
<Icon
icon=
"ep:refresh"
class=
"mr-5px"
/>
同步
</el-button>
</
template
>
</
WxAccountSelect
>
</
el-form-item
>
</
el-form
>
</ContentWrap>
<!-- 列表 -->
...
...
@@ -66,25 +94,37 @@
<UserForm
ref=
"formRef"
@
success=
"getList"
/>
</template>
<
script
lang=
"ts"
setup
name=
"MpUser"
>
import
WxAccountSelect
from
'@/views/mp/components/wx-account-select/main.vue'
import
{
dateFormatter
}
from
'@/utils/formatTime'
import
*
as
MpUserApi
from
'@/api/mp/user'
import
*
as
MpTagApi
from
'@/api/mp/tag'
import
WxMpSelect
from
'@/views/mp/components/WxMpSelect.vue'
import
{
FormInstance
}
from
'element-plus'
import
UserForm
from
'./UserForm.vue'
const
message
=
useMessage
()
// 消息
const
loading
=
ref
(
true
)
// 列表的加载中
const
total
=
ref
(
0
)
// 列表的总页数
const
list
=
ref
([])
// 列表的数据
const
queryParams
=
reactive
({
const
list
=
ref
<
any
[]
>
([])
// 列表的数据
interface
QueryParams
{
pageNo
:
number
pageSize
:
number
accountId
?:
number
openid
:
string
|
null
nickname
:
string
|
null
}
const
queryParams
:
QueryParams
=
reactive
({
pageNo
:
1
,
pageSize
:
10
,
accountId
:
null
,
accountId
:
undefined
,
openid
:
null
,
nickname
:
null
})
const
tagList
=
ref
([])
// 公众号标签列表
const
tagList
=
ref
<
any
[]
>
([])
// 公众号标签列表
const
queryFormRef
=
ref
<
FormInstance
|
null
>
(
null
)
// 搜索的表单
const
formRef
=
ref
<
InstanceType
<
typeof
UserForm
>
|
null
>
(
null
)
/** 初始化 */
onMounted
(
async
()
=>
{
...
...
@@ -92,9 +132,9 @@ onMounted(async () => {
})
/** 侦听公众号变化 **/
const
accountChanged
=
(
accountId
)
=>
{
const
onAccountChanged
=
(
id
?:
number
)
=>
{
queryParams
.
pageNo
=
1
queryParams
.
accountId
=
accountI
d
queryParams
.
accountId
=
i
d
getList
()
}
...
...
@@ -110,20 +150,32 @@ const getList = async () => {
}
}
/** 搜索按钮操作 */
const
handleQuery
=
()
=>
{
queryParams
.
pageNo
=
1
getList
()
}
/** 重置按钮操作 */
const
resetQuery
=
()
=>
{
const
accountId
=
queryParams
.
accountId
queryFormRef
.
value
?.
resetFields
()
queryParams
.
accountId
=
accountId
handleQuery
()
}
/** 添加/修改操作 */
const
formRef
=
ref
()
const
openForm
=
(
id
:
number
)
=>
{
formRef
.
value
.
open
(
id
)
formRef
.
value
?
.
open
(
id
)
}
/** 同步标签 */
const
handleSync
=
async
()
=>
{
const
accountId
=
queryParams
.
accountId
try
{
await
message
.
confirm
(
'是否确认同步粉丝?'
)
await
MpUserApi
.
syncUser
(
accountId
)
await
MpUserApi
.
syncUser
(
queryParams
.
accountId
)
message
.
success
(
'开始从微信公众号同步粉丝信息,同步需要一段时间,建议稍后再查询'
)
await
getList
()
getList
()
}
catch
{}
}
</
script
>
src/views/pay/order/orderForm.vue
0 → 100644
View file @
df3b381d
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
width=
"50%"
>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"商户名称"
>
{{
orderDetail
.
merchantName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"应用名称"
>
{{
orderDetail
.
appName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"商品名称"
>
{{
orderDetail
.
subject
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"商户订单号"
>
<el-tag
size=
"small"
>
{{
orderDetail
.
merchantOrderId
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"渠道订单号"
>
<el-tag
class=
"tag-purple"
size=
"small"
>
{{
orderDetail
.
channelOrderNo
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"支付订单号"
>
<el-tag
v-if=
"orderDetail.payOrderExtension.no !== ''"
class=
"tag-pink"
size=
"small"
>
{{
orderDetail
.
payOrderExtension
.
no
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"金额"
>
<el-tag
type=
"success"
size=
"small"
>
{{
parseFloat
(
orderDetail
.
amount
/
100
,
2
)
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"手续费"
>
<el-tag
type=
"warning"
size=
"small"
>
{{
parseFloat
(
orderDetail
.
channelFeeAmount
/
100
,
2
)
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"手续费比例"
>
{{
parseFloat
(
orderDetail
.
channelFeeRate
/
100
,
2
)
}}
%
</el-descriptions-item>
<el-descriptions-item
label=
"支付状态"
>
<dict-tag
:type=
"DICT_TYPE.PAY_ORDER_STATUS"
:value=
"orderDetail.status"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"回调状态"
>
<dict-tag
:type=
"DICT_TYPE.PAY_ORDER_NOTIFY_STATUS"
:value=
"orderDetail.notifyStatus"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"回调地址"
>
{{
orderDetail
.
notifyUrl
}}
</el-descriptions-item>
<el-descriptions-item
label=
"创建时间"
:formatter=
"dateFormatter"
>
{{
formatDate
(
orderDetail
.
createTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"支付时间"
:formatter=
"dateFormatter"
>
{{
formatDate
(
orderDetail
.
successTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"失效时间"
:formatter=
"dateFormatter"
>
{{
formatDate
(
orderDetail
.
expireTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"通知时间"
:formatter=
"dateFormatter"
>
{{
formatDate
(
orderDetail
.
notifyTime
)
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"支付渠道"
>
{{
orderDetail
.
channelCodeName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"支付IP"
>
{{
orderDetail
.
userIp
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款状态"
>
<dict-tag
:type=
"DICT_TYPE.PAY_ORDER_REFUND_STATUS"
:value=
"orderDetail.refundStatus"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"退款次数"
>
{{
orderDetail
.
refundTimes
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款金额"
>
<el-tag
type=
"warning"
>
{{
parseFloat
(
orderDetail
.
refundAmount
/
100
,
2
)
}}
</el-tag>
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"1"
label-class-name=
"desc-label"
direction=
"vertical"
border
>
<el-descriptions-item
label=
"商品描述"
>
{{
orderDetail
.
body
}}
</el-descriptions-item>
<el-descriptions-item
label=
"支付通道异步回调内容"
>
{{
orderDetail
.
payOrderExtension
.
channelNotifyData
}}
</el-descriptions-item>
</el-descriptions>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
name=
"orderForm"
>
import
{
DICT_TYPE
}
from
'@/utils/dict'
import
*
as
OrderApi
from
'@/api/pay/order'
import
{
dateFormatter
,
formatDate
}
from
'@/utils/formatTime'
const
{
t
}
=
useI18n
()
// 国际化
// const message = useMessage() // 消息弹窗
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
'订单详情'
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
defaultOrderDetail
=
{
merchantName
:
''
,
appName
:
''
,
channelCodeName
:
''
,
subject
:
''
,
merchantOrderId
:
null
,
channelOrderNo
:
''
,
body
:
''
,
amount
:
null
,
channelFeeRate
:
null
,
channelFeeAmount
:
null
,
userIp
:
''
,
status
:
null
,
notifyUrl
:
''
,
notifyStatus
:
null
,
refundStatus
:
null
,
refundTimes
:
''
,
refundAmount
:
null
,
createTime
:
''
,
successTime
:
''
,
notifyTime
:
''
,
expireTime
:
''
,
payOrderExtension
:
{
channelNotifyData
:
''
,
no
:
''
}
}
const
orderDetail
=
ref
(
JSON
.
parse
(
JSON
.
stringify
(
defaultOrderDetail
)))
/** 打开弹窗 */
const
open
=
async
(
id
?:
number
)
=>
{
dialogVisible
.
value
=
true
dialogTitle
.
value
=
t
(
'action.preview'
)
// 修改时,设置数据
if
(
id
)
{
formLoading
.
value
=
true
try
{
orderDetail
.
value
=
await
OrderApi
.
getOrderDetail
(
id
)
if
(
orderDetail
.
value
.
payOrderExtension
===
null
)
{
orderDetail
.
value
.
payOrderExtension
=
Object
.
assign
(
defaultOrderDetail
.
payOrderExtension
,
{}
)
}
}
finally
{
formLoading
.
value
=
false
}
}
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
</
script
>
<
style
>
.tag-purple
{
color
:
#722ed1
;
background
:
#f9f0ff
;
border-color
:
#d3adf7
;
}
.tag-pink
{
color
:
#eb2f96
;
background
:
#fff0f6
;
border-color
:
#ffadd2
;
}
</
style
>
src/views/pay/refund/refundForm.vue
0 → 100644
View file @
df3b381d
<
template
>
<Dialog
:title=
"dialogTitle"
v-model=
"dialogVisible"
width=
"50%"
>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"商户名称"
>
{{
refundDetail
.
merchantName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"应用名称"
>
{{
refundDetail
.
appName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"商品名称"
>
{{
refundDetail
.
subject
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"商户退款单号"
>
<el-tag
size=
"small"
>
{{
refundDetail
.
merchantRefundNo
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"商户订单号"
>
{{
refundDetail
.
merchantOrderId
}}
</el-descriptions-item>
<el-descriptions-item
label=
"交易订单号"
>
{{
refundDetail
.
tradeNo
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"支付金额"
>
{{
parseFloat
(
refundDetail
.
payAmount
/
100
).
toFixed
(
2
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款金额"
size=
"small"
>
<el-tag
class=
"tag-purple"
size=
"small"
>
{{
parseFloat
(
refundDetail
.
refundAmount
/
100
).
toFixed
(
2
)
}}
</el-tag>
</el-descriptions-item>
<el-descriptions-item
label=
"退款类型"
>
<dict-tag
:type=
"DICT_TYPE.PAY_REFUND_ORDER_TYPE"
:value=
"refundDetail.type"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"退款状态"
>
<dict-tag
:type=
"DICT_TYPE.PAY_REFUND_ORDER_STATUS"
:value=
"refundDetail.status"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"创建时间"
>
{{
formatDate
(
refundDetail
.
createTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款成功时间"
>
{{
formatDate
(
refundDetail
.
successTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款失效时间"
>
{{
formatDate
(
refundDetail
.
expireTime
)
}}
</el-descriptions-item>
<el-descriptions-item
label=
"更新时间"
>
{{
formatDate
(
refundDetail
.
updateTime
)
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"支付渠道"
>
{{
refundDetail
.
channelCodeName
}}
</el-descriptions-item>
<el-descriptions-item
label=
"支付IP"
size=
"small"
>
{{
refundDetail
.
userIp
}}
</el-descriptions-item>
<el-descriptions-item
label=
"回调地址"
>
{{
refundDetail
.
notifyUrl
}}
</el-descriptions-item>
<el-descriptions-item
label=
"回调状态"
>
<dict-tag
:type=
"DICT_TYPE.PAY_ORDER_NOTIFY_STATUS"
:value=
"refundDetail.notifyStatus"
/>
</el-descriptions-item>
<el-descriptions-item
label=
"回调时间"
>
{{
formatDate
(
refundDetail
.
notifyTime
)
}}
</el-descriptions-item>
</el-descriptions>
<el-divider
/>
<el-descriptions
:column=
"2"
label-class-name=
"desc-label"
>
<el-descriptions-item
label=
"渠道订单号"
>
{{
refundDetail
.
channelOrderNo
}}
</el-descriptions-item>
<el-descriptions-item
label=
"渠道退款单号"
>
{{
refundDetail
.
channelRefundNo
}}
</el-descriptions-item>
<el-descriptions-item
label=
"渠道错误码"
>
{{
refundDetail
.
channelErrorCode
}}
</el-descriptions-item>
<el-descriptions-item
label=
"渠道错误码描述"
>
{{
refundDetail
.
channelErrorMsg
}}
</el-descriptions-item>
</el-descriptions>
<br
/>
<el-descriptions
:column=
"1"
label-class-name=
"desc-label"
direction=
"vertical"
border
>
<el-descriptions-item
label=
"渠道额外参数"
>
{{
refundDetail
.
channelExtras
}}
</el-descriptions-item>
<el-descriptions-item
label=
"退款原因"
>
{{
refundDetail
.
reason
}}
</el-descriptions-item>
</el-descriptions>
</Dialog>
</
template
>
<
script
setup
lang=
"ts"
name=
"refundForm"
>
import
{
DICT_TYPE
}
from
'@/utils/dict'
import
*
as
RefundApi
from
'@/api/pay/refund'
import
{
formatDate
}
from
'@/utils/formatTime'
const
{
t
}
=
useI18n
()
// 国际化
// const message = useMessage() // 消息弹窗
const
dialogVisible
=
ref
(
false
)
// 弹窗的是否展示
const
dialogTitle
=
ref
(
'退款订单详情'
)
// 弹窗的标题
const
formLoading
=
ref
(
false
)
// 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
const
defaultrefundDetail
=
{
id
:
null
,
appId
:
null
,
appName
:
''
,
channelCode
:
''
,
channelCodeName
:
''
,
channelErrorCode
:
''
,
channelErrorMsg
:
''
,
channelExtras
:
''
,
channelId
:
null
,
channelOrderNo
:
''
,
channelRefundNo
:
''
,
createTime
:
null
,
expireTime
:
null
,
merchantId
:
null
,
merchantName
:
''
,
merchantOrderId
:
''
,
merchantRefundNo
:
''
,
notifyStatus
:
null
,
notifyTime
:
null
,
notifyUrl
:
''
,
orderId
:
null
,
payAmount
:
null
,
reason
:
''
,
refundAmount
:
null
,
status
:
null
,
subject
:
''
,
successTime
:
null
,
tradeNo
:
''
,
type
:
null
,
userIp
:
''
}
const
refundDetail
=
ref
(
JSON
.
parse
(
JSON
.
stringify
(
defaultrefundDetail
)))
/** 打开弹窗 */
const
open
=
async
(
id
?:
number
)
=>
{
dialogVisible
.
value
=
true
dialogTitle
.
value
=
t
(
'action.preview'
)
// 修改时,设置数据
if
(
id
)
{
formLoading
.
value
=
true
try
{
refundDetail
.
value
=
await
RefundApi
.
getRefundApi
(
id
)
}
finally
{
formLoading
.
value
=
false
}
}
}
defineExpose
({
open
})
// 提供 open 方法,用于打开弹窗
</
script
>
<
style
>
.tag-purple
{
color
:
#722ed1
;
background
:
#f9f0ff
;
border-color
:
#d3adf7
;
}
</
style
>
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