forked from yoone/WEB
1
0
Fork 0
WEB/src/pages/Subscription/List/index.tsx

309 lines
9.0 KiB
TypeScript

import React, { useRef, useState } from 'react';
import {
ActionType,
DrawerForm,
PageContainer,
ProColumns,
ProFormSelect,
ProTable,
} from '@ant-design/pro-components';
import { App, Button, Tag, Drawer, List } from 'antd';
import dayjs from 'dayjs';
import { request } from 'umi';
import {
subscriptioncontrollerList,
subscriptioncontrollerSync,
} from '@/servers/api/subscription';
import { sitecontrollerAll } from '@/servers/api/site';
/**
* 订阅状态枚举(用于筛选与展示)
* 保持与后端同步的原始状态值
*/
const SUBSCRIPTION_STATUS_ENUM: Record<string, { text: string }> = {
active: { text: '激活' },
cancelled: { text: '已取消' },
expired: { text: '已过期' },
pending: { text: '待处理' },
'on-hold': { text: '暂停' },
};
/**
* 订阅列表页:展示、筛选、触发订阅同步
*/
const ListPage: React.FC = () => {
// 表格操作引用:用于在同步后触发表格刷新
const actionRef = useRef<ActionType>();
const { message } = App.useApp();
// 关联订单抽屉状态
const [drawerOpen, setDrawerOpen] = useState(false);
const [drawerTitle, setDrawerTitle] = useState('详情');
const [relatedOrders, setRelatedOrders] = useState<any[]>([]);
// 表格列定义(尽量与项目风格保持一致)
const columns: ProColumns<API.Subscription>[] = [
{
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: 'parent_id',
hideInSearch: true,
width: 120,
render: (_, row) => (row?.parent_id ? <Tag>{row.parent_id}</Tag> : '-'),
},
{
title: '状态',
dataIndex: 'status',
valueType: 'select',
valueEnum: SUBSCRIPTION_STATUS_ENUM,
// 以 Tag 形式展示,更易辨识
render: (_, row) =>
row?.status ? <Tag>{SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status}</Tag> : '-',
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,
},
{
title: '操作',
dataIndex: 'actions',
hideInSearch: true,
width: 120,
render: (_, row) => (
<Button
size="small"
onClick={async () => {
try {
const parentNumber = String(row?.parent_id || '');
if (!parentNumber) {
message.warning('该订阅缺少父订单号');
return;
}
// 通过父订单号查询关联订单(模糊匹配)
const resp = await request('/order/getOrderByNumber', {
method: 'POST',
data: { number: parentNumber },
});
const { success, data, message: errMsg } = resp as any;
if (!success) throw new Error(errMsg || '获取失败');
// 仅保留与父订单号完全一致的订单(避免模糊匹配误入)
const candidates: any[] = (Array.isArray(data) ? data : []).filter(
(c: any) => String(c?.externalOrderId) === parentNumber
);
// 拉取详情,补充状态、金额、时间
const details = [] as any[];
for (const c of candidates) {
const d = await request(`/order/${c.id}`, { method: 'GET' });
if ((d as any)?.success) {
const od = (d as any)?.data || {};
details.push({
id: c.id,
externalOrderId: c.externalOrderId,
siteName: c.siteName,
status: od?.status,
total: od?.total,
currency_symbol: od?.currency_symbol,
date_created: od?.date_created,
relationship: 'Parent Order',
});
} else {
details.push({
id: c.id,
externalOrderId: c.externalOrderId,
siteName: c.siteName,
relationship: 'Parent Order',
});
}
}
setRelatedOrders(details);
setDrawerTitle(`详情`);
setDrawerOpen(true);
} catch (e: any) {
message.error(e?.message || '获取失败');
}
}}
>
</Button>
),
},
];
return (
<PageContainer header={{ title: '订阅列表' }}>
<ProTable<API.Subscription>
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={() => [<SyncForm key="sync" tableRef={actionRef} />]}
/>
{/* 关联订单抽屉:展示订单号、关系、时间、状态与金额 */}
<Drawer
open={drawerOpen}
title={drawerTitle}
width={720}
onClose={() => setDrawerOpen(false)}
>
<List
header={<div></div>}
dataSource={relatedOrders}
renderItem={(item: any) => (
<List.Item>
<List.Item.Meta
title={`#${item?.externalOrderId || '-'}`}
description={`关系:${item?.relationship || '-'},站点:${item?.siteName || '-'}`}
/>
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
<span>{item?.date_created ? dayjs(item.date_created).format('YYYY-MM-DD HH:mm') : '-'}</span>
<Tag>{item?.status || '-'}</Tag>
<span>
{item?.currency_symbol || ''}
{typeof item?.total === 'number' ? item.total.toFixed(2) : item?.total ?? '-'}
</span>
</div>
</List.Item>
)}
/>
</Drawer>
</PageContainer>
);
};
/**
* 同步订阅抽屉表单:选择站点后触发同步
*/
const SyncForm: React.FC<{
tableRef: React.MutableRefObject<ActionType | undefined>;
}> = ({ tableRef }) => {
const { message } = App.useApp();
return (
<DrawerForm<API.subscriptioncontrollerSyncParams>
title="同步订阅"
trigger={
<Button key="syncSite" type="primary">
</Button>
}
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 || '同步失败');
}
}}
>
<ProFormSelect
name="siteId"
width="lg"
label="站点"
placeholder="请选择站点"
// 动态加载站点选项
request={async () => {
const { data = [] } = await sitecontrollerAll();
return data.map((item) => ({
label: item.siteName,
value: item.id,
}));
}}
rules={[{ required: true, message: '请选择站点' }]}
/>
</DrawerForm>
);
};
export default ListPage;