feat: 系统管理
This commit is contained in:
187
src/pages/system/menu/icon.tsx
Normal file
187
src/pages/system/menu/icon.tsx
Normal file
@@ -0,0 +1,187 @@
|
||||
import * as IconPark from '@icon-park/react';
|
||||
|
||||
import { Input, List, Modal, Pagination } from 'antd';
|
||||
import { useState } from 'react';
|
||||
|
||||
const allIcons = Object.keys(IconPark);
|
||||
|
||||
// 动态构造 iconMap
|
||||
const iconMap: Record<string, React.ReactNode> = {};
|
||||
|
||||
allIcons.forEach((iconName) => {
|
||||
// 排除不需要的属性(如默认导出等)
|
||||
if (
|
||||
iconName !== 'default' &&
|
||||
typeof IconPark[iconName as keyof typeof IconPark] === 'function'
|
||||
) {
|
||||
const IconComponent = IconPark[
|
||||
iconName as keyof typeof IconPark
|
||||
] as React.ComponentType<any>;
|
||||
iconMap[iconName] = <IconComponent theme="outline" size="16" />;
|
||||
}
|
||||
});
|
||||
|
||||
// 图标选项列表
|
||||
const iconOptions = Object.keys(iconMap).map((key) => ({
|
||||
label: (
|
||||
<span>
|
||||
{iconMap[key]} {key}
|
||||
</span>
|
||||
),
|
||||
value: key,
|
||||
}));
|
||||
|
||||
interface IconSelectorProps {
|
||||
value?: string;
|
||||
onChange?: (value: string) => void;
|
||||
}
|
||||
|
||||
const IconSelector: React.FC<IconSelectorProps> = ({ value, onChange }) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [searchValue, setSearchValue] = useState('');
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const pageSize = 48;
|
||||
|
||||
// 过滤图标
|
||||
const filteredIcons = iconOptions.filter(
|
||||
(option) =>
|
||||
option.value.toLowerCase().includes(searchValue.toLowerCase()) ||
|
||||
option.value.toLowerCase().includes(searchValue.toLowerCase()),
|
||||
);
|
||||
|
||||
// 分页数据
|
||||
const paginatedIcons = filteredIcons.slice(
|
||||
(currentPage - 1) * pageSize,
|
||||
currentPage * pageSize,
|
||||
);
|
||||
|
||||
const handleSelect = (iconName: string) => {
|
||||
onChange?.(iconName);
|
||||
setOpen(false);
|
||||
setCurrentPage(1);
|
||||
setSearchValue('');
|
||||
};
|
||||
|
||||
const handleClear = () => {
|
||||
onChange?.('');
|
||||
setOpen(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div
|
||||
onClick={() => setOpen(true)}
|
||||
style={{
|
||||
border: '1px solid #d9d9d9',
|
||||
borderRadius: 6,
|
||||
padding: '4px 11px',
|
||||
cursor: 'pointer',
|
||||
minHeight: 32,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{value ? (
|
||||
<span>
|
||||
{iconMap[value as keyof typeof iconMap]}
|
||||
<span style={{ marginLeft: 8 }}>{value}</span>
|
||||
</span>
|
||||
) : (
|
||||
<span style={{ color: '#bfbfbf' }}>请选择图标</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Modal
|
||||
title="选择图标"
|
||||
open={open}
|
||||
onCancel={() => {
|
||||
setOpen(false);
|
||||
setCurrentPage(1);
|
||||
setSearchValue('');
|
||||
}}
|
||||
onOk={() => setOpen(false)}
|
||||
width={800}
|
||||
styles={{ body: { padding: 0 } }}
|
||||
okText="确定"
|
||||
cancelText="取消"
|
||||
>
|
||||
<div style={{ padding: 16 }}>
|
||||
<Input.Search
|
||||
placeholder="搜索图标..."
|
||||
value={searchValue}
|
||||
onChange={(e) => {
|
||||
setSearchValue(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
style={{ marginBottom: 16 }}
|
||||
/>
|
||||
|
||||
{filteredIcons.length === 0 ? (
|
||||
<IconPark.Empty title="未找到相关图标" />
|
||||
) : (
|
||||
<>
|
||||
<List
|
||||
grid={{ gutter: 16, column: 8 }}
|
||||
dataSource={paginatedIcons}
|
||||
renderItem={(item) => (
|
||||
<List.Item
|
||||
onClick={() => handleSelect(item.value)}
|
||||
style={{
|
||||
cursor: 'pointer',
|
||||
textAlign: 'center',
|
||||
border:
|
||||
value === item.value
|
||||
? '1px solid #1890ff'
|
||||
: '1px solid transparent',
|
||||
borderRadius: 6,
|
||||
padding: 8,
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
<div style={{ fontSize: 20, marginBottom: 4 }}>
|
||||
{iconMap[item.value as keyof typeof iconMap]}
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
fontSize: 12,
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
}}
|
||||
>
|
||||
{item.value}
|
||||
</div>
|
||||
</div>
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
marginTop: 16,
|
||||
}}
|
||||
>
|
||||
<Pagination
|
||||
current={currentPage}
|
||||
pageSize={pageSize}
|
||||
total={filteredIcons.length}
|
||||
onChange={setCurrentPage}
|
||||
showSizeChanger={false}
|
||||
size="small"
|
||||
/>
|
||||
<div>
|
||||
<a onClick={handleClear} style={{ marginRight: 16 }}>
|
||||
清除选择
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default IconSelector;
|
||||
Reference in New Issue
Block a user