WEB/src/pages/Product/Attribute/index.tsx

353 lines
10 KiB
TypeScript

import { UploadOutlined } from '@ant-design/icons';
import {
ActionType,
PageContainer,
ProTable,
} from '@ant-design/pro-components';
import { request } from '@umijs/max';
import {
Button,
Form,
Input,
Layout,
Modal,
Space,
Table,
Upload,
message,
} from 'antd';
import React, { useEffect, useRef, useState } from 'react';
const { Sider, Content } = Layout;
import { attributes } from './consts';
const AttributePage: React.FC = () => {
// 左侧字典列表状态
const [dicts, setDicts] = useState<any[]>([]);
const [loadingDicts, setLoadingDicts] = useState(false);
const [searchText, setSearchText] = useState('');
const [selectedDict, setSelectedDict] = useState<any>(null);
// 右侧字典项 ProTable 的引用
const actionRef = useRef<ActionType>();
// 字典项新增/编辑模态框控制
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
const [editingDictItem, setEditingDictItem] = useState<any>(null);
const [dictItemForm] = Form.useForm();
const fetchDicts = async (title?: string) => {
setLoadingDicts(true);
try {
const res = await request('/dict/list', { params: { title } });
// 条件判断,过滤只保留 allowedDictNames 中的字典
const filtered = (res || []).filter((d: any) => attributes.has(d?.name));
setDicts(filtered);
} catch (error) {
message.error('获取字典列表失败');
}
setLoadingDicts(false);
};
// 组件挂载时初始化数据
useEffect(() => {
fetchDicts();
}, []);
// 搜索触发过滤
const handleSearch = (value: string) => {
fetchDicts(value);
};
// 打开添加字典项模态框
const handleAddDictItem = () => {
setEditingDictItem(null);
dictItemForm.resetFields();
setIsDictItemModalVisible(true);
};
// 打开编辑字典项模态框
const handleEditDictItem = (item: any) => {
setEditingDictItem(item);
dictItemForm.setFieldsValue(item);
setIsDictItemModalVisible(true);
};
// 字典项表单提交(新增或编辑)
const handleDictItemFormSubmit = async (values: any) => {
try {
if (editingDictItem) {
// 条件判断,存在编辑项则执行更新
await request(`/dict/item/${editingDictItem.id}`, {
method: 'PUT',
data: values,
});
message.success('更新成功');
} else {
// 否则执行新增,绑定到当前选择的字典
await request('/dict/item', {
method: 'POST',
data: { ...values, dictId: selectedDict.id },
});
message.success('添加成功');
}
setIsDictItemModalVisible(false);
actionRef.current?.reload(); // 刷新 ProTable
} catch (error) {
message.error(editingDictItem ? '更新失败' : '添加失败');
}
};
// 删除字典项
const handleDeleteDictItem = async (itemId: number) => {
try {
const res = await request(`/dict/item/${itemId}`, { method: 'DELETE' });
const isOk =
typeof res === 'boolean'
? res
: res && res.code === 0
? res.data === true || res.data === null
: false;
if (!isOk) {
message.error('删除失败');
return;
}
if (selectedDict?.id) {
const list = await request('/dict/items', {
params: {
dictId: selectedDict.id,
},
});
const exists =
Array.isArray(list) && list.some((it: any) => it.id === itemId);
if (exists) {
message.error('删除失败');
} else {
message.success('删除成功');
actionRef.current?.reload();
}
} else {
message.success('删除成功');
actionRef.current?.reload();
}
} catch (error) {
message.error('删除失败');
}
};
// 左侧字典列表列定义(紧凑样式)
const dictColumns = [
{ title: '名称', dataIndex: 'name', key: 'name' },
{ title: '标题', dataIndex: 'title', key: 'title' },
];
// 右侧字典项列表列定义(紧凑样式)
const dictItemColumns: any[] = [
{ title: '名称', dataIndex: 'name', key: 'name', copyable: true },
{ title: '标题', dataIndex: 'title', key: 'title', copyable: true },
{ title: '中文标题', dataIndex: 'titleCN', key: 'titleCN', copyable: true },
{ title: '简称', dataIndex: 'shortName', key: 'shortName', copyable: true },
{
title: '图片',
dataIndex: 'image',
key: 'image',
valueType: 'image',
width: 80,
},
{
title: '操作',
key: 'action',
valueType: 'option',
render: (_: any, record: any) => (
<Space size="small">
<Button
size="small"
type="link"
onClick={() => handleEditDictItem(record)}
>
</Button>
<Button
size="small"
type="link"
danger
onClick={() => handleDeleteDictItem(record.id)}
>
</Button>
</Space>
),
},
];
return (
<PageContainer>
<Layout style={{ background: '#fff' }}>
<Sider
width={240}
style={{
background: '#fff',
padding: '8px',
borderRight: '1px solid #f0f0f0',
}}
>
<Space direction="vertical" style={{ width: '100%' }} size="small">
<Input.Search
placeholder="搜索字典"
onSearch={handleSearch}
onChange={(e) => setSearchText(e.target.value)}
enterButton
allowClear
size="small"
/>
</Space>
<div
style={{
marginTop: '8px',
overflowY: 'auto',
height: 'calc(100vh - 150px)',
}}
>
<Table
dataSource={dicts}
columns={dictColumns}
rowKey="id"
loading={loadingDicts}
size="small"
onRow={(record) => ({
onClick: () => {
// 条件判断,重复点击同一行则取消选择
if (selectedDict?.id === record.id) {
setSelectedDict(null);
} else {
setSelectedDict(record);
}
},
})}
rowClassName={(record) =>
selectedDict?.id === record.id ? 'ant-table-row-selected' : ''
}
pagination={false}
/>
</div>
</Sider>
<Content style={{ padding: '8px' }}>
<ProTable
columns={dictItemColumns}
actionRef={actionRef}
request={async (params) => {
// 当没有选择字典时,不发起请求
if (!selectedDict?.id) {
return {
data: [],
success: true,
};
}
const { name, title } = params;
const res = await request('/dict/items', {
params: {
dictId: selectedDict.id,
name,
title,
},
});
return {
data: res,
success: true,
};
}}
rowKey="id"
search={{
layout: 'vertical',
}}
pagination={false}
options={false}
size="small"
key={selectedDict?.id}
headerTitle={
<Space>
<Button
type="primary"
size="small"
onClick={handleAddDictItem}
disabled={!selectedDict}
>
</Button>
<Upload
name="file"
action={`/dict/item/import`}
data={{ dictId: selectedDict?.id }}
showUploadList={false}
disabled={!selectedDict}
onChange={(info) => {
// 条件判断,上传状态处理
if (info.file.status === 'done') {
message.success(`${info.file.name} 文件上传成功`);
actionRef.current?.reload();
} else if (info.file.status === 'error') {
message.error(`${info.file.name} 文件上传失败`);
}
}}
>
<Button
size="small"
icon={<UploadOutlined />}
disabled={!selectedDict}
>
</Button>
</Upload>
</Space>
}
/>
</Content>
</Layout>
<Modal
title={editingDictItem ? '编辑字典项' : '添加字典项'}
open={isDictItemModalVisible}
onOk={() => dictItemForm.submit()}
onCancel={() => setIsDictItemModalVisible(false)}
destroyOnClose
>
<Form
form={dictItemForm}
layout="vertical"
onFinish={handleDictItemFormSubmit}
>
<Form.Item
label="名称"
name="name"
rules={[{ required: true, message: '请输入名称' }]}
>
<Input size="small" placeholder="名称 (e.g., zyn)" />
</Form.Item>
<Form.Item
label="标题"
name="title"
rules={[{ required: true, message: '请输入标题' }]}
>
<Input size="small" placeholder="标题 (e.g., ZYN)" />
</Form.Item>
<Form.Item label="中文标题" name="titleCN">
<Input size="small" placeholder="中文标题 (e.g., 品牌)" />
</Form.Item>
<Form.Item label="简称 (可选)" name="shortName">
<Input size="small" placeholder="简称 (可选)" />
</Form.Item>
<Form.Item label="图片 (可选)" name="image">
<Input size="small" placeholder="图片链接 (可选)" />
</Form.Item>
<Form.Item label="值 (可选)" name="value">
<Input size="small" placeholder="值 (可选)" />
</Form.Item>
</Form>
</Modal>
</PageContainer>
);
};
export default AttributePage;