From 737cfef73304becdc22eb2b9e02df2a32eabe384 Mon Sep 17 00:00:00 2001 From: tikkhun Date: Mon, 17 Nov 2025 17:54:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E8=AE=A2=E9=98=85):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E8=AE=A2=E9=98=85=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=E5=92=8C?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加订阅管理路由分组并复用现有权限 实现订阅列表页面,包含查询、筛选和同步功能 --- .umirc.ts | 13 ++ src/pages/Subscription/List/index.tsx | 202 ++++++++++++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 src/pages/Subscription/List/index.tsx diff --git a/.umirc.ts b/.umirc.ts index 723972c..b60deb3 100644 --- a/.umirc.ts +++ b/.umirc.ts @@ -124,6 +124,19 @@ export default defineConfig({ }, ], }, + // 新增:订阅管理路由分组(权限复用 canSeeOrder) + { + name: '订阅管理', + path: '/subscription', + access: 'canSeeOrder', + routes: [ + { + name: '订阅列表', + path: '/subscription/list', + component: './Subscription/List', + }, + ], + }, { name: '客户管理', path: '/customer', diff --git a/src/pages/Subscription/List/index.tsx b/src/pages/Subscription/List/index.tsx new file mode 100644 index 0000000..41f801d --- /dev/null +++ b/src/pages/Subscription/List/index.tsx @@ -0,0 +1,202 @@ +import React, { useRef } from 'react'; +import { + ActionType, + DrawerForm, + PageContainer, + ProColumns, + ProFormSelect, + ProTable, +} from '@ant-design/pro-components'; +import { App, Button, Tag } from 'antd'; +import { + subscriptioncontrollerList, + subscriptioncontrollerSync, +} from '@/servers/api/subscription'; +import { sitecontrollerAll } from '@/servers/api/site'; + +/** + * 订阅状态枚举(用于筛选与展示) + * 保持与后端同步的原始状态值 + */ +const SUBSCRIPTION_STATUS_ENUM: Record = { + active: { text: '激活' }, + cancelled: { text: '已取消' }, + expired: { text: '已过期' }, + pending: { text: '待处理' }, + 'on-hold': { text: '暂停' }, +}; + +/** + * 订阅列表页:展示、筛选、触发订阅同步 + */ +const ListPage: React.FC = () => { + // 表格操作引用:用于在同步后触发表格刷新 + const actionRef = useRef(); + + // 表格列定义(尽量与项目风格保持一致) + const columns: ProColumns[] = [ + { + title: '站点', + dataIndex: 'siteId', + valueType: 'select', + // 动态加载站点选项 + request: async () => { + const { data = [] } = await sitecontrollerAll(); + return data.map((item) => ({ + label: item.siteName, + value: item.id, + })); + }, + render: (_, row) => row?.siteId ?? '-', + }, + { + title: '订阅ID', + dataIndex: 'externalSubscriptionId', + hideInSearch: true, + width: 120, + }, + { + title: '状态', + dataIndex: 'status', + valueType: 'select', + valueEnum: SUBSCRIPTION_STATUS_ENUM, + // 以 Tag 形式展示,更易辨识 + render: (_, row) => + row?.status ? {SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status} : '-', + width: 120, + }, + { + title: '客户邮箱', + dataIndex: 'customer_email', + width: 180, + }, + { + title: '金额', + dataIndex: 'total', + hideInSearch: true, + width: 100, + }, + { + title: '币种', + dataIndex: 'currency', + hideInSearch: true, + width: 80, + }, + { + title: '开始时间', + dataIndex: 'start_date', + hideInSearch: true, + width: 160, + }, + { + title: '下次支付', + dataIndex: 'next_payment_date', + hideInSearch: true, + width: 160, + }, + { + title: '结束时间', + dataIndex: 'end_date', + hideInSearch: true, + width: 160, + }, + { + title: '创建时间', + dataIndex: 'createdAt', + valueType: 'dateTime', + hideInSearch: true, + width: 160, + }, + { + title: '更新时间', + dataIndex: 'updatedAt', + valueType: 'dateTime', + hideInSearch: true, + width: 160, + }, + ]; + + return ( + + + headerTitle="查询表格" + rowKey="id" + actionRef={actionRef} + /** + * 列表数据请求;保持与后端分页参数一致 + * 兼容后端 data.items 或 data.list 返回字段 + */ + request={async (params) => { + const { data, success } = await subscriptioncontrollerList(params); + return { + total: data?.total || 0, + data: data?.items || data?.list || [], + success, + }; + }} + columns={columns} + // 工具栏:订阅同步入口 + toolBarRender={() => []} + /> + + ); +}; + +/** + * 同步订阅抽屉表单:选择站点后触发同步 + */ +const SyncForm: React.FC<{ + tableRef: React.MutableRefObject; +}> = ({ tableRef }) => { + const { message } = App.useApp(); + + return ( + + title="同步订阅" + trigger={ + + } + autoFocusFirstInput + drawerProps={{ destroyOnHidden: true }} + /** + * 提交逻辑: + * 1. 必填校验由 ProForm + rules 保证 + * 2. 调用同步接口,失败时友好提示 + * 3. 成功后刷新列表 + */ + onFinish={async (values) => { + try { + const { success, message: errMsg } = await subscriptioncontrollerSync(values); + if (!success) { + throw new Error(errMsg); + } + message.success('同步成功'); + tableRef.current?.reload(); + return true; + } catch (error: any) { + message.error(error.message || '同步失败'); + } + }} + > + { + const { data = [] } = await sitecontrollerAll(); + return data.map((item) => ({ + label: item.siteName, + value: item.id, + })); + }} + rules={[{ required: true, message: '请选择站点' }]} + /> + + ); +}; + +export default ListPage;