工单和发票

酒厂银行账户
This commit is contained in:
2026-07-23 14:51:20 +08:00
parent 75765bf9d4
commit 82c447b193
13 changed files with 575 additions and 21 deletions
+100 -5
View File
@@ -1,5 +1,18 @@
import { useState } from 'react';
import { Button, Descriptions, Drawer, Form, Image, Input, Select, Table, Typography, message } from 'antd';
import {
Button,
Descriptions,
Drawer,
Form,
Image,
Input,
Modal,
Select,
Space,
Table,
Typography,
message,
} from 'antd';
import type { ColumnsType } from 'antd/es/table';
import { TICKET_TYPE_LABELS, type TicketTypeDto } from '@dukang/shared-types';
import { request } from '../lib/api';
@@ -32,6 +45,13 @@ export default function TicketsPage() {
);
const [detail, setDetail] = useState<Row | null>(null);
const [drawerOpen, setDrawerOpen] = useState(false);
const [createOpen, setCreateOpen] = useState(false);
const [creating, setCreating] = useState(false);
const [createForm] = Form.useForm<{
ticketType: TicketTypeDto;
orderNo: string;
remark?: string;
}>();
async function approve(id: string) {
await request(`/admin/tickets/${id}/approve`, { method: 'POST', body: JSON.stringify({}) });
@@ -50,6 +70,29 @@ export default function TicketsPage() {
setDrawerOpen(false);
}
async function submitCreate() {
const values = await createForm.validateFields();
setCreating(true);
try {
await request('/admin/tickets', {
method: 'POST',
body: JSON.stringify({
ticketType: values.ticketType,
orderNo: values.orderNo.trim(),
remark: values.remark?.trim() || undefined,
}),
});
message.success('工单已创建');
setCreateOpen(false);
createForm.resetFields();
reload();
} catch (e) {
message.error(e instanceof Error ? e.message : '创建失败');
} finally {
setCreating(false);
}
}
const columns: ColumnsType<Row> = [
{ title: '工单号', dataIndex: 'ticketNo', width: 180 },
{
@@ -84,7 +127,21 @@ export default function TicketsPage() {
return (
<div>
<Typography.Title level={4}></Typography.Title>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginBottom: 16,
}}
>
<Typography.Title level={4} style={{ margin: 0 }}>
</Typography.Title>
<Button type="primary" onClick={() => setCreateOpen(true)}>
</Button>
</div>
<Form
layout="inline"
style={{ marginBottom: 16 }}
@@ -137,14 +194,14 @@ export default function TicketsPage() {
onClose={() => setDrawerOpen(false)}
extra={
detail && (detail.status === 'PENDING' || detail.status === 'OPEN') ? (
<>
<Button type="primary" onClick={() => approve(String(detail.id))} style={{ marginRight: 8 }}>
<Space>
<Button type="primary" onClick={() => approve(String(detail.id))}>
</Button>
<Button danger onClick={() => reject(String(detail.id))}>
</Button>
</>
</Space>
) : null
}
>
@@ -173,6 +230,44 @@ export default function TicketsPage() {
</Descriptions>
)}
</Drawer>
<Modal
title="创建工单"
open={createOpen}
onCancel={() => setCreateOpen(false)}
onOk={() => void submitCreate()}
confirmLoading={creating}
destroyOnClose
okText="提交"
>
<Form form={createForm} layout="vertical" initialValues={{ ticketType: 'REFUND' }}>
<Form.Item
name="ticketType"
label="工单类型"
rules={[{ required: true, message: '请选择类型' }]}
>
<Select
options={[
{ value: 'REFUND', label: '仅退款' },
{ value: 'RESHIPMENT', label: '破损补发' },
{ value: 'DAMAGE_RETURN', label: '破损退货' },
{ value: 'RETURN_REFUND', label: '退货退款' },
{ value: 'ALERT', label: '异常' },
]}
/>
</Form.Item>
<Form.Item
name="orderNo"
label="订单号"
rules={[{ required: true, message: '请填写订单号' }]}
>
<Input placeholder="关联订单号" allowClear />
</Form.Item>
<Form.Item name="remark" label="备注">
<Input.TextArea rows={3} placeholder="可选" maxLength={512} showCount />
</Form.Item>
</Form>
</Modal>
</div>
);
}