lanjianrong 3 лет назад
Родитель
Сommit
630e8f7cd2

+ 5 - 6
config/routes.ts

@@ -115,17 +115,16 @@
         icon: 'icon-schema',
         component: './Schema/Base/index.tsx'
       },
-      // {
-      //   path: 'test',
-      //   name: 'test',
-      //   layout: false,
-      //   component: './Schema/Designable/index.tsx'
-      // },
       {
         path: '/work-setting/schema/detail',
         name: 'schema-detail',
         hideInMenu: true,
         component: './Schema/Base/detail.tsx'
+      },
+      {
+        path: 'option',
+        name: 'option',
+        component: './Schema/Option'
       }
     ]
   },

+ 5 - 3
package.json

@@ -64,7 +64,8 @@
     "@formily/shared": "^2.0.11",
     "@umijs/route-utils": "^1.0.36",
     "ahooks": "^3.0.0",
-    "antd": "^4.18.2",
+    "antd": "4.20.7",
+    "array-move": "^4.0.0",
     "classnames": "^2.2.6",
     "dayjs": "^1.10.7",
     "lodash": "^4.17.11",
@@ -75,11 +76,10 @@
     "react": "^17.0.0",
     "react-dom": "^17.0.0",
     "react-flow-renderer": "^9.6.11",
+    "react-sortable-hoc": "^2.0.0",
     "umi": "^3.5.0"
   },
   "devDependencies": {
-    "umi-serve": "^1.9.10",
-    "react-dev-inspector": "^1.1.1",
     "@ant-design/pro-cli": "^2.0.2",
     "@types/express": "^4.17.0",
     "@types/history": "^4.7.2",
@@ -108,8 +108,10 @@
     "lint-staged": "^10.0.0",
     "prettier": "^2.3.2",
     "puppeteer-core": "^8.0.0",
+    "react-dev-inspector": "^1.1.1",
     "stylelint": "13.0.0",
     "typescript": "^4.2.2",
+    "umi-serve": "^1.9.10",
     "windicss": "^3.2.0",
     "windicss-webpack-plugin": "^1.5.5"
   },

Разница между файлами не показана из-за своего большого размера
+ 294 - 236
pnpm-lock.yaml


+ 1 - 0
src/locales/zh-CN/menu.ts

@@ -28,6 +28,7 @@ export default {
   'menu.institutions.company.detail': '单位详情',
   'menu.work-setting': '表单设置',
   'menu.work-setting.schema': '基础数据设置',
+  'menu.work-setting.option': '数据源设置',
   'menu.work-setting.test': '测试',
   'menu.work-setting.schema-detail': '编辑数据模型',
   'menu.system': '系统管理',

+ 5 - 5
src/pages/Schema/Base/components/Designable/antd/components/Field/shared.ts

@@ -78,11 +78,11 @@ export const createFieldSchema = (
         type: 'void',
         'x-component': 'CollapseItem',
         properties: {
-          // name: {
-          //   type: 'string',
-          //   'x-decorator': 'FormItem',
-          //   'x-component': 'Input'
-          // },
+          name: {
+            type: 'string',
+            'x-decorator': 'FormItem',
+            'x-component': 'Input'
+          },
           title: {
             type: 'string',
             'x-decorator': 'FormItem',

+ 7 - 7
src/pages/Schema/Base/components/LeftMenu/index.tsx

@@ -13,14 +13,14 @@ const LeftMenu: React.FC<LeftMenuProps> = ({ title = '栏目/功能', options, o
   return (
     <div
       className="w-216px rounded-20px"
-      style={{ height: 'calc(100vh - 122px)', background: '#ffffff' }}
-    >
+      style={{ height: 'calc(100vh - 122px)', background: '#ffffff' }}>
       <div className="p-5 text-16px text-opacity-85 menu-title">{title}</div>
-      <Menu defaultSelectedKeys={[value]} onSelect={({ key }) => onChange(key)} mode="inline">
-        {options.map(item => (
-          <Menu.Item key={item.value}>{item.label}</Menu.Item>
-        ))}
-      </Menu>
+      <Menu
+        defaultSelectedKeys={[value]}
+        onSelect={({ key }) => onChange(key)}
+        mode="inline"
+        items={options.map(item => ({ label: item.label, key: item.value }))}
+      />
     </div>
   )
 }

+ 113 - 0
src/pages/Schema/Option/Components/LeftMenu.tsx

@@ -0,0 +1,113 @@
+import {
+  DeleteOutlined,
+  FormOutlined,
+  PlusOutlined,
+  QuestionCircleOutlined
+} from '@ant-design/icons'
+import { ModalForm, ProFormText } from '@ant-design/pro-form'
+
+import { Button, Input, Popconfirm, Tree } from 'antd'
+import type { DirectoryTreeProps } from 'antd/lib/tree'
+import '@/pages/Permission/Role/components/RoleLeftMenu/index.less'
+import { addDataSource } from '@/services/api/schema'
+
+const { DirectoryTree } = Tree
+
+type LeftMenuProps = {
+  onSelect: (key: string) => void
+  options: API.DataSourceMenuItem[]
+  initFn: () => Promise<void>
+}
+
+const LeftMenu: React.FC<LeftMenuProps> = ({ onSelect, options }) => {
+  const handleOnSelect: DirectoryTreeProps['onSelect'] = keys => {
+    console.log('Trigger Select', keys, info)
+    onSelect?.(keys[0])
+  }
+
+  const renderTreeNode = tree => {
+    return tree.map((item: API.DataSourceMenuItem & { title: string; key: string }) => {
+      const newItem = {
+        ...item,
+        title: (
+          <div className="department-node py-1">
+            {item.ID === activeID ? (
+              <Input
+                autoFocus
+                defaultValue={item.name}
+                bordered={false}
+                size="small"
+                style={{ width: '70%', backgroundColor: 'white' }}
+                onBlur={e => handleOnFocus(e, item.name, item.ID)}
+                onPressEnter={e => handleOnFocus(e, item.name, item.ID)}
+              />
+            ) : (
+              <div className="title">{item.name}</div>
+            )}
+            <div className="extra">
+              <FormOutlined className="pr-2" onClick={() => setActiveID(item.ID)} />
+              <Popconfirm
+                disabled={!showDelIcon}
+                title="确认删除吗?"
+                onText="确认"
+                cancelText="取消"
+                onConfirm={() => tryDelRole(item.ID)}
+                icon={<QuestionCircleOutlined style={{ color: 'red' }} />}>
+                <DeleteOutlined
+                  onClick={() => {
+                    !showDelIcon && message.warning('请先移除该栏目下所有配置')
+                  }}
+                />
+              </Popconfirm>
+            </div>
+          </div>
+        )
+      }
+      if (newItem.children?.length) {
+        newItem.children = renderTreeNode(newItem.items)
+      }
+      return newItem
+    })
+  }
+  return (
+    <div className="w-54 rounded-20px flex flex-col bg-white ">
+      <div className="p-4 border-b-1 rounded-tl-20px rounded-tr-20px border-solid border-black border-opacity-10 bg-hex-f7f9fa flex justify-around items-center flex-row">
+        <div className="text-base">数据源列表</div>
+        <ModalForm
+          layout="horizontal"
+          title="新增数据源"
+          width="30%"
+          isKeyPressSubmit
+          labelCol={{ span: 5 }}
+          trigger={
+            <Button size="small" type="primary" ghost>
+              <PlusOutlined />
+              添加
+            </Button>
+          }
+          onFinish={async values => {
+            const { code = -1 } = await addDataSource(values)
+            return code === consts.RET_CODE.SUCCESS
+          }}>
+          <ProFormText
+            label="数据源名称"
+            name="name"
+            rules={[{ required: true, message: '请输入数据源名称' }]}
+          />
+          <ProFormText label="URL" name="url" required disabled placeholder="自动生成" />
+        </ModalForm>
+      </div>
+      <div className="flex-1">
+        <DirectoryTree
+          treeData={renderTreeNode(
+            options.map(item => ({ title: item.name, key: item.ID, ...item }))
+          )}
+          onSelect={handleOnSelect}
+          defaultExpandAll
+        />
+      </div>
+    </div>
+  )
+}
+
+export default LeftMenu

+ 111 - 0
src/pages/Schema/Option/index.tsx

@@ -0,0 +1,111 @@
+import { Table } from 'antd'
+import LeftMenu from './Components/LeftMenu'
+import { SortableContainer, SortableElement, SortableHandle } from 'react-sortable-hoc'
+import { arrayMoveImmutable } from 'array-move'
+import type { ColumnsType } from 'antd/lib/table'
+import type { SortableContainerProps, SortEnd } from 'react-sortable-hoc'
+
+import { useState } from 'react'
+import { useRequest } from 'umi'
+import { addDataSource, queryDataSource } from '@/services/api/schema'
+import { MenuOutlined } from '@ant-design/icons'
+
+const DragHandle = SortableHandle(() => <MenuOutlined style={{ cursor: 'grab', color: '#999' }} />)
+
+const columns: ColumnsType<DataType> = [
+  {
+    title: 'Sort',
+    dataIndex: 'sort',
+    width: 30,
+    className: 'drag-visible',
+    render: () => <DragHandle />
+  },
+  {
+    title: 'Name',
+    dataIndex: 'name',
+    className: 'drag-visible'
+  },
+  {
+    title: 'Age',
+    dataIndex: 'age'
+  },
+  {
+    title: 'Address',
+    dataIndex: 'address'
+  }
+]
+
+const SortableItem = SortableElement((props: React.HTMLAttributes<HTMLTableRowElement>) => (
+  <tr {...props} />
+))
+const SortableBody = SortableContainer((props: React.HTMLAttributes<HTMLTableSectionElement>) => (
+  <tbody {...props} />
+))
+const Option = () => {
+  const [menuData, setMenuData] = useState<API.DataSourceMenuItem[]>([])
+  const [activeID, setActiveID] = useState<Nullable<string>>(null)
+  const { run: tryFetchList } = useRequest(queryDataSource, {
+    onSuccess: (result: API.DataSourceMenuItem) => {
+      setMenuData(result)
+    }
+  })
+
+  const onSortEnd = ({ oldIndex, newIndex }: SortEnd) => {
+    if (oldIndex !== newIndex) {
+      const newData = arrayMoveImmutable(dataSource.slice(), oldIndex, newIndex).filter(
+        (el: DataType) => !!el
+      )
+      const newMenuData = menuData.map(item => {
+        const newItem = { ...item }
+        if (item.ID === activeID) {
+          newItem.items = newData
+          return newItem
+        }
+        return item
+      })
+      setMenuData(newMenuData)
+    }
+  }
+
+  const DraggableContainer = (props: SortableContainerProps) => (
+    <SortableBody
+      useDragHandle
+      disableAutoscroll
+      helperClass="row-dragging"
+      onSortEnd={onSortEnd}
+      {...props}
+    />
+  )
+
+  const DraggableBodyRow = ({ className, style, ...restProps }) => {
+    const index = menuData
+      .find(item => item.ID === activeID)
+      ?.items.findIndex(x => x.index === restProps['data-row-key'])
+    return <SortableItem index={index} {...restProps} />
+  }
+
+  return (
+    <div className="flex flex-nowrap h-full">
+      <LeftMenu
+        onSelect={(key: string) => setActiveID(key)}
+        options={menuData}
+        initFn={() => tryFetchList()}
+      />
+      <div className="ml-6 shadow-hex-3e2c5a" style={{ width: 'calc(100% - 12rem)' }}>
+        <Table<API.DataSourceItem>
+          columns={columns}
+          dataSource={menuData.find(item => item.ID === activeID)?.items || []}
+          rowKey="ID"
+          components={{
+            body: {
+              wrapper: DraggableContainer,
+              row: DraggableBodyRow
+            }
+          }}
+        />
+      </div>
+    </div>
+  )
+}
+
+export default Option

+ 10 - 0
src/services/api/schema.ts

@@ -14,3 +14,13 @@ export async function updateSchema(params) {
     data: params
   })
 }
+
+/** 新增数据源 */
+export async function addDataSource(params: { name: string }) {
+  return request('/form/v1/ds/create', { method: 'POST', data: params })
+}
+
+/** 获取数据源 */
+export async function queryDataSource() {
+  return request('/form/v1/ds/list')
+}

+ 13 - 0
src/services/api/typings.d.ts

@@ -341,4 +341,17 @@ declare namespace API {
     ID: string
     frontPermission: string
   }
+
+  type DataSourceMenuItem = {
+    ID?: string
+    name?: string
+    url?: string
+    items?: DataSourceItem[]
+  }
+
+  type DataSourceItem = {
+    ID?: string
+    name?: string
+    enable?: string
+  }
 }