You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

174 lines
6.2 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. import { useTranslation } from '@/i18n.ts'
  2. import { PageContainer, ProCard } from '@ant-design/pro-components'
  3. import { Button, Form, Input, message, Radio, TreeSelect, InputNumber, notification } from 'antd'
  4. import { useAtomValue } from 'jotai'
  5. import { menuDataAtom, saveOrUpdateMenuAtom, selectedMenuAtom } from './store.ts'
  6. import IconPicker from '@/components/icon/picker'
  7. import ButtonTable from './components/ButtonTable.tsx'
  8. import { Flexbox } from 'react-layout-kit'
  9. import { DraggablePanel } from '@/components/draggable-panel'
  10. import { useStyle } from './style.ts'
  11. import { MenuItem } from '@/types'
  12. import MenuTree from './components/MenuTree.tsx'
  13. import BatchButton from '@/pages/system/menus/components/BatchButton.tsx'
  14. import { useEffect } from 'react'
  15. import { createLazyFileRoute } from '@tanstack/react-router'
  16. const Menus = () => {
  17. const { styles } = useStyle()
  18. const { t } = useTranslation()
  19. const [ form ] = Form.useForm()
  20. const { mutate, isPending, isSuccess, error, isError } = useAtomValue(saveOrUpdateMenuAtom)
  21. const { data = [] } = useAtomValue(menuDataAtom)
  22. const currentMenu = useAtomValue<MenuItem>(selectedMenuAtom) ?? {}
  23. useEffect(() => {
  24. if (isSuccess) {
  25. message.success(t('saveSuccess', '保存成功'))
  26. }
  27. if (isError) {
  28. notification.error({
  29. message: t('errorTitle', '错误'),
  30. description: (error as any).message ?? t('saveFail', '保存失败'),
  31. })
  32. }
  33. }, [ isError, isSuccess ])
  34. return (
  35. <PageContainer breadcrumbRender={false} title={false} className={styles.container}>
  36. <Flexbox horizontal>
  37. <DraggablePanel expandable={false}
  38. placement="left"
  39. defaultSize={{ width: 300 }}
  40. maxWidth={500}
  41. >
  42. <ProCard title={t('system.menus.title', '菜单')}
  43. extra={
  44. <BatchButton/>
  45. }
  46. >
  47. <MenuTree form={form}/>
  48. </ProCard>
  49. </DraggablePanel>
  50. <Flexbox className={styles.box}>
  51. <Form form={form}
  52. initialValues={currentMenu!}
  53. labelCol={{ flex: '110px' }}
  54. labelAlign="left"
  55. labelWrap
  56. wrapperCol={{ flex: 1 }}
  57. colon={false}
  58. className={styles.form}
  59. >
  60. <ProCard title={t('system.menus.setting', '配置')}
  61. className={styles.formSetting}
  62. >
  63. <Form.Item hidden={true} label={'id'} name={'id'}>
  64. <Input disabled={true}/>
  65. </Form.Item>
  66. <Form.Item label={t('system.menus.form.title', '菜单名称')} name={'title'}>
  67. <Input/>
  68. </Form.Item>
  69. <Form.Item label={t('system.menus.form.parent', '上级菜单')} name={'parent_id'}>
  70. <TreeSelect
  71. treeData={[
  72. { id: 0, title: '顶级菜单', children: data as any },
  73. ]}
  74. treeDefaultExpandAll={true}
  75. fieldNames={{
  76. label: 'title',
  77. value: 'id'
  78. }}/>
  79. </Form.Item>
  80. <Form.Item label={t('system.menus.form.type', '类型')} name={'type'}>
  81. <Radio.Group
  82. options={[
  83. {
  84. label: t('system.menus.form.typeOptions.menu', '菜单'),
  85. value: 'menu'
  86. },
  87. {
  88. label: t('system.menus.form.typeOptions.iframe', 'iframe'),
  89. value: 'iframe'
  90. },
  91. {
  92. label: t('system.menus.form.typeOptions.link', '外链'),
  93. value: 'link'
  94. },
  95. {
  96. label: t('system.menus.form.typeOptions.button', '按钮'),
  97. value: 'button'
  98. },
  99. ]}
  100. optionType="button"
  101. buttonStyle="solid"
  102. />
  103. </Form.Item>
  104. <Form.Item label={t('system.menus.form.name', '别名')} name={'name'}>
  105. <Input/>
  106. </Form.Item>
  107. <Form.Item label={t('system.menus.form.icon', '图标')} name={'icon'}>
  108. <IconPicker placement={'left'}/>
  109. </Form.Item>
  110. <Form.Item label={t('system.menus.form.sort', '排序')} name={'sort'}>
  111. <InputNumber/>
  112. </Form.Item>
  113. <Form.Item label={t('system.menus.form.path', '路由')} name={'path'}>
  114. <Input/>
  115. </Form.Item>
  116. <Form.Item label={t('system.menus.form.component', '视图')}
  117. name={'component'}
  118. help={t('system.menus.form.component.componentHelp', '视图路径,相对于src/pages')}
  119. >
  120. <Input addonBefore={'pages/'}/>
  121. </Form.Item>
  122. <Form.Item label={' '}>
  123. <Button type="primary"
  124. htmlType={'submit'}
  125. loading={isPending}
  126. onClick={() => {
  127. form.validateFields().then((values) => {
  128. mutate(values)
  129. })
  130. }}
  131. >
  132. {t('system.menus.form.save', '保存')}
  133. </Button>
  134. </Form.Item>
  135. </ProCard>
  136. <ProCard title={t('system.menus.form.button', '按钮')}
  137. className={styles.formButtons}
  138. colSpan={8}>
  139. <Form.Item noStyle={true} name={'button'}
  140. shouldUpdate={(prevValues: MenuItem, curValues) => {
  141. return prevValues.id !== curValues.id
  142. }}>
  143. <ButtonTable form={form} key={(currentMenu as any).id}/>
  144. </Form.Item>
  145. </ProCard>
  146. </Form>
  147. </Flexbox>
  148. </Flexbox>
  149. </PageContainer>
  150. )
  151. }
  152. export const Route = createLazyFileRoute('/system/menus')({
  153. component: Menus
  154. })
  155. export default Menus