index.tsx 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import { Button } from 'antd'
  2. import LeftMenu from './Components/LeftMenu'
  3. import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'
  4. import { arrayMoveImmutable } from 'array-move'
  5. import type { ColumnsType } from 'antd/lib/table'
  6. import type { SortableContainerProps, SortEnd } from 'react-sortable-hoc'
  7. import { useRef, useState } from 'react'
  8. import { useRequest } from 'umi'
  9. import type { ProFormInstance } from '@ant-design/pro-form'
  10. import {
  11. addDataSource,
  12. addDataSourceItem,
  13. queryDataSource,
  14. updateDataSourceItem
  15. } from '@/services/api/schema'
  16. import { MenuOutlined, PlusOutlined } from '@ant-design/icons'
  17. import classNames from 'classnames'
  18. import { ModalForm, ProFormRadio, ProFormText } from '@ant-design/pro-form'
  19. import ProTable from '@ant-design/pro-table'
  20. import { ModalType } from '@/utils/enum'
  21. const DragHandle = SortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />)
  22. const SortableItem = SortableElement((props: React.HTMLAttributes<HTMLTableRowElement>) => (
  23. <tr {...props} />
  24. ))
  25. const SortableBody = SortableContainer((props: React.HTMLAttributes<HTMLTableSectionElement>) => (
  26. <tbody {...props} />
  27. ))
  28. type iState = {
  29. menuData: API.DataSourceMenuItem[]
  30. activeID: Nullable<string>
  31. modalType: ModalType
  32. menuDataItems: API.DataSourceItem[]
  33. }
  34. const Option = () => {
  35. const formRef = useRef<ProFormInstance>(null)
  36. const [state, setState] = useState<iState>({
  37. menuData: [],
  38. activeID: null,
  39. modalType: ModalType.ADD,
  40. menuDataItems: []
  41. })
  42. const { run: tryFetchList } = useRequest(queryDataSource, {
  43. onSuccess: (result: API.DataSourceMenuItem) => {
  44. setState({ ...state, menuData: result })
  45. }
  46. })
  47. const { run: tryAddDataSourceItem } = useRequest(
  48. (params: API.LinkAccountParams) => addDataSourceItem(params),
  49. {
  50. manual: true,
  51. onSuccess: async () => {
  52. await tryFetchList()
  53. }
  54. }
  55. )
  56. const { run: tryUpdateItem } = useRequest(updateDataSourceItem, {
  57. manual: true,
  58. onSuccess: () => tryFetchList()
  59. })
  60. const columns: ColumnsType<API.DataSourceItem> = [
  61. {
  62. title: '排序',
  63. dataIndex: 'sort',
  64. width: 50,
  65. render: () => <DragHandle />
  66. },
  67. {
  68. title: '序号',
  69. dataIndex: 'num',
  70. width: 50,
  71. render: (num, record, index) => `${index + 1}`
  72. },
  73. {
  74. title: '选项名称',
  75. dataIndex: 'name'
  76. },
  77. {
  78. title: '状态',
  79. dataIndex: 'enable',
  80. render: text => (
  81. <div>
  82. <span
  83. className={classNames('w-4 h-4 rounded-1/2', text ? 'bg-green-500' : 'bg-red-500')}
  84. />
  85. <span className="ml-1">{text ? '已启用' : '已停用'}</span>
  86. </div>
  87. )
  88. },
  89. {
  90. title: '操作',
  91. dataIndex: 'opreate',
  92. render: (_, record) => (
  93. <Button
  94. type="text"
  95. onClick={() => {
  96. formRef.current?.setFieldsValue({ ...record })
  97. setState({ ...state, modalType: ModalType.UPDATE })
  98. }}>
  99. 编辑
  100. </Button>
  101. )
  102. }
  103. ]
  104. const onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
  105. if (oldIndex !== newIndex) {
  106. const newData = arrayMoveImmutable(dataSource.slice(), oldIndex, newIndex).filter(
  107. (el: DataType) => !!el
  108. )
  109. const newMenuData = menuData.map(item => {
  110. const newItem = { ...item }
  111. if (item.ID === state.activeID) {
  112. newItem.items = newData
  113. return newItem
  114. }
  115. return item
  116. })
  117. setState({ ...state, menuData: newMenuData })
  118. }
  119. }
  120. const DraggableContainer = (props: SortableContainerProps) => (
  121. <SortableBody
  122. useDragHandle
  123. disableAutoscroll
  124. helperClass="row-dragging"
  125. onSortEnd={onSortEnd}
  126. {...props}
  127. />
  128. )
  129. const DraggableBodyRow = ({ className, style, ...restProps }) => {
  130. const index = state.menuData
  131. .find(item => item.ID === state.activeID)
  132. ?.items.findIndex(x => x.index === restProps['data-row-key'])
  133. return <SortableItem index={index} {...restProps} />
  134. }
  135. return (
  136. <div className="flex flex-nowrap h-full">
  137. <LeftMenu
  138. onSelect={(key: string, node) =>
  139. setState({ ...state, activeID: key, menuDataItems: node.node.items })
  140. }
  141. showDelIcon={!state.menuDataItems.length}
  142. options={state.menuData}
  143. initFn={() => tryFetchList()}
  144. />
  145. <div className="ml-6 shadow-hex-3e2c5a" style={{ width: 'calc(100% - 12rem)' }}>
  146. <ProTable<API.DataSourceItem>
  147. columns={columns}
  148. search={false}
  149. dataSource={state.menuData.find(item => item.ID === state.activeID)?.items || []}
  150. rowKey="ID"
  151. toolbar={{
  152. actions: [
  153. <ModalForm
  154. labelCol={{ span: 6 }}
  155. key="form"
  156. width="30%"
  157. title="添加选项"
  158. formRef={formRef}
  159. layout="horizontal"
  160. onVisibleChange={visible => !visible && formRef.current?.resetFields()}
  161. initialValues={{ enable: true }}
  162. trigger={
  163. <Button size="small" type="primary" ghost>
  164. <PlusOutlined />
  165. 添加选项
  166. </Button>
  167. }
  168. onFinish={async values => {
  169. await tryAddDataSourceItem({
  170. ...values,
  171. dataSourceID: state.activeID
  172. })
  173. message.success('添加成功')
  174. return true
  175. }}>
  176. <ProFormText
  177. name="name"
  178. label="选项名称"
  179. rules={[{ required: true, message: '请输入选项名称' }]}
  180. />
  181. <ProFormRadio.Group
  182. name="enable"
  183. label="状态"
  184. options={[
  185. { label: '启用', value: true },
  186. { label: '停用', value: false }
  187. ]}
  188. rules={[{ required: true, message: '请选择启用/停用' }]}
  189. />
  190. </ModalForm>
  191. ]
  192. }}
  193. components={{
  194. body: {
  195. wrapper: DraggableContainer,
  196. row: DraggableBodyRow
  197. }
  198. }}
  199. />
  200. </div>
  201. </div>
  202. )
  203. }
  204. export default Option