feat: 添加产品工具, 重构产品 #31

Closed
zksu wants to merge 37 commits from (deleted):main into main
4 changed files with 378 additions and 14 deletions
Showing only changes of commit 1000c42b6a - Show all commits

View File

@ -43,18 +43,31 @@ export default defineConfig({
},
],
},
{
name: '站点管理',
path: '/site',
access: 'canSeeSite',
routes: [
{
name: '站点列表',
path: '/site/list',
component: './Site/List',
},
],
},
name: '字典管理',
path: '/dict',
access: 'canSeeDict',
routes: [
{
name: '字典列表',
path: '/dict/list',
component: './Dict/List',
},
],
},
{
name: '站点管理',
path: '/site',
access: 'canSeeSite',
routes: [
{
name: '站点列表',
path: '/site/list',
component: './Site/List',
},
],
},
{
name: '商品管理',
path: '/product',

View File

@ -10,6 +10,7 @@ export default (initialState: any) => {
const canSeeLogistics = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('logistics') ?? false);
const canSeeStatistics = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('statistics') ?? false);
const canSeeSite = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('site') ?? false);
const canSeeDict = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('dict') ?? false);
return {
canSeeOrganiza,
@ -20,5 +21,6 @@ export default (initialState: any) => {
canSeeLogistics,
canSeeStatistics,
canSeeSite,
canSeeDict,
};
};

View File

@ -0,0 +1,350 @@
import { PageContainer } from '@ant-design/pro-components';
import { Input, Button, Table, Layout, Space, Modal, message, Form, Upload } from 'antd';
import React, { useState, useEffect } from 'react';
import { request } from '@umijs/max';
import { UploadOutlined } from '@ant-design/icons';
const { Sider, Content } = Layout;
const DictPage: React.FC = () => {
// 左侧字典列表的状态
const [dicts, setDicts] = useState<any[]>([]);
const [loadingDicts, setLoadingDicts] = useState(false);
const [searchText, setSearchText] = useState('');
const [selectedDict, setSelectedDict] = useState<any>(null);
// 右侧字典项列表的状态
const [dictItems, setDictItems] = useState<any[]>([]);
const [loadingDictItems, setLoadingDictItems] = useState(false);
// 控制添加字典模态框的显示
const [isAddDictModalVisible, setIsAddDictModalVisible] = useState(false);
const [newDictName, setNewDictName] = useState('');
const [newDictTitle, setNewDictTitle] = useState('');
const [editingDict, setEditingDict] = useState<any>(null);
// 控制字典项模态框的显示
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
const [editingDictItem, setEditingDictItem] = useState<any>(null);
const [dictItemForm, setDictItemForm] = useState({ name: '', title: '', value: '' });
// 获取字典列表
const fetchDicts = async (title?: string) => {
setLoadingDicts(true);
try {
const res = await request('/api/dicts', { params: { title } });
if (res) {
setDicts(res);
}
} catch (error) {
message.error('获取字典列表失败');
}
setLoadingDicts(false);
};
// 获取字典项列表
const fetchDictItems = async (dictId?: number) => {
setLoadingDictItems(true);
try {
const res = await request('/api/dict-items', { params: { dictId } });
if (res) {
setDictItems(res);
}
} catch (error) {
message.error('获取字典项列表失败');
}
setLoadingDictItems(false);
};
// 组件加载时获取数据
useEffect(() => {
fetchDicts();
fetchDictItems();
}, []);
// 当选择的字典变化时,重新获取字典项
useEffect(() => {
fetchDictItems(selectedDict?.id);
}, [selectedDict]);
// 处理字典搜索
const handleSearch = (value: string) => {
fetchDicts(value);
};
// 处理添加字典
const handleAddDict = async () => {
if (!newDictName || !newDictTitle) {
message.warning('请输入字典名称和标题');
return;
}
try {
if (editingDict) {
await request(`/api/dicts/${editingDict.id}`, {
method: 'PUT',
data: { name: newDictName, title: newDictTitle },
});
message.success('更新成功');
} else {
await request('/api/dicts', {
method: 'POST',
data: { name: newDictName, title: newDictTitle },
});
message.success('添加成功');
}
setIsAddDictModalVisible(false);
setNewDictName('');
setNewDictTitle('');
setEditingDict(null);
fetchDicts(); // 重新获取字典列表
} catch (error) {
message.error(editingDict ? '更新失败' : '添加失败');
}
};
const handleEditDict = (dict: any) => {
setEditingDict(dict);
setNewDictName(dict.name);
setNewDictTitle(dict.title);
setIsAddDictModalVisible(true);
};
// 打开添加字典项模态框
const handleAddDictItem = () => {
setEditingDictItem(null);
setDictItemForm({ name: '', title: '', value: '' });
setIsDictItemModalVisible(true);
};
// 打开编辑字典项模态框
const handleEditDictItem = (item: any) => {
setEditingDictItem(item);
setDictItemForm(item);
setIsDictItemModalVisible(true);
};
// 处理字典项表单提交(添加/编辑)
const handleDictItemFormSubmit = async () => {
if (!dictItemForm.name || !dictItemForm.title) {
message.warning('请输入名称和标题');
return;
}
try {
if (editingDictItem) {
// 编辑
await request(`/api/dict-items/${editingDictItem.id}`, {
method: 'PUT',
data: dictItemForm,
});
message.success('更新成功');
} else {
// 添加
await request('/api/dict-items', {
method: 'POST',
data: { ...dictItemForm, dictId: selectedDict.id },
});
message.success('添加成功');
}
setIsDictItemModalVisible(false);
fetchDictItems(selectedDict.id);
} catch (error) {
message.error(editingDictItem ? '更新失败' : '添加失败');
}
};
// 处理删除字典项
const handleDeleteDictItem = async (itemId: number) => {
try {
await request(`/api/dict-items/${itemId}`, { method: 'DELETE' });
message.success('删除成功');
fetchDictItems(selectedDict.id);
} catch (error) {
message.error('删除失败');
}
};
// 处理删除字典
const handleDeleteDict = async (dictId: number) => {
try {
await request(`/api/dicts/${dictId}`, { method: 'DELETE' });
message.success('删除成功');
fetchDicts(); // 重新获取字典列表
// 如果删除的是当前选中的字典,则清空右侧列表
if (selectedDict?.id === dictId) {
setSelectedDict(null);
setDictItems([]);
}
} catch (error) {
message.error('删除失败');
}
};
// 左侧字典列表的列定义
const dictColumns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '标题',
dataIndex: 'title',
key: 'title',
},
{
title: '操作',
key: 'action',
render: (_: any, record: any) => (
<Space>
<Button type="link" onClick={(e) => { e.stopPropagation(); handleEditDict(record); }}>
</Button>
<Button type="link" danger onClick={(e) => { e.stopPropagation(); handleDeleteDict(record.id); }}>
</Button>
</Space>
),
},
];
// 右侧字典项列表的列定义
const dictItemColumns = [
{
title: '名称',
dataIndex: 'name',
key: 'name',
},
{
title: '标题',
dataIndex: 'title',
key: 'title',
},
{
title: '操作',
key: 'action',
render: (_: any, record: any) => (
<Space size="middle">
<Button type="link" onClick={() => handleEditDictItem(record)}></Button>
<Button type="link" danger onClick={() => handleDeleteDictItem(record.id)}></Button>
</Space>
),
},
];
return (
<PageContainer>
<Layout style={{ background: '#fff' }}>
<Sider width={300} style={{ background: '#fff', padding: '16px', borderRight: '1px solid #f0f0f0' }}>
<Space direction="vertical" style={{ width: '100%' }}>
<Input.Search placeholder="搜索字典" onSearch={handleSearch} onChange={e => setSearchText(e.target.value)} enterButton allowClear />
<Button type="primary" onClick={() => setIsAddDictModalVisible(true)} block>
</Button>
<Space>
<Upload
name="file"
action="/api/dicts/import"
showUploadList={false}
onChange={info => {
if (info.file.status === 'done') {
message.success(`${info.file.name} 文件上传成功`);
fetchDicts();
} else if (info.file.status === 'error') {
message.error(`${info.file.name} 文件上传失败`);
}
}}
>
<Button icon={<UploadOutlined />}></Button>
</Upload>
<Button onClick={() => window.open('/api/dicts/template')}></Button>
</Space>
<Table
dataSource={dicts}
columns={dictColumns}
rowKey="id"
loading={loadingDicts}
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}
/>
</Space>
</Sider>
<Content style={{ padding: '16px' }}>
<Space direction="vertical" style={{ width: '100%' }}>
<Button type="primary" onClick={handleAddDictItem} disabled={!selectedDict}>
</Button>
<Space>
<Upload
name="file"
action={`/api/dict-items/import`}
data={{ dictId: selectedDict?.id }}
showUploadList={false}
disabled={!selectedDict}
onChange={info => {
if (info.file.status === 'done') {
message.success(`${info.file.name} 文件上传成功`);
fetchDictItems(selectedDict.id);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} 文件上传失败`);
}
}}
>
<Button icon={<UploadOutlined />} disabled={!selectedDict}></Button>
</Upload>
<Button onClick={() => window.open('/api/dict-items/template')} disabled={!selectedDict}></Button>
</Space>
<Table dataSource={dictItems} columns={dictItemColumns} rowKey="id" loading={loadingDictItems} />
</Space>
</Content>
</Layout>
<Modal
title={editingDictItem ? '编辑字典项' : '添加字典项'}
visible={isDictItemModalVisible}
onOk={handleDictItemFormSubmit}
onCancel={() => setIsDictItemModalVisible(false)}
>
<Form layout="vertical">
<Form.Item label="名称">
<Input placeholder="名称 (e.g., zyn)" value={dictItemForm.name} onChange={e => setDictItemForm({ ...dictItemForm, name: e.target.value })} />
</Form.Item>
<Form.Item label="标题">
<Input placeholder="标题 (e.g., ZYN)" value={dictItemForm.title} onChange={e => setDictItemForm({ ...dictItemForm, title: e.target.value })} />
</Form.Item>
<Form.Item label="值 (可选)">
<Input placeholder="值 (可选)" value={dictItemForm.value} onChange={e => setDictItemForm({ ...dictItemForm, value: e.target.value })} />
</Form.Item>
</Form>
</Modal>
<Modal
title={editingDict ? '编辑字典' : '添加新字典'}
visible={isAddDictModalVisible}
onOk={handleAddDict}
onCancel={() => setIsAddDictModalVisible(false)}
>
<Form layout="vertical">
<Form.Item label="字典名称">
<Input placeholder="字典名称 (e.g., brand)" value={newDictName} onChange={e => setNewDictName(e.target.value)} />
</Form.Item>
<Form.Item label="字典标题">
<Input placeholder="字典标题 (e.g., 品牌)" value={newDictTitle} onChange={e => setNewDictTitle(e.target.value)} />
</Form.Item>
</Form>
</Modal>
</PageContainer>
);
};
export default DictPage;

View File

@ -35,9 +35,8 @@ const List: React.FC = () => {
},
},
{
title: '标识',
dataIndex: 'unique_key',
hideInSearch: true,
title: '显示名称',
dataIndex: 'title',
},
{
title: '更新时间',