|
|
@@ -1,11 +1,70 @@ |
|
|
|
import React, { useEffect, useState } from 'react' |
|
|
|
import { CloseOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons'; |
|
|
|
import { TreeSelect, Form, Input, InputNumber, Radio, Modal, Switch } from 'antd' |
|
|
|
import React, { lazy, useEffect, useState } from 'react' |
|
|
|
import { TreeSelect, Form, Input, InputNumber, Radio, Modal, Switch, Popover, Button, Space, List } from 'antd'; |
|
|
|
import { ProList } from '@ant-design/pro-components'; |
|
|
|
import menuService from '@/request/service/menu'; |
|
|
|
import { useRequest } from '@/hooks/use-request'; |
|
|
|
import type { DefaultOptionType } from 'antd/es/select'; |
|
|
|
import type { Menu } from '@/models' |
|
|
|
import { antdUtils } from '@/utils/antd'; |
|
|
|
import * as Icon from "@ant-design/icons"; |
|
|
|
// 编写生成ReactNode的方法 |
|
|
|
const icon2Element = (name: string) => { |
|
|
|
return <React.Suspense fallback={<></>}> |
|
|
|
{React.createElement((Icon as any)[name], { |
|
|
|
style: { fontSize: '16px' } |
|
|
|
})} |
|
|
|
</React.Suspense> |
|
|
|
} |
|
|
|
|
|
|
|
interface MenuIconProps { |
|
|
|
value?: string; |
|
|
|
onChange?: (value: string) => void; |
|
|
|
} |
|
|
|
|
|
|
|
const MenuIcon: React.FC<MenuIconProps> = ({ value = '', onChange }) => { |
|
|
|
const [hovered, setHovered] = useState(false); |
|
|
|
const icons: string[] = []; |
|
|
|
for (const i in Icon) { |
|
|
|
if (i != 'default') icons.push(i) |
|
|
|
} |
|
|
|
const girdItems = icons.filter(i => i.endsWith('Outlined')) |
|
|
|
if (value === '' || girdItems.indexOf(value) < 0) { |
|
|
|
value = 'MenuUnfoldOutlined' |
|
|
|
} |
|
|
|
const triggerChange = (changedValue: string) => { |
|
|
|
onChange?.(changedValue); |
|
|
|
setHovered(false) |
|
|
|
}; |
|
|
|
const renderIcons = () => { |
|
|
|
return (<List |
|
|
|
className='w-[400px]' |
|
|
|
grid={{ column: 10 }} |
|
|
|
dataSource={girdItems} |
|
|
|
pagination={{ |
|
|
|
defaultPageSize: 80, |
|
|
|
showSizeChanger: false, |
|
|
|
}} |
|
|
|
size='small' |
|
|
|
renderItem={(item) => ( |
|
|
|
<List.Item onClick={() => { triggerChange(item) }}> |
|
|
|
<div className='w-[16px]'>{icon2Element(item)}</div> |
|
|
|
</List.Item> |
|
|
|
)} |
|
|
|
/>) |
|
|
|
} |
|
|
|
|
|
|
|
return <Popover placement="bottom" |
|
|
|
content={renderIcons} |
|
|
|
open={hovered} |
|
|
|
trigger="click"> |
|
|
|
<Space> |
|
|
|
<Input value={value} /> |
|
|
|
{icon2Element(value)} |
|
|
|
</Space> |
|
|
|
</Popover> |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const layout = { |
|
|
|
labelCol: { span: 4, }, |
|
|
@@ -29,6 +88,8 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { |
|
|
|
const { runAsync: updateMenu } = useRequest(menuService.updateMenu, { manual: true }); |
|
|
|
const { runAsync: createMenu } = useRequest(menuService.createMenu, { manual: true }); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isEdit = !!editData; |
|
|
|
|
|
|
|
const menu2Tree = (menu: Menu) => { |
|
|
@@ -66,28 +127,29 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { |
|
|
|
const save = async () => { |
|
|
|
setSaveLoading(true); |
|
|
|
const menuValues = form.getFieldsValue(); |
|
|
|
if(typeof(menuValues.status) === 'boolean') { |
|
|
|
menuValues.status = menuValues.status? 0 : 1 |
|
|
|
if (typeof (menuValues.status) === 'boolean') { |
|
|
|
menuValues.status = menuValues.status ? 0 : 1 |
|
|
|
} |
|
|
|
const newValue = isEdit? {...editData, ...menuValues} : { |
|
|
|
const newValue = isEdit ? { ...editData, ...menuValues } : { |
|
|
|
permission: '', |
|
|
|
icon: '', |
|
|
|
keepAlive:true, |
|
|
|
keepAlive: true, |
|
|
|
createTime: new Date(), |
|
|
|
...menuValues, |
|
|
|
} |
|
|
|
const [error, { msg, code }] = isEdit? await updateMenu(newValue): await createMenu(newValue); |
|
|
|
if(!error && code === 0) { |
|
|
|
const [error, { msg, code }] = isEdit ? await updateMenu(newValue) : await createMenu(newValue); |
|
|
|
if (!error && code === 0) { |
|
|
|
onSave(newValue); |
|
|
|
} else { |
|
|
|
antdUtils.message?.open({ |
|
|
|
type: 'error', |
|
|
|
content: msg??'操作失败', |
|
|
|
content: msg ?? '操作失败', |
|
|
|
}); |
|
|
|
} |
|
|
|
setSaveLoading(false); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return ( |
|
|
|
<> |
|
|
|
<Modal |
|
|
@@ -130,7 +192,8 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { |
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
<Form.Item name="icon" label="菜单图标" > |
|
|
|
<Input /> |
|
|
|
<MenuIcon /> |
|
|
|
|
|
|
|
</Form.Item> |
|
|
|
|
|
|
|
<Form.Item name="path" label="路由地址" |
|
|
@@ -159,11 +222,11 @@ const MenuEditor: React.FC<MenuEditorProps> = (props) => { |
|
|
|
// return { checked } |
|
|
|
// }} |
|
|
|
valuePropName="checked" |
|
|
|
// normalize={(value: boolean) => { |
|
|
|
// // prevValues.status = value ? 0 : 1 |
|
|
|
// return value ? 0 : 1 |
|
|
|
// }} |
|
|
|
> |
|
|
|
// normalize={(value: boolean) => { |
|
|
|
// // prevValues.status = value ? 0 : 1 |
|
|
|
// return value ? 0 : 1 |
|
|
|
// }} |
|
|
|
> |
|
|
|
<Switch checkedChildren="开启" unCheckedChildren="关闭" /> |
|
|
|
</Form.Item> |
|
|
|
|
|
|
|