diff --git a/apps/web-antd/.env.development b/apps/web-antd/.env.development index cff2557643921373fa5e308f9113b18aedc65de3..fe07e867ccb036f849f96a1f772a991e0d4ba985 100644 --- a/apps/web-antd/.env.development +++ b/apps/web-antd/.env.development @@ -4,9 +4,9 @@ VITE_PORT=5666 VITE_BASE=/ # 请求路径 -VITE_BASE_URL=http://127.0.0.1:48080 +VITE_BASE_URL=http://47.103.66.220:48080 # 接口地址 -VITE_GLOB_API_URL=/admin-api +VITE_GLOB_API_URL=http://47.103.66.220:48080/admin-api # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务 VITE_UPLOAD_TYPE=server # 是否打开 devtools,true 为打开,false 为关闭 diff --git a/apps/web-antd/package.json b/apps/web-antd/package.json index dd8b2435c65d5563542d4187a929cb48acff8081..ef663925f36de219ef2c0024177f6ba5603f03f2 100644 --- a/apps/web-antd/package.json +++ b/apps/web-antd/package.json @@ -43,9 +43,11 @@ "@vben/styles": "workspace:*", "@vben/types": "workspace:*", "@vben/utils": "workspace:*", + "@videojs-player/vue": "catalog:", "@vueuse/core": "catalog:", "@vueuse/integrations": "catalog:", "ant-design-vue": "catalog:", + "benz-amr-recorder": "^1.1.5", "bpmn-js": "catalog:", "bpmn-js-properties-panel": "catalog:", "bpmn-js-token-simulation": "catalog:", @@ -58,6 +60,7 @@ "pinia": "catalog:", "steady-xml": "catalog:", "tinymce": "catalog:", + "video.js": "catalog:", "vue": "catalog:", "vue-dompurify-html": "catalog:", "vue-router": "catalog:", diff --git a/apps/web-antd/src/api/mp/draft/index.ts b/apps/web-antd/src/api/mp/draft/index.ts index 435f13e0bc60bccb6c4cf865322793f687a59da4..9032ffd572ad1a31c282fdb059120cea7a930ac9 100644 --- a/apps/web-antd/src/api/mp/draft/index.ts +++ b/apps/web-antd/src/api/mp/draft/index.ts @@ -35,9 +35,13 @@ export function getDraftPage(params: PageParam) { /** 创建草稿 */ export function createDraft(accountId: number, articles: MpDraftApi.Article[]) { - return requestClient.post('/mp/draft/create', articles, { - params: { accountId }, - }); + return requestClient.post( + '/mp/draft/create', + { articles }, + { + params: { accountId }, + }, + ); } /** 更新草稿 */ @@ -46,9 +50,13 @@ export function updateDraft( mediaId: string, articles: MpDraftApi.Article[], ) { - return requestClient.put('/mp/draft/update', articles, { - params: { accountId, mediaId }, - }); + return requestClient.put( + '/mp/draft/update', + { articles }, + { + params: { accountId, mediaId }, + }, + ); } /** 删除草稿 */ diff --git a/apps/web-antd/src/assets/imgs/wechat.png b/apps/web-antd/src/assets/imgs/wechat.png new file mode 100644 index 0000000000000000000000000000000000000000..6afc5e41cfc29d258e0c6135981a7a09b7bcf126 Binary files /dev/null and b/apps/web-antd/src/assets/imgs/wechat.png differ diff --git a/apps/web-antd/src/utils/index.ts b/apps/web-antd/src/utils/index.ts index 3a066fd4a326340a3fb2c95e32824f515a013980..5877010a3df2e61e889427e71c1add8ab07c3316 100644 --- a/apps/web-antd/src/utils/index.ts +++ b/apps/web-antd/src/utils/index.ts @@ -1,2 +1,29 @@ +import type { Recordable } from '@vben/types'; + export * from './rangePickerProps'; export * from './routerHelper'; + +/** + * 查找数组对象的某个下标 + * @param {Array} ary 查找的数组 + * @param {Function} fn 判断的方法 + */ +type Fn = (item: T, index: number, array: Array) => boolean; +export const findIndex = >( + ary: Array, + fn: Fn, +): number => { + if (ary.findIndex) { + return ary.findIndex((item, index, array) => fn(item, index, array)); + } + let index = -1; + ary.some((item: T, i: number, ary: Array) => { + const ret: boolean = fn(item, i, ary); + if (ret) { + index = i; + return true; + } + return false; + }); + return index; +}; diff --git a/apps/web-antd/src/utils/routerHelper.ts b/apps/web-antd/src/utils/routerHelper.ts index 0f3c52096e0bde377f3d4a83574f29c0509db17d..a9e99f54b90cdcebda70b39e8fd6c72614abc136 100644 --- a/apps/web-antd/src/utils/routerHelper.ts +++ b/apps/web-antd/src/utils/routerHelper.ts @@ -1,3 +1,8 @@ +import type { + RouteLocationNormalized, + RouteRecordNormalized, +} from 'vue-router'; + import { defineAsyncComponent } from 'vue'; const modules = import.meta.glob('../views/**/*.{vue,tsx}'); @@ -14,3 +19,20 @@ export function registerComponent(componentPath: string) { } } } + +export const getRawRoute = ( + route: RouteLocationNormalized, +): RouteLocationNormalized => { + if (!route) return route; + const { matched, ...opt } = route; + return { + ...opt, + matched: (matched + ? matched.map((item) => ({ + meta: item.meta, + name: item.name, + path: item.path, + })) + : undefined) as RouteRecordNormalized[], + }; +}; diff --git a/apps/web-antd/src/utils/useUpload.ts b/apps/web-antd/src/utils/useUpload.ts new file mode 100644 index 0000000000000000000000000000000000000000..4c87c04ec2d0c605b9b6f8cf2547fe975a20b039 --- /dev/null +++ b/apps/web-antd/src/utils/useUpload.ts @@ -0,0 +1,63 @@ +import { message } from 'ant-design-vue'; + +enum UploadType { + Image = 'image', + Video = 'video', + Voice = 'voice', +} + +const useBeforeUpload = (type: UploadType, maxSizeMB: number) => { + const fn = (file: File): boolean => { + let allowTypes: string[] = []; + let name = ''; + + switch (type) { + case UploadType.Image: { + allowTypes = [ + 'image/jpeg', + 'image/png', + 'image/gif', + 'image/bmp', + 'image/jpg', + ]; + maxSizeMB = 2; + name = '图片'; + break; + } + case UploadType.Video: { + allowTypes = ['video/mp4']; + maxSizeMB = 10; + name = '视频'; + break; + } + case UploadType.Voice: { + allowTypes = [ + 'audio/mp3', + 'audio/mpeg', + 'audio/wma', + 'audio/wav', + 'audio/amr', + ]; + maxSizeMB = 2; + name = '语音'; + break; + } + } + // 格式不正确 + if (!allowTypes.includes(file.type)) { + message.error(`上传${name}格式不对!`); + return false; + } + // 大小不正确 + if (file.size / 1024 / 1024 > maxSizeMB) { + message.error(`上传${name}大小不能超过${maxSizeMB}M!`); + return false; + } + + return true; + }; + + return fn; +}; + +export { UploadType, useBeforeUpload }; diff --git a/apps/web-antd/src/views/mp/autoReply/data.ts b/apps/web-antd/src/views/mp/autoReply/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..31c0481b54beb24d43113ca4202149fb42d9a540 --- /dev/null +++ b/apps/web-antd/src/views/mp/autoReply/data.ts @@ -0,0 +1,88 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeGridPropTypes } from '#/adapter/vxe-table'; + +import { markRaw } from 'vue'; + +import { DICT_TYPE } from '@vben/constants'; + +import WxAccountSelect from '#/views/mp/modules/wx-account-select/main.vue'; + +import { MsgType } from './modules/types'; + +/** 获取表格列配置 */ +export function useGridColumns(msgType: MsgType): VxeGridPropTypes.Columns { + const columns: VxeGridPropTypes.Columns = []; + // 请求消息类型列(仅消息回复显示) + if (msgType === MsgType.Message) { + columns.push({ + field: 'requestMessageType', + title: '请求消息类型', + minWidth: 120, + }); + } + + // 关键词列(仅关键词回复显示) + if (msgType === MsgType.Keyword) { + columns.push({ + field: 'requestKeyword', + title: '关键词', + minWidth: 150, + }); + } + + // 匹配类型列(仅关键词回复显示) + if (msgType === MsgType.Keyword) { + columns.push({ + field: 'requestMatch', + title: '匹配类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MP_AUTO_REPLY_REQUEST_MATCH }, + }, + }); + } + + // 回复消息类型列 + columns.push( + { + field: 'responseMessageType', + title: '回复消息类型', + minWidth: 120, + cellRender: { + name: 'CellDict', + props: { type: DICT_TYPE.MP_MESSAGE_TYPE }, + }, + }, + { + field: 'responseContent', + title: '回复内容', + minWidth: 200, + slots: { default: 'replyContent' }, + }, + { + field: 'createTime', + title: '创建时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 140, + fixed: 'right', + slots: { default: 'actions' }, + }, + ); + return columns; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: markRaw(WxAccountSelect), + }, + ]; +} diff --git a/apps/web-antd/src/views/mp/autoReply/index.vue b/apps/web-antd/src/views/mp/autoReply/index.vue index 06996f6d127e27fb802709f5bb270052030d02dd..fb1b9ce3fdc5bab7d275de18bae9131be94f7c67 100644 --- a/apps/web-antd/src/views/mp/autoReply/index.vue +++ b/apps/web-antd/src/views/mp/autoReply/index.vue @@ -1,29 +1,259 @@ diff --git a/apps/web-antd/src/views/mp/autoReply/modules/ReplyForm.vue b/apps/web-antd/src/views/mp/autoReply/modules/ReplyForm.vue new file mode 100644 index 0000000000000000000000000000000000000000..d8e13708307e0d922df612cade1f5be347b84dd5 --- /dev/null +++ b/apps/web-antd/src/views/mp/autoReply/modules/ReplyForm.vue @@ -0,0 +1,139 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/autoReply/modules/ReplyTable.vue b/apps/web-antd/src/views/mp/autoReply/modules/ReplyTable.vue new file mode 100644 index 0000000000000000000000000000000000000000..bdfb41ade88e9abf314e2dfa3b1a2b3f86827432 --- /dev/null +++ b/apps/web-antd/src/views/mp/autoReply/modules/ReplyTable.vue @@ -0,0 +1,55 @@ + + + diff --git a/apps/web-antd/src/views/mp/autoReply/modules/form.vue b/apps/web-antd/src/views/mp/autoReply/modules/form.vue new file mode 100644 index 0000000000000000000000000000000000000000..7f9b9ed384041ebf79914d4c41e687d610521bd5 --- /dev/null +++ b/apps/web-antd/src/views/mp/autoReply/modules/form.vue @@ -0,0 +1,142 @@ + + + diff --git a/apps/web-antd/src/views/mp/autoReply/modules/types.ts b/apps/web-antd/src/views/mp/autoReply/modules/types.ts new file mode 100644 index 0000000000000000000000000000000000000000..f07a7d758ad19a6b994e3c879535dc8460fbd778 --- /dev/null +++ b/apps/web-antd/src/views/mp/autoReply/modules/types.ts @@ -0,0 +1,7 @@ +// 消息类型(Follow: 关注时回复;Message: 消息回复;Keyword: 关键词回复) +// 作为 tab.name,enum 的数字不能随意修改,与 api 参数相关 +export enum MsgType { + Follow = 1, + Keyword = 3, + Message = 2, +} diff --git a/apps/web-antd/src/views/mp/draft/data.ts b/apps/web-antd/src/views/mp/draft/data.ts new file mode 100644 index 0000000000000000000000000000000000000000..89492f63e036fe1526fd83d541923d2b657bc186 --- /dev/null +++ b/apps/web-antd/src/views/mp/draft/data.ts @@ -0,0 +1,41 @@ +import type { VbenFormSchema } from '#/adapter/form'; +import type { VxeTableGridOptions } from '#/adapter/vxe-table'; + +import { markRaw } from 'vue'; + +import WxAccountSelect from '#/views/mp/modules/wx-account-select/main.vue'; + +/** 获取表格列配置 */ +export function useGridColumns(): VxeTableGridOptions['columns'] { + return [ + { + field: 'content', + title: '图文内容', + minWidth: 300, + slots: { default: 'content' }, + }, + { + field: 'updateTime', + title: '更新时间', + minWidth: 180, + formatter: 'formatDateTime', + }, + { + title: '操作', + width: 200, + fixed: 'right', + slots: { default: 'actions' }, + }, + ]; +} + +/** 列表的搜索表单 */ +export function useGridFormSchema(): VbenFormSchema[] { + return [ + { + fieldName: 'accountId', + label: '公众号', + component: markRaw(WxAccountSelect), + }, + ]; +} diff --git a/apps/web-antd/src/views/mp/draft/index.vue b/apps/web-antd/src/views/mp/draft/index.vue index 9f1b6297b383279bf7d601c258aec366817a87ff..e9ef78442b73f2d0aca2411a98c0796f839adb15 100644 --- a/apps/web-antd/src/views/mp/draft/index.vue +++ b/apps/web-antd/src/views/mp/draft/index.vue @@ -1,29 +1,316 @@ + + diff --git a/apps/web-antd/src/views/mp/draft/modules/cover-select.vue b/apps/web-antd/src/views/mp/draft/modules/cover-select.vue new file mode 100644 index 0000000000000000000000000000000000000000..747437173fdd59d84e7d93c8259f9e0445d53bbd --- /dev/null +++ b/apps/web-antd/src/views/mp/draft/modules/cover-select.vue @@ -0,0 +1,189 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/draft/modules/draft-table.vue b/apps/web-antd/src/views/mp/draft/modules/draft-table.vue new file mode 100644 index 0000000000000000000000000000000000000000..36ef5b0a663ce4b75fc4402fcaf2e975888a878b --- /dev/null +++ b/apps/web-antd/src/views/mp/draft/modules/draft-table.vue @@ -0,0 +1,25 @@ + + + + + diff --git a/apps/web-antd/src/views/mp/draft/modules/form.vue b/apps/web-antd/src/views/mp/draft/modules/form.vue new file mode 100644 index 0000000000000000000000000000000000000000..9b73ca3fb5a95abf55cbae90ae156e02a1fdd0d4 --- /dev/null +++ b/apps/web-antd/src/views/mp/draft/modules/form.vue @@ -0,0 +1,103 @@ + + + diff --git a/apps/web-antd/src/views/mp/draft/modules/news-form.vue b/apps/web-antd/src/views/mp/draft/modules/news-form.vue new file mode 100644 index 0000000000000000000000000000000000000000..62b2b57dc6433da7c70cd1cba179d20b4d8a9184 --- /dev/null +++ b/apps/web-antd/src/views/mp/draft/modules/news-form.vue @@ -0,0 +1,341 @@ + + +