generated from template/vite-react-template
308 lines
8.5 KiB
TypeScript
308 lines
8.5 KiB
TypeScript
import { useEffect, useState } from 'react';
|
||
import { Modal, Table, Form, Input, Button, Space, Select, DatePicker, message, Popconfirm } from 'antd';
|
||
import { SearchOutlined, PlusOutlined, EditOutlined, DeleteOutlined } from '@ant-design/icons';
|
||
import { useTicketStore } from '@/store';
|
||
import type { Ticket as TicketType } from '@/pages/define/type';
|
||
import type { ColumnsType } from 'antd/es/table';
|
||
import dayjs from 'dayjs';
|
||
|
||
export const Ticket = () => {
|
||
const [searchForm] = Form.useForm();
|
||
const store = useTicketStore();
|
||
|
||
useEffect(() => {
|
||
fetchTickets();
|
||
}, [store.searchForm]);
|
||
|
||
const fetchTickets = async () => {
|
||
try {
|
||
store.setLoading(true);
|
||
await store.getTickets(store.searchForm);
|
||
} catch (error) {
|
||
message.error('获取票据列表失败');
|
||
} finally {
|
||
store.setLoading(false);
|
||
}
|
||
};
|
||
|
||
const handleSearch = async (values: any) => {
|
||
store.setSearchForm({
|
||
...store.searchForm,
|
||
...values,
|
||
current: 1,
|
||
startDate: values.dateRange?.[0]?.format('YYYY-MM-DD'),
|
||
endDate: values.dateRange?.[1]?.format('YYYY-MM-DD'),
|
||
});
|
||
};
|
||
|
||
const handleReset = () => {
|
||
searchForm.resetFields();
|
||
store.setSearchForm({
|
||
current: 1,
|
||
pageSize: 10,
|
||
});
|
||
};
|
||
|
||
const handleAdd = () => {
|
||
store.setFormData(null);
|
||
store.setShowEdit(true);
|
||
};
|
||
|
||
const handleEdit = (record: TicketType) => {
|
||
store.setFormData(record);
|
||
store.setShowEdit(true);
|
||
};
|
||
|
||
const handleDelete = async (id: string) => {
|
||
try {
|
||
store.setLoading(true);
|
||
await store.deleteTicket(id);
|
||
} catch (error) {
|
||
} finally {
|
||
store.setLoading(false);
|
||
}
|
||
};
|
||
|
||
const columns: ColumnsType<TicketType> = [
|
||
{
|
||
title: 'ID',
|
||
dataIndex: 'id',
|
||
key: 'id',
|
||
width: 320,
|
||
},
|
||
{
|
||
title: '类型',
|
||
dataIndex: 'type',
|
||
key: 'type',
|
||
width: 120,
|
||
},
|
||
{
|
||
title: '标题',
|
||
dataIndex: 'title',
|
||
key: 'title',
|
||
width: 200,
|
||
},
|
||
{
|
||
title: '描述',
|
||
dataIndex: 'description',
|
||
key: 'description',
|
||
ellipsis: true,
|
||
},
|
||
{
|
||
title: '状态',
|
||
dataIndex: 'status',
|
||
key: 'status',
|
||
width: 100,
|
||
},
|
||
{
|
||
title: '价格',
|
||
dataIndex: 'price',
|
||
key: 'price',
|
||
width: 100,
|
||
},
|
||
{
|
||
title: '创建时间',
|
||
dataIndex: 'createdAt',
|
||
key: 'createdAt',
|
||
width: 180,
|
||
render: (text) => (text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'),
|
||
},
|
||
{
|
||
title: '更新时间',
|
||
dataIndex: 'updatedAt',
|
||
key: 'updatedAt',
|
||
width: 180,
|
||
render: (text) => (text ? dayjs(text).format('YYYY-MM-DD HH:mm') : '-'),
|
||
},
|
||
{
|
||
title: '操作',
|
||
key: 'action',
|
||
width: 150,
|
||
render: (_, record) => (
|
||
<Space size='middle'>
|
||
<Button type='text' icon={<EditOutlined />} onClick={() => handleEdit(record)} />
|
||
<Popconfirm title='确定要删除这条记录吗?' onConfirm={() => handleDelete(record.id)} okText='确定' cancelText='取消'>
|
||
<Button type='text' danger icon={<DeleteOutlined />} />
|
||
</Popconfirm>
|
||
</Space>
|
||
),
|
||
},
|
||
];
|
||
|
||
return (
|
||
<div className='ticket-container p-4 h-full overflow-auto scrollbar'>
|
||
<h1>票据管理系统</h1>
|
||
|
||
{/* 搜索表单 */}
|
||
<div className='search-form-container' style={{ marginBottom: 16, padding: 16, background: '#f9f9f9', borderRadius: 4 }}>
|
||
<Form form={searchForm} layout='inline' onFinish={handleSearch}>
|
||
<Form.Item name='search' label='关键词'>
|
||
<Input placeholder='标题/描述' allowClear />
|
||
</Form.Item>
|
||
<Form.Item name='type' label='类型'>
|
||
<Select
|
||
placeholder='选择类型'
|
||
allowClear
|
||
style={{ width: 120 }}
|
||
options={[
|
||
{ value: 'type1', label: '类型1' },
|
||
{ value: 'type2', label: '类型2' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name='status' label='状态'>
|
||
<Select
|
||
placeholder='选择状态'
|
||
allowClear
|
||
style={{ width: 120 }}
|
||
options={[
|
||
{ value: 'rule', label: '规则' },
|
||
{ value: 'people', label: '人工' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
<Form.Item name='dateRange' label='日期范围'>
|
||
<DatePicker.RangePicker />
|
||
</Form.Item>
|
||
<Form.Item>
|
||
<Space>
|
||
<Button type='primary' htmlType='submit' icon={<SearchOutlined />}>
|
||
搜索
|
||
</Button>
|
||
<Button onClick={handleReset}>重置</Button>
|
||
</Space>
|
||
</Form.Item>
|
||
</Form>
|
||
</div>
|
||
|
||
{/* 操作按钮 */}
|
||
<div style={{ marginBottom: 16 }}>
|
||
<Button type='primary' icon={<PlusOutlined />} onClick={handleAdd}>
|
||
新建票据
|
||
</Button>
|
||
</div>
|
||
|
||
{/* 表格 */}
|
||
<Table
|
||
columns={columns}
|
||
dataSource={store.tickets}
|
||
rowKey='id'
|
||
loading={store.loading}
|
||
pagination={{
|
||
...store.pagination,
|
||
current: store.searchForm.current,
|
||
pageSize: store.searchForm.pageSize,
|
||
onChange: (page, pageSize) => {
|
||
store.setSearchForm({
|
||
...store.searchForm,
|
||
current: page,
|
||
pageSize,
|
||
});
|
||
},
|
||
showSizeChanger: true,
|
||
showQuickJumper: true,
|
||
showTotal: (total) => `共 ${total} 条`,
|
||
}}
|
||
/>
|
||
|
||
{/* 引入弹窗表单组件 */}
|
||
<TicketModelForm />
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export const TicketModelForm = () => {
|
||
const [form] = Form.useForm();
|
||
const store = useTicketStore();
|
||
const isEdit = !!store.formData?.id;
|
||
|
||
useEffect(() => {
|
||
if (!store.showEdit) {
|
||
return;
|
||
}
|
||
if (store.formData) {
|
||
form.setFieldsValue(store.formData);
|
||
} else {
|
||
// form.resetFields();
|
||
form.setFieldsValue({ title: '', type: '', status: '', price: '', description: '' });
|
||
}
|
||
}, [store.showEdit, store.formData, form]);
|
||
|
||
const handleSubmit = async () => {
|
||
try {
|
||
const values = await form.validateFields();
|
||
store.setLoading(true);
|
||
|
||
if (isEdit) {
|
||
// 编辑模式
|
||
await store.updateTicket({
|
||
...store.formData,
|
||
...values,
|
||
});
|
||
} else {
|
||
// 新增模式 - 假设后端会生成ID,这里模拟API调用
|
||
const newTicket = {
|
||
...values,
|
||
};
|
||
|
||
// 这里应该是一个创建票据的API调用
|
||
// 暂时使用更新方法模拟
|
||
await store.updateTicket(newTicket as TicketType);
|
||
}
|
||
|
||
// 关闭弹窗并刷新列表
|
||
store.setShowEdit(false);
|
||
store.getTickets(store.searchForm);
|
||
} catch (error) {
|
||
console.error('表单提交错误:', error);
|
||
message.error('操作失败,请检查表单');
|
||
} finally {
|
||
store.setLoading(false);
|
||
}
|
||
};
|
||
|
||
return (
|
||
<Modal
|
||
title={isEdit ? '编辑票据' : '新增票据'}
|
||
open={store.showEdit}
|
||
onCancel={() => store.setShowEdit(false)}
|
||
onOk={handleSubmit}
|
||
confirmLoading={store.loading}
|
||
maskClosable={false}>
|
||
<Form form={form} layout='vertical' initialValues={store.formData || {}}>
|
||
<Form.Item name='title' label='标题' rules={[{ required: true, message: '请输入标题' }]}>
|
||
<Input placeholder='请输入标题' />
|
||
</Form.Item>
|
||
|
||
<Form.Item name='type' label='类型' rules={[{ required: true, message: '请选择类型' }]}>
|
||
<Select
|
||
placeholder='请选择类型'
|
||
options={[
|
||
{ value: 'type1', label: '类型1' },
|
||
{ value: 'type2', label: '类型2' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item name='status' label='状态' rules={[{ required: true, message: '请选择状态' }]}>
|
||
<Select
|
||
placeholder='请选择状态'
|
||
options={[
|
||
{ value: 'rule', label: '规则' },
|
||
{ value: 'people', label: '人工' },
|
||
]}
|
||
/>
|
||
</Form.Item>
|
||
|
||
<Form.Item name='price' label='价格' rules={[{ required: true, message: '请输入价格' }]}>
|
||
<Input placeholder='请输入价格' />
|
||
</Form.Item>
|
||
|
||
<Form.Item name='description' label='描述' rules={[{ required: true, message: '请输入描述' }]}>
|
||
<Input.TextArea rows={4} placeholder='请输入描述' />
|
||
</Form.Item>
|
||
</Form>
|
||
</Modal>
|
||
);
|
||
};
|