|
|
import Glass from '@/components/glass' import { useTranslation } from '@/i18n.ts'
import { PlusOutlined } from '@ant-design/icons' import { PageContainer, ProCard } from '@ant-design/pro-components' import { Button, Form, Input, Radio, TreeSelect, InputNumber, notification, Alert, InputRef, Divider } from 'antd' import { useAtom, useAtomValue } from 'jotai' import { defaultMenu, menuDataAtom, saveOrUpdateMenuAtom, selectedMenuAtom } from '@/store/menu.ts' import IconPicker from '@/components/icon/picker' import ButtonTable from './components/ButtonTable.tsx' import { Flexbox } from 'react-layout-kit' import { DraggablePanel } from '@/components/draggable-panel' import { useStyle } from './style.ts' import { MenuItem } from '@/global' import MenuTree from './components/MenuTree.tsx' import BatchButton from '@/pages/system/menus/components/BatchButton.tsx' import { useEffect, useRef } from 'react'
const Menus = () => {
const { styles, cx } = useStyle() const { t } = useTranslation() const [ form ] = Form.useForm() const { mutate, isPending, error, isError } = useAtomValue(saveOrUpdateMenuAtom) const { data = [] } = useAtomValue(menuDataAtom) const [ currentMenu, setMenuData ] = useAtom<MenuItem>(selectedMenuAtom) ?? {} const menuInputRef = useRef<InputRef | undefined>(undefined)
useEffect(() => {
if (isError) { notification.error({ message: t('message.error', '错误'), description: (error as any).message ?? t('message.saveFail', '保存失败'), }) } }, [ isError ])
useEffect(() => { if (currentMenu.id === 0 && menuInputRef.current) { menuInputRef.current.focus() } }, [ currentMenu ])
return ( <PageContainer breadcrumbRender={false} title={false} className={styles.container}>
<Flexbox horizontal> <DraggablePanel expandable={false} placement="left" defaultSize={{ width: 300 }} maxWidth={500} style={{ position: 'relative' }} > <ProCard title={t('system.menus.title', '菜单')} extra={ <> <BatchButton/> </> } > <MenuTree form={form}/>
</ProCard> <div className={styles.treeActions}> <Divider style={{ flex: 1, margin: '8px 0' }}/> <Button style={{ flex: 1 }} size={'small'} block={true} type={'dashed'} icon={<PlusOutlined/>} onClick={() => { const menu = { ...defaultMenu, parent_id: currentMenu.id ?? 0, } setMenuData(menu) form.setFieldsValue(menu) }} >{t('actions.news')}</Button> </div> </DraggablePanel> <Flexbox className={styles.box}> <Glass enabled={currentMenu.id === undefined} description={<> <Alert message={t('message.infoTitle', '提示')} description={t('system.menus.form.empty', '请从左侧选择一行数据操作')} type="info" /> </>} > <Form form={form} initialValues={currentMenu!} labelCol={{ flex: '110px' }} labelAlign="left" labelWrap wrapperCol={{ flex: 1 }} colon={false} className={cx(styles.form, { [styles.emptyForm]: currentMenu.id === undefined })} >
<ProCard title={t('system.menus.setting', '配置')} className={styles.formSetting} >
<Form.Item hidden={true} label={'id'} name={'id'}> <Input disabled={true}/> </Form.Item> <Form.Item rules={[ { required: true, message: t('rules.required') } ]} label={t('system.menus.form.title', '菜单名称')} name={'title'}> <Input ref={menuInputRef as any} placeholder={t('system.menus.form.title', '菜单名称')}/> </Form.Item> <Form.Item label={t('system.menus.form.parent', '上级菜单')} name={'parent_id'}> <TreeSelect treeData={[ { id: 0, title: '顶级菜单', children: data as any }, ]} treeDefaultExpandAll={true} fieldNames={{ label: 'title', value: 'id' }}/> </Form.Item> <Form.Item label={t('system.menus.form.type', '类型')} name={'type'}> <Radio.Group options={[ { label: t('system.menus.form.typeOptions.menu', '菜单'), value: 'menu' }, { label: t('system.menus.form.typeOptions.iframe', 'iframe'), value: 'iframe' }, { label: t('system.menus.form.typeOptions.link', '外链'), value: 'link' }, { label: t('system.menus.form.typeOptions.button', '按钮'), value: 'button' }, ]} optionType="button" buttonStyle="solid" /> </Form.Item> <Form.Item rules={[ { required: true, message: t('rules.required') } ]} label={t('system.menus.form.name', '别名')} name={'name'}> <Input placeholder={t('system.menus.form.name', '别名')}/> </Form.Item> <Form.Item label={t('system.menus.form.icon', '图标')} name={'icon'}> <IconPicker placement={'left'}/> </Form.Item> <Form.Item label={t('system.menus.form.sort', '排序')} name={'sort'}> <InputNumber/> </Form.Item> <Form.Item label={t('system.menus.form.path', '路由')} name={'path'}> <Input/> </Form.Item>
<Form.Item label={t('system.menus.form.component', '视图')} name={'component'} help={t('system.menus.form.componentHelp', '视图路径,相对于src/pages')} > <Input addonBefore={'pages/'}/> </Form.Item> <Form.Item label={' '}> <Button type="primary" htmlType={'submit'} loading={isPending} onClick={() => { form.validateFields().then((values) => { mutate(values) }) }} > {t('system.menus.form.save', '保存')} </Button> </Form.Item>
</ProCard> <ProCard title={t('system.menus.button', '按钮')} className={styles.formButtons} colSpan={8}> <Form.Item noStyle={true} name={'button'} shouldUpdate={(prevValues: MenuItem, curValues) => { return prevValues.id !== curValues.id }}> <ButtonTable form={form} key={(currentMenu as any).id}/> </Form.Item>
</ProCard> </Form> </Glass> </Flexbox> </Flexbox> </PageContainer> ) }
export default Menus
|