231 lines
6.2 KiB
TypeScript
231 lines
6.2 KiB
TypeScript
/**
|
|
* Static Data Management Page
|
|
*/
|
|
import { useState, useEffect } from 'react';
|
|
import { Modal, Form, Input, Select } from 'antd';
|
|
import type { ColumnsType } from 'antd/es/table';
|
|
import { DataTable } from '../../components/admin/DataTable';
|
|
import { request } from '../../utils/request';
|
|
import { useToast } from '../../contexts/ToastContext';
|
|
|
|
interface StaticDataItem {
|
|
id: number;
|
|
category: string;
|
|
name: string;
|
|
name_zh: string;
|
|
data: any;
|
|
}
|
|
|
|
export function StaticData() {
|
|
const [loading, setLoading] = useState(false);
|
|
const [data, setData] = useState<StaticDataItem[]>([]);
|
|
const [filteredData, setFilteredData] = useState<StaticDataItem[]>([]);
|
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
const [editingRecord, setEditingRecord] = useState<StaticDataItem | null>(null);
|
|
const [form] = Form.useForm();
|
|
const toast = useToast();
|
|
|
|
useEffect(() => {
|
|
loadData();
|
|
}, []);
|
|
|
|
const loadData = async () => {
|
|
setLoading(true);
|
|
try {
|
|
const { data: result } = await request.get('/celestial/static/list');
|
|
setData(result.items || []);
|
|
setFilteredData(result.items || []);
|
|
} catch (error) {
|
|
toast.error('加载数据失败');
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
const handleSearch = (keyword: string) => {
|
|
const lowerKeyword = keyword.toLowerCase();
|
|
const filtered = data.filter(
|
|
(item) =>
|
|
item.name.toLowerCase().includes(lowerKeyword) ||
|
|
item.name_zh?.toLowerCase().includes(lowerKeyword) ||
|
|
item.category.toLowerCase().includes(lowerKeyword)
|
|
);
|
|
setFilteredData(filtered);
|
|
};
|
|
|
|
const handleAdd = () => {
|
|
setEditingRecord(null);
|
|
form.resetFields();
|
|
form.setFieldsValue({ category: 'star' });
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const handleEdit = (record: StaticDataItem) => {
|
|
setEditingRecord(record);
|
|
// Convert JSON data to string for editing
|
|
form.setFieldsValue({
|
|
...record,
|
|
data: JSON.stringify(record.data, null, 2)
|
|
});
|
|
setIsModalOpen(true);
|
|
};
|
|
|
|
const handleDelete = async (record: StaticDataItem) => {
|
|
try {
|
|
await request.delete(`/celestial/static/${record.id}`);
|
|
toast.success('删除成功');
|
|
loadData();
|
|
} catch (error) {
|
|
toast.error('删除失败');
|
|
}
|
|
};
|
|
|
|
const handleModalOk = async () => {
|
|
try {
|
|
const values = await form.validateFields();
|
|
|
|
// Parse JSON data
|
|
try {
|
|
values.data = JSON.parse(values.data);
|
|
} catch (e) {
|
|
toast.error('JSON格式错误');
|
|
return;
|
|
}
|
|
|
|
if (editingRecord) {
|
|
await request.put(`/celestial/static/${editingRecord.id}`, values);
|
|
toast.success('更新成功');
|
|
} else {
|
|
await request.post('/celestial/static', values);
|
|
toast.success('创建成功');
|
|
}
|
|
|
|
setIsModalOpen(false);
|
|
loadData();
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
|
|
const columns: ColumnsType<StaticDataItem> = [
|
|
{
|
|
title: 'ID',
|
|
dataIndex: 'id',
|
|
width: 80,
|
|
sorter: (a, b) => a.id - b.id,
|
|
},
|
|
{
|
|
title: '分类',
|
|
dataIndex: 'category',
|
|
width: 120,
|
|
filters: [
|
|
{ text: '恒星', value: 'star' },
|
|
{ text: '星座', value: 'constellation' },
|
|
{ text: '星系', value: 'galaxy' },
|
|
{ text: '星云', value: 'nebula' },
|
|
{ text: '小行星带', value: 'asteroid_belt' },
|
|
{ text: '柯伊伯带', value: 'kuiper_belt' },
|
|
{ text: '恒星际', value: 'interstellar' },
|
|
],
|
|
onFilter: (value, record) => record.category === value,
|
|
},
|
|
{
|
|
title: '英文名',
|
|
dataIndex: 'name',
|
|
sorter: (a, b) => a.name.localeCompare(b.name),
|
|
},
|
|
{
|
|
title: '中文名',
|
|
dataIndex: 'name_zh',
|
|
},
|
|
{
|
|
title: '数据 (JSON)',
|
|
dataIndex: 'data',
|
|
ellipsis: true,
|
|
render: (text) => JSON.stringify(text),
|
|
},
|
|
];
|
|
|
|
return (
|
|
<>
|
|
<DataTable
|
|
title="静态数据管理"
|
|
columns={columns}
|
|
dataSource={filteredData}
|
|
loading={loading}
|
|
total={filteredData.length}
|
|
onSearch={handleSearch}
|
|
onAdd={handleAdd}
|
|
onEdit={handleEdit}
|
|
onDelete={handleDelete}
|
|
rowKey="id"
|
|
pageSize={10}
|
|
/>
|
|
|
|
<Modal
|
|
title={editingRecord ? '编辑静态数据' : '新增静态数据'}
|
|
open={isModalOpen}
|
|
onOk={handleModalOk}
|
|
onCancel={() => setIsModalOpen(false)}
|
|
width={700}
|
|
>
|
|
<Form
|
|
form={form}
|
|
layout="vertical"
|
|
>
|
|
<Form.Item
|
|
name="category"
|
|
label="分类"
|
|
rules={[{ required: true }]}
|
|
>
|
|
<Select>
|
|
<Select.Option value="star">恒星 (Star)</Select.Option>
|
|
<Select.Option value="constellation">星座 (Constellation)</Select.Option>
|
|
<Select.Option value="galaxy">星系 (Galaxy)</Select.Option>
|
|
<Select.Option value="nebula">星云 (Nebula)</Select.Option>
|
|
<Select.Option value="asteroid_belt">小行星带 (Asteroid Belt)</Select.Option>
|
|
<Select.Option value="kuiper_belt">柯伊伯带 (Kuiper Belt)</Select.Option>
|
|
<Select.Option value="interstellar">恒星际 (Interstellar)</Select.Option>
|
|
</Select>
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
name="name"
|
|
label="英文名"
|
|
rules={[{ required: true }]}
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
name="name_zh"
|
|
label="中文名"
|
|
>
|
|
<Input />
|
|
</Form.Item>
|
|
|
|
<Form.Item
|
|
name="data"
|
|
label="JSON数据"
|
|
rules={[
|
|
{ required: true, message: '请输入JSON数据' },
|
|
{
|
|
validator: (_, value) => {
|
|
try {
|
|
JSON.parse(value);
|
|
return Promise.resolve();
|
|
} catch (err) {
|
|
return Promise.reject(new Error('无效的JSON格式'));
|
|
}
|
|
}
|
|
}
|
|
]}
|
|
>
|
|
<Input.TextArea rows={15} className="font-mono" />
|
|
</Form.Item>
|
|
</Form>
|
|
</Modal>
|
|
</>
|
|
);
|
|
}
|