From b45b85984ce55f09e80d0b3a4ba7e788ad1c8742 Mon Sep 17 00:00:00 2001 From: dhb52 <dhb52@126.com> Date: Fri, 21 Apr 2023 20:22:11 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20mp=E6=A8=A1=E5=9D=97=EF=BC=8C?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=A4=A7=E5=A4=A7=E7=9A=84=E9=87=8D=E6=9E=84?= =?UTF-8?q?+fix?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mp/autoReply/components/ReplyTable.vue | 8 +- src/views/mp/autoReply/components/types.ts | 41 ------- src/views/mp/autoReply/index.vue | 55 ++++----- .../mp/components/wx-account-select/index.ts | 3 + .../mp/components/wx-account-select/main.vue | 14 ++- src/views/mp/components/wx-location/index.ts | 3 + .../mp/components/wx-material-select/index.ts | 6 + .../mp/components/wx-material-select/main.vue | 58 +++++----- .../mp/components/wx-material-select/types.ts | 11 ++ src/views/mp/components/wx-msg/index.ts | 6 + src/views/mp/components/wx-msg/main.vue | 28 ++--- src/views/mp/components/wx-music/index.ts | 3 + src/views/mp/components/wx-news/index.ts | 3 + src/views/mp/components/wx-news/main.vue | 36 +++--- .../wx-reply/components/TabImage.vue | 50 ++++----- .../wx-reply/components/TabMusic.vue | 41 ++++--- .../wx-reply/components/TabNews.vue | 34 +++--- .../wx-reply/components/TabText.vue | 11 +- .../wx-reply/components/TabVideo.vue | 52 ++++----- .../wx-reply/components/TabVoice.vue | 54 ++++----- .../components/wx-reply/components/types.ts | 59 +++++++--- src/views/mp/components/wx-reply/index.ts | 7 ++ src/views/mp/components/wx-reply/main.vue | 105 ++++++++++++++---- .../mp/components/wx-video-play/index.ts | 3 + .../mp/components/wx-voice-play/index.ts | 3 + .../mp/components/wx-voice-play/main.vue | 6 +- src/views/mp/draft/components/CoverSelect.vue | 36 ++---- src/views/mp/draft/components/DraftTable.vue | 2 +- src/views/mp/draft/index.vue | 9 +- src/views/mp/freePublish/index.vue | 10 +- src/views/mp/hooks/useUpload.ts | 12 +- .../mp/material/components/UploadFile.vue | 15 +-- .../mp/material/components/UploadVideo.vue | 28 ++--- .../mp/material/components/VideoTable.vue | 2 +- .../mp/material/components/VoiceTable.vue | 2 +- src/views/mp/material/components/upload.ts | 12 +- src/views/mp/material/index.vue | 28 ++--- src/views/mp/menu/components/MenuEditor.vue | 14 +-- src/views/mp/menu/index.vue | 8 +- src/views/mp/message/MessageTable.vue | 10 +- src/views/mp/message/index.vue | 20 ++-- src/views/mp/tag/index.vue | 32 ++++-- src/views/mp/user/index.vue | 16 ++- 43 files changed, 518 insertions(+), 438 deletions(-) create mode 100644 src/views/mp/components/wx-account-select/index.ts create mode 100644 src/views/mp/components/wx-location/index.ts create mode 100644 src/views/mp/components/wx-material-select/index.ts create mode 100644 src/views/mp/components/wx-material-select/types.ts create mode 100644 src/views/mp/components/wx-msg/index.ts create mode 100644 src/views/mp/components/wx-music/index.ts create mode 100644 src/views/mp/components/wx-news/index.ts create mode 100644 src/views/mp/components/wx-reply/index.ts create mode 100644 src/views/mp/components/wx-video-play/index.ts create mode 100644 src/views/mp/components/wx-voice-play/index.ts diff --git a/src/views/mp/autoReply/components/ReplyTable.vue b/src/views/mp/autoReply/components/ReplyTable.vue index 0b739cef..e3e53905 100644 --- a/src/views/mp/autoReply/components/ReplyTable.vue +++ b/src/views/mp/autoReply/components/ReplyTable.vue @@ -94,10 +94,10 @@ </el-table> </template> <script setup lang="ts"> -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 WxVideoPlayer from '@/views/mp/components/wx-video-play' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' +import WxMusic from '@/views/mp/components/wx-music' +import WxNews from '@/views/mp/components/wx-news' import { dateFormatter } from '@/utils/formatTime' import { DICT_TYPE } from '@/utils/dict' import { MsgType } from './types' diff --git a/src/views/mp/autoReply/components/types.ts b/src/views/mp/autoReply/components/types.ts index 0d78fd85..68bc5c94 100644 --- a/src/views/mp/autoReply/components/types.ts +++ b/src/views/mp/autoReply/components/types.ts @@ -5,44 +5,3 @@ export enum MsgType { Message = 2, Keyword = 3 } - -type ReplyType = 'text' | 'image' | 'voice' | 'video' | 'shortvideo' | 'location' | 'link' - -export 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 -} - -// TODO @Dhb52:ObjData 这个类名可以在看看,ObjData 有点通用 -export 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 -} diff --git a/src/views/mp/autoReply/index.vue b/src/views/mp/autoReply/index.vue index 2d9b492d..20a1e683 100644 --- a/src/views/mp/autoReply/index.vue +++ b/src/views/mp/autoReply/index.vue @@ -82,7 +82,7 @@ <el-input v-model="replyForm.requestKeyword" placeholder="请输入内容" clearable /> </el-form-item> <el-form-item label="回复消息"> - <WxReplySelect :objData="objData" /> + <WxReplySelect v-model="reply" /> </el-form-item> </el-form> <template #footer> @@ -93,14 +93,14 @@ </ContentWrap> </template> <script setup lang="ts" name="MpAutoReply"> -import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxReplySelect, { type Reply, ReplyType } from '@/views/mp/components/wx-reply' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import * as MpAutoReplyApi from '@/api/mp/autoReply' import { DICT_TYPE, getDictOptions, getIntDictOptions } from '@/utils/dict' import { ContentWrap } from '@/components/ContentWrap' -import type { TabPaneName } from 'element-plus' +import type { FormInstance, TabPaneName } from 'element-plus' import ReplyTable from './components/ReplyTable.vue' -import { MsgType, ReplyForm, ObjData } from './components/types' +import { MsgType } from './components/types' const message = useMessage() // 消息 const msgType = ref<MsgType>(MsgType.Keyword) // 消息类型 @@ -108,26 +108,26 @@ const RequestMessageTypes = ['text', 'image', 'voice', 'video', 'shortvideo', 'l const loading = ref(true) // 遮罩层 const total = ref(0) // 总条数 const list = ref<any[]>([]) // 自动回复列表 -const formRef = ref() // 表单 ref +const formRef = ref<FormInstance | null>(null) // 表单 ref // 查询参数 interface QueryParams { pageNo: number pageSize: number - accountId?: number + accountId: number } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: undefined + accountId: 0 }) const dialogTitle = ref('') // 弹出层标题 const showFormDialog = ref(false) // 是否显示弹出层 -const replyForm = ref<ReplyForm>({}) // 表单参数 +const replyForm = ref<any>({}) // 表单参数 // 回复消息 -const objData = ref<ObjData>({ - type: 'text', - accountId: undefined +const reply = ref<Reply>({ + type: ReplyType.Text, + accountId: 0 }) // 表单校验 const rules = { @@ -136,8 +136,9 @@ const rules = { } /** 侦听账号变化 */ -const onAccountChanged = (id?: number) => { +const onAccountChanged = (id: number) => { queryParams.accountId = id + reply.value.accountId = id getList() } @@ -171,8 +172,8 @@ const onTabChange = (tabName: TabPaneName) => { const onCreate = () => { reset() // 打开表单,并设置初始化 - objData.value = { - type: 'text', + reply.value = { + type: ReplyType.Text, accountId: queryParams.accountId } @@ -193,7 +194,7 @@ const onUpdate = async (id: number) => { delete replyForm.value['responseMediaUrl'] delete replyForm.value['responseDescription'] delete replyForm.value['responseArticles'] - objData.value = { + reply.value = { type: data.responseMessageType, accountId: queryParams.accountId, content: data.responseContent, @@ -227,17 +228,17 @@ const onSubmit = async () => { // 处理回复消息 const submitForm: any = { ...replyForm.value } - submitForm.responseMessageType = objData.value.type - submitForm.responseContent = objData.value.content - submitForm.responseMediaId = objData.value.mediaId - submitForm.responseMediaUrl = objData.value.url - submitForm.responseTitle = objData.value.title - submitForm.responseDescription = objData.value.description - submitForm.responseThumbMediaId = objData.value.thumbMediaId - submitForm.responseThumbMediaUrl = objData.value.thumbMediaUrl - submitForm.responseArticles = objData.value.articles - submitForm.responseMusicUrl = objData.value.musicUrl - submitForm.responseHqMusicUrl = objData.value.hqMusicUrl + submitForm.responseMessageType = reply.value.type + submitForm.responseContent = reply.value.content + submitForm.responseMediaId = reply.value.mediaId + submitForm.responseMediaUrl = reply.value.url + submitForm.responseTitle = reply.value.title + submitForm.responseDescription = reply.value.description + submitForm.responseThumbMediaId = reply.value.thumbMediaId + submitForm.responseThumbMediaUrl = reply.value.thumbMediaUrl + submitForm.responseArticles = reply.value.articles + submitForm.responseMusicUrl = reply.value.musicUrl + submitForm.responseHqMusicUrl = reply.value.hqMusicUrl if (replyForm.value.id !== undefined) { await MpAutoReplyApi.updateAutoReply(submitForm) diff --git a/src/views/mp/components/wx-account-select/index.ts b/src/views/mp/components/wx-account-select/index.ts new file mode 100644 index 00000000..97556b2f --- /dev/null +++ b/src/views/mp/components/wx-account-select/index.ts @@ -0,0 +1,3 @@ +import WxAccountSelect from './main.vue' + +export default WxAccountSelect diff --git a/src/views/mp/components/wx-account-select/main.vue b/src/views/mp/components/wx-account-select/main.vue index 5359b366..8dbad499 100644 --- a/src/views/mp/components/wx-account-select/main.vue +++ b/src/views/mp/components/wx-account-select/main.vue @@ -14,7 +14,7 @@ const account: MpAccountApi.AccountVO = reactive({ const accountList: Ref<MpAccountApi.AccountVO[]> = ref([]) const emit = defineEmits<{ - (e: 'change', id?: number, name?: string): void + (e: 'change', id: number, name: string): void }>() const handleQuery = async () => { @@ -22,15 +22,19 @@ const handleQuery = async () => { // 默认选中第一个 if (accountList.value.length > 0) { account.id = accountList.value[0].id - account.name = accountList.value[0].name - emit('change', account.id, account.name) + if (account.id) { + account.name = accountList.value[0].name + emit('change', account.id, account.name) + } } } const onChanged = (id?: number) => { const found = accountList.value.find((v) => v.id === id) - account.name = found ? found.name : '' - emit('change', account.id, account.name) + if (account.id) { + account.name = found ? found.name : '' + emit('change', account.id, account.name) + } } /** 初始化 */ diff --git a/src/views/mp/components/wx-location/index.ts b/src/views/mp/components/wx-location/index.ts new file mode 100644 index 00000000..14ba8644 --- /dev/null +++ b/src/views/mp/components/wx-location/index.ts @@ -0,0 +1,3 @@ +import WxLocation from './main.vue' + +export default WxLocation diff --git a/src/views/mp/components/wx-material-select/index.ts b/src/views/mp/components/wx-material-select/index.ts new file mode 100644 index 00000000..eeda31d5 --- /dev/null +++ b/src/views/mp/components/wx-material-select/index.ts @@ -0,0 +1,6 @@ +import WxMaterialSelect from './main.vue' +import { NewsType, MaterialType } from './types' + +export { NewsType, MaterialType } + +export default WxMaterialSelect diff --git a/src/views/mp/components/wx-material-select/main.vue b/src/views/mp/components/wx-material-select/main.vue index d67ffb1f..e711c022 100644 --- a/src/views/mp/components/wx-material-select/main.vue +++ b/src/views/mp/components/wx-material-select/main.vue @@ -7,7 +7,7 @@ <template> <div class="pb-30px"> <!-- 类型:image --> - <div v-if="objData.type === 'image'"> + <div v-if="props.type === 'image'"> <div class="waterfall" v-loading="loading"> <div class="waterfall-item" v-for="item in list" :key="item.mediaId"> <img class="material-img" :src="item.url" /> @@ -29,7 +29,7 @@ /> </div> <!-- 类型:voice --> - <div v-else-if="objData.type === 'voice'"> + <div v-else-if="props.type === 'voice'"> <!-- 列表 --> <el-table v-loading="loading" :data="list"> <el-table-column label="编号" align="center" prop="mediaId" /> @@ -64,7 +64,7 @@ /> </div> <!-- 类型:video --> - <div v-else-if="objData.type === 'video'"> + <div v-else-if="props.type === 'video'"> <!-- 列表 --> <el-table v-loading="loading" :data="list"> <el-table-column label="编号" align="center" prop="mediaId" /> @@ -106,7 +106,7 @@ /> </div> <!-- 类型:news --> - <div v-else-if="objData.type === 'news'"> + <div v-else-if="props.type === 'news'"> <div class="waterfall" v-loading="loading"> <div class="waterfall-item" v-for="item in list" :key="item.mediaId"> <div v-if="item.content && item.content.newsItem"> @@ -132,25 +132,25 @@ </template> <script lang="ts" setup name="WxMaterialSelect"> -import WxNews from '@/views/mp/components/wx-news/main.vue' -import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' -import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' +import WxNews from '@/views/mp/components/wx-news' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' +import WxVideoPlayer from '@/views/mp/components/wx-video-play' +import { NewsType } from './types' import * as MpMaterialApi from '@/api/mp/material' import * as MpFreePublishApi from '@/api/mp/freePublish' import * as MpDraftApi from '@/api/mp/draft' import { dateFormatter } from '@/utils/formatTime' -const props = defineProps({ - objData: { - type: Object, // type - 类型;accountId - 公众号账号编号 - required: true - }, - newsType: { - // 图文类型:1、已发布图文;2、草稿箱图文 - type: String as PropType<string>, - default: '1' +const props = withDefaults( + defineProps<{ + type: string + accountId: number + newsType?: NewsType + }>(), + { + newsType: NewsType.Published } -}) +) const emit = defineEmits(['select-material']) @@ -159,15 +159,13 @@ const loading = ref(false) // 总条数 const total = ref(0) // 数据列表 -const list = ref([]) +const list = ref<any[]>([]) // 查询参数 const queryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: props.objData.accountId + accountId: props.accountId }) -const objDataRef = reactive(props.objData) -const newsTypeRef = ref(props.newsType) const selectMaterialFun = (item) => { emit('select-material', item) @@ -176,10 +174,10 @@ const selectMaterialFun = (item) => { const getPage = async () => { loading.value = true try { - if (objDataRef.type === 'news' && newsTypeRef.value === '1') { + if (props.type === 'news' && props.newsType === NewsType.Published) { // 【图文】+ 【已发布】 await getFreePublishPageFun() - } else if (objDataRef.type === 'news' && newsTypeRef.value === '2') { + } else if (props.type === 'news' && props.newsType === NewsType.Draft) { // 【图文】+ 【草稿】 await getDraftPageFun() } else { @@ -194,7 +192,7 @@ const getPage = async () => { const getMaterialPageFun = async () => { const data = await MpMaterialApi.getMaterialPage({ ...queryParams, - type: objDataRef.type + type: props.type }) list.value = data.list total.value = data.total @@ -202,9 +200,9 @@ const getMaterialPageFun = async () => { const getFreePublishPageFun = async () => { const data = await MpFreePublishApi.getFreePublishPage(queryParams) - data.list.forEach((item) => { - const newsItem = item.content.newsItem - newsItem.forEach((article) => { + data.list.forEach((item: any) => { + const articles = item.content.newsItem + articles.forEach((article: any) => { article.picUrl = article.thumbUrl }) }) @@ -214,9 +212,9 @@ const getFreePublishPageFun = async () => { const getDraftPageFun = async () => { const data = await MpDraftApi.getDraftPage(queryParams) - data.list.forEach((item) => { - const newsItem = item.content.newsItem - newsItem.forEach((article) => { + data.list.forEach((draft: any) => { + const articles = draft.content.newsItem + articles.forEach((article: any) => { article.picUrl = article.thumbUrl }) }) diff --git a/src/views/mp/components/wx-material-select/types.ts b/src/views/mp/components/wx-material-select/types.ts new file mode 100644 index 00000000..d4add1d5 --- /dev/null +++ b/src/views/mp/components/wx-material-select/types.ts @@ -0,0 +1,11 @@ +export enum NewsType { + Draft = '2', + Published = '1' +} + +export enum MaterialType { + Image = 'image', + Voice = 'voice', + Video = 'video', + News = 'news' +} diff --git a/src/views/mp/components/wx-msg/index.ts b/src/views/mp/components/wx-msg/index.ts new file mode 100644 index 00000000..fd9eddd7 --- /dev/null +++ b/src/views/mp/components/wx-msg/index.ts @@ -0,0 +1,6 @@ +import WxMsg from './main.vue' +import { MsgType } from './types' + +export { MsgType } + +export default WxMsg diff --git a/src/views/mp/components/wx-msg/main.vue b/src/views/mp/components/wx-msg/main.vue index c7354628..19763245 100644 --- a/src/views/mp/components/wx-msg/main.vue +++ b/src/views/mp/components/wx-msg/main.vue @@ -125,19 +125,19 @@ </div> </div> <div class="msg-send" v-loading="sendLoading"> - <WxReplySelect ref="replySelectRef" :objData="objData" /> + <WxReplySelect ref="replySelectRef" v-model="reply" /> <el-button type="success" class="send-but" @click="sendMsg">发送(S)</el-button> </div> </ContentWrap> </template> <script setup lang="ts" name="WxMsg"> -import WxReplySelect from '@/views/mp/components/wx-reply/main.vue' -import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' -import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' -import WxNews from '@/views/mp/components/wx-news/main.vue' -import WxLocation from '@/views/mp/components/wx-location/main.vue' -import WxMusic from '@/views/mp/components/wx-music/main.vue' +import WxReplySelect from '@/views/mp/components/wx-reply' +import WxVideoPlayer from '@/views/mp/components/wx-video-play' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' +import WxNews from '@/views/mp/components/wx-news' +import WxLocation from '@/views/mp/components/wx-location' +import WxMusic from '@/views/mp/components/wx-music' import { getMessagePage, sendMessage } from '@/api/mp/message' import { getUser } from '@/api/mp/user' import { formatDate } from '@/utils/formatTime' @@ -187,14 +187,14 @@ const mp: Mp = reactive({ // ========= 消息发送 ========= const sendLoading = ref(false) // 发送消息是否加载中 -interface ObjData { +interface Reply { type: MsgType accountId: number | null articles: any[] } // 微信发送消息 -const objData: ObjData = reactive({ +const reply = ref<Reply>({ type: MsgType.Text, accountId: null, articles: [] @@ -209,23 +209,23 @@ onMounted(async () => { user.avatar = user.avatar?.length > 0 ? data.avatar : user.avatar user.accountId = data.accountId queryParams.accountId = data.accountId - objData.accountId = data.accountId + reply.value.accountId = data.accountId refreshChange() }) // 执行发送 const sendMsg = async () => { - if (!objData) { + if (!reply) { return } // 公众号限制:客服消息,公众号只允许发送一条 - if (objData.type === MsgType.News && objData.articles.length > 1) { - objData.articles = [objData.articles[0]] + if (reply.value.type === MsgType.News && reply.value.articles.length > 1) { + reply.value.articles = [reply.value.articles[0]] message.success('图文消息条数限制在 1 条以内,已默认发送第一条') } - const data = await sendMessage(Object.assign({ userId: props.userId }, { ...objData })) + const data = await sendMessage({ userId: props.userId, ...reply.value }) sendLoading.value = false list.value = [...list.value, ...[data]] diff --git a/src/views/mp/components/wx-music/index.ts b/src/views/mp/components/wx-music/index.ts new file mode 100644 index 00000000..c4211261 --- /dev/null +++ b/src/views/mp/components/wx-music/index.ts @@ -0,0 +1,3 @@ +import WxMusic from './main.vue' + +export default WxMusic diff --git a/src/views/mp/components/wx-news/index.ts b/src/views/mp/components/wx-news/index.ts new file mode 100644 index 00000000..e68f4d5d --- /dev/null +++ b/src/views/mp/components/wx-news/index.ts @@ -0,0 +1,3 @@ +import WxNews from './main.vue' + +export default WxNews diff --git a/src/views/mp/components/wx-news/main.vue b/src/views/mp/components/wx-news/main.vue index 326b674c..779a446f 100644 --- a/src/views/mp/components/wx-news/main.vue +++ b/src/views/mp/components/wx-news/main.vue @@ -39,12 +39,14 @@ </template> <script lang="ts" name="WxNews" setup> -const props = defineProps({ - articles: { - type: Array, - default: () => null +const props = withDefaults( + defineProps<{ + articles: any[] | null + }>(), + { + articles: null } -}) +) defineExpose({ articles: props.articles @@ -53,9 +55,9 @@ defineExpose({ <style lang="scss" scoped> .news-home { - background-color: #ffffff; width: 100%; margin: auto; + background-color: #fff; } .news-main { @@ -64,29 +66,29 @@ defineExpose({ } .news-content { - background-color: #acadae; - width: 100%; position: relative; + width: 100%; + background-color: #acadae; } .news-content-title { - display: inline-block; - font-size: 12px; - color: #ffffff; position: absolute; - left: 0; bottom: 0; - background-color: black; + left: 0; + display: inline-block; width: 98%; padding: 1%; - opacity: 0.65; + font-size: 12px; + color: #fff; white-space: normal; + background-color: black; + opacity: 0.65; box-sizing: unset !important; } .news-main-item { - background-color: #ffffff; padding: 5px 0; + background-color: #fff; border-top: 1px solid #eaeaea; } @@ -96,17 +98,17 @@ defineExpose({ .news-content-item-title { display: inline-block; - font-size: 10px; width: 70%; margin-left: 1%; + font-size: 10px; white-space: normal; } .news-content-item-img { display: inline-block; width: 25%; - background-color: #acadae; margin-right: 1%; + background-color: #acadae; } .material-img { diff --git a/src/views/mp/components/wx-reply/components/TabImage.vue b/src/views/mp/components/wx-reply/components/TabImage.vue index eecc24cb..1a82c3aa 100644 --- a/src/views/mp/components/wx-reply/components/TabImage.vue +++ b/src/views/mp/components/wx-reply/components/TabImage.vue @@ -1,12 +1,9 @@ <template> - <el-tab-pane name="image"> - <template #label> - <el-row align="middle"><Icon icon="ep:picture" class="mr-5px" /> 图片</el-row> - </template> + <div> <!-- 情况一:已经选择好素材、或者上传好图片 --> - <div class="select-item" v-if="objData.url"> - <img class="material-img" :src="objData.url" /> - <p class="item-name" v-if="objData.name">{{ objData.name }}</p> + <div class="select-item" v-if="reply.url"> + <img class="material-img" :src="reply.url" /> + <p class="item-name" v-if="reply.name">{{ reply.name }}</p> <el-row class="ope-row" justify="center"> <el-button type="danger" circle @click="onDelete"> <Icon icon="ep:delete" /> @@ -27,7 +24,11 @@ append-to-body destroy-on-close > - <WxMaterialSelect :objData="objData" @select-material="selectMaterial" /> + <WxMaterialSelect + type="image" + :account-id="reply.accountId" + @select-material="selectMaterial" + /> </el-dialog> </el-col> <!-- 文件上传 --> @@ -51,27 +52,27 @@ </el-upload> </el-col> </el-row> - </el-tab-pane> + </div> </template> <script setup lang="ts"> -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' -import { MaterialType, useBeforeUpload } from '@/views/mp/hooks/useUpload' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' import type { UploadRawFile } from 'element-plus' import { getAccessToken } from '@/utils/auth' -import { ObjData } from './types' +import { Reply } from './types' const message = useMessage() const UPLOAD_URL = import.meta.env.VITE_API_BASEPATH + '/admin-api/mp/material/upload-temporary' const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 const props = defineProps<{ - modelValue: ObjData + modelValue: Reply }>() const emit = defineEmits<{ - (e: 'update:modelValue', v: ObjData) + (e: 'update:modelValue', v: Reply) }>() -const objData = computed<ObjData>({ +const reply = computed<Reply>({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) @@ -79,14 +80,13 @@ const objData = computed<ObjData>({ const showDialog = ref(false) const fileList = ref([]) const uploadData = reactive({ - accountId: objData.value.accountId, + accountId: reply.value.accountId, type: 'image', title: '', introduction: '' }) -const beforeImageUpload = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Image, 2)(rawFile) +const beforeImageUpload = (rawFile: UploadRawFile) => useBeforeUpload(UploadType.Image, 2)(rawFile) const onUploadSuccess = (res: any) => { if (res.code !== 0) { @@ -104,18 +104,18 @@ const onUploadSuccess = (res: any) => { } const onDelete = () => { - objData.value.mediaId = null - objData.value.url = null - objData.value.name = null + reply.value.mediaId = null + reply.value.url = null + reply.value.name = null } const selectMaterial = (item) => { showDialog.value = false - objData.value.type = 'image' - objData.value.mediaId = item.mediaId - objData.value.url = item.url - objData.value.name = item.name + // reply.value.type = 'image' + reply.value.mediaId = item.mediaId + reply.value.url = item.url + reply.value.name = item.name } </script> diff --git a/src/views/mp/components/wx-reply/components/TabMusic.vue b/src/views/mp/components/wx-reply/components/TabMusic.vue index 2c3b04e5..b19fb592 100644 --- a/src/views/mp/components/wx-reply/components/TabMusic.vue +++ b/src/views/mp/components/wx-reply/components/TabMusic.vue @@ -1,14 +1,11 @@ <template> - <el-tab-pane name="music"> - <template #label> - <el-row align="middle"><Icon icon="ep:service" />音乐</el-row> - </template> + <div> <el-row align="middle" justify="center"> <el-col :span="6"> <el-row align="middle" justify="center" class="thumb-div"> <el-col :span="24"> <el-row align="middle" justify="center"> - <img style="width: 100px" v-if="objData.thumbMediaUrl" :src="objData.thumbMediaUrl" /> + <img style="width: 100px" v-if="reply.thumbMediaUrl" :src="reply.thumbMediaUrl" /> <icon v-else icon="ep:plus" /> </el-row> <el-row align="middle" justify="center" style="margin-top: 2%"> @@ -42,30 +39,31 @@ destroy-on-close > <WxMaterialSelect - :objData="{ type: 'image', accountId: objData.accountId }" + type="image" + :account-id="reply.accountId" @select-material="selectMaterial" /> </el-dialog> </el-col> <el-col :span="18"> - <el-input v-model="objData.title" placeholder="请输入标题" /> + <el-input v-model="reply.title" placeholder="请输入标题" /> <div style="margin: 20px 0"></div> - <el-input v-model="objData.description" placeholder="请输入描述" /> + <el-input v-model="reply.description" placeholder="请输入描述" /> </el-col> </el-row> <div style="margin: 20px 0"></div> - <el-input v-model="objData.musicUrl" placeholder="请输入音乐链接" /> + <el-input v-model="reply.musicUrl" placeholder="请输入音乐链接" /> <div style="margin: 20px 0"></div> - <el-input v-model="objData.hqMusicUrl" placeholder="请输入高质量音乐链接" /> - </el-tab-pane> + <el-input v-model="reply.hqMusicUrl" placeholder="请输入高质量音乐链接" /> + </div> </template> <script setup lang="ts"> -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' import type { UploadRawFile } from 'element-plus' -import { MaterialType, useBeforeUpload } from '@/views/mp/hooks/useUpload' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' import { getAccessToken } from '@/utils/auth' -import { ObjData } from './types' +import { Reply } from './types' const message = useMessage() @@ -73,12 +71,12 @@ const UPLOAD_URL = import.meta.env.VITE_API_BASEPATH + '/admin-api/mp/material/u const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 const props = defineProps<{ - modelValue: ObjData + modelValue: Reply }>() const emit = defineEmits<{ - (e: 'update:modelValue', v: ObjData) + (e: 'update:modelValue', v: Reply) }>() -const objData = computed<ObjData>({ +const reply = computed<Reply>({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) @@ -86,14 +84,13 @@ const objData = computed<ObjData>({ const showDialog = ref(false) const fileList = ref([]) const uploadData = reactive({ - accountId: objData.value.accountId, + accountId: reply.value.accountId, type: 'thumb', // 音乐类型为thumb title: '', introduction: '' }) -const beforeImageUpload = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Image, 2)(rawFile) +const beforeImageUpload = (rawFile: UploadRawFile) => useBeforeUpload(UploadType.Image, 2)(rawFile) const onUploadSuccess = (res: any) => { if (res.code !== 0) { @@ -113,7 +110,7 @@ const onUploadSuccess = (res: any) => { const selectMaterial = (item: any) => { showDialog.value = false - objData.value.thumbMediaId = item.mediaId - objData.value.thumbMediaUrl = item.url + reply.value.thumbMediaId = item.mediaId + reply.value.thumbMediaUrl = item.url } </script> diff --git a/src/views/mp/components/wx-reply/components/TabNews.vue b/src/views/mp/components/wx-reply/components/TabNews.vue index bb9272e7..88a82a53 100644 --- a/src/views/mp/components/wx-reply/components/TabNews.vue +++ b/src/views/mp/components/wx-reply/components/TabNews.vue @@ -1,11 +1,8 @@ <template> - <el-tab-pane name="news"> - <template #label> - <el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row> - </template> + <div> <el-row> - <div class="select-item" v-if="objData.articles?.length > 0"> - <WxNews :articles="objData.articles" /> + <div class="select-item" v-if="reply.articles && reply.articles.length > 0"> + <WxNews :articles="reply.articles" /> <el-col class="ope-row"> <el-button type="danger" circle @click="onDelete"> <Icon icon="ep:delete" /> @@ -13,7 +10,7 @@ </el-col> </div> <!-- 选择素材 --> - <el-col :span="24" v-if="!objData.content"> + <el-col :span="24" v-if="!reply.content"> <el-row style="text-align: center" align="middle"> <el-col :span="24"> <el-button type="success" @click="showDialog = true"> @@ -25,28 +22,29 @@ </el-col> <el-dialog title="选择图文" v-model="showDialog" width="90%" append-to-body destroy-on-close> <WxMaterialSelect - :objData="objData" - @select-material="selectMaterial" + type="news" + :account-id="reply.accountId" :newsType="newsType" + @select-material="selectMaterial" /> </el-dialog> </el-row> - </el-tab-pane> + </div> </template> <script setup lang="ts"> -import WxNews from '@/views/mp/components/wx-news/main.vue' -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' -import { ObjData, NewsType } from './types' +import WxNews from '@/views/mp/components/wx-news' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' +import { Reply, NewsType } from './types' const props = defineProps<{ - modelValue: ObjData + modelValue: Reply newsType: NewsType }>() const emit = defineEmits<{ - (e: 'update:modelValue', v: ObjData) + (e: 'update:modelValue', v: Reply) }>() -const objData = computed<ObjData>({ +const reply = computed<Reply>({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) @@ -55,11 +53,11 @@ const showDialog = ref(false) const selectMaterial = (item: any) => { showDialog.value = false - objData.value.articles = item.content.newsItem + reply.value.articles = item.content.newsItem } const onDelete = () => { - objData.value.articles = [] + reply.value.articles = [] } </script> diff --git a/src/views/mp/components/wx-reply/components/TabText.vue b/src/views/mp/components/wx-reply/components/TabText.vue index bd7b0187..aac698e8 100644 --- a/src/views/mp/components/wx-reply/components/TabText.vue +++ b/src/views/mp/components/wx-reply/components/TabText.vue @@ -1,15 +1,10 @@ <template> - <el-tab-pane name="text"> - <template #label> - <el-row align="middle"><Icon icon="ep:document" /> 文本</el-row> - </template> - <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="content" /> - </el-tab-pane> + <el-input type="textarea" :rows="5" placeholder="请输入内容" v-model="content" /> </template> <script setup lang="ts"> const props = defineProps<{ - modelValue: string | null + modelValue?: string | null }>() const emit = defineEmits<{ @@ -17,7 +12,7 @@ const emit = defineEmits<{ (e: 'input', v: string | null) }>() -const content = computed<string | null>({ +const content = computed<string | null | undefined>({ get: () => props.modelValue, set: (val: string | null) => { emit('update:modelValue', val) diff --git a/src/views/mp/components/wx-reply/components/TabVideo.vue b/src/views/mp/components/wx-reply/components/TabVideo.vue index c924bc2a..52553521 100644 --- a/src/views/mp/components/wx-reply/components/TabVideo.vue +++ b/src/views/mp/components/wx-reply/components/TabVideo.vue @@ -1,17 +1,10 @@ <template> - <el-tab-pane name="video"> - <template #label> - <el-row align="middle"><Icon icon="ep:share" /> 视频</el-row> - </template> + <div> <el-row> - <el-input v-model="objData.title" class="input-margin-bottom" placeholder="请输入标题" /> - <el-input - class="input-margin-bottom" - v-model="objData.description" - placeholder="请输入描述" - /> + <el-input v-model="reply.title" class="input-margin-bottom" placeholder="请输入标题" /> + <el-input class="input-margin-bottom" v-model="reply.description" placeholder="请输入描述" /> <el-row class="ope-row" justify="center"> - <WxVideoPlayer v-if="objData.url" :url="objData.url" /> + <WxVideoPlayer v-if="reply.url" :url="reply.url" /> </el-row> <el-col> <el-row style="text-align: center" align="middle"> @@ -27,7 +20,11 @@ append-to-body destroy-on-close > - <WxMaterialSelect :objData="objData" @select-material="selectMaterial" /> + <WxMaterialSelect + type="video" + :account-id="reply.accountId" + @select-material="selectMaterial" + /> </el-dialog> </el-col> <!-- 文件上传 --> @@ -48,16 +45,16 @@ </el-row> </el-col> </el-row> - </el-tab-pane> + </div> </template> <script setup lang="ts"> -import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' +import WxVideoPlayer from '@/views/mp/components/wx-video-play' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' import type { UploadRawFile } from 'element-plus' -import { MaterialType, useBeforeUpload } from '@/views/mp/hooks/useUpload' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' import { getAccessToken } from '@/utils/auth' -import { ObjData } from './types' +import { Reply } from './types' const message = useMessage() @@ -65,12 +62,12 @@ const UPLOAD_URL = import.meta.env.VITE_API_BASEPATH + '/admin-api/mp/material/u const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } const props = defineProps<{ - modelValue: ObjData + modelValue: Reply }>() const emit = defineEmits<{ - (e: 'update:modelValue', v: ObjData) + (e: 'update:modelValue', v: Reply) }>() -const objData = computed<ObjData>({ +const reply = computed<Reply>({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) @@ -78,14 +75,13 @@ const objData = computed<ObjData>({ const showDialog = ref(false) const fileList = ref([]) const uploadData = reactive({ - accountId: objData.value.accountId, + accountId: reply.value.accountId, type: 'video', title: '', introduction: '' }) -const beforeVideoUpload = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Video, 10)(rawFile) +const beforeVideoUpload = (rawFile: UploadRawFile) => useBeforeUpload(UploadType.Video, 10)(rawFile) const onUploadSuccess = (res: any) => { if (res.code !== 0) { @@ -105,16 +101,16 @@ const onUploadSuccess = (res: any) => { const selectMaterial = (item: any) => { showDialog.value = false - objData.value.mediaId = item.mediaId - objData.value.url = item.url - objData.value.name = item.name + reply.value.mediaId = item.mediaId + reply.value.url = item.url + reply.value.name = item.name // title、introduction:从 item 到 tempObjItem,因为素材里有 title、introduction if (item.title) { - objData.value.title = item.title || '' + reply.value.title = item.title || '' } if (item.introduction) { - objData.value.description = item.introduction || '' + reply.value.description = item.introduction || '' } } </script> diff --git a/src/views/mp/components/wx-reply/components/TabVoice.vue b/src/views/mp/components/wx-reply/components/TabVoice.vue index 6d40a052..c4868cf8 100644 --- a/src/views/mp/components/wx-reply/components/TabVoice.vue +++ b/src/views/mp/components/wx-reply/components/TabVoice.vue @@ -1,12 +1,9 @@ <template> - <el-tab-pane name="voice"> - <template #label> - <el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row> - </template> - <div class="select-item2" v-if="objData.url"> - <p class="item-name">{{ objData.name }}</p> + <div> + <div class="select-item2" v-if="reply.url"> + <p class="item-name">{{ reply.name }}</p> <el-row class="ope-row" justify="center"> - <WxVoicePlayer :url="objData.url" /> + <WxVoicePlayer :url="reply.url" /> </el-row> <el-row class="ope-row" justify="center"> <el-button type="danger" circle @click="onDelete"><Icon icon="ep:delete" /></el-button> @@ -25,7 +22,11 @@ append-to-body destroy-on-close > - <WxMaterialSelect :objData="objData" @select-material="selectMaterial" /> + <WxMaterialSelect + type="voice" + :account-id="reply.accountId" + @select-material="selectMaterial" + /> </el-dialog> </el-col> <!-- 文件上传 --> @@ -49,27 +50,27 @@ </el-upload> </el-col> </el-row> - </el-tab-pane> + </div> </template> <script setup lang="ts"> -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' -import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' -import { MaterialType, useBeforeUpload } from '@/views/mp/hooks/useUpload' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' import type { UploadRawFile } from 'element-plus' import { getAccessToken } from '@/utils/auth' -import { ObjData } from './types' +import { Reply } from './types' const message = useMessage() const UPLOAD_URL = import.meta.env.VITE_API_BASEPATH + '/admin-api/mp/material/upload-temporary' const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 const props = defineProps<{ - modelValue: ObjData + modelValue: Reply }>() const emit = defineEmits<{ - (e: 'update:modelValue', v: ObjData) + (e: 'update:modelValue', v: Reply) }>() -const objData = computed<ObjData>({ +const reply = computed<Reply>({ get: () => props.modelValue, set: (val) => emit('update:modelValue', val) }) @@ -77,14 +78,13 @@ const objData = computed<ObjData>({ const showDialog = ref(false) const fileList = ref([]) const uploadData = reactive({ - accountId: objData.value.accountId, + accountId: reply.value.accountId, type: 'voice', title: '', introduction: '' }) -const beforeVoiceUpload = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Voice, 10)(rawFile) +const beforeVoiceUpload = (rawFile: UploadRawFile) => useBeforeUpload(UploadType.Voice, 10)(rawFile) const onUploadSuccess = (res: any) => { if (res.code !== 0) { @@ -102,18 +102,18 @@ const onUploadSuccess = (res: any) => { } const onDelete = () => { - objData.value.mediaId = null - objData.value.url = null - objData.value.name = null + reply.value.mediaId = null + reply.value.url = null + reply.value.name = null } -const selectMaterial = (item: ObjData) => { +const selectMaterial = (item: Reply) => { showDialog.value = false - objData.value.type = 'voice' - objData.value.mediaId = item.mediaId - objData.value.url = item.url - objData.value.name = item.name + // reply.value.type = ReplyType.Voice + reply.value.mediaId = item.mediaId + reply.value.url = item.url + reply.value.name = item.name } </script> diff --git a/src/views/mp/components/wx-reply/components/types.ts b/src/views/mp/components/wx-reply/components/types.ts index d5273334..3e07d6e5 100644 --- a/src/views/mp/components/wx-reply/components/types.ts +++ b/src/views/mp/components/wx-reply/components/types.ts @@ -1,25 +1,54 @@ -type ReplyType = '' | 'news' | 'image' | 'voice' | 'video' | 'music' | 'text' +enum ReplyType { + News = 'news', + Image = 'image', + Voice = 'voice', + Video = 'video', + Music = 'music', + Text = 'text' +} -interface ObjData { +interface _Reply { accountId: number type: ReplyType - name: string | null - content: string | null - mediaId: string | null - url: string | null - title: string | null - description: string | null - thumbMediaId: string | null - thumbMediaUrl: string | null - musicUrl: string | null - hqMusicUrl: string | null - introduction: string | null - articles: any[] + name?: string | null + content?: string | null + mediaId?: string | null + url?: string | null + title?: string | null + description?: string | null + thumbMediaId?: string | null + thumbMediaUrl?: string | null + musicUrl?: string | null + hqMusicUrl?: string | null + introduction?: string | null + articles?: any[] } +type Reply = _Reply //Partial<_Reply> + enum NewsType { Published = '1', Draft = '2' } -export { ObjData, NewsType } +/** 利用旧的reply[accountId, type]初始化新的Reply */ +const createEmptyReply = (old: Reply | Ref<Reply>): Reply => { + return { + accountId: unref(old).accountId, + type: unref(old).type, + name: null, + content: null, + mediaId: null, + url: null, + title: null, + description: null, + thumbMediaId: null, + thumbMediaUrl: null, + musicUrl: null, + hqMusicUrl: null, + introduction: null, + articles: [] + } +} + +export { Reply, NewsType, ReplyType, createEmptyReply } diff --git a/src/views/mp/components/wx-reply/index.ts b/src/views/mp/components/wx-reply/index.ts new file mode 100644 index 00000000..d1da217e --- /dev/null +++ b/src/views/mp/components/wx-reply/index.ts @@ -0,0 +1,7 @@ +import { Reply, NewsType, ReplyType, createEmptyReply } from './components/types' + +import WxReplySelect from './main.vue' + +export type { Reply } +export { createEmptyReply, NewsType, ReplyType } +export default WxReplySelect diff --git a/src/views/mp/components/wx-reply/main.vue b/src/views/mp/components/wx-reply/main.vue index b00e4345..32a31222 100644 --- a/src/views/mp/components/wx-reply/main.vue +++ b/src/views/mp/components/wx-reply/main.vue @@ -8,24 +8,59 @@ ④ 支持发送【视频】消息时,支持新建视频 --> <template> - <el-tabs type="border-card" v-model="objData.type" @tab-click="onTabClick"> + <el-tabs type="border-card" v-model="currentTab"> <!-- 类型 1:文本 --> - <TabText v-model="objData.content" /> + <el-tab-pane :name="ReplyType.Text"> + <template #label> + <el-row align="middle"><Icon icon="ep:document" /> 文本</el-row> + </template> + <TabText v-model="reply.content" /> + </el-tab-pane> + <!-- 类型 2:图片 --> - <TabImage v-model="objData" /> + <el-tab-pane :name="ReplyType.Image"> + <template #label> + <el-row align="middle"><Icon icon="ep:picture" class="mr-5px" /> 图片</el-row> + </template> + <TabImage v-model="reply" /> + </el-tab-pane> + <!-- 类型 3:语音 --> - <TabVoice v-model="objData" /> + <el-tab-pane :name="ReplyType.Voice"> + <template #label> + <el-row align="middle"><Icon icon="ep:phone" /> 语音</el-row> + </template> + <TabVoice v-model="reply" /> + </el-tab-pane> + <!-- 类型 4:视频 --> - <TabVideo v-model="objData" /> + <el-tab-pane :name="ReplyType.Video"> + <template #label> + <el-row align="middle"><Icon icon="ep:share" /> 视频</el-row> + </template> + <TabVideo v-model="reply" /> + </el-tab-pane> + <!-- 类型 5:图文 --> - <TabNews v-model="objData" :news-type="newsType" /> + <el-tab-pane :name="ReplyType.News"> + <template #label> + <el-row align="middle"><Icon icon="ep:reading" /> 图文</el-row> + </template> + <TabNews v-model="reply" :news-type="newsType" /> + </el-tab-pane> + <!-- 类型 6:音乐 --> - <TabMusic v-model="objData" /> + <el-tab-pane :name="ReplyType.Music"> + <template #label> + <el-row align="middle"><Icon icon="ep:service" />音乐</el-row> + </template> + <TabMusic v-model="reply" /> + </el-tab-pane> </el-tabs> </template> <script setup lang="ts" name="WxReplySelect"> -import { ObjData, NewsType } from './components/types' +import { Reply, NewsType, ReplyType, createEmptyReply } from './components/types' import TabText from './components/TabText.vue' import TabImage from './components/TabImage.vue' import TabVoice from './components/TabVoice.vue' @@ -34,30 +69,54 @@ import TabNews from './components/TabNews.vue' import TabMusic from './components/TabMusic.vue' interface Props { - objData: ObjData + modelValue: Reply newsType?: NewsType } const props = withDefaults(defineProps<Props>(), { newsType: () => NewsType.Published }) +const emit = defineEmits<{ + (e: 'update:modelValue', v: Reply) +}>() -const objData = reactive(props.objData) -// TODO @Dhb52:Tab 切换的时候,应该表单还保留着;清除只有两个时机:1)发送成功后;2)关闭窗口后;我捉摸,是不是每个 TabXXX 组件,是个独立的 Form,然后有自己的对象,不粘在 objData 一起。这样最终就是 MusicMessageForm、ImageMessageForm -// const tempObj = new Map().set(objData.type, Object.assign({}, objData)) +const reply = computed<Reply>({ + get: () => props.modelValue, + set: (val) => emit('update:modelValue', val) +}) +// 作为多个标签保存各自Reply的缓存 +const objCache = new Map<ReplyType, Reply>() +// 采用独立的ref来保存当前tab,避免在watch标签变化,对reply进行赋值会产生了循环调用 +const currentTab = ref<ReplyType>(props.modelValue.type || ReplyType.Text) -/** 切换消息类型的 tab */ -const onTabClick = () => { - clear() -} +watch( + currentTab, + (newTab, oldTab) => { + // 第一次进入:oldTab 为 undefined + // 判断 newTab 是因为 Reply 为 Partial + if (oldTab === undefined || newTab === undefined) { + return + } -/** 清除除了`type`的字段 */ + objCache.set(oldTab, unref(reply)) + + // 从缓存里面取出新tab内容,有则覆盖Reply,没有则创建空Reply + const temp = objCache.get(newTab) + if (temp) { + reply.value = temp + } else { + let newData = createEmptyReply(reply) + newData.type = newTab + reply.value = newData + } + }, + { + immediate: true + } +) + +/** 清除除了`type`, `accountId`的字段 */ const clear = () => { - objData.content = '' - objData.mediaId = '' - objData.url = '' - objData.title = '' - objData.description = '' - objData.articles = [] + reply.value = createEmptyReply(reply) } defineExpose({ diff --git a/src/views/mp/components/wx-video-play/index.ts b/src/views/mp/components/wx-video-play/index.ts new file mode 100644 index 00000000..91e00efa --- /dev/null +++ b/src/views/mp/components/wx-video-play/index.ts @@ -0,0 +1,3 @@ +import WxVideoPlayer from './main.vue' + +export default WxVideoPlayer diff --git a/src/views/mp/components/wx-voice-play/index.ts b/src/views/mp/components/wx-voice-play/index.ts new file mode 100644 index 00000000..9eb78e02 --- /dev/null +++ b/src/views/mp/components/wx-voice-play/index.ts @@ -0,0 +1,3 @@ +import WxVoicePlayer from './main.vue' + +export default WxVoicePlayer diff --git a/src/views/mp/components/wx-voice-play/main.vue b/src/views/mp/components/wx-voice-play/main.vue index 3ce970c1..169593d8 100644 --- a/src/views/mp/components/wx-voice-play/main.vue +++ b/src/views/mp/components/wx-voice-play/main.vue @@ -7,7 +7,7 @@ 1)joolun 的做法:使用 mediaId 从微信公众号,下载对应的 mp4 素材,从而播放内容; 存在的问题:mediaId 有效期是 3 天,超过时间后无法播放 2)重构后的做法:后端接收到微信公众号的视频消息后,将视频消息的 media_id 的文件内容保存到文件服务器中,这样前端可以直接使用 URL 播放。 - ② 代码优化:将 props 中的 objData 调成为 data 中对应的属性,并补充相关注释 + ② 代码优化:将 props 中的 reply 调成为 data 中对应的属性,并补充相关注释 --> <template> <div class="wx-voice-div" @click="playVoice"> @@ -89,8 +89,8 @@ const amrStop = () => { padding: 5px; background-color: #eaeaea; border-radius: 10px; - width: 40px; - height: 40px; + width: 120px; + height: 50px; display: flex; justify-content: center; diff --git a/src/views/mp/draft/components/CoverSelect.vue b/src/views/mp/draft/components/CoverSelect.vue index ae2b6591..944b7d96 100644 --- a/src/views/mp/draft/components/CoverSelect.vue +++ b/src/views/mp/draft/components/CoverSelect.vue @@ -27,9 +27,7 @@ :on-success="onUploadSuccess" > <template #trigger> - <el-button size="small" type="primary" :loading="isUploading" disabled="isUploading"> - {{ isUploading ? '正在上传' : '本地上传' }} - </el-button> + <el-button size="small" type="primary">本地上传</el-button> </template> <el-button size="small" @@ -52,7 +50,8 @@ destroy-on-close > <WxMaterialSelect - :objData="{ type: 'image', accountId: accountId }" + type="image" + :account-id="accountId" @select-material="onMaterialSelected" /> </el-dialog> @@ -61,13 +60,13 @@ </template> <script setup lang="ts"> -import WxMaterialSelect from '@/views/mp/components/wx-material-select/main.vue' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' import { getAccessToken } from '@/utils/auth' import type { UploadFiles, UploadProps, UploadRawFile } from 'element-plus' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' import { NewsItem } from './types' const message = useMessage() -// const UPLOAD_URL = 'http://localhost:8000/upload/' // 上传永久素材的地址 const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传永久素材的地址 const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 设置上传的请求头部 @@ -93,14 +92,13 @@ const showImageDialog = ref(false) const fileList = ref<UploadFiles>([]) interface UploadData { - type: 'image' | 'video' | 'audio' - accountId?: number + type: UploadType + accountId: number | undefined } const uploadData: UploadData = reactive({ - type: 'image', + type: UploadType.Image, accountId: accountId }) -const isUploading = ref(false) /** 素材选择完成事件*/ const onMaterialSelected = (item: any) => { @@ -109,22 +107,8 @@ const onMaterialSelected = (item: any) => { newsItem.value.thumbUrl = item.url } -const onBeforeUpload: 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 - } - - if (rawFile.size / 1024 / 1024 > 2) { - message.error('上传图片大小不能超过 2M!') - return false - } - // 校验通过 - return true -} +const onBeforeUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => + useBeforeUpload(UploadType.Image, 2)(rawFile) const onUploadSuccess: UploadProps['onSuccess'] = (res: any) => { if (res.code !== 0) { diff --git a/src/views/mp/draft/components/DraftTable.vue b/src/views/mp/draft/components/DraftTable.vue index 63cee31f..62a18528 100644 --- a/src/views/mp/draft/components/DraftTable.vue +++ b/src/views/mp/draft/components/DraftTable.vue @@ -36,7 +36,7 @@ </template> <script setup lang="ts"> -import WxNews from '@/views/mp/components/wx-news/main.vue' +import WxNews from '@/views/mp/components/wx-news' import { Article } from './types' diff --git a/src/views/mp/draft/index.vue b/src/views/mp/draft/index.vue index 7de992cd..d8e771a0 100644 --- a/src/views/mp/draft/index.vue +++ b/src/views/mp/draft/index.vue @@ -46,7 +46,6 @@ </ContentWrap> <!-- 添加或修改草稿对话框 --> - <!-- TODO @Dhb52:是不是整个做成一个组件 --> <el-dialog :title="isCreating ? '新建图文' : '修改图文'" width="80%" @@ -63,7 +62,7 @@ </template> <script setup lang="ts" name="MpDraft"> -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import * as MpDraftApi from '@/api/mp/draft' import * as MpFreePublishApi from '@/api/mp/freePublish' import { @@ -77,7 +76,7 @@ import { const message = useMessage() // 消息 -const accountId = ref(0) +const accountId = ref<number>(0) provide('accountId', accountId) const loading = ref(true) // 列表的加载中 @@ -91,7 +90,7 @@ interface QueryParams { const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: accountId.value + accountId: 0 }) interface UploadData { @@ -100,7 +99,7 @@ interface UploadData { } const uploadData: UploadData = reactive({ type: 'image', - accountId: accountId.value + accountId: 0 }) // ========== 草稿新建 or 修改 ========== diff --git a/src/views/mp/freePublish/index.vue b/src/views/mp/freePublish/index.vue index 04edb705..2fda5100 100644 --- a/src/views/mp/freePublish/index.vue +++ b/src/views/mp/freePublish/index.vue @@ -50,8 +50,8 @@ <script lang="ts" setup name="MpFreePublish"> import * as FreePublishApi from '@/api/mp/freePublish' -import WxNews from '@/views/mp/components/wx-news/main.vue' -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxNews from '@/views/mp/components/wx-news' +import WxAccountSelect from '@/views/mp/components/wx-account-select' const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 @@ -62,16 +62,16 @@ const list = ref<any[]>([]) // 列表的数据 interface QueryParams { pageNo: number pageSize: number - accountId?: number + accountId: number } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: undefined + accountId: 0 }) /** 侦听公众号变化 **/ -const onAccountChanged = (id: number | undefined) => { +const onAccountChanged = (id: number) => { queryParams.accountId = id getList() } diff --git a/src/views/mp/hooks/useUpload.ts b/src/views/mp/hooks/useUpload.ts index b2d32d71..b0e70531 100644 --- a/src/views/mp/hooks/useUpload.ts +++ b/src/views/mp/hooks/useUpload.ts @@ -2,29 +2,29 @@ import type { UploadRawFile } from 'element-plus' const message = useMessage() // 消息 -enum MaterialType { +enum UploadType { Image = 'image', Voice = 'voice', Video = 'video' } -const useBeforeUpload = (type: MaterialType, maxSizeMB: number) => { +const useBeforeUpload = (type: UploadType, maxSizeMB: number) => { const fn = (rawFile: UploadRawFile): boolean => { let allowTypes: string[] = [] let name = '' switch (type) { - case MaterialType.Image: + case UploadType.Image: allowTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/bmp', 'image/jpg'] maxSizeMB = 2 name = '图片' break - case MaterialType.Voice: + case UploadType.Voice: allowTypes = ['audio/mp3', 'audio/mpeg', 'audio/wma', 'audio/wav', 'audio/amr'] maxSizeMB = 2 name = '语音' break - case MaterialType.Video: + case UploadType.Video: allowTypes = ['video/mp4'] maxSizeMB = 10 name = '视频' @@ -47,4 +47,4 @@ const useBeforeUpload = (type: MaterialType, maxSizeMB: number) => { return fn } -export { MaterialType, useBeforeUpload } +export { UploadType, useBeforeUpload } diff --git a/src/views/mp/material/components/UploadFile.vue b/src/views/mp/material/components/UploadFile.vue index be7e323b..f58084bb 100644 --- a/src/views/mp/material/components/UploadFile.vue +++ b/src/views/mp/material/components/UploadFile.vue @@ -6,14 +6,11 @@ :limit="1" :file-list="fileList" :data="uploadData" - :on-progress="(isUploading = true)" :on-error="onUploadError" :before-upload="onBeforeUpload" :on-success="onUploadSuccess" > - <el-button type="primary" plain :loading="isUploading" :disabled="isUploading"> - {{ isUploading ? '正在上传' : '点击上传' }} - </el-button> + <el-button type="primary" plain> 点击上传 </el-button> <template #tip> <span class="el-upload__tip" style="margin-left: 5px"> <slot></slot> @@ -27,14 +24,14 @@ import { HEADERS, UPLOAD_URL, UploadData, - MaterialType, + UploadType, beforeImageUpload, beforeVoiceUpload } from './upload' const message = useMessage() -const props = defineProps<{ type: MaterialType }>() +const props = defineProps<{ type: UploadType }>() const fileList = ref<UploadUserFile[]>([]) const emit = defineEmits<{ @@ -42,14 +39,13 @@ const emit = defineEmits<{ }>() const uploadData: UploadData = reactive({ - type: MaterialType.Image, + type: UploadType.Image, title: '', introduction: '' }) -const isUploading = ref(false) /** 上传前检查 */ -const onBeforeUpload = props.type === MaterialType.Image ? beforeImageUpload : beforeVoiceUpload +const onBeforeUpload = props.type === UploadType.Image ? beforeImageUpload : beforeVoiceUpload /** 上传成功处理 */ const onUploadSuccess: UploadProps['onSuccess'] = (res: any) => { @@ -64,7 +60,6 @@ const onUploadSuccess: UploadProps['onSuccess'] = (res: any) => { uploadData.introduction = '' message.notifySuccess('上传成功') - isUploading.value = false emit('uploaded') } diff --git a/src/views/mp/material/components/UploadVideo.vue b/src/views/mp/material/components/UploadVideo.vue index 8e374767..9d2fd861 100644 --- a/src/views/mp/material/components/UploadVideo.vue +++ b/src/views/mp/material/components/UploadVideo.vue @@ -1,5 +1,5 @@ <template> - <el-dialog title="新建视频" v-model="showDialog" width="600px" destroy-on-close> + <el-dialog title="新建视频" v-model="showDialog" width="600px"> <el-upload :action="UPLOAD_URL" :headers="HEADERS" @@ -8,7 +8,6 @@ :file-list="fileList" :data="uploadData" :before-upload="beforeVideoUpload" - :on-progress="(isUploading = true)" :on-error="onUploadError" :on-success="onUploadSuccess" ref="uploadVideoRef" @@ -18,12 +17,14 @@ <template #trigger> <el-button type="primary" plain>选择视频</el-button> </template> - <span class="el-upload__tip" style="margin-left: 10px" - >格式支持 MP4,文件大小不超过 10MB</span - > + <template #tip> + <span class="el-upload__tip" style="margin-left: 10px" + >格式支持 MP4,文件大小不超过 10MB</span + > + </template> </el-upload> <el-divider /> - <el-form :model="uploadData" :rules="uploadRules" ref="uploadFormRef" v-loading="isUploading"> + <el-form :model="uploadData" :rules="uploadRules" ref="uploadFormRef"> <el-form-item label="标题" prop="title"> <el-input v-model="uploadData.title" @@ -41,9 +42,7 @@ </el-form> <template #footer> <el-button @click="showDialog = false">取 消</el-button> - <el-button type="primary" @click="submitVideo" :loading="isUploading" :disabled="isUploading" - >提 交</el-button - > + <el-button type="primary" @click="submitVideo">提 交</el-button> </template> </el-dialog> </template> @@ -56,7 +55,7 @@ import type { UploadProps, UploadUserFile } from 'element-plus' -import { HEADERS, UploadData, UPLOAD_URL, beforeVideoUpload, MaterialType } from './upload' +import { HEADERS, UploadData, UPLOAD_URL, UploadType, beforeVideoUpload } from './upload' const message = useMessage() @@ -85,18 +84,16 @@ const showDialog = computed<boolean>({ } }) -const isUploading = ref(false) - const fileList = ref<UploadUserFile[]>([]) const uploadData: UploadData = reactive({ - type: MaterialType.Video, + type: UploadType.Video, title: '', introduction: '' }) -const uploadFormRef = ref<FormInstance>() -const uploadVideoRef = ref<UploadInstance>() +const uploadFormRef = ref<FormInstance | null>(null) +const uploadVideoRef = ref<UploadInstance | null>(null) const submitVideo = () => { uploadFormRef.value?.validate((valid) => { @@ -109,7 +106,6 @@ const submitVideo = () => { /** 上传成功处理 */ const onUploadSuccess: UploadProps['onSuccess'] = (res: any) => { - isUploading.value = false if (res.code !== 0) { message.error('上传出错:' + res.msg) return false diff --git a/src/views/mp/material/components/VideoTable.vue b/src/views/mp/material/components/VideoTable.vue index 81472959..b1e14dd5 100644 --- a/src/views/mp/material/components/VideoTable.vue +++ b/src/views/mp/material/components/VideoTable.vue @@ -39,7 +39,7 @@ </template> <script setup lang="ts"> -import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' +import WxVideoPlayer from '@/views/mp/components/wx-video-play' import { dateFormatter } from '@/utils/formatTime' const props = defineProps<{ diff --git a/src/views/mp/material/components/VoiceTable.vue b/src/views/mp/material/components/VoiceTable.vue index 6f37e1a0..4ae5174b 100644 --- a/src/views/mp/material/components/VoiceTable.vue +++ b/src/views/mp/material/components/VoiceTable.vue @@ -37,7 +37,7 @@ </template> <script setup lang="ts"> -import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' import { dateFormatter } from '@/utils/formatTime' const props = defineProps<{ diff --git a/src/views/mp/material/components/upload.ts b/src/views/mp/material/components/upload.ts index dc52a9eb..7158ab12 100644 --- a/src/views/mp/material/components/upload.ts +++ b/src/views/mp/material/components/upload.ts @@ -1,29 +1,29 @@ import type { UploadProps, UploadRawFile } from 'element-plus' import { getAccessToken } from '@/utils/auth' -import { MaterialType, useBeforeUpload } from '@/views/mp/hooks/useUpload' +import { UploadType, useBeforeUpload } from '@/views/mp/hooks/useUpload' const HEADERS = { Authorization: 'Bearer ' + getAccessToken() } // 请求头 const UPLOAD_URL = import.meta.env.VITE_BASE_URL + '/admin-api/mp/material/upload-permanent' // 上传地址 interface UploadData { - type: MaterialType + type: UploadType title: string introduction: string } const beforeImageUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Image, 2)(rawFile) + useBeforeUpload(UploadType.Image, 2)(rawFile) const beforeVoiceUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Voice, 2)(rawFile) + useBeforeUpload(UploadType.Voice, 2)(rawFile) const beforeVideoUpload: UploadProps['beforeUpload'] = (rawFile: UploadRawFile) => - useBeforeUpload(MaterialType.Video, 10)(rawFile) + useBeforeUpload(UploadType.Video, 10)(rawFile) export { HEADERS, UPLOAD_URL, - MaterialType, + UploadType, UploadData, beforeImageUpload, beforeVoiceUpload, diff --git a/src/views/mp/material/index.vue b/src/views/mp/material/index.vue index 4d8d3707..0e2a87d6 100644 --- a/src/views/mp/material/index.vue +++ b/src/views/mp/material/index.vue @@ -12,13 +12,13 @@ <ContentWrap> <el-tabs v-model="type" @tab-change="onTabChange"> <!-- tab 1:图片 --> - <el-tab-pane :name="MaterialType.Image"> + <el-tab-pane :name="UploadType.Image"> <template #label> - <span> <Icon icon="ep:picture" />图片 </span> + <el-row align="middle"> <Icon icon="ep:picture" />图片 </el-row> </template> <UploadFile v-hasPermi="['mp:material:upload-permanent']" - :type="MaterialType.Image" + :type="UploadType.Image" @uploaded="getList" > 支持 bmp/png/jpeg/jpg/gif 格式,大小不超过 2M @@ -35,13 +35,13 @@ </el-tab-pane> <!-- tab 2:语音 --> - <el-tab-pane :name="MaterialType.Voice"> + <el-tab-pane :name="UploadType.Voice"> <template #label> - <span> <Icon icon="ep:microphone" />语音 </span> + <el-row align="middle"> <Icon icon="ep:microphone" />语音 </el-row> </template> <UploadFile v-hasPermi="['mp:material:upload-permanent']" - :type="MaterialType.Voice" + :type="UploadType.Voice" @uploaded="getList" > 格式支持 mp3/wma/wav/amr,文件大小不超过 2M,播放长度不超过 60s @@ -58,9 +58,9 @@ </el-tab-pane> <!-- tab 3:视频 --> - <el-tab-pane :name="MaterialType.Video"> + <el-tab-pane :name="UploadType.Video"> <template #label> - <span> <Icon icon="ep:video-play" /> 视频 </span> + <el-row align="middle"> <Icon icon="ep:video-play" /> 视频 </el-row> </template> <el-button v-hasPermi="['mp:material:upload-permanent']" @@ -85,17 +85,17 @@ </ContentWrap> </template> <script lang="ts" setup name="MpMaterial"> -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import ImageTable from './components/ImageTable.vue' import VoiceTable from './components/VoiceTable.vue' import VideoTable from './components/VideoTable.vue' import UploadFile from './components/UploadFile.vue' import UploadVideo from './components/UploadVideo.vue' -import { MaterialType } from './components/upload' +import { UploadType } from './components/upload' import * as MpMaterialApi from '@/api/mp/material' const message = useMessage() // 消息 -const type = ref<MaterialType>(MaterialType.Image) // 素材类型 +const type = ref<UploadType>(UploadType.Image) // 素材类型 const loading = ref(false) // 遮罩层 const list = ref<any[]>([]) // 总条数 const total = ref(0) // 数据列表 @@ -103,19 +103,19 @@ const total = ref(0) // 数据列表 interface QueryParams { pageNo: number pageSize: number - accountId?: number + accountId: number permanent: boolean } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: undefined, + accountId: 0, permanent: true }) const showCreateVideo = ref(false) // 是否新建视频的弹窗 /** 侦听公众号变化 **/ -const onAccountChanged = (id?: number) => { +const onAccountChanged = (id: number) => { queryParams.accountId = id getList() } diff --git a/src/views/mp/menu/components/MenuEditor.vue b/src/views/mp/menu/components/MenuEditor.vue index 0bd27f19..684d66f6 100644 --- a/src/views/mp/menu/components/MenuEditor.vue +++ b/src/views/mp/menu/components/MenuEditor.vue @@ -94,7 +94,8 @@ </div> <el-dialog title="选择图文" v-model="showNewsDialog" width="80%" destroy-on-close> <WxMaterialSelect - :objData="{ type: 'news', accountId: props.accountId }" + type="news" + :account-id="props.accountId" @select-material="selectMaterial" /> </el-dialog> @@ -104,7 +105,7 @@ class="configur_content" v-if="menu.type === 'click' || menu.type === 'scancode_waitmsg'" > - <WxReplySelect v-if="hackResetWxReplySelect" :objData="menu.reply" /> + <WxReplySelect v-if="hackResetWxReplySelect" v-model="menu.reply" /> </div> </div> </div> @@ -112,15 +113,15 @@ </template> <script setup lang="ts"> -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 WxReplySelect from '@/views/mp/components/wx-reply' +import WxNews from '@/views/mp/components/wx-news' +import WxMaterialSelect from '@/views/mp/components/wx-material-select' import menuOptions from './menuOptions' const message = useMessage() const props = defineProps<{ - accountId?: number + accountId: number modelValue: any isParent: boolean }>() @@ -130,7 +131,6 @@ const emit = defineEmits<{ (e: 'update:modelValue', v: any) }>() -// TODO @Dhb52 输入的 table 切换时,表单应该保留 const menu = computed({ get() { return props.modelValue diff --git a/src/views/mp/menu/index.vue b/src/views/mp/menu/index.vue index c053a47c..c8ed0835 100644 --- a/src/views/mp/menu/index.vue +++ b/src/views/mp/menu/index.vue @@ -53,7 +53,7 @@ </template> <script lang="ts" setup name="MpMenu"> -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import MenuEditor from './components/MenuEditor.vue' import MenuPreviewer from './components/MenuPreviewer.vue' import * as MpMenuApi from '@/api/mp/menu' @@ -65,8 +65,8 @@ const MENU_NOT_SELECTED = '__MENU_NOT_SELECTED__' // ======================== 列表查询 ======================== const loading = ref(false) // 遮罩层 -const accountId = ref<number | undefined>() -const accountName = ref<string | undefined>('') +const accountId = ref<number>(0) +const accountName = ref<string>('') const menuList = ref<Menu[]>([]) // ======================== 菜单操作 ======================== @@ -103,7 +103,7 @@ const tempSelfObj = ref<{ const dialogNewsVisible = ref(false) // 跳转图文时的素材选择弹窗 /** 侦听公众号变化 **/ -const onAccountChanged = (id?: number, name?: string) => { +const onAccountChanged = (id: number, name: string) => { accountId.value = id accountName.value = name getList() diff --git a/src/views/mp/message/MessageTable.vue b/src/views/mp/message/MessageTable.vue index 23eb9aae..fc5e55ff 100644 --- a/src/views/mp/message/MessageTable.vue +++ b/src/views/mp/message/MessageTable.vue @@ -122,11 +122,11 @@ </template> <script setup lang="ts"> -import WxVideoPlayer from '@/views/mp/components/wx-video-play/main.vue' -import WxVoicePlayer from '@/views/mp/components/wx-voice-play/main.vue' -import WxLocation from '@/views/mp/components/wx-location/main.vue' -import WxMusic from '@/views/mp/components/wx-music/main.vue' -import WxNews from '@/views/mp/components/wx-news/main.vue' +import WxVideoPlayer from '@/views/mp/components/wx-video-play' +import WxVoicePlayer from '@/views/mp/components/wx-voice-play' +import WxLocation from '@/views/mp/components/wx-location' +import WxMusic from '@/views/mp/components/wx-music' +import WxNews from '@/views/mp/components/wx-news' import { dateFormatter } from '@/utils/formatTime' import { MsgType } from '@/views/mp/components/wx-msg/types' diff --git a/src/views/mp/message/index.vue b/src/views/mp/message/index.vue index c115813c..85048f38 100644 --- a/src/views/mp/message/index.vue +++ b/src/views/mp/message/index.vue @@ -81,8 +81,8 @@ </template> <script setup lang="ts" name="MpMessage"> import * as MpMessageApi from '@/api/mp/message' -import WxMsg from '@/views/mp/components/wx-msg/main.vue' -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxMsg from '@/views/mp/components/wx-msg' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import MessageTable from './MessageTable.vue' import { DICT_TYPE, getStrDictOptions } from '@/utils/dict' import { MsgType } from '@/views/mp/components/wx-msg/types' @@ -96,17 +96,17 @@ const list = ref<any[]>([]) // 当前页的列表数据 interface QueryParams { pageNo: number pageSize: number - openid: string | null - accountId: number | null - type: MsgType | null + openid: string | undefined + accountId: number + type: MsgType | undefined createTime: string[] | [] } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - openid: null, - accountId: null, - type: null, + openid: undefined, + accountId: 0, + type: undefined, createTime: [] }) const queryFormRef = ref<FormInstance | null>(null) // 搜索的表单 @@ -118,8 +118,8 @@ const messageBox = reactive({ }) /** 侦听accountId */ -const onAccountChanged = (id?: number) => { - queryParams.accountId = id as number +const onAccountChanged = (id: number) => { + queryParams.accountId = id handleQuery() } diff --git a/src/views/mp/tag/index.vue b/src/views/mp/tag/index.vue index 6689529d..a92d9127 100644 --- a/src/views/mp/tag/index.vue +++ b/src/views/mp/tag/index.vue @@ -14,10 +14,22 @@ <WxAccountSelect @change="onAccountChanged" /> </el-form-item> <el-form-item> - <el-button type="primary" plain @click="openForm('create')" v-hasPermi="['mp:tag:create']"> + <el-button + type="primary" + plain + @click="openForm('create')" + v-hasPermi="['mp:tag:create']" + :disabled="queryParams.accountId === 0" + > <Icon icon="ep:plus" class="mr-5px" /> 新增 </el-button> - <el-button type="success" plain @click="handleSync" v-hasPermi="['mp:tag:sync']"> + <el-button + type="success" + plain + @click="handleSync" + v-hasPermi="['mp:tag:sync']" + :disabled="queryParams.accountId === 0" + > <Icon icon="ep:refresh" class="mr-5px" /> 同步 </el-button> </el-form-item> @@ -74,28 +86,30 @@ import { dateFormatter } from '@/utils/formatTime' import * as MpTagApi from '@/api/mp/tag' import TagForm from './TagForm.vue' -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxAccountSelect from '@/views/mp/components/wx-account-select' + const message = useMessage() // 消息弹窗 const { t } = useI18n() // 国际化 const loading = ref(true) // 列表的加载中 const total = ref(0) // 列表的总页数 -const list = ref<any>([]) // 列表的数据 +const list = ref<any[]>([]) // 列表的数据 interface QueryParams { pageNo: number pageSize: number - accountId?: number + accountId: number } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: undefined + accountId: 0 }) + const formRef = ref<InstanceType<typeof TagForm> | null>(null) /** 侦听公众号变化 **/ -const onAccountChanged = (id?: number) => { +const onAccountChanged = (id: number) => { queryParams.pageNo = 1 queryParams.accountId = id getList() @@ -114,8 +128,8 @@ const getList = async () => { } /** 添加/修改操作 */ -const openForm = (type: string, id?: number) => { - formRef.value?.open(type, queryParams.accountId as number, id) +const openForm = (type: 'create' | 'update', id?: number) => { + formRef.value?.open(type, queryParams.accountId, id) } /** 删除按钮操作 */ diff --git a/src/views/mp/user/index.vue b/src/views/mp/user/index.vue index 8a54d38e..03e58a7f 100644 --- a/src/views/mp/user/index.vue +++ b/src/views/mp/user/index.vue @@ -34,7 +34,13 @@ <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']"> + <el-button + type="success" + plain + @click="handleSync" + v-hasPermi="['mp:user:sync']" + :disabled="queryParams.accountId === 0" + > <Icon icon="ep:refresh" class="mr-5px" /> 同步 </el-button> </el-form-item> @@ -97,7 +103,7 @@ import { dateFormatter } from '@/utils/formatTime' import * as MpUserApi from '@/api/mp/user' import * as MpTagApi from '@/api/mp/tag' -import WxAccountSelect from '@/views/mp/components/wx-account-select/main.vue' +import WxAccountSelect from '@/views/mp/components/wx-account-select' import type { FormInstance } from 'element-plus' import UserForm from './UserForm.vue' @@ -110,14 +116,14 @@ const list = ref<any[]>([]) // 列表的数据 interface QueryParams { pageNo: number pageSize: number - accountId?: number + accountId: number openid: string | null nickname: string | null } const queryParams: QueryParams = reactive({ pageNo: 1, pageSize: 10, - accountId: undefined, + accountId: 0, openid: null, nickname: null }) @@ -125,7 +131,7 @@ const queryFormRef = ref<FormInstance | null>(null) // 搜索的表单 const tagList = ref<any[]>([]) // 公众号标签列表 /** 侦听公众号变化 **/ -const onAccountChanged = (id?: number) => { +const onAccountChanged = (id: number) => { queryParams.pageNo = 1 queryParams.accountId = id getList()