lanjianrong 3 vuotta sitten
vanhempi
commit
dc791c04ee

+ 4 - 4
src/pages/Permission/Role/components/RoleLeftMenu/index.less

@@ -8,10 +8,10 @@
   justify-content: space-between;
   align-items: center;
   .title {
-    word-wrap: break-word; /*强制换行*/
-    overflow: hidden; /*超出隐藏*/
-    text-overflow: ellipsis; /*隐藏后添加省略号*/
-    white-space: nowrap; /*强制不换行*/
+    word-wrap: break-word;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
   }
   .extra {
     display: none;

+ 156 - 0
src/pages/Schema/Option/components/LeftMenu/index.tsx

@@ -0,0 +1,156 @@
+import { DeleteOutlined, FormOutlined, PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons'
+import { ModalForm, ProFormText } from '@ant-design/pro-form'
+import { useRequest } from '@umijs/max'
+import type { ProFormInstance } from '@ant-design/pro-form'
+import { Button, Input, message, Popconfirm, Tree } from 'antd'
+import type { DirectoryTreeProps } from 'antd/lib/tree'
+import { addDataSource, delDataSourceID, updateDataSourceItem } from '@/services/api/schema'
+import { useRef, useState } from 'react'
+import '@/pages/Permission/Role/components/RoleLeftMenu/index.less'
+
+const { DirectoryTree } = Tree
+
+type LeftMenuProps = {
+  onSelect: (key: string) => void
+  options: API.DataSourceMenuItem[]
+  initFn: () => Promise<void>
+}
+
+const LeftMenu: React.FC<LeftMenuProps> = ({ onSelect, options, showDelIcon, initFn }) => {
+  const [activeID, setActiveID] = useState<Nullable<string>>(null)
+  const formRef = useRef<ProFormInstance>(null)
+  const handleOnSelect: DirectoryTreeProps['onSelect'] = (keys, node) => {
+    // console.log('Trigger Select', node)
+    onSelect?.(keys[0], node)
+  }
+  const { run: tryUpdateDataSourceItem } = useRequest(
+    (params: Partial<API.UpdateRoleParams>) => updateDataSourceItem(params),
+    {
+      manual: true,
+      onSuccess: () => {
+        message.success('修改成功')
+        initFn()
+      }
+    }
+  )
+  const { run: tryAddDataSource } = useRequest((params: API.CreateRoleParams) => addDataSource(params), {
+    manual: true,
+    onSuccess: () => {
+      initFn()
+    }
+  })
+  const { run: tryDelRole } = useRequest((ID: string) => delDataSourceID({ ID }), {
+    manual: true,
+    onSuccess: () => {
+      message.success('删除成功')
+      initFn()
+    }
+  })
+
+  const handleOnFocus = async (
+    e: React.FocusEvent<HTMLInputElement> | React.KeyboardEvent<HTMLElement>,
+    oldTitle: string,
+    ID: string
+  ) => {
+    const val = e.currentTarget.value || e.currentTarget.nodeValue
+    if (val !== oldTitle) {
+      await tryUpdateDataSourceItem({ ID, name: val })
+    }
+    setActiveID(null)
+  }
+
+  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
+    })
+  }
+  const virtualHeigh = document.getElementById('role-list')?.clientHeight
+  return (
+    <div
+      className="min-w-54 rounded-20px flex flex-col bg-white "
+      style={{ height: 'calc(100vh - 122px)', background: '#ffffff' }}>
+      <div className="menu-title flex items-center justify-around">
+        <div className="py-4 text-base text-opacity-85">数据源列表</div>
+        <ModalForm
+          layout="horizontal"
+          title="新增数据源"
+          width="30%"
+          formRef={formRef}
+          onVisibleChange={visible => !visible && formRef.current?.resetFields()}
+          isKeyPressSubmit
+          labelCol={{ span: 5 }}
+          trigger={
+            <Button size="small" type="primary" ghost>
+              <PlusOutlined />
+              添加
+            </Button>
+          }
+          onFinish={async values => {
+            await tryAddDataSource(values)
+            message.success('添加成功')
+            return true
+          }}>
+          <ProFormText
+            label="数据源名称"
+            name="name"
+            rules={[{ required: true, message: '请输入数据源名称' }]}
+          />
+          {/* <ProFormText label="URL" name="url" required disabled placeholder="自动生成" /> */}
+        </ModalForm>
+      </div>
+      <div
+        id="role-list"
+        className="p-4 bg-white rounded-b-20px"
+        style={{ height: 'calc(100% - 1rem*2 - 20px)' }}>
+        <DirectoryTree
+          treeData={renderTreeNode(options.map(item => ({ title: item.name, key: item.ID, ...item })))}
+          height={virtualHeigh - 32}
+          onSelect={handleOnSelect}
+          showIcon={false}
+          defaultExpandAll
+        />
+      </div>
+    </div>
+  )
+}
+
+export default LeftMenu

+ 1 - 11
src/pages/Schema/Option/index.less

@@ -1,6 +1,6 @@
 .row-dragging {
   background: #fafafa;
-  border: 1px solid #ccc;
+  border: 1px solid #cccccc;
 }
 
 .row-dragging td {
@@ -10,13 +10,3 @@
 .row-dragging .drag-visible {
   visibility: visible;
 }
-
-.copy-wrap {
-  :global(.ant-pro-core-label-tip-title) {
-    max-width: 400px;
-    overflow: hidden;
-    display: -webkit-box !important;
-    -webkit-box-orient: vertical;
-    -webkit-line-clamp: 1;
-  }
-}

+ 7 - 9
src/pages/Schema/Option/index.tsx

@@ -13,7 +13,6 @@ import { CopyToClipboard } from 'react-copy-to-clipboard'
 import { isEmpty, isNullOrUnDef } from '@/utils/is'
 import classNames from 'classnames'
 import './index.less'
-import styles from './index.less'
 import { ModalForm, ProFormRadio, ProFormText } from '@ant-design/pro-form'
 import ProTable from '@ant-design/pro-table'
 import { ModalType } from '@/utils/enum'
@@ -167,8 +166,9 @@ const Option = () => {
   )
 
   useEffect(() => {
-    const menuTitleCopyUrl =
-      `/api/form/v1/ds/items?ID=` + state.menuData.find(i => i.ID === state.activeID)?.ID
+    const menuTitleCopyUrl = `/api/form/v1/ds/items?ID=${
+      state.menuData.find(i => i.ID === state.activeID)?.ID
+    }`
     if (isNullOrUnDef(menuTitleCopyUrl) || isEmpty(menuTitleCopyUrl)) return null
     if (state.activeID) {
       setState({
@@ -198,11 +198,7 @@ const Option = () => {
           options={state.menuData}
           initFn={() => tryFetchList()}
         />
-        <div
-          className={classNames(
-            'w-6/7 ml-8 bg-white p-4 rounded-20px shadow-hex-3e2c5a relative',
-            styles['copy-wrap']
-          )}>
+        <div className="w-6/7 ml-8 bg-white p-4 rounded-20px shadow-hex-3e2c5a relative">
           <ProTable<API.DataSourceItem>
             columns={columns}
             search={false}
@@ -212,7 +208,9 @@ const Option = () => {
             dataSource={state.menuDataItems || []}
             rowKey="index"
             toolbar={{
-              title: state.activeID ? state.menuDataTitle : null,
+              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">