feat:分类模块,单位模块完成
This commit is contained in:
435
src/pages/company/service/itemsManange/sections/index.jsx
Normal file
435
src/pages/company/service/itemsManange/sections/index.jsx
Normal file
@@ -0,0 +1,435 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Table, Button, Drawer, Modal, Form, Input, InputNumber, Space, message, Popconfirm, Select } from 'antd';
|
||||
import { PlusOutlined, DeleteOutlined, EditOutlined } from '@ant-design/icons';
|
||||
import { supabase } from '@/config/supabase';
|
||||
|
||||
const SectionManagement = () => {
|
||||
const [data, setData] = useState([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [drawerVisible, setDrawerVisible] = useState(false);
|
||||
const [modalVisible, setModalVisible] = useState(false);
|
||||
const [editingRecord, setEditingRecord] = useState(null);
|
||||
const [form] = Form.useForm();
|
||||
const [units, setUnits] = useState([]);
|
||||
|
||||
// 获取子模块数据
|
||||
const fetchSections = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const { data: sections, error } = await supabase
|
||||
.from('resources')
|
||||
.select('*')
|
||||
.eq('type', 'sections');
|
||||
|
||||
if (error) throw error;
|
||||
setData(sections || []);
|
||||
} catch (error) {
|
||||
message.error('获取子模块数据失败');
|
||||
console.error(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// 获取单位数据
|
||||
const fetchUnits = async () => {
|
||||
try {
|
||||
const { data: unitsData, error } = await supabase
|
||||
.from('resources')
|
||||
.select('*')
|
||||
.eq('type', 'units')
|
||||
.order('created_at', { ascending: false });
|
||||
|
||||
if (error) throw error;
|
||||
setUnits(unitsData || []);
|
||||
} catch (error) {
|
||||
message.error('获取单位数据失败');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (drawerVisible) {
|
||||
fetchSections();
|
||||
}
|
||||
fetchUnits();
|
||||
}, [drawerVisible]);
|
||||
|
||||
// 打开新增/编辑模态框
|
||||
const showModal = async (record = null) => {
|
||||
setModalVisible(true);
|
||||
setEditingRecord(record);
|
||||
|
||||
if (record) {
|
||||
try {
|
||||
const { data: section, error } = await supabase
|
||||
.from('resources')
|
||||
.select('*')
|
||||
.eq('id', record.id)
|
||||
.single();
|
||||
|
||||
if (error) throw error;
|
||||
|
||||
form.setFieldsValue({
|
||||
name: section.attributes.name,
|
||||
items: section.attributes.items
|
||||
});
|
||||
} catch (error) {
|
||||
message.error('获取子模块详情失败');
|
||||
console.error(error);
|
||||
}
|
||||
} else {
|
||||
form.setFieldsValue({
|
||||
name: '',
|
||||
items: [{ name: '', description: '', price: 0, quantity: 1, unit: '' }]
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 保存子模块数据
|
||||
const handleSave = async (values) => {
|
||||
try {
|
||||
if (editingRecord) {
|
||||
// 更新
|
||||
const { error } = await supabase
|
||||
.from('resources')
|
||||
.update({
|
||||
attributes: {
|
||||
name: values.name,
|
||||
items: values.items
|
||||
},
|
||||
updated_at: new Date().toISOString()
|
||||
})
|
||||
.eq('id', editingRecord.id);
|
||||
|
||||
if (error) throw error;
|
||||
} else {
|
||||
// 新增
|
||||
const { error } = await supabase
|
||||
.from('resources')
|
||||
.insert([{
|
||||
type: 'sections',
|
||||
attributes: {
|
||||
name: values.name,
|
||||
items: values.items
|
||||
},
|
||||
schema_version: 1
|
||||
}]);
|
||||
|
||||
if (error) throw error;
|
||||
}
|
||||
|
||||
message.success('保存成功');
|
||||
setModalVisible(false);
|
||||
form.resetFields();
|
||||
fetchSections();
|
||||
} catch (error) {
|
||||
message.error('保存失败');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
// 删除子模块
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
const { error } = await supabase
|
||||
.from('resources')
|
||||
.delete()
|
||||
.eq('id', id);
|
||||
|
||||
if (error) throw error;
|
||||
message.success('删除成功');
|
||||
fetchSections();
|
||||
} catch (error) {
|
||||
message.error('删除失败');
|
||||
console.error(error);
|
||||
}
|
||||
};
|
||||
|
||||
const drawerColumns = [
|
||||
{
|
||||
title: '项目名称',
|
||||
dataIndex: 'name',
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
},
|
||||
{
|
||||
title: '单价',
|
||||
dataIndex: 'price',
|
||||
render: (price) => `¥${price}`
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'quantity',
|
||||
},
|
||||
{
|
||||
title: '单位',
|
||||
dataIndex: 'unit',
|
||||
}
|
||||
];
|
||||
|
||||
// 添加模态框内表格列定义
|
||||
const modalColumns = [
|
||||
{
|
||||
title: '项目名称',
|
||||
dataIndex: 'name',
|
||||
render: (_, __, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'name']}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Input placeholder="项目名称" />
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '描述',
|
||||
dataIndex: 'description',
|
||||
render: (_, __, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'description']}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Input placeholder="描述" />
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '单价',
|
||||
dataIndex: 'price',
|
||||
render: (_, __, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'price']}
|
||||
rules={[{ required: true, message: '请输入单价!' }]}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<InputNumber
|
||||
min={0}
|
||||
placeholder="单价"
|
||||
className="w-full"
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '数量',
|
||||
dataIndex: 'quantity',
|
||||
render: (_, __, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'quantity']}
|
||||
rules={[{ required: true, message: '请输入数量!' }]}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<InputNumber
|
||||
min={1}
|
||||
placeholder="数量"
|
||||
className="w-full"
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '单位',
|
||||
dataIndex: 'unit',
|
||||
render: (_, __, index) => (
|
||||
<Form.Item
|
||||
name={[index, 'unit']}
|
||||
rules={[{ required: true, message: '请选择或输入单位!' }]}
|
||||
style={{ margin: 0 }}
|
||||
>
|
||||
<Select
|
||||
placeholder="请选择或输入单位"
|
||||
className="w-full"
|
||||
showSearch
|
||||
allowClear
|
||||
options={units.map(unit => ({
|
||||
label: unit.attributes.name,
|
||||
value: unit.attributes.name
|
||||
}))}
|
||||
filterOption={(input, option) =>
|
||||
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
render: (_, __, index, { remove }) => (
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
onClick={() => remove(index)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
];
|
||||
|
||||
return (
|
||||
<div>
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => setDrawerVisible(true)}
|
||||
className="flex items-center"
|
||||
>
|
||||
子模块管理
|
||||
</Button>
|
||||
|
||||
<Drawer
|
||||
title={
|
||||
<span className="text-lg font-medium text-gray-800 dark:text-gray-200">
|
||||
子模块管理
|
||||
</span>
|
||||
}
|
||||
placement="right"
|
||||
width={1000}
|
||||
onClose={() => setDrawerVisible(false)}
|
||||
open={drawerVisible}
|
||||
className="dark:bg-gray-800"
|
||||
>
|
||||
<div className="flex flex-col h-full">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => showModal()}
|
||||
className="mb-4 w-32 flex items-center justify-center"
|
||||
>
|
||||
新增子模块
|
||||
</Button>
|
||||
|
||||
<div className="space-y-6">
|
||||
{data.map((section) => (
|
||||
<div
|
||||
key={section.id}
|
||||
className="bg-white rounded-lg shadow-sm dark:bg-gray-800 border border-gray-200 dark:border-gray-700"
|
||||
>
|
||||
<div className="p-4 border-b border-gray-200 dark:border-gray-700 flex justify-between items-center">
|
||||
<h3 className="text-lg font-medium text-gray-800 dark:text-gray-200">
|
||||
{section.attributes.name}
|
||||
</h3>
|
||||
<Space>
|
||||
<Button
|
||||
type="link"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => showModal(section)}
|
||||
className="text-blue-600 hover:text-blue-500"
|
||||
>
|
||||
编辑
|
||||
</Button>
|
||||
<Popconfirm
|
||||
title="确定要删除吗?"
|
||||
onConfirm={() => handleDelete(section.id)}
|
||||
okButtonProps={{
|
||||
className: "bg-red-500 hover:bg-red-600 border-red-500"
|
||||
}}
|
||||
>
|
||||
<Button
|
||||
type="link"
|
||||
danger
|
||||
icon={<DeleteOutlined />}
|
||||
className="text-red-600 hover:text-red-500"
|
||||
/>
|
||||
</Popconfirm>
|
||||
</Space>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<Table
|
||||
scroll={{ x: true }}
|
||||
dataSource={section.attributes.items}
|
||||
columns={drawerColumns}
|
||||
pagination={false}
|
||||
rowKey={(record, index) => `${section.id}-${index}`}
|
||||
className="border dark:border-gray-700 rounded-lg"
|
||||
rowClassName="hover:bg-gray-50 dark:hover:bg-gray-700/50"
|
||||
size="small"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Drawer>
|
||||
|
||||
<Modal
|
||||
title={`${editingRecord ? '编辑' : '新增'}子模块`}
|
||||
open={modalVisible}
|
||||
onCancel={() => {
|
||||
setModalVisible(false);
|
||||
setEditingRecord(null);
|
||||
form.resetFields();
|
||||
}}
|
||||
footer={null}
|
||||
width={1200}
|
||||
destroyOnClose={true}
|
||||
>
|
||||
<Form
|
||||
form={form}
|
||||
onFinish={handleSave}
|
||||
layout="vertical"
|
||||
className="mt-4"
|
||||
>
|
||||
<Form.Item
|
||||
name="name"
|
||||
label="子模块名称"
|
||||
rules={[{ required: true, message: '请输入子模块名称!' }]}
|
||||
>
|
||||
<Input placeholder="请输入子模块名称" />
|
||||
</Form.Item>
|
||||
|
||||
<Form.List name="items">
|
||||
{(fields, { add, remove }) => (
|
||||
<div className="bg-white rounded-lg border dark:bg-gray-800 dark:border-gray-700">
|
||||
<Table
|
||||
dataSource={fields}
|
||||
columns={modalColumns.map(col => ({
|
||||
...col,
|
||||
render: (...args) => col.render(...args, { remove })
|
||||
}))}
|
||||
pagination={false}
|
||||
rowKey="key"
|
||||
className="mb-4"
|
||||
/>
|
||||
<div className="p-4 border-t dark:border-gray-700">
|
||||
<Button
|
||||
type="dashed"
|
||||
onClick={() => add({
|
||||
name: '',
|
||||
description: '',
|
||||
price: 0,
|
||||
quantity: 1,
|
||||
unit: ''
|
||||
})}
|
||||
icon={<PlusOutlined />}
|
||||
className="w-full hover:border-blue-400 hover:text-blue-500
|
||||
dark:border-gray-600 dark:text-gray-400 dark:hover:text-blue-400"
|
||||
>
|
||||
添加项目
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Form.List>
|
||||
|
||||
<div className="flex justify-end gap-4 mt-6">
|
||||
<Button onClick={() => {
|
||||
setModalVisible(false);
|
||||
setEditingRecord(null);
|
||||
form.resetFields();
|
||||
}}>
|
||||
取消
|
||||
</Button>
|
||||
<Button type="primary" htmlType="submit">
|
||||
保存
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SectionManagement;
|
||||
Reference in New Issue
Block a user