feat:【antd】【mp】mp 的代码评审(autoReply)

This commit is contained in:
YunaiV
2025-11-20 21:18:21 +08:00
parent 8d2da0a4d1
commit e2848653f9
6 changed files with 52 additions and 34 deletions

View File

@@ -8,7 +8,7 @@ import { getDictOptions } from '@vben/hooks';
import { WxReply } from '#/views/mp/components'; import { WxReply } from '#/views/mp/components';
// TODO @芋艿:要不要使用统一枚举? // TODO @hw:要不要使用统一枚举?
const RequestMessageTypes = new Set([ const RequestMessageTypes = new Set([
'image', 'image',
'link', 'link',

View File

@@ -5,6 +5,7 @@ import type { MpAutoReplyApi } from '#/api/mp/autoReply';
import { computed, nextTick, ref } from 'vue'; import { computed, nextTick, ref } from 'vue';
import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui'; import { confirm, DocAlert, Page, useVbenModal } from '@vben/common-ui';
// TODO @hw直接使用 AutoReplyMsgType不用 as
import { AutoReplyMsgType as MsgType } from '@vben/constants'; import { AutoReplyMsgType as MsgType } from '@vben/constants';
import { IconifyIcon } from '@vben/icons'; import { IconifyIcon } from '@vben/icons';
@@ -51,7 +52,7 @@ function handleAccountChange(accountId: number) {
} }
/** 切换回复类型 */ /** 切换回复类型 */
async function onTabChange(tabName: any) { async function onTabChange(tabName: string) {
msgType.value = tabName; msgType.value = tabName;
await nextTick(); await nextTick();
// 更新 columns // 更新 columns

View File

@@ -26,7 +26,7 @@ const props = defineProps<{
</div> </div>
<div v-else-if="props.row.responseMessageType === 'image'"> <div v-else-if="props.row.responseMessageType === 'image'">
<a target="_blank" :href="props.row.responseMediaUrl"> <a target="_blank" :href="props.row.responseMediaUrl">
<img :src="props.row.responseMediaUrl" style="width: 100px" /> <img :src="props.row.responseMediaUrl" class="w-[100px]" />
</a> </a>
</div> </div>
<div <div
@@ -38,7 +38,7 @@ const props = defineProps<{
<WxVideoPlayer <WxVideoPlayer
v-if="props.row.responseMediaUrl" v-if="props.row.responseMediaUrl"
:url="props.row.responseMediaUrl" :url="props.row.responseMediaUrl"
style="margin-top: 10px" class="mt-[10px]"
/> />
</div> </div>
<div v-else-if="props.row.responseMessageType === 'news'"> <div v-else-if="props.row.responseMessageType === 'news'">

View File

@@ -16,6 +16,7 @@ import { MsgType } from './modules/types';
let accountList: MpAccountApi.AccountSimple[] = []; let accountList: MpAccountApi.AccountSimple[] = [];
getSimpleAccountList().then((data) => (accountList = data)); getSimpleAccountList().then((data) => (accountList = data));
// TODO @hw要不要使用统一枚举
const RequestMessageTypes = new Set([ const RequestMessageTypes = new Set([
'image', 'image',
'link', 'link',
@@ -25,6 +26,7 @@ const RequestMessageTypes = new Set([
'video', 'video',
'voice', 'voice',
]); // 允许选择的请求消息类型 ]); // 允许选择的请求消息类型
/** 获取表格列配置 */ /** 获取表格列配置 */
export function useGridColumns(msgType: MsgType): VxeGridPropTypes.Columns { export function useGridColumns(msgType: MsgType): VxeGridPropTypes.Columns {
const columns: VxeGridPropTypes.Columns = []; const columns: VxeGridPropTypes.Columns = [];
@@ -65,6 +67,7 @@ export function useGridColumns(msgType: MsgType): VxeGridPropTypes.Columns {
field: 'responseMessageType', field: 'responseMessageType',
title: '回复消息类型', title: '回复消息类型',
minWidth: 120, minWidth: 120,
// TODO @hw这里和 antd 有差别。两侧尽量统一;
formatter: ({ cellValue }) => formatter: ({ cellValue }) =>
getDictObj(DICT_TYPE.MP_MESSAGE_TYPE, String(cellValue))?.label ?? '', getDictObj(DICT_TYPE.MP_MESSAGE_TYPE, String(cellValue))?.label ?? '',
}, },
@@ -93,7 +96,9 @@ export function useGridColumns(msgType: MsgType): VxeGridPropTypes.Columns {
/** 新增/修改的表单 */ /** 新增/修改的表单 */
export function useFormSchema(msgType: MsgType): VbenFormSchema[] { export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
const schema: VbenFormSchema[] = []; const schema: VbenFormSchema[] = [];
// 消息类型(仅消息回复显示) // 消息类型(仅消息回复显示)
// TODO @hw这里和 antd 有差别。两侧尽量统一;
if (Number(msgType) === MsgType.Message) { if (Number(msgType) === MsgType.Message) {
schema.push({ schema.push({
fieldName: 'requestMessageType', fieldName: 'requestMessageType',
@@ -109,6 +114,7 @@ export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
} }
// 匹配类型(仅关键词回复显示) // 匹配类型(仅关键词回复显示)
// TODO @hw这里和 antd 有差别。两侧尽量统一;
if (Number(msgType) === MsgType.Keyword) { if (Number(msgType) === MsgType.Keyword) {
schema.push({ schema.push({
fieldName: 'requestMatch', fieldName: 'requestMatch',
@@ -127,6 +133,7 @@ export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
} }
// 关键词(仅关键词回复显示) // 关键词(仅关键词回复显示)
// TODO @hw这里和 antd 有差别。两侧尽量统一;
if (Number(msgType) === MsgType.Keyword) { if (Number(msgType) === MsgType.Keyword) {
schema.push({ schema.push({
fieldName: 'requestKeyword', fieldName: 'requestKeyword',
@@ -140,6 +147,7 @@ export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
}); });
} }
// 回复消息 // 回复消息
// TODO @hw这里和 antd 有差别。两侧尽量统一;
schema.push({ schema.push({
fieldName: 'reply', fieldName: 'reply',
label: '回复消息', label: '回复消息',
@@ -149,6 +157,7 @@ export function useFormSchema(msgType: MsgType): VbenFormSchema[] {
} }
/** 列表的搜索表单 */ /** 列表的搜索表单 */
// TODO @hw是不是用 wxselect 组件哈?
export function useGridFormSchema(): VbenFormSchema[] { export function useGridFormSchema(): VbenFormSchema[] {
return [ return [
{ {

View File

@@ -16,7 +16,11 @@ import {
} from 'element-plus'; } from 'element-plus';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table'; import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import * as MpAutoReplyApi from '#/api/mp/autoReply'; import {
deleteAutoReply,
getAutoReply,
getAutoReplyPage,
} from '#/api/mp/autoReply';
import { $t } from '#/locales'; import { $t } from '#/locales';
import { WxAccountSelect } from '#/views/mp/components'; import { WxAccountSelect } from '#/views/mp/components';
@@ -29,11 +33,29 @@ defineOptions({ name: 'MpAutoReply' });
const msgType = ref<string>(String(MsgType.Keyword)); // 消息类型 const msgType = ref<string>(String(MsgType.Keyword)); // 消息类型
const showCreateButton = computed(() => {
if (Number(msgType.value) !== MsgType.Follow) {
return true;
}
try {
const tableData = gridApi.grid?.getTableData();
return (tableData?.tableData?.length || 0) <= 0;
} catch {
return true;
}
}); // 计算是否显示新增按钮:关注时回复类型只有在没有数据时才显示
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
/** 公众号变化时查询数据 */ /** 公众号变化时查询数据 */
function handleAccountChange(accountId: number) { function handleAccountChange(accountId: number) {
gridApi.formApi.setValues({ accountId }); gridApi.formApi.setValues({ accountId });
gridApi.formApi.submitForm(); gridApi.formApi.submitForm();
} }
/** 切换回复类型 */ /** 切换回复类型 */
async function onTabChange(tabName: string) { async function onTabChange(tabName: string) {
msgType.value = tabName; msgType.value = tabName;
@@ -50,24 +72,26 @@ async function onTabChange(tabName: string) {
await gridApi.query(); await gridApi.query();
} }
/** 新增按钮操作 */ /** 新增自动回复 */
async function handleCreate() { async function handleCreate() {
const formValues = await gridApi.formApi.getValues(); const formValues = await gridApi.formApi.getValues();
formModalApi formModalApi
.setData({ .setData({
// TODO @hw这里和 antd 不同,需要 number 下么?
msgType: msgType.value, msgType: msgType.value,
accountId: formValues.accountId, accountId: formValues.accountId,
}) })
.open(); .open();
} }
/** 修改按钮操作 */ /** 修改自动回复 */
async function handleEdit(row: any) { async function handleEdit(row: any) {
const data = (await MpAutoReplyApi.getAutoReply(row.id)) as any; const data = (await getAutoReply(row.id)) as any;
// TODO @hw这里使用 formValues还是使用 row
const formValues = await gridApi.formApi.getValues(); const formValues = await gridApi.formApi.getValues();
formModalApi formModalApi
.setData({ .setData({
// TODO @hw这里和 antd 不同,需要 number 下么?
msgType: msgType.value, msgType: msgType.value,
row: data, row: data,
accountId: formValues.accountId, accountId: formValues.accountId,
@@ -75,14 +99,14 @@ async function handleEdit(row: any) {
.open(); .open();
} }
/** 删除按钮操作 */ /** 删除自动回复 */
async function handleDelete(row: any) { async function handleDelete(row: any) {
await ElMessageBox.confirm('是否确认删除此数据?'); await ElMessageBox.confirm('是否确认删除此数据?');
const loadingInstance = ElLoading.service({ const loadingInstance = ElLoading.service({
text: $t('ui.actionMessage.deleting', ['自动回复']), text: $t('ui.actionMessage.deleting', ['自动回复']),
}); });
try { try {
await MpAutoReplyApi.deleteAutoReply(row.id); await deleteAutoReply(row.id);
ElMessage.success('删除成功'); ElMessage.success('删除成功');
handleRefresh(); handleRefresh();
} finally { } finally {
@@ -106,7 +130,7 @@ const [Grid, gridApi] = useVbenVxeGrid({
proxyConfig: { proxyConfig: {
ajax: { ajax: {
query: async ({ page }, formValues) => { query: async ({ page }, formValues) => {
return await MpAutoReplyApi.getAutoReplyPage({ return await getAutoReplyPage({
pageNo: page.currentPage, pageNo: page.currentPage,
pageSize: page.pageSize, pageSize: page.pageSize,
type: Number(msgType.value) as MsgType, type: Number(msgType.value) as MsgType,
@@ -124,26 +148,9 @@ const [Grid, gridApi] = useVbenVxeGrid({
refresh: true, refresh: true,
search: true, search: true,
}, },
// TODO @hw这里要调整下linter 报错;
} as VxeTableGridOptions<any>, } as VxeTableGridOptions<any>,
}); });
/** 刷新表格 */
function handleRefresh() {
gridApi.query();
}
// 计算是否显示新增按钮:关注时回复类型只有在没有数据时才显示
const showCreateButton = computed(() => {
if (Number(msgType.value) !== MsgType.Follow) {
return true;
}
try {
const tableData = gridApi.grid?.getTableData();
return (tableData?.tableData?.length || 0) <= 0;
} catch {
return true;
}
});
</script> </script>
<template> <template>
@@ -153,11 +160,10 @@ const showCreateButton = computed(() => {
</template> </template>
<FormModal @success="handleRefresh" /> <FormModal @success="handleRefresh" />
<Grid table-title="自动回复列表"> <Grid>
<template #form-accountId> <template #form-accountId>
<WxAccountSelect @change="handleAccountChange" /> <WxAccountSelect @change="handleAccountChange" />
</template> </template>
<!-- 在工具栏上方放置 Tab 切换 -->
<template #toolbar-actions> <template #toolbar-actions>
<ElTabs <ElTabs
v-model="msgType" v-model="msgType"

View File

@@ -37,12 +37,11 @@ const [Form, formApi] = useVbenForm({
labelWidth: 100, labelWidth: 100,
}, },
layout: 'horizontal', layout: 'horizontal',
// TODO @hwantd 和 ele 存在差异
schema: useFormSchema(Number(formData.value?.msgType) as MsgType), schema: useFormSchema(Number(formData.value?.msgType) as MsgType),
showDefaultActions: false, showDefaultActions: false,
}); });
// 注意schema 的更新现在在 onOpenChange 中手动处理,避免时序问题
const [Modal, modalApi] = useVbenModal({ const [Modal, modalApi] = useVbenModal({
async onConfirm() { async onConfirm() {
const { valid } = await formApi.validate(); const { valid } = await formApi.validate();
@@ -60,6 +59,7 @@ const [Modal, modalApi] = useVbenModal({
if (formData.value?.row?.id && !submitForm.id) { if (formData.value?.row?.id && !submitForm.id) {
submitForm.id = formData.value.row.id; submitForm.id = formData.value.row.id;
} }
// TODO @hwantd 和 ele 存在差异
const reply = submitForm.reply as Reply | undefined; const reply = submitForm.reply as Reply | undefined;
if (reply) { if (reply) {
submitForm.responseMessageType = reply.type; submitForm.responseMessageType = reply.type;
@@ -99,6 +99,7 @@ const [Modal, modalApi] = useVbenModal({
// 加载数据 // 加载数据
const data = modalApi.getData<{ const data = modalApi.getData<{
accountId?: number; accountId?: number;
// TODO @hwantd 和 ele 存在差异
msgType: MsgType; msgType: MsgType;
row?: any; row?: any;
}>(); }>();
@@ -137,6 +138,7 @@ const [Modal, modalApi] = useVbenModal({
accountId: data.accountId || -1, accountId: data.accountId || -1,
type: data.msgType, type: data.msgType,
requestKeyword: undefined, requestKeyword: undefined,
// TODO @hwantd 和 ele 存在差异
requestMatch: data.msgType === MsgType.Keyword ? 1 : undefined, requestMatch: data.msgType === MsgType.Keyword ? 1 : undefined,
requestMessageType: undefined, requestMessageType: undefined,
reply: { reply: {