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.

243 lines
7.2 KiB

1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
  1. import Switch from '@/components/switch'
  2. import { usePageStoreOptions } from '@/store'
  3. import { IMenu } from '@/types/menus'
  4. import {
  5. ActionType,
  6. PageContainer,
  7. ProColumns,
  8. ProTable,
  9. BetaSchemaForm, ProFormColumnsType,
  10. } from '@ant-design/pro-components'
  11. import { useStyle } from './style.ts'
  12. import { memo, useEffect, useMemo, useRef, useState } from 'react'
  13. import { useAtom, useAtomValue } from 'jotai'
  14. import {
  15. deleteRoleAtom,
  16. pageAtom,
  17. roleAtom,
  18. roleIdsAtom,
  19. rolesAtom,
  20. saveOrUpdateRoleAtom,
  21. searchAtom
  22. } from '@/store/role.ts'
  23. import { useTranslation } from '@/i18n.ts'
  24. import { Button, Form, Space, Spin, Table, Tree, Popconfirm } from 'antd'
  25. import { PlusOutlined } from '@ant-design/icons'
  26. import { menuDataAtom } from '@/store/menu.ts'
  27. import { getTreeCheckedStatus } from '@/utils/tree.ts'
  28. const MenuTree = (props: any) => {
  29. const { data: menuList, isLoading: menuLoading } = useAtomValue(menuDataAtom)
  30. const { value, onChange, form, id, mode } = props
  31. const onCheck = (checkedKeys: any, info: any) => {
  32. if (onChange) {
  33. onChange([ ...checkedKeys, ...info.halfCheckedKeys ])
  34. } else {
  35. form.setFieldsValue({ [id]: [ ...checkedKeys, ...info.halfCheckedKeys ] })
  36. }
  37. }
  38. if (menuLoading) {
  39. return <Spin spinning={true} size={'small'}/>
  40. }
  41. return <Tree treeData={menuList}
  42. fieldNames={{ title: 'title', key: 'id' }}
  43. disabled={mode !== 'edit'} checkable={true} onCheck={onCheck}
  44. checkedKeys={getTreeCheckedStatus<IMenu>(menuList!, value)}/>
  45. }
  46. const Roles = memo(() => {
  47. const { t } = useTranslation()
  48. const { styles } = useStyle()
  49. const [ form ] = Form.useForm()
  50. const actionRef = useRef<ActionType>()
  51. const [ page, setPage ] = useAtom(pageAtom, usePageStoreOptions())
  52. const [ search, setSearch ] = useAtom(searchAtom, usePageStoreOptions())
  53. const [ roleIds, setRoleIds ] = useAtom(roleIdsAtom, usePageStoreOptions())
  54. const { data, isLoading, isFetching, refetch } = useAtomValue(rolesAtom, usePageStoreOptions())
  55. const { isPending, mutate, isSuccess } = useAtomValue(saveOrUpdateRoleAtom, usePageStoreOptions())
  56. const { mutate: deleteRole, isPending: isDeleting } = useAtomValue(deleteRoleAtom, usePageStoreOptions())
  57. const [ , setRole ] = useAtom(roleAtom, usePageStoreOptions())
  58. const [ open, setOpen ] = useState(false)
  59. const columns = useMemo(() => {
  60. return [
  61. {
  62. title: 'id', dataIndex: 'id',
  63. hideInTable: true,
  64. hideInSearch: true,
  65. formItemProps: {
  66. hidden: true
  67. }
  68. },
  69. {
  70. title: t('system.roles.columns.name'), dataIndex: 'name', valueType: 'text',
  71. formItemProps: {
  72. rules: [ { required: true, message: t('message.required') } ]
  73. }
  74. },
  75. {
  76. title: t('system.roles.columns.code'), dataIndex: 'code', valueType: 'text',
  77. formItemProps: {
  78. rules: [ { required: true, message: t('message.required') } ]
  79. }
  80. },
  81. {
  82. title: t('system.roles.columns.status'), dataIndex: 'status', valueType: 'switch',
  83. render: (text) => {
  84. return <Switch value={!!text} size={'small'}/>
  85. }
  86. },
  87. {
  88. title: t('system.roles.columns.sort'), dataIndex: 'sort', valueType: 'digit',
  89. },
  90. { title: t('system.roles.columns.description'), dataIndex: 'description', valueType: 'textarea' },
  91. {
  92. title: t('system.roles.columns.menu_ids'),
  93. hideInTable: true,
  94. hideInSearch: true,
  95. dataIndex: 'menu_ids',
  96. valueType: 'text',
  97. renderFormItem: (item, config, form) => {
  98. return <MenuTree {...config} form={form} {...item.fieldProps} />
  99. }
  100. },
  101. {
  102. title: t('system.roles.columns.option'), valueType: 'option',
  103. key: 'option',
  104. render: (_, record) => [
  105. <a key="editable"
  106. onClick={() => {
  107. setRole(record)
  108. setOpen(true)
  109. form.setFieldsValue(record)
  110. }}
  111. >
  112. {t('actions.edit', '编辑')}
  113. </a>,
  114. <Popconfirm
  115. key={'del_confirm'}
  116. onConfirm={() => {
  117. deleteRole([ record.id ])
  118. }}
  119. title={t('message.deleteConfirm')}>
  120. <a key="del">
  121. {t('actions.delete', '删除')}
  122. </a>
  123. </Popconfirm>
  124. ,
  125. ],
  126. },
  127. ] as ProColumns[]
  128. }, [])
  129. useEffect(() => {
  130. if (isSuccess) {
  131. setOpen(false)
  132. }
  133. }, [ isSuccess ])
  134. return (
  135. <PageContainer breadcrumbRender={false} title={false} className={styles.container}>
  136. <ProTable
  137. rowKey={'id'}
  138. actionRef={actionRef}
  139. headerTitle={t('system.roles.title', '角色管理')}
  140. columns={columns}
  141. loading={isLoading || isFetching}
  142. dataSource={data?.rows}
  143. search={false}
  144. rowSelection={{
  145. onChange: (selectedRowKeys) => {
  146. setRoleIds(selectedRowKeys as number[])
  147. },
  148. selectedRowKeys: roleIds,
  149. selections: [ Table.SELECTION_ALL, Table.SELECTION_INVERT ],
  150. }}
  151. tableAlertOptionRender={() => {
  152. return (
  153. <Space size={16}>
  154. <Popconfirm
  155. onConfirm={() => {
  156. deleteRole(roleIds)
  157. }}
  158. title={t('message.batchDelete')}>
  159. <Button type={'link'} loading={isDeleting}>{t('actions.batchDel')}</Button>
  160. </Popconfirm>
  161. </Space>
  162. )
  163. }}
  164. options={{
  165. reload: () => {
  166. refetch()
  167. },
  168. }}
  169. toolbar={{
  170. search: {
  171. loading: isFetching && !!search.key,
  172. onSearch: (value: string) => {
  173. setSearch({ key: value })
  174. },
  175. placeholder: t('system.roles.search.placeholder')
  176. },
  177. actions: [
  178. <Button
  179. key="button"
  180. icon={<PlusOutlined/>}
  181. onClick={() => {
  182. form.resetFields()
  183. form.setFieldsValue({
  184. id: 0,
  185. })
  186. setOpen(true)
  187. }}
  188. type="primary"
  189. >
  190. {t('actions.add', '添加')}
  191. </Button>,
  192. ]
  193. }}
  194. pagination={{
  195. total: data?.total,
  196. current: page.page,
  197. pageSize: page.pageSize,
  198. onChange: (page) => {
  199. setPage((prev) => {
  200. return { ...prev, page }
  201. })
  202. }
  203. }}
  204. />
  205. <BetaSchemaForm
  206. width={600}
  207. form={form}
  208. layout={'horizontal'}
  209. title={t('system.roles.edit.title', '角色编辑')}
  210. colProps={{ span: 24 }}
  211. labelCol={{ span: 6 }}
  212. wrapperCol={{ span: 16 }}
  213. layoutType={'ModalForm'}
  214. open={open}
  215. modalProps={{
  216. maskClosable: false,
  217. }}
  218. onOpenChange={(open) => {
  219. setOpen(open)
  220. }}
  221. loading={isPending}
  222. onFinish={async (values) => {
  223. // console.log('values', values)
  224. mutate(values)
  225. return true
  226. }}
  227. columns={columns as ProFormColumnsType[]}/>
  228. </PageContainer>
  229. )
  230. })
  231. export default Roles