发布商品,商品图片使用oss服务器地址

This commit is contained in:
2026-07-06 08:53:03 +08:00
parent a0466f023e
commit 5ba69eb935
60 changed files with 2776 additions and 157 deletions
@@ -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>
);
}