!266 【antd/ele】【mp】迁移

Merge pull request !266 from hw/reform-mp
This commit is contained in:
芋道源码
2025-11-20 09:57:17 +00:00
committed by Gitee
78 changed files with 353 additions and 569 deletions

View File

@@ -24,6 +24,49 @@ export namespace MpDraftApi {
articles: Article[];
createTime?: Date;
}
/** 图文项(包含预览字段) */
export interface NewsItem {
title: string;
thumbMediaId: string;
author: string;
digest: string;
showCoverPic: number;
content: string;
contentSourceUrl: string;
needOpenComment: number;
onlyFansCanComment: number;
thumbUrl: string;
picUrl?: string; // 用于预览封面
}
/** 图文列表 */
export interface NewsItemList {
newsItem: NewsItem[];
}
/** 草稿文章(用于展示) */
export interface DraftArticle {
mediaId: string;
content: NewsItemList;
updateTime: number;
}
}
/** 创建空的图文项 */
export function createEmptyNewsItem(): MpDraftApi.NewsItem {
return {
title: '',
thumbMediaId: '',
author: '',
digest: '',
showCoverPic: 0,
content: '',
contentSourceUrl: '',
needOpenComment: 0,
onlyFansCanComment: 0,
thumbUrl: '',
};
}
/** 查询草稿列表 */

View File

@@ -1,15 +1,8 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
import { MaterialType } from '@vben/constants';
/** 素材类型枚举 */
// TODO @hwmp 相关的枚举,可以考虑放这里来。
export enum MaterialType {
IMAGE = 1, // 图片
THUMB = 4, // 缩略图
VIDEO = 3, // 视频
VOICE = 2, // 语音
}
import { requestClient } from '#/api/request';
export namespace MpMaterialApi {
/** 素材信息 */

View File

@@ -1,20 +1,6 @@
import { requestClient } from '#/api/request';
import { MenuType } from '@vben/constants';
/** 菜单类型枚举 */
// TODO @hwmp 相关的枚举,可以考虑放这里来。
export enum MenuType {
CLICK = 'click', // 点击推事件
LOCATION_SELECT = 'location_select', // 发送位置
MEDIA_ID = 'media_id', // 下发消息
MINIPROGRAM = 'miniprogram', // 小程序
PIC_PHOTO_OR_ALBUM = 'pic_photo_or_album', // 拍照或者相册发图
PIC_SYSPHOTO = 'pic_sysphoto', // 系统拍照发图
PIC_WEIXIN = 'pic_weixin', // 微信相册发图
SCANCODE_PUSH = 'scancode_push', // 扫码推事件
SCANCODE_WAITMSG = 'scancode_waitmsg', // 扫码带提示
VIEW = 'view', // 跳转 URL
VIEW_LIMITED = 'view_limited', // 跳转图文消息URL
}
import { requestClient } from '#/api/request';
export namespace MpMenuApi {
/** 菜单按钮信息 */

View File

@@ -1,19 +1,8 @@
import type { PageParam, PageResult } from '@vben/request';
import { requestClient } from '#/api/request';
import { MessageType } from '@vben/constants';
/** 消息类型枚举 */
// TODO @hwmp 相关的枚举,可以考虑放这里来。
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 {
/** 消息信息 */

View File

@@ -3,13 +3,11 @@ import type { VxeGridPropTypes } from '#/adapter/vxe-table';
import { markRaw } from 'vue';
import { DICT_TYPE } from '@vben/constants';
import { DICT_TYPE, AutoReplyMsgType as MsgType } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { WxReply } from '#/views/mp/components';
import { MsgType } from './types';
// TODO @芋艿:要不要使用统一枚举?
const RequestMessageTypes = new Set([
'image',
@@ -143,10 +141,6 @@ export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
fieldName: 'reply',
label: '回复消息',
component: markRaw(WxReply),
// TODO @hw这里注释要不要删除掉
// componentProps: {
// modelValue: { type: 'video', content: '12456' },
// },
modelPropName: 'modelValue',
});
return schema;

View File

@@ -5,6 +5,7 @@ import type { MpAutoReplyApi } from '#/api/mp/autoReply';
import { computed, nextTick, ref } from 'vue';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { AutoReplyMsgType as MsgType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import { message, Row, Tabs } from 'ant-design-vue';
@@ -21,7 +22,6 @@ import { WxAccountSelect } from '#/views/mp/components';
import { useGridColumns, useGridFormSchema } from './data';
import ReplyContentCell from './modules/content.vue';
import Form from './modules/form.vue';
import { MsgType } from './types';
defineOptions({ name: 'MpAutoReply' });

View File

@@ -4,16 +4,15 @@ import type { Reply } from '#/views/mp/components';
import { computed, nextTick, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { AutoReplyMsgType as MsgType, ReplyType } from '@vben/constants';
import { message } from 'ant-design-vue';
import { useVbenForm } from '#/adapter/form';
import { createAutoReply, updateAutoReply } from '#/api/mp/autoReply';
import { $t } from '#/locales';
import { ReplyType } from '#/views/mp/components/constants';
import { useFormSchema } from '../data';
import { MsgType } from '../types';
const emit = defineEmits(['success']);
@@ -113,13 +112,6 @@ const [Modal, modalApi] = useVbenModal({
// 编辑:加载数据
const rowData = data.row;
const formValues: any = { ...rowData };
// TODO @hw下面要删除掉么注释。
// delete formValues.responseMessageType;
// delete formValues.responseContent;
// delete formValues.responseMediaId;
// delete formValues.responseMediaUrl;
// delete formValues.responseDescription;
// delete formValues.responseArticles;
formValues.reply = {
type: rowData.responseMessageType,
accountId: data.accountId || -1,

View File

@@ -1,8 +0,0 @@
// 消息类型Follow: 关注时回复Message: 消息回复Keyword: 关键词回复)
// 作为 tab.nameenum 的数字不能随意修改,与 api 参数相关
// TODO @hw可以搞到 biz-mp-enum.ts 里。
export enum MsgType {
Follow = 1,
Keyword = 3,
Message = 2,
}

View File

@@ -1,33 +0,0 @@
// TODO @hw看看要不要迁移到 packages/constants/src/biz-mp-enum.ts
export enum ReplyType {
Image = 'image',
Music = 'music',
News = 'news',
Text = 'text',
Video = 'video',
Voice = 'voice',
}
export enum NewsType {
Draft = '2',
Published = '1',
}
export enum MaterialType {
Image = 'image',
News = 'news',
Video = 'video',
Voice = 'voice',
}
export enum MsgType {
Event = 'event',
Image = 'image',
Link = 'link',
Location = 'location',
Music = 'music',
News = 'news',
Text = 'text',
Video = 'video',
Voice = 'voice',
}

View File

@@ -1,13 +1,9 @@
export * from './constants';
export * from './wx-account-select';
export * from './wx-location';
export * from './wx-material-select';
export * from './wx-msg';
export * from './wx-music';
export * from './wx-news';
export * from './wx-reply';
export * from './wx-video-play';
export * from './wx-voice-play';
// TODO @hw要不使用 export { default as WxAccountSelect } from './account-select.vue'; 形式;
export { default as WxAccountSelect } from './wx-account-select/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';
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';
export { default as WxVideoPlayer } from './wx-video-play/wx-video-play.vue';
export { default as WxVoicePlayer } from './wx-voice-play/wx-voice-play.vue';

View File

@@ -4,6 +4,7 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { reactive, ref, watch } from 'vue';
import { Page } from '@vben/common-ui';
import { NewsType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import { Button, Pagination, Row, Spin } from 'ant-design-vue';
@@ -14,8 +15,6 @@ import { getFreePublishPage } from '#/api/mp/freePublish';
import { getMaterialPage } from '#/api/mp/material';
import { WxNews, WxVideoPlayer, WxVoicePlayer } from '#/views/mp/components';
import { NewsType } from '../constants';
defineOptions({ name: 'WxMaterialSelect' });
const props = withDefaults(

View File

@@ -1,4 +1,5 @@
<script lang="ts" setup>
import { MpMsgType as MsgType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import {
@@ -9,7 +10,6 @@ import {
WxVoicePlayer,
} from '#/views/mp/components';
import { MsgType } from '../constants';
import MsgEvent from './msg-event.vue';
defineOptions({ name: 'Msg' });

View File

@@ -28,7 +28,7 @@ defineExpose({
<Image
:src="article.picUrl"
:preview="false"
class="material-img"
class="material-img flex items-center justify-center"
/>
<div class="news-content-title">
<span>{{ article.title }}</span>

View File

@@ -3,14 +3,13 @@ import type { Reply } from './types';
import { computed, ref } from 'vue';
import { NewsType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import { Button, Col, Modal, Row } from 'ant-design-vue';
import { WxMaterialSelect, WxNews } from '#/views/mp/components';
import { NewsType } from '../constants';
defineOptions({ name: 'TabNews' });
const props = defineProps<{

View File

@@ -1,6 +1,6 @@
import type { Ref } from 'vue';
import type { ReplyType } from '../constants';
import type { ReplyType } from '@vben/constants';
import { unref } from 'vue';

View File

@@ -12,11 +12,11 @@ import type { Reply } from './types';
import { computed, ref, unref, watch } from 'vue';
import { NewsType, ReplyType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons';
import { Row, Tabs } from 'ant-design-vue';
import { NewsType, ReplyType } from '../constants';
import TabImage from './tab-image.vue';
import TabMusic from './tab-music.vue';
import TabNews from './tab-news.vue';

View File

@@ -1,7 +1,6 @@
<script lang="ts" setup>
import type { Article } from './modules/types';
import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import type { MpDraftApi } from '#/api/mp/draft';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
import { $t } from '@vben/locales';
@@ -9,10 +8,9 @@ import { $t } from '@vben/locales';
import { message } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import { deleteDraft, getDraftPage } from '#/api/mp/draft';
import { createEmptyNewsItem, deleteDraft, getDraftPage } from '#/api/mp/draft';
import { submitFreePublish } from '#/api/mp/freePublish';
import { WxAccountSelect } from '#/views/mp/components';
import { createEmptyNewsItem } from '#/views/mp/draft/modules/types';
import { useGridColumns, useGridFormSchema } from './data';
import DraftTableCell from './modules/draft-table.vue';
@@ -49,7 +47,7 @@ async function handleCreate() {
}
/** 修改草稿 */
async function handleEdit(row: Article) {
async function handleEdit(row: MpDraftApi.DraftArticle) {
const formValues = await gridApi.formApi.getValues();
const accountId = formValues.accountId;
if (!accountId) {
@@ -67,7 +65,7 @@ async function handleEdit(row: Article) {
}
/** 删除草稿 */
async function handleDelete(row: Article) {
async function handleDelete(row: MpDraftApi.DraftArticle) {
const formValues = await gridApi.formApi.getValues();
const accountId = formValues.accountId;
if (!accountId) {
@@ -89,7 +87,7 @@ async function handleDelete(row: Article) {
}
/** 发布草稿 */
async function handlePublish(row: Article) {
async function handlePublish(row: MpDraftApi.DraftArticle) {
const formValues = await gridApi.formApi.getValues();
const accountId = formValues.accountId;
if (!accountId) {
@@ -145,7 +143,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
}
});
return {
list: drafts.list as unknown as Article[],
list: drafts.list as unknown as MpDraftApi.DraftArticle[],
total: drafts.total,
};
},
@@ -160,8 +158,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: true,
search: true,
},
// TODO @hw这里有点纠结一般是 MpDraftApi.Article但是一改貌似就 linter 告警了。
} as VxeTableGridOptions<Article>,
} as VxeTableGridOptions<MpDraftApi.DraftArticle>,
});
</script>

View File

@@ -1,7 +1,7 @@
<script lang="ts" setup>
import type { UploadFile } from 'ant-design-vue';
import type { NewsItem } from './types';
import type { MpDraftApi } from '#/api/mp/draft';
import { computed, inject, reactive, ref } from 'vue';
@@ -14,16 +14,16 @@ import { UploadType, useBeforeUpload } from '#/utils/useUpload';
const props = defineProps<{
isFirst: boolean;
modelValue: NewsItem;
modelValue: MpDraftApi.NewsItem;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', v: NewsItem): void;
(e: 'update:modelValue', v: MpDraftApi.NewsItem): void;
}>();
const UPLOAD_URL = `${import.meta.env.VITE_BASE_URL}/admin-api/mp/material/upload-permanent`; // 上传永久素材的地址
const HEADERS = { Authorization: `Bearer ${useAccessStore().accessToken}` };
const newsItem = computed<NewsItem>({
const newsItem = computed<MpDraftApi.NewsItem>({
get() {
return props.modelValue;
},

View File

@@ -1,12 +1,12 @@
<script lang="ts" setup>
import type { Article } from './types';
import type { MpDraftApi } from '#/api/mp/draft';
import { WxNews } from '#/views/mp/components';
defineOptions({ name: 'DraftTableCell' });
const props = defineProps<{
row: Article;
row: MpDraftApi.DraftArticle;
}>();
</script>

View File

@@ -1,11 +1,11 @@
<script lang="ts" setup>
import type { NewsItem } from './types';
import type { MpDraftApi } from '#/api/mp/draft';
import { computed, provide, ref } from 'vue';
import { useVbenModal } from '@vben/common-ui';
import { message, Spin } from 'ant-design-vue';
import { message } from 'ant-design-vue';
import { createDraft, updateDraft } from '#/api/mp/draft';
@@ -16,10 +16,9 @@ const emit = defineEmits(['success']);
const formData = ref<{
accountId: number;
mediaId?: string;
newsList?: NewsItem[];
newsList?: MpDraftApi.NewsItem[];
}>();
const newsList = ref<NewsItem[]>([]);
const isSubmitting = ref(false);
const newsList = ref<MpDraftApi.NewsItem[]>([]);
const getTitle = computed(() => {
return formData.value?.mediaId ? '修改图文' : '新建图文';
@@ -35,9 +34,6 @@ const [Modal, modalApi] = useVbenModal({
if (!formData.value) {
return;
}
// TODO @hw是不是 isSubmitting 非必须哈?因为 modal 已经去 lock 啦。
isSubmitting.value = true;
modalApi.lock();
try {
if (formData.value.mediaId) {
@@ -54,7 +50,6 @@ const [Modal, modalApi] = useVbenModal({
await modalApi.close();
emit('success');
} finally {
isSubmitting.value = false;
modalApi.unlock();
}
},
@@ -68,7 +63,7 @@ const [Modal, modalApi] = useVbenModal({
accountId: number;
isCreating: boolean;
mediaId?: string;
newsList?: NewsItem[];
newsList?: MpDraftApi.NewsItem[];
}>();
if (!data) {
return;
@@ -85,12 +80,10 @@ const [Modal, modalApi] = useVbenModal({
<template>
<Modal :title="getTitle" class="w-4/5" destroy-on-close>
<Spin :spinning="isSubmitting">
<NewsForm
v-if="formData"
v-model="newsList"
:is-creating="!formData.mediaId"
/>
</Spin>
<NewsForm
v-if="formData"
v-model="newsList"
:is-creating="!formData.mediaId"
/>
</Modal>
</template>

View File

@@ -1,5 +1,5 @@
<script lang="ts" setup>
import type { NewsItem } from './types';
import type { MpDraftApi } from '#/api/mp/draft';
import { computed, ref } from 'vue';
@@ -8,23 +8,23 @@ import { IconifyIcon } from '@vben/icons';
import { Button, Col, Input, Layout, Row, Textarea } from 'ant-design-vue';
import { createEmptyNewsItem } from '#/api/mp/draft';
import { Tinymce as RichTextarea } from '#/components/tinymce';
import CoverSelect from './cover-select.vue';
import { createEmptyNewsItem } from './types';
defineOptions({ name: 'NewsForm' });
const props = defineProps<{
isCreating: boolean;
modelValue: NewsItem[] | null;
modelValue: MpDraftApi.NewsItem[] | null;
}>();
const emit = defineEmits<{
(e: 'update:modelValue', v: NewsItem[]): void;
(e: 'update:modelValue', v: MpDraftApi.NewsItem[]): void;
}>();
const newsList = computed<NewsItem[]>({
const newsList = computed<MpDraftApi.NewsItem[]>({
get() {
return props.modelValue === null
? [createEmptyNewsItem()]

View File

@@ -1,42 +0,0 @@
// TODO @hw这个要融合到 draftApi 里么?类似其他模块的。
interface NewsItem {
title: string;
thumbMediaId: string;
author: string;
digest: string;
showCoverPic: number;
content: string;
contentSourceUrl: string;
needOpenComment: number;
onlyFansCanComment: number;
thumbUrl: string;
picUrl?: string; // 用于预览封面
}
interface NewsItemList {
newsItem: NewsItem[];
}
interface Article {
mediaId: string;
content: NewsItemList;
updateTime: number;
}
function createEmptyNewsItem(): NewsItem {
return {
title: '',
thumbMediaId: '',
author: '',
digest: '',
showCoverPic: 0,
content: '',
contentSourceUrl: '',
needOpenComment: 0,
onlyFansCanComment: 0,
thumbUrl: '',
};
}
export type { Article, NewsItem, NewsItemList };
export { createEmptyNewsItem };

View File

@@ -3,13 +3,13 @@ import type { VxeTableGridOptions } from '#/adapter/vxe-table';
import { onMounted, watch } from 'vue';
import { MpMsgType as MsgType } from '@vben/constants';
import { formatDate2 } from '@vben/utils';
import { Button, Image, Tag } from 'ant-design-vue';
import { useVbenVxeGrid } from '#/adapter/vxe-table';
import {
MsgType,
WxLocation,
WxMusic,
WxNews,

View File

@@ -4,7 +4,7 @@ import type { Dayjs } from 'dayjs';
import { reactive, ref } from 'vue';
import { Page } from '@vben/common-ui';
import { DICT_TYPE } from '@vben/constants';
import { DICT_TYPE, MpMsgType as MsgType } from '@vben/constants';
import { getDictOptions } from '@vben/hooks';
import { IconifyIcon } from '@vben/icons';
@@ -20,7 +20,7 @@ import {
} from 'ant-design-vue';
import { getMessagePage } from '#/api/mp/message';
import { MsgType, WxAccountSelect, WxMsg } from '#/views/mp/components';
import { WxAccountSelect, WxMsg } from '#/views/mp/components';
import MessageTable from './MessageTable.vue';