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.

186 lines
6.9 KiB

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