zksu
/
WEB
forked from yoone/WEB
1
0
Fork 0
WEB/src/pages/Product/Attribute/index.tsx

409 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import * as dictApi from '@/servers/api/dict';
import {
ActionType,
PageContainer,
ProTable,
} from '@ant-design/pro-components';
import { request } from '@umijs/max';
import { Button, Input, Layout, Space, Table, message } from 'antd';
import React, { useEffect, useRef, useState } from 'react';
import DictItemActions from '../../Dict/components/DictItemActions';
import DictItemModal from '../../Dict/components/DictItemModal';
const { Sider, Content } = Layout;
import { notAttributes } 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>();
// 字典项模态框状态(由 DictItemModal 组件管理)
const [isDictItemModalVisible, setIsDictItemModalVisible] = useState(false);
const [isEditDictItem, setIsEditDictItem] = useState(false);
const [editingDictItemData, setEditingDictItemData] = useState<any>(null);
// 导出字典项数据
const handleExportDictItems = async () => {
// 条件判断,确保已选择字典
if (!selectedDict) {
message.warning('请先选择字典');
return;
}
try {
// 获取当前字典的所有数据
const response = await request('/dict/items', {
params: {
dictId: selectedDict.id,
},
});
// 确保返回的是数组
const data = Array.isArray(response) ? response : response?.data || [];
// 条件判断,检查是否有数据可导出
if (data.length === 0) {
message.warning('当前字典没有数据可导出');
return;
}
// 将数据转换为CSV格式
const headers = [
'name',
'title',
'titleCN',
'value',
'sort',
'image',
'shortName',
];
const csvContent = [
headers.join(','),
...data.map((item: any) =>
headers
.map((header) => {
const value = item[header] || '';
// 条件判断,如果值包含逗号或引号,需要转义
if (
typeof value === 'string' &&
(value.includes(',') || value.includes('"'))
) {
return `"${value.replace(/"/g, '""')}"`;
}
return value;
})
.join(','),
),
].join('\n');
// 创建blob并下载
const blob = new Blob(['\ufeff' + csvContent], {
// 添加BOM以支持中文
type: 'text/csv;charset=utf-8',
});
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `${selectedDict.name}_dict_items.csv`);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
message.success(`成功导出 ${data.length} 条数据`);
} catch (error: any) {
message.error('导出字典项失败:' + (error.message || '未知错误'));
}
};
const fetchDicts = async (title?: string) => {
setLoadingDicts(true);
try {
const res = await request('/dict/list', { params: { title } });
// 条件判断,确保res是数组再进行过滤
const dataList = Array.isArray(res) ? res : res?.data || [];
const filtered = dataList.filter((d: any) => !notAttributes.has(d?.name));
setDicts(filtered);
} catch (error) {
console.error('获取字典列表失败:', error);
message.error('获取字典列表失败');
setDicts([]);
}
setLoadingDicts(false);
};
// 组件挂载时初始化数据
useEffect(() => {
fetchDicts();
}, []);
// 搜索触发过滤
const handleSearch = (value: string) => {
fetchDicts(value);
};
// 打开添加字典项模态框
const handleAddDictItem = () => {
setIsEditDictItem(false);
setEditingDictItemData(null);
setIsDictItemModalVisible(true);
};
// 打开编辑字典项模态框
const handleEditDictItem = (item: any) => {
setIsEditDictItem(true);
setEditingDictItemData(item);
setIsDictItemModalVisible(true);
};
// 字典项表单提交(新增或编辑)
const handleDictItemFormSubmit = async (values: any) => {
try {
if (isEditDictItem && editingDictItemData) {
// 条件判断,存在编辑项则执行更新
await request(`/dict/item/${editingDictItemData.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(isEditDictItem ? '更新失败' : '添加失败');
}
};
// 删除字典项
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) {
try {
const list = await request('/dict/items', {
params: {
dictId: selectedDict.id,
},
});
// 确保list是数组再进行some操作
const dataList = Array.isArray(list) ? list : list?.data || [];
const exists = dataList.some((it: any) => it.id === itemId);
if (exists) {
message.error('删除失败');
} else {
message.success('删除成功');
actionRef.current?.reload();
}
} catch (error) {
console.error('验证删除结果失败:', error);
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;
try {
const res = await request('/dict/items', {
params: {
dictId: selectedDict.id,
name,
title,
},
});
// 确保返回的是数组
const data = Array.isArray(res) ? res : res?.data || [];
return {
data: data,
success: true,
};
} catch (error) {
console.error('获取字典项失败:', error);
return {
data: [],
success: false,
};
}
}}
rowKey="id"
search={{
layout: 'vertical',
}}
pagination={false}
options={{
reload: true,
density: false,
setting: {
draggable: true,
checkable: true,
checkedReset: false,
},
search: false,
fullScreen: false,
}}
size="small"
key={selectedDict?.id}
headerTitle={
<DictItemActions
selectedDict={selectedDict}
actionRef={actionRef}
showExport={true}
onImport={async (file: File, dictId: number) => {
// 创建 FormData 对象
const formData = new FormData();
// 添加文件到 FormData
formData.append('file', file);
// 添加字典 ID 到 FormData
formData.append('dictId', String(dictId));
// 调用导入字典项的 API
const response = await dictApi.dictcontrollerImportdictitems(
formData,
);
// 返回 JSON 响应
return await response.json();
}}
onExport={handleExportDictItems}
onAdd={handleAddDictItem}
onRefreshDicts={fetchDicts}
/>
}
/>
</Content>
</Layout>
{/* 字典项 Modal添加或编辑 */}
<DictItemModal
visible={isDictItemModalVisible}
isEdit={isEditDictItem}
editingData={editingDictItemData}
selectedDict={selectedDict}
onCancel={() => {
setIsDictItemModalVisible(false);
setEditingDictItemData(null);
}}
onOk={handleDictItemFormSubmit}
/>
</PageContainer>
);
};
export default AttributePage;