发布商品,商品图片使用oss服务器地址
This commit is contained in:
@@ -0,0 +1,124 @@
|
||||
import { useState } from 'react';
|
||||
import { Button, Form, Input, Select, Table, Typography, message, Modal, DatePicker } from 'antd';
|
||||
import type { ColumnsType } from 'antd/es/table';
|
||||
import dayjs from 'dayjs';
|
||||
import { request } from '../lib/api';
|
||||
import { fmtTime } from '../lib/constants';
|
||||
import { useAdminList } from '../lib/useAdminList';
|
||||
|
||||
type Row = {
|
||||
id: string;
|
||||
billNo: string;
|
||||
orderCommission: number;
|
||||
redeemCommission: number;
|
||||
totalAmount: number;
|
||||
status: string;
|
||||
periodStart: string;
|
||||
periodEnd: string;
|
||||
partner?: { companyName: string };
|
||||
};
|
||||
|
||||
export default function PartnerBillsPage() {
|
||||
const [filters, setFilters] = useState<Record<string, string>>({});
|
||||
const { data, loading, page, pageSize, setPage, setPageSize, reload } = useAdminList<Row>(
|
||||
'/admin/partner-bills',
|
||||
() => {
|
||||
const qs = new URLSearchParams();
|
||||
if (filters.status) qs.set('status', filters.status);
|
||||
if (filters.partnerId) qs.set('partnerId', filters.partnerId);
|
||||
return qs;
|
||||
},
|
||||
[filters],
|
||||
);
|
||||
const [genOpen, setGenOpen] = useState(false);
|
||||
const [genForm] = Form.useForm();
|
||||
|
||||
async function generateBill(values: { partnerId: string; month: dayjs.Dayjs }) {
|
||||
await request('/admin/partner-bills/generate', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
partnerId: values.partnerId,
|
||||
year: values.month.year(),
|
||||
month: values.month.month() + 1,
|
||||
}),
|
||||
});
|
||||
message.success('账单已生成');
|
||||
setGenOpen(false);
|
||||
reload();
|
||||
}
|
||||
|
||||
async function confirmBill(id: string) {
|
||||
await request(`/admin/partner-bills/${id}/confirm`, { method: 'POST' });
|
||||
message.success('已确认');
|
||||
reload();
|
||||
}
|
||||
|
||||
async function markPaid(id: string) {
|
||||
await request(`/admin/partner-bills/${id}/mark-paid`, {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({ paymentRef: `PAY-${Date.now()}` }),
|
||||
});
|
||||
message.success('已标记打款');
|
||||
reload();
|
||||
}
|
||||
|
||||
async function exportCsv() {
|
||||
const result = await request<{ csv: string }>('/admin/partner-bills/export');
|
||||
const blob = new Blob([result.csv], { type: 'text/csv;charset=utf-8' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'partner-bills.csv';
|
||||
a.click();
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
const columns: ColumnsType<Row> = [
|
||||
{ title: '账单号', dataIndex: 'billNo', width: 180 },
|
||||
{ title: '合伙人', dataIndex: ['partner', 'companyName'] },
|
||||
{ title: '订单佣金', dataIndex: 'orderCommission', width: 100, render: (v) => `¥${v}` },
|
||||
{ title: '核销佣金', dataIndex: 'redeemCommission', width: 100, render: (v) => `¥${v}` },
|
||||
{ title: '合计', dataIndex: 'totalAmount', width: 100, render: (v) => `¥${v}` },
|
||||
{ title: '状态', dataIndex: 'status', width: 90 },
|
||||
{ title: '周期', width: 200, render: (_, r) => `${fmtTime(r.periodStart).slice(0, 10)} ~ ${fmtTime(r.periodEnd).slice(0, 10)}` },
|
||||
{
|
||||
title: '操作', width: 180,
|
||||
render: (_, row) => (
|
||||
<>
|
||||
{row.status === 'DRAFT' && <Button type="link" size="small" onClick={() => confirmBill(row.id)}>确认</Button>}
|
||||
{row.status === 'CONFIRMED' && <Button type="link" size="small" onClick={() => markPaid(row.id)}>标记打款</Button>}
|
||||
</>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Typography.Title level={4}>合伙人结算(T+30)</Typography.Title>
|
||||
<div style={{ marginBottom: 16, display: 'flex', gap: 8 }}>
|
||||
<Button type="primary" onClick={() => setGenOpen(true)}>生成账单</Button>
|
||||
<Button onClick={exportCsv}>导出 CSV</Button>
|
||||
</div>
|
||||
<Form layout="inline" style={{ marginBottom: 16 }} onFinish={(v) => { setFilters(v); setPage(1); }}>
|
||||
<Form.Item name="status" label="状态">
|
||||
<Select allowClear style={{ width: 120 }} options={[
|
||||
{ value: 'DRAFT', label: '草稿' },
|
||||
{ value: 'CONFIRMED', label: '已确认' },
|
||||
{ value: 'PAID', label: '已打款' },
|
||||
]} />
|
||||
</Form.Item>
|
||||
<Form.Item name="partnerId" label="合伙人ID"><Input allowClear /></Form.Item>
|
||||
<Button type="primary" htmlType="submit">筛选</Button>
|
||||
</Form>
|
||||
<Table rowKey="id" loading={loading} columns={columns} dataSource={data?.items ?? []} scroll={{ x: 1100 }}
|
||||
pagination={{ current: page, pageSize, total: data?.total ?? 0, showSizeChanger: true, onChange: (p, ps) => { setPage(p); setPageSize(ps); } }} />
|
||||
<Modal title="生成合伙人账单" open={genOpen} onCancel={() => setGenOpen(false)} footer={null}>
|
||||
<Form form={genForm} layout="vertical" onFinish={generateBill}>
|
||||
<Form.Item name="partnerId" label="合伙人 ID" rules={[{ required: true }]}><Input /></Form.Item>
|
||||
<Form.Item name="month" label="账单月份" rules={[{ required: true }]}><DatePicker picker="month" style={{ width: '100%' }} /></Form.Item>
|
||||
<Button type="primary" htmlType="submit" block>生成</Button>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user