first version
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import {
|
||||
Button,
|
||||
Descriptions,
|
||||
Drawer,
|
||||
Dropdown,
|
||||
Tag,
|
||||
Typography,
|
||||
} from 'antd';
|
||||
import { DownloadOutlined } from '@ant-design/icons';
|
||||
import XinTable from '@/components/XinTable';
|
||||
import type {
|
||||
XinTableColumn,
|
||||
XinTableInstance,
|
||||
XinTableProps,
|
||||
} from '@/components/XinTable/typings.ts';
|
||||
import type ISettlement from '@/domain/iSettlement.ts';
|
||||
import { SETTLEMENT_STATUS_MAP } from '@/domain/iSettlement.ts';
|
||||
import { getStoreOptions } from '@/api/customer/store.ts';
|
||||
import type IStore from '@/domain/iStore.ts';
|
||||
import { downloadSettlement } from '@/api/recon/settlement.ts';
|
||||
import { Get } from '@/api/common/table.ts';
|
||||
import AuthButton from '@/components/AuthButton';
|
||||
|
||||
const { Title, Text } = Typography;
|
||||
|
||||
/**
|
||||
* 结算表(D9 生成于对账结算,D10 导出存档)
|
||||
*/
|
||||
const SettlementPage: React.FC = () => {
|
||||
const tableRef = useRef<XinTableInstance<ISettlement>>(null);
|
||||
const [stores, setStores] = useState<IStore[]>([]);
|
||||
|
||||
const [detailOpen, setDetailOpen] = useState(false);
|
||||
const [detail, setDetail] = useState<ISettlement | null>(null);
|
||||
const [detailLoading, setDetailLoading] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
getStoreOptions().then((res) => setStores(res.data.data ?? []));
|
||||
}, []);
|
||||
|
||||
const openDetail = async (id: number) => {
|
||||
setDetailOpen(true);
|
||||
setDetailLoading(true);
|
||||
try {
|
||||
const res = await Get<ISettlement>('/recon/settlement', id);
|
||||
setDetail(res.data.data ?? null);
|
||||
} finally {
|
||||
setDetailLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const columns: XinTableColumn<ISettlement>[] = [
|
||||
{
|
||||
title: '结算单号',
|
||||
dataIndex: 'settlement_no',
|
||||
valueType: 'text',
|
||||
hideInForm: true,
|
||||
render: (_, record) => <Text copyable={{ text: record.settlement_no }}>{record.settlement_no}</Text>,
|
||||
},
|
||||
{
|
||||
title: '门店',
|
||||
dataIndex: 'store_id',
|
||||
valueType: 'select',
|
||||
hideInForm: true,
|
||||
fieldProps: {
|
||||
options: stores.map((s) => ({ label: s.name, value: s.id })),
|
||||
showSearch: true,
|
||||
optionFilterProp: 'label',
|
||||
},
|
||||
render: (_, record) => record.store?.name ?? '-',
|
||||
},
|
||||
{
|
||||
title: '来源对账单',
|
||||
dataIndex: 'recon',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
render: (_, record) =>
|
||||
record.recon ? (
|
||||
<span>
|
||||
{record.recon.recon_no}
|
||||
<Text type="secondary">({record.recon.title})</Text>
|
||||
</span>
|
||||
) : (
|
||||
'-'
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '结算周期',
|
||||
dataIndex: 'period',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
render: (_, record) => `${record.period_start} ~ ${record.period_end}`,
|
||||
},
|
||||
{
|
||||
title: '公布金额',
|
||||
dataIndex: 'total_amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'right',
|
||||
render: (_, record) => `¥${record.total_amount}`,
|
||||
},
|
||||
{
|
||||
title: '实际金额',
|
||||
dataIndex: 'actual_amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'right',
|
||||
render: (_, record) => <Text strong>¥{record.actual_amount}</Text>,
|
||||
},
|
||||
{
|
||||
title: '差额',
|
||||
dataIndex: 'diff_amount',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'right',
|
||||
render: (_, record) => {
|
||||
const num = Number(record.diff_amount ?? 0);
|
||||
return (
|
||||
<Text type={num === 0 ? 'secondary' : 'danger'} strong={num !== 0}>
|
||||
¥{record.diff_amount}
|
||||
</Text>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
valueType: 'select',
|
||||
hideInForm: true,
|
||||
fieldProps: {
|
||||
options: Object.entries(SETTLEMENT_STATUS_MAP).map(([value, item]) => ({
|
||||
value: Number(value),
|
||||
label: item.text,
|
||||
})),
|
||||
},
|
||||
render: (_, record) => {
|
||||
const item = SETTLEMENT_STATUS_MAP[record.status ?? 0];
|
||||
return <Tag color={item?.color}>{item?.text}</Tag>;
|
||||
},
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
title: '结算时间',
|
||||
dataIndex: 'settled_at',
|
||||
hideInForm: true,
|
||||
hideInSearch: true,
|
||||
align: 'center',
|
||||
render: (_, record) => record.settled_at ?? '-',
|
||||
},
|
||||
];
|
||||
|
||||
const operateRender: XinTableProps<ISettlement>['operateRender'] = (record) => [
|
||||
<Button key="detail" size="small" onClick={() => openDetail(record.id!)}>
|
||||
详情
|
||||
</Button>,
|
||||
<AuthButton key="download" auth="recon.settlement.download">
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: [
|
||||
{ key: 'xlsx', label: '下载 Excel', onClick: () => downloadSettlement(record.id!, 'xlsx') },
|
||||
{ key: 'pdf', label: '下载 PDF', onClick: () => downloadSettlement(record.id!, 'pdf') },
|
||||
],
|
||||
}}
|
||||
>
|
||||
<Button size="small" type="primary" ghost icon={<DownloadOutlined />}>
|
||||
下载
|
||||
</Button>
|
||||
</Dropdown>
|
||||
</AuthButton>,
|
||||
];
|
||||
|
||||
const tableProps: XinTableProps<ISettlement> = {
|
||||
api: '/recon/settlement',
|
||||
columns,
|
||||
rowKey: 'id',
|
||||
accessName: 'recon.settlement',
|
||||
tableRef,
|
||||
operateRender,
|
||||
formProps: false,
|
||||
scroll: { x: 1200 },
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="mb-5">
|
||||
<Title level={3}>结算表</Title>
|
||||
<Text type="secondary">
|
||||
由财务对账结算按门店聚合生成;支持 Excel / PDF 导出存档(回框统计表规则待业务确认后补充)。
|
||||
</Text>
|
||||
</div>
|
||||
<XinTable<ISettlement> {...tableProps} />
|
||||
|
||||
<Drawer
|
||||
title={detail ? `结算表 ${detail.settlement_no}` : '结算表详情'}
|
||||
open={detailOpen}
|
||||
onClose={() => setDetailOpen(false)}
|
||||
width={640}
|
||||
loading={detailLoading}
|
||||
>
|
||||
{detail ? (
|
||||
<Descriptions column={2} size="small" bordered>
|
||||
<Descriptions.Item label="门店">{detail.store?.name}</Descriptions.Item>
|
||||
<Descriptions.Item label="状态">
|
||||
<Tag color={SETTLEMENT_STATUS_MAP[detail.status ?? 0]?.color}>
|
||||
{SETTLEMENT_STATUS_MAP[detail.status ?? 0]?.text}
|
||||
</Tag>
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="来源对账单">
|
||||
{detail.recon?.recon_no ?? '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="结算周期">
|
||||
{detail.period_start} ~ {detail.period_end}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="公布金额">¥{detail.total_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="实际金额">¥{detail.actual_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="差额">¥{detail.diff_amount}</Descriptions.Item>
|
||||
<Descriptions.Item label="结算时间">
|
||||
{detail.settled_at ?? '-'}
|
||||
</Descriptions.Item>
|
||||
<Descriptions.Item label="存档文件" span={2}>
|
||||
{detail.file_path ?? <Text type="secondary">未导出</Text>}
|
||||
</Descriptions.Item>
|
||||
{detail.remark ? (
|
||||
<Descriptions.Item label="备注" span={2}>
|
||||
{detail.remark}
|
||||
</Descriptions.Item>
|
||||
) : null}
|
||||
</Descriptions>
|
||||
) : null}
|
||||
</Drawer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettlementPage;
|
||||
Reference in New Issue
Block a user