123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320 |
- import { Button, message, Tooltip } from 'antd'
- import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'
- import { arrayMoveImmutable } from 'array-move'
- import { useEffect, useRef, useState } from 'react'
- import { useRequest } from '@umijs/max'
- import { addDataSourceItem, queryDataSource, updateDataSourceItem } from '@/services/api/schema'
- import { CopyOutlined, MenuOutlined, PlusOutlined } from '@ant-design/icons'
- import { CopyToClipboard } from 'react-copy-to-clipboard'
- import { isEmpty, isNullOrUnDef } from '@/utils/is'
- import { ModalForm, ProFormRadio, ProFormText } from '@ant-design/pro-form'
- import { ModalType } from '@/utils/enum'
- import { PageContainer } from '@ant-design/pro-layout'
- import ProTable from '@ant-design/pro-table'
- import LeftMenu from './components/LeftMenu'
- import classNames from 'classnames'
- import type { ProFormInstance } from '@ant-design/pro-form'
- import type { ColumnsType } from 'antd/lib/table'
- import type { SortableContainerProps, SortEnd } from 'react-sortable-hoc'
- import './index.less'
- enum OptionModalType {
- ADD,
- UPDATE
- }
- const DragHandle = SortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />)
- const SortableItem = SortableElement((props: React.HTMLAttributes<HTMLTableRowElement>) => <tr {...props} />)
- const SortableBody = SortableContainer((props: React.HTMLAttributes<HTMLTableSectionElement>) => (
- <tbody {...props} />
- ))
- type iState = {
- menuData: API.DataSourceMenuItem[]
- activeID: Nullable<string>
- menuDataTitle: Nullable<string>
- current: NonNullable<API.DataSourceItem>
- modalType: ModalType
- menuDataItems?: API.DataSourceItem[]
- }
- const Option = () => {
- const formRef = useRef<ProFormInstance>(null)
- const [state, setState] = useState<iState>({
- menuData: [],
- activeID: null,
- modalVisible: false,
- modalType: OptionModalType.ADD,
- menuDataItems: [],
- current: null,
- menuDataTitle: null,
- menuTitleUrl: null
- })
- const { run: tryFetchList } = useRequest(queryDataSource, {
- onSuccess: (result: API.DataSourceMenuItem) => {
- setState({
- ...state,
- menuData: result
- })
- }
- })
- const { run: tryAddDataSourceItem } = useRequest(
- (params: API.DataSourceItem) => addDataSourceItem(params),
- {
- manual: true,
- onSuccess: async () => {
- await tryFetchList()
- }
- }
- )
- const { run: tryUpdateDataSourceItem } = useRequest(
- (params: API.DataSourceItem) => updateDataSourceItem(params),
- {
- manual: true,
- onSuccess: async () => {
- await tryFetchList()
- }
- }
- )
- const onSelect = (key: string, node) => {
- setState({ ...state, activeID: key, menuDataItems: node.node.items })
- }
- const columns: ColumnsType<API.DataSourceItem> = [
- {
- title: '排序',
- dataIndex: 'sort',
- width: 50,
- render: () => <DragHandle />
- },
- {
- title: '序号',
- dataIndex: 'num',
- width: 50,
- render: (num, record, index) => `${index + 1}`
- },
- {
- title: '选项名称',
- dataIndex: 'name'
- },
- {
- title: '状态',
- dataIndex: 'enable',
- render: text => (
- <div className="flex items-center">
- <span
- className={classNames('w-3 h-3 rounded-1/2 inline-flex', text ? 'bg-green-500' : 'bg-red-500')}
- />
- <span className="ml-1">{text ? '已启用' : '已停用'}</span>
- </div>
- )
- },
- {
- title: '操作',
- dataIndex: 'opreate',
- render: (_, record) => (
- <div
- className="text-primary cursor-pointer hover:text-hex-967bbd"
- onClick={() => {
- setTimeout(() => {
- formRef.current?.setFieldsValue({ ...record })
- }, 80)
- setState({
- ...state,
- modalType: ModalType.UPDATE,
- modalVisible: true,
- current: {
- ID: record.ID,
- name: record.name,
- enable: record.enable
- }
- })
- }}>
- 编辑
- </div>
- )
- }
- ]
- const onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
- const { menuDataItems } = state
- if (oldIndex !== newIndex) {
- const newData = arrayMoveImmutable([].concat(menuDataItems), oldIndex, newIndex).filter(
- (el: DataType) => !!el
- )
- setState({ ...state, menuDataItems: newData })
- const oldIndexItem = menuDataItems?.find(item => item.index === oldIndex)
- const newIndexItem = menuDataItems?.find(item => item.index === newIndex)
- if (oldIndexItem && newIndexItem) {
- tryUpdateDataSourceItem({ ID: state.activeID, items: newData })
- message.success('编辑成功')
- }
- }
- }
- const DraggableContainer = (props: SortableContainerProps) => (
- <SortableBody
- useDragHandle
- disableAutoscroll
- helperClass="row-dragging"
- onSortEnd={onSortEnd}
- {...props}
- />
- )
- useEffect(() => {
- if (state.menuData.length) {
- setState({
- ...state,
- activeID: state.menuData[0].ID
- })
- }
- const menuTitleCopyUrl = `/api/form/v1/ds/items?name=${
- state.menuData.find(i => i.ID === state.activeID)?.name
- }`
- if (isNullOrUnDef(menuTitleCopyUrl) || isEmpty(menuTitleCopyUrl)) return null
- if (state.activeID) {
- setState({
- ...state,
- menuDataTitle: state.menuData.find(i => i.ID === state.activeID)?.name + `:` + menuTitleCopyUrl,
- menuTitleUrl: menuTitleCopyUrl,
- menuDataItems: state.menuData.find(item => item.ID === state.activeID)?.items
- })
- }
- }, [state.activeID, state.menuData])
- const DraggableBodyRow = ({ ...restProps }) => {
- if (state.menuDataItems?.length) {
- const index = state.menuDataItems?.findIndex(x => x.index === restProps['data-row-key'])
- return <SortableItem index={index} {...restProps} />
- }
- return <SortableItem index={0} {...restProps} />
- }
- const wrapHeight = document.querySelector('.ant-pro-page-container-warp')?.clientHeight || 0
- return (
- <PageContainer title={false}>
- <div className="h-full w-full flex flex-row">
- <LeftMenu
- onSelect={onSelect}
- defaultActiveID={state.activeID}
- showDelIcon={!state.menuDataItems?.length}
- options={state.menuData}
- initFn={() => tryFetchList()}
- />
- <div className="w-6/7 ml-8 bg-white p-4 rounded-20px shadow-hex-3e2c5a relative">
- {state.menuData.length ? (
- <ProTable<API.DataSourceItem>
- columns={columns}
- search={false}
- scroll={{
- y: document.body.clientHeight - (287 + wrapHeight)
- }}
- dataSource={state.menuDataItems || []}
- rowKey="index"
- toolbar={{
- title: state.activeID ? (
- <div className="max-w-400px truncate">{state.menuDataTitle}</div>
- ) : null,
- subTitle: state.activeID ? (
- <CopyToClipboard onCopy={() => message.success('复制成功')} text={state.menuTitleUrl}>
- <Tooltip placement="right" title="复制" className="ml-5px">
- <CopyOutlined />
- </Tooltip>
- </CopyToClipboard>
- ) : null,
- actions: [
- state.activeID ? (
- <Button
- key="btn-key"
- size="small"
- type="primary"
- onClick={() => {
- setState({
- ...state,
- modalType: OptionModalType.ADD,
- modalVisible: true
- })
- }}
- ghost>
- <PlusOutlined />
- 添加选项
- </Button>
- ) : null
- ]
- }}
- components={{
- body: {
- wrapper: DraggableContainer,
- row: DraggableBodyRow
- }
- }}
- />
- ) : null}
- <ModalForm
- labelCol={{ span: 6 }}
- key="form"
- width="30%"
- title={`${state.modalType === OptionModalType.ADD ? '添加' : '编辑'}选项`}
- formRef={formRef}
- layout="horizontal"
- visible={state.modalVisible}
- onVisibleChange={visible => {
- setState({ ...state, modalVisible: visible })
- setTimeout(() => {
- if (!visible) formRef.current?.resetFields()
- }, 80)
- }}
- initialValues={{ enable: true }}
- onFinish={async values => {
- try {
- if (state.modalType === OptionModalType.ADD) {
- await tryAddDataSourceItem({ ...values, dataSourceID: state.activeID })
- } else {
- const newItemData = state.menuDataItems?.map(item => {
- if (item.ID === values.ID) {
- const newItem = { ...values }
- return newItem
- }
- return item
- })
- await tryUpdateDataSourceItem({
- ID: state.activeID,
- items: newItemData
- })
- }
- message.success(`${state.modalType === OptionModalType.ADD ? '新增' : '编辑'}成功`)
- return true
- } catch (error) {
- message.error(error)
- return false
- }
- }}>
- <ProFormText name="ID" hidden />
- <ProFormText
- name="name"
- label="选项名称"
- rules={[{ required: true, message: '请输入选项名称' }]}
- />
- <ProFormRadio.Group
- name="enable"
- label="状态"
- options={[
- { label: '启用', value: true },
- { label: '停用', value: false }
- ]}
- rules={[{ required: true, message: '请选择启用/停用' }]}
- />
- </ModalForm>
- </div>
- </div>
- </PageContainer>
- )
- }
- export default Option
|