diff --git a/apps/web-ele/src/api/mp/message/index.ts b/apps/web-ele/src/api/mp/message/index.ts index dc393baca..8395e2933 100644 --- a/apps/web-ele/src/api/mp/message/index.ts +++ b/apps/web-ele/src/api/mp/message/index.ts @@ -1,36 +1,41 @@ import type { PageParam, PageResult } from '@vben/request'; -import { requestClient } from '#/api/request'; +import { MessageType } from '@vben/constants'; -// TODO @dylan:这个类的代码,应该和对应的 antd 是一致的。调整下~看看相关的 vue 是不是也要调整掉。 -/** 消息类型枚举 */ -export enum MessageType { - IMAGE = 'image', // 图片消息 - MPNEWS = 'mpnews', // 公众号图文消息 - MUSIC = 'music', // 音乐消息 - NEWS = 'news', // 图文消息 - TEXT = 'text', // 文本消息 - VIDEO = 'video', // 视频消息 - VOICE = 'voice', // 语音消息 - WXCARD = 'wxcard', // 卡券消息 -} +import { requestClient } from '#/api/request'; export namespace MpMessageApi { /** 消息信息 */ export interface Message { id?: number; accountId: number; - type: MessageType; + type: MessageType | string; openid: string; content: string; mediaId?: string; status: number; remark?: string; createTime?: Date; + sendFrom?: number; + userId?: number; + event?: string; + eventKey?: string; + mediaUrl?: string; + recognition?: string; + url?: string; + title?: string; + label?: string; + locationX?: number; + locationY?: number; + thumbMediaUrl?: string; + musicUrl?: string; + hqMusicUrl?: string; + description?: string; + articles?: any[]; } /** 发送消息请求 */ - export interface SendMessageRequest { + export interface MessageSendRequestVO { accountId: number; openid: string; type: MessageType; @@ -50,6 +55,6 @@ export function getMessagePage(params: PageParam) { } /** 发送消息 */ -export function sendMessage(data: MpMessageApi.SendMessageRequest) { +export function sendMessage(data: MpMessageApi.MessageSendRequestVO) { return requestClient.post('/mp/message/send', data); } diff --git a/apps/web-ele/src/views/mp/components/index.ts b/apps/web-ele/src/views/mp/components/index.ts index b14a2c4c1..abab7ffb5 100644 --- a/apps/web-ele/src/views/mp/components/index.ts +++ b/apps/web-ele/src/views/mp/components/index.ts @@ -1,7 +1,7 @@ export { default as WxAccountSelect } from './wx-account-select/wx-account-select.vue'; export { default as WxLocation } from './wx-location/wx-location.vue'; export { default as WxMaterialSelect } from './wx-material-select/wx-material-select.vue'; -export { default as WxMsg } from './wx-msg/msg.vue'; // TODO @hw、@dylan:貌似和 antd 不同。antd 这里是 export { default as WxMsg } from './wx-msg/wx-msg.vue'; 看看哪个是对的 +export { default as WxMsg } from './wx-msg/wx-msg.vue'; export { default as WxMusic } from './wx-music/wx-music.vue'; export { default as WxNews } from './wx-news/wx-news.vue'; export { default as WxReply } from './wx-reply/wx-reply.vue'; diff --git a/apps/web-ele/src/views/mp/hooks/useUpload.ts b/apps/web-ele/src/views/mp/hooks/useUpload.ts new file mode 100644 index 000000000..08d6367f9 --- /dev/null +++ b/apps/web-ele/src/views/mp/hooks/useUpload.ts @@ -0,0 +1,77 @@ +import { ElMessage } from 'element-plus'; + +import { $t } from '#/locales'; + +export enum UploadType { + Image = 'image', + Video = 'video', + Voice = 'voice', +} + +interface UploadTypeConfig { + allowTypes: string[]; + maxSizeMB: number; + i18nKey: string; +} + +export interface UploadRawFile { + name: string; + size: number; + type: string; +} + +const UPLOAD_CONFIGS: Record = { + [UploadType.Image]: { + allowTypes: [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/bmp', + 'image/jpg', + ], + maxSizeMB: 2, + i18nKey: 'mp.upload.image', + }, + [UploadType.Video]: { + allowTypes: ['video/mp4'], + maxSizeMB: 10, + i18nKey: 'mp.upload.video', + }, + [UploadType.Voice]: { + allowTypes: [ + 'audio/mp3', + 'audio/mpeg', + 'audio/wma', + 'audio/wav', + 'audio/amr', + ], + maxSizeMB: 2, + i18nKey: 'mp.upload.voice', + }, +}; + +export const useBeforeUpload = (type: UploadType, maxSizeMB?: number) => { + const fn = (rawFile: UploadRawFile): boolean => { + const config = UPLOAD_CONFIGS[type]; + const finalMaxSize = maxSizeMB ?? config.maxSizeMB; + + // 格式不正确 + if (!config.allowTypes.includes(rawFile.type)) { + const typeName = $t(config.i18nKey); + ElMessage.error($t('mp.upload.invalidFormat', [typeName])); + return false; + } + + // 大小不正确 + if (rawFile.size / 1024 / 1024 > finalMaxSize) { + const typeName = $t(config.i18nKey); + ElMessage.error($t('mp.upload.maxSize', [typeName, finalMaxSize])); + return false; + } + + return true; + }; + + return fn; +}; + diff --git a/apps/web-ele/src/views/mp/material/index.vue b/apps/web-ele/src/views/mp/material/index.vue new file mode 100644 index 000000000..a53235949 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/index.vue @@ -0,0 +1,238 @@ + + + diff --git a/apps/web-ele/src/views/mp/material/modules/UploadFile.vue b/apps/web-ele/src/views/mp/material/modules/UploadFile.vue new file mode 100644 index 000000000..62e2b7c37 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/UploadFile.vue @@ -0,0 +1,107 @@ + + + + diff --git a/apps/web-ele/src/views/mp/material/modules/UploadVideo.vue b/apps/web-ele/src/views/mp/material/modules/UploadVideo.vue new file mode 100644 index 000000000..02a6e51e6 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/UploadVideo.vue @@ -0,0 +1,166 @@ + + + + diff --git a/apps/web-ele/src/views/mp/material/modules/data.ts b/apps/web-ele/src/views/mp/material/modules/data.ts new file mode 100644 index 000000000..cab6f251e --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/data.ts @@ -0,0 +1,133 @@ +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; +import type { MpMaterialApi } from '#/api/mp/material'; + +/** 视频表格列配置 */ +export function useVideoGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'mediaId', + title: '编号', + align: 'center', + width: 160, + }, + { + field: 'name', + title: '文件名', + align: 'center', + minWidth: 100, + }, + { + field: 'title', + title: '标题', + align: 'center', + minWidth: 200, + }, + { + field: 'introduction', + title: '介绍', + align: 'center', + minWidth: 220, + }, + { + field: 'video', + title: '视频', + align: 'center', + width: 220, + slots: { default: 'video' }, + }, + { + field: 'createTime', + title: '上传时间', + align: 'center', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + align: 'center', + fixed: 'right', + width: 180, + slots: { default: 'actions' }, + }, + ]; +} + +/** 语音表格列配置 */ +export function useVoiceGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'mediaId', + title: '编号', + align: 'center', + width: 160, + }, + { + field: 'name', + title: '文件名', + align: 'center', + minWidth: 100, + }, + { + field: 'voice', + title: '语音', + align: 'center', + width: 220, + slots: { default: 'voice' }, + }, + { + field: 'createTime', + title: '上传时间', + align: 'center', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + align: 'center', + fixed: 'right', + width: 160, + slots: { default: 'actions' }, + }, + ]; +} + +/** 图片表格列配置 */ +export function useImageGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'mediaId', + title: '编号', + align: 'center', + width: 400, + }, + { + field: 'name', + title: '文件名', + align: 'center', + width: 200, + }, + { + field: 'url', + title: '图片', + align: 'center', + width: 200, + slots: { default: 'image' }, + }, + { + field: 'createTime', + title: '上传时间', + align: 'center', + width: 180, + formatter: 'formatDateTime', + }, + { + field: 'actions', + title: '操作', + align: 'center', + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} diff --git a/apps/web-ele/src/views/mp/material/modules/image-table.vue b/apps/web-ele/src/views/mp/material/modules/image-table.vue new file mode 100644 index 000000000..8d925f0ae --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/image-table.vue @@ -0,0 +1,107 @@ + + + + diff --git a/apps/web-ele/src/views/mp/material/modules/upload.ts b/apps/web-ele/src/views/mp/material/modules/upload.ts new file mode 100644 index 000000000..22497c4e0 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/upload.ts @@ -0,0 +1,47 @@ +import type { UploadProps } from 'element-plus'; + +import type { UploadRawFile } from '#/views/mp/hooks/useUpload'; + +import { useAccessStore } from '@vben/stores'; + +import { UploadType, useBeforeUpload } from '#/views/mp/hooks/useUpload'; + +const accessStore = useAccessStore(); +const HEADERS = { Authorization: `Bearer ${accessStore.accessToken}` }; // 请求头 +const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-permanent`; // 上传地址 + +interface UploadData { + accountId: number; + introduction: string; + title: string; + type: UploadType; +} + +const beforeImageUpload: UploadProps['beforeUpload'] = function ( + rawFile: UploadRawFile, +) { + return useBeforeUpload(UploadType.Image, 2)(rawFile); +}; + +const beforeVoiceUpload: UploadProps['beforeUpload'] = function ( + rawFile: UploadRawFile, +) { + return useBeforeUpload(UploadType.Voice, 2)(rawFile); +}; + +const beforeVideoUpload: UploadProps['beforeUpload'] = function ( + rawFile: UploadRawFile, +) { + return useBeforeUpload(UploadType.Video, 10)(rawFile); +}; + +export { + beforeImageUpload, + beforeVideoUpload, + beforeVoiceUpload, + HEADERS, + UPLOAD_URL, + type UploadData, + UploadType, +}; + diff --git a/apps/web-ele/src/views/mp/material/modules/video-table.vue b/apps/web-ele/src/views/mp/material/modules/video-table.vue new file mode 100644 index 000000000..555360687 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/video-table.vue @@ -0,0 +1,99 @@ + + + + diff --git a/apps/web-ele/src/views/mp/material/modules/voice-table.vue b/apps/web-ele/src/views/mp/material/modules/voice-table.vue new file mode 100644 index 000000000..a42f10465 --- /dev/null +++ b/apps/web-ele/src/views/mp/material/modules/voice-table.vue @@ -0,0 +1,99 @@ + + + + diff --git a/apps/web-ele/src/views/mp/message/index.vue b/apps/web-ele/src/views/mp/message/index.vue new file mode 100644 index 000000000..7fb9e3c21 --- /dev/null +++ b/apps/web-ele/src/views/mp/message/index.vue @@ -0,0 +1,211 @@ + + + + + + diff --git a/apps/web-ele/src/views/mp/message/message-table.vue b/apps/web-ele/src/views/mp/message/message-table.vue new file mode 100644 index 000000000..53d24373e --- /dev/null +++ b/apps/web-ele/src/views/mp/message/message-table.vue @@ -0,0 +1,263 @@ + + +