Przeglądaj źródła

fix(merge): merge

Lind 3 lat temu
rodzic
commit
06b4c237ce
35 zmienionych plików z 388 dodań i 256 usunięć
  1. 1 1
      src/app.tsx
  2. 13 13
      src/components/CheckButton/index.less
  3. 12 14
      src/components/CheckButton/index.tsx
  4. 1 1
      src/components/RadioCard/index.less
  5. 1 0
      src/locales/zh-CN/pages.ts
  6. 17 1
      src/pages/device/Instance/Detail/ChildDevice/BindChildDevice/index.tsx
  7. 5 1
      src/pages/device/Instance/Detail/ChildDevice/index.tsx
  8. 24 0
      src/pages/device/Instance/Detail/index.tsx
  9. 10 4
      src/pages/device/Instance/Save/index.tsx
  10. 43 51
      src/pages/device/Product/Detail/Access/index.tsx
  11. 34 9
      src/pages/device/Product/Detail/index.tsx
  12. 11 4
      src/pages/device/Product/Save/index.tsx
  13. 1 0
      src/pages/device/Product/index.tsx
  14. 42 46
      src/pages/link/AccessConfig/Detail/Access/index.tsx
  15. 2 2
      src/pages/link/AccessConfig/index.tsx
  16. 0 0
      src/pages/link/Type/Detail/index.less
  17. 1 1
      src/pages/link/Type/Save/index.tsx
  18. 1 1
      src/pages/link/Type/index.tsx
  19. 5 2
      src/pages/system/Department/index.tsx
  20. 7 5
      src/pages/system/Menu/Detail/buttons.tsx
  21. 59 34
      src/pages/system/Menu/Detail/edit.tsx
  22. 9 11
      src/pages/system/Menu/Detail/index.tsx
  23. 6 0
      src/pages/system/Menu/service.ts
  24. 43 38
      src/pages/system/Role/Edit/Permission/Allocate/MenuPermission.tsx
  25. 0 0
      src/pages/system/Role/Detail/Permission/Allocate/index.less
  26. 0 0
      src/pages/system/Role/Detail/Permission/Allocate/index.tsx
  27. 0 0
      src/pages/system/Role/Detail/Permission/index.less
  28. 28 4
      src/pages/system/Role/Edit/Permission/index.tsx
  29. 0 0
      src/pages/system/Role/Detail/UserManage/BindUser.tsx
  30. 0 0
      src/pages/system/Role/Detail/UserManage/index.tsx
  31. 2 2
      src/pages/system/Role/Edit/index.tsx
  32. 2 1
      src/pages/system/Role/index.tsx
  33. 2 7
      src/pages/user/Login/index.tsx
  34. 4 1
      src/utils/menu/index.ts
  35. 2 2
      src/utils/menu/router.ts

+ 1 - 1
src/app.tsx

@@ -237,7 +237,7 @@ export function patchRoutes(routes: any) {
 
 export function render(oldRender: any) {
   if (history.location.pathname !== loginPath) {
-    MenuService.queryMenuThree({ paging: false }).then((res) => {
+    MenuService.queryOwnThree({ paging: false }).then((res) => {
       if (res.status === 200) {
         extraRoutes = handleRoutes(res.result);
         saveMenusCache(extraRoutes);

+ 13 - 13
src/components/CheckButton/index.less

@@ -1,19 +1,19 @@
+@import '../../../node_modules/antd/lib/style/themes/variable';
+
 .box {
   display: flex;
+
   .item {
-    width: 30px;
-    height: 30px;
-    font-size: 20px;
-    line-height: 30px;
-    text-align: center;
-    border-top: 1px solid lightgray;
-    border-bottom: 1px solid lightgray;
+    color: rgba(0, 0, 0, 0.75);
+    font-size: 16px;
     cursor: pointer;
-  }
-  .left {
-    border-left: 1px solid lightgray;
-  }
-  .right {
-    border-right: 1px solid lightgray;
+
+    &.active {
+      color: @primary-color-active;
+    }
+
+    &:hover {
+      color: @primary-color-hover;
+    }
   }
 }

+ 12 - 14
src/components/CheckButton/index.tsx

@@ -1,36 +1,34 @@
-import { AppstoreFilled, UnorderedListOutlined } from '@ant-design/icons';
+import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons';
 import classnames from 'classnames';
-import styles from './index.less';
+import './index.less';
+
 interface Props {
   value: boolean;
   change: (value: boolean) => void;
 }
 
 const CheckButton = (props: Props) => {
-  const activeStyle = {
-    border: '1px solid #1d39c4',
-    color: '#1d39c4',
-  };
-
   return (
-    <div className={styles.box}>
+    <div className="box">
       <div
-        className={classnames(styles.item, styles.left)}
-        style={props.value ? activeStyle : {}}
+        className={classnames('item', {
+          active: props.value,
+        })}
         onClick={() => {
           props.change(true);
         }}
       >
-        <AppstoreFilled />
+        <AppstoreOutlined />
       </div>
       <div
-        className={classnames(styles.item, styles.right)}
-        style={!props.value ? activeStyle : {}}
+        className={classnames('item', {
+          active: !props.value,
+        })}
         onClick={() => {
           props.change(false);
         }}
       >
-        <UnorderedListOutlined />
+        <BarsOutlined />
       </div>
     </div>
   );

+ 1 - 1
src/components/RadioCard/index.less

@@ -1,4 +1,4 @@
-@import '~antd/lib/style/themes/variable';
+@import '~antd/es/style/themes/default.less';
 
 @border: 1px solid @border-color-base;
 

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

@@ -31,6 +31,7 @@ export default {
   'pages.data.option.assets': '资产分配',
   'pages.data.option.invoke': '执行',
   'pages.data.option.cancel': '取消',
+  'pages.data.option.view': '查看',
   'pages.searchTable.new': '新建',
   'pages.searchTable.titleStatus': '状态',
   'pages.searchTable.titleStatus.all': '全部',

+ 17 - 1
src/pages/device/Instance/Detail/ChildDevice/BindChildDevice/index.tsx

@@ -26,18 +26,22 @@ const BindChildDevice = (props: Props) => {
     {
       title: 'ID',
       dataIndex: 'id',
+      ellipsis: true,
     },
     {
       title: '设备名称',
+      ellipsis: true,
       dataIndex: 'name',
     },
     {
       title: '所属产品',
+      ellipsis: true,
       dataIndex: 'productName',
     },
     {
       title: '注册时间',
       dataIndex: 'registryTime',
+      ellipsis: true,
       width: '200px',
       render: (text: any) => (!!text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
       sorter: true,
@@ -45,6 +49,8 @@ const BindChildDevice = (props: Props) => {
     {
       title: '状态',
       dataIndex: 'state',
+      ellipsis: true,
+      width: 100,
       renderText: (record) =>
         record ? <Badge status={statusMap.get(record.value)} text={record.text} /> : '',
       valueType: 'select',
@@ -124,7 +130,17 @@ const BindChildDevice = (props: Props) => {
         target="child-device-bind"
         enableSave={false}
         // pattern={'simple'}
-        defaultParam={[{ column: 'parentId$isnull', value: '1' }]}
+        defaultParam={[
+          {
+            terms: [
+              { column: 'parentId$isnull', value: '' },
+              { column: 'parentId$not', value: InstanceModel.detail.id!, type: 'or' },
+            ],
+          },
+          {
+            terms: [{ column: 'id$not', value: InstanceModel.detail.id!, type: 'and' }],
+          },
+        ]}
         onSearch={(param) => {
           actionRef.current?.reset?.();
           setSearchParams(param);

+ 5 - 1
src/pages/device/Instance/Detail/ChildDevice/index.tsx

@@ -10,6 +10,7 @@ import SearchComponent from '@/components/SearchComponent';
 import BindChildDevice from './BindChildDevice';
 import moment from 'moment';
 import { Link } from 'umi';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 const ChildDevice = () => {
   const intl = useIntl();
@@ -86,7 +87,10 @@ const ChildDevice = () => {
       align: 'center',
       width: 200,
       render: (text, record) => [
-        <Link to={`/device/instance/detail/${record.id}`} key="link">
+        <Link
+          to={`${getMenuPathByParams(MENUS_CODE['device/Instance/Detail'], record.id)}`}
+          key="link"
+        >
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.detail',

+ 24 - 0
src/pages/device/Instance/Detail/index.tsx

@@ -17,11 +17,13 @@ import MetadataAction from '@/pages/device/components/Metadata/DataBaseAction';
 import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
+import useSendWebsocketMessage from '@/hooks/websocket/useSendWebsocketMessage';
 
 export const deviceStatus = new Map();
 deviceStatus.set('online', <Badge status="success" text={'在线'} />);
 deviceStatus.set('offline', <Badge status="error" text={'离线'} />);
 deviceStatus.set('notActive', <Badge status="processing" text={'未启用'} />);
+
 const InstanceDetail = observer(() => {
   const intl = useIntl();
   const [tab, setTab] = useState<string>('detail');
@@ -35,6 +37,28 @@ const InstanceDetail = observer(() => {
   };
   const params = useParams<{ id: string }>();
 
+  const [subscribeTopic] = useSendWebsocketMessage();
+
+  useEffect(() => {
+    if (subscribeTopic) {
+      subscribeTopic(
+        `instance-editor-info-status-${params.id}`,
+        `/dashboard/device/status/change/realTime`,
+        {
+          deviceId: params.id,
+        },
+        // @ts-ignore
+      ).subscribe((data: any) => {
+        const payload = data.payload;
+        const state = payload.value.type;
+        InstanceModel.detail.state = {
+          value: state,
+          text: '',
+        };
+      });
+    }
+  }, []);
+
   useEffect(() => {
     Store.subscribe(SystemConst.REFRESH_DEVICE, () => {
       MetadataAction.clean();

+ 10 - 4
src/pages/device/Instance/Save/index.tsx

@@ -17,6 +17,7 @@ interface Props {
 const Save = (props: Props) => {
   const { visible, close, data } = props;
   const [productList, setProductList] = useState<any[]>([]);
+  const [loading, setLoading] = useState(false);
   const [form] = Form.useForm();
 
   useEffect(() => {
@@ -55,20 +56,24 @@ const Save = (props: Props) => {
       });
       paramsObj.name = paramsMsg;
     }
-    const msg = intl.formatMessage(
+    return intl.formatMessage(
       {
         id,
         defaultMessage,
       },
       paramsObj,
     );
-    return msg;
   };
 
   const handleSave = async () => {
     const values = await form.validateFields();
     if (values) {
+      if (values.id === '') {
+        delete values.id;
+      }
+      setLoading(true);
       const resp = (await service.update(values)) as any;
+      setLoading(false);
       if (resp.status === 200) {
         message.success('保存成功');
         if (props.reload) {
@@ -81,7 +86,7 @@ const Save = (props: Props) => {
   };
 
   const vailId = (_: any, value: any, callback: Function) => {
-    if (props.model === 'add') {
+    if (props.model === 'add' && value) {
       service.isExists(value).then((resp: any) => {
         if (resp.status === 200 && resp.result) {
           callback(
@@ -107,11 +112,12 @@ const Save = (props: Props) => {
         form.resetFields();
         close(undefined);
       }}
-      width="30vw"
+      width="580px"
       title={intl.formatMessage({
         id: `pages.data.option.${props.model || 'add'}`,
         defaultMessage: '新增',
       })}
+      confirmLoading={loading}
       onOk={handleSave}
     >
       <Form

+ 43 - 51
src/pages/device/Product/Detail/Access/index.tsx

@@ -1,9 +1,10 @@
-import { Badge, Descriptions, Empty, Table, Tooltip } from 'antd';
+import { Badge, Empty, Table, Tooltip } from 'antd';
 import { service } from '@/pages/link/AccessConfig';
 import styles from './index.less';
 import { useEffect, useState } from 'react';
 import { productModel } from '@/pages/device/Product';
 import AccessConfig from './AccessConfig';
+import ReactMarkdown from 'react-markdown';
 
 const Access = () => {
   const [visible, setVisible] = useState<boolean>(true);
@@ -91,11 +92,16 @@ const Access = () => {
       key: 'stream',
       ellipsis: true,
       align: 'center',
-      render: (text: any, record: any) => (
-        <span>
-          上行: {String(record?.upstream)}, 下行: {String(record?.downstream)}
-        </span>
-      ),
+      render: (text: any, record: any) => {
+        const list = [];
+        if (record?.upstream) {
+          list.push('上行');
+        }
+        if (record?.downstream) {
+          list.push('下行');
+        }
+        return <span>{list.join(',')}</span>;
+      },
     },
     {
       title: '说明',
@@ -216,52 +222,38 @@ const Access = () => {
         </div>
       ) : (
         <div className={styles.config}>
-          <div className={styles.title}>
-            配置概览
-            <a
-              style={{ marginLeft: 20 }}
-              onClick={() => {
-                setConfigVisible(true);
-              }}
-            >
-              更换
-            </a>
+          <div>
+            <div className={styles.title}>
+              接入方式
+              <a
+                style={{ marginLeft: 20 }}
+                onClick={() => {
+                  setConfigVisible(true);
+                }}
+              >
+                更换
+              </a>
+            </div>
+            {providers.find((i) => i.id === access?.provider)?.name || ''}
           </div>
-          <Descriptions column={1}>
-            <Descriptions.Item label="接入方式">
-              {providers.find((i) => i.id === access?.provider)?.name || ''}
-            </Descriptions.Item>
-            {providers.find((i) => i.id === access?.provider)?.description && (
-              <Descriptions.Item label="">
-                <span style={{ color: 'rgba(0,0,0,0.55)' }}>
-                  {providers.find((i) => i.id === access?.provider)?.description || ''}
-                </span>
-              </Descriptions.Item>
-            )}
-            <Descriptions.Item label="消息协议">
-              {access?.protocolDetail?.name || ''}
-            </Descriptions.Item>
-            {access?.protocolDetail?.description && (
-              <Descriptions.Item label="">
-                <span style={{ color: 'rgba(0,0,0,0.55)' }}>
-                  {access?.protocolDetail?.description || ''}
-                </span>
-              </Descriptions.Item>
-            )}
-            <Descriptions.Item label="网络组件">
-              {(networkList.find((i) => i.id === access?.channelId)?.addresses || []).map(
-                (item: any) => (
-                  <div key={item.address}>
-                    <Badge
-                      color={item.health === -1 ? 'red' : 'green'}
-                      text={item.address}
-                      style={{ marginLeft: '20px' }}
-                    />
-                  </div>
-                ),
-              )}
-            </Descriptions.Item>
-          </Descriptions>
+          {providers.find((i) => i.id === access?.provider)?.description && (
+            <span>{providers.find((i) => i.id === access?.provider)?.description || ''}</span>
+          )}
+          <div className={styles.title}>消息协议</div>
+          {access?.protocolDetail?.name || ''}
+          <ReactMarkdown>{config?.document}</ReactMarkdown>
+          <div className={styles.title}>网络组件</div>
+          {(networkList.find((i) => i.id === access?.channelId)?.addresses || []).map(
+            (item: any) => (
+              <div key={item.address}>
+                <Badge
+                  color={item.health === -1 ? 'red' : 'green'}
+                  text={item.address}
+                  style={{ marginLeft: '20px' }}
+                />
+              </div>
+            ),
+          )}
           {config?.routes && config?.routes?.length > 0 && (
             <div>
               <Table

+ 34 - 9
src/pages/device/Product/Detail/index.tsx

@@ -1,6 +1,18 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { history, Link, useParams } from 'umi';
-import { Badge, Card, Descriptions, message, Space, Spin, Switch, Tabs, Tooltip } from 'antd';
+import {
+  Badge,
+  Button,
+  Card,
+  Descriptions,
+  message,
+  Popconfirm,
+  Space,
+  Spin,
+  Switch,
+  Tabs,
+  Tooltip,
+} from 'antd';
 import BaseInfo from '@/pages/device/Product/Detail/BaseInfo';
 import { observer } from '@formily/react';
 import { productModel, service } from '@/pages/device/Product';
@@ -149,14 +161,27 @@ const ProductDetail = observer(() => {
           }}
         />
       }
-      // extra={[
-      //   <Button key="1" type="primary" onClick={() => changeDeploy('deploy')}>
-      //     {intl.formatMessage({
-      //       id: 'pages.device.productDetail.setting',
-      //       defaultMessage: '应用配置',
-      //     })}
-      //   </Button>,
-      // ]}
+      extra={[
+        <Popconfirm title={'确定应用配置?'} key="1" onConfirm={() => changeDeploy('deploy')}>
+          {productModel.current?.state === 0 ? (
+            <Tooltip title={'请先发布产品'}>
+              <Button disabled type="primary">
+                {intl.formatMessage({
+                  id: 'pages.device.productDetail.setting',
+                  defaultMessage: '应用配置',
+                })}
+              </Button>
+            </Tooltip>
+          ) : (
+            <Button key="1" type="primary">
+              {intl.formatMessage({
+                id: 'pages.device.productDetail.setting',
+                defaultMessage: '应用配置',
+              })}
+            </Button>
+          )}
+        </Popconfirm>,
+      ]}
     >
       <Card>
         <Tabs defaultActiveKey="base">

+ 11 - 4
src/pages/device/Product/Save/index.tsx

@@ -1,4 +1,4 @@
-import { useEffect } from 'react';
+import { useEffect, useState } from 'react';
 import { service } from '@/pages/device/Product';
 import type { ProductItem } from '@/pages/device/Product/typings';
 import { useIntl } from '@@/plugin-locale/localeExports';
@@ -19,6 +19,7 @@ const Save = (props: Props) => {
   const { visible, close, data } = props;
   const intl = useIntl();
   const [form] = Form.useForm();
+  const [loading, setLoading] = useState(false);
   const { data: classOptions, run: classRequest } = useRequest(service.category, {
     manual: true,
     formatResult: (response) => {
@@ -50,14 +51,14 @@ const Save = (props: Props) => {
       });
       paramsObj.name = paramsMsg;
     }
-    const msg = intl.formatMessage(
+
+    return intl.formatMessage(
       {
         id,
         defaultMessage,
       },
       paramsObj,
     );
-    return msg;
   };
 
   useEffect(() => {
@@ -71,9 +72,14 @@ const Save = (props: Props) => {
   const handleSave = async () => {
     const formData = await form.validateFields();
     if (formData) {
+      if (formData.id === '') {
+        delete formData.id;
+      }
       const { deviceTypeId, ...extraFormData } = formData;
       extraFormData.deviceType = formData.deviceTypeId;
+      setLoading(true);
       const res = await service.update(extraFormData);
+      setLoading(false);
       if (res.status === 200) {
         message.success('保存成功');
         if (props.reload) {
@@ -86,7 +92,7 @@ const Save = (props: Props) => {
   };
 
   const vailId = (_: any, value: any, callback: Function) => {
-    if (props.model === 'add') {
+    if (props.model === 'add' && value) {
       service.existsID(value).then((res) => {
         if (res.status === 200 && res.result) {
           callback(
@@ -116,6 +122,7 @@ const Save = (props: Props) => {
         id: `pages.data.option.${props.model || 'add'}`,
         defaultMessage: '新增',
       })}
+      confirmLoading={loading}
       onOk={handleSave}
     >
       <Form

+ 1 - 0
src/pages/device/Product/index.tsx

@@ -159,6 +159,7 @@ const Product = observer(() => {
                 defaultMessage: '产品',
               }),
             );
+            message.success('操作成功');
           }}
         />
       </Button>

+ 42 - 46
src/pages/link/AccessConfig/Detail/Access/index.tsx

@@ -4,7 +4,6 @@ import {
   Button,
   Card,
   Col,
-  Descriptions,
   Empty,
   Form,
   Input,
@@ -19,6 +18,8 @@ import styles from './index.less';
 import { service } from '@/pages/link/AccessConfig';
 import encodeQuery from '@/utils/encodeQuery';
 import { useHistory, useLocation } from 'umi';
+import ReactMarkdown from 'react-markdown';
+import { getMenuPathByCode, MENUS_CODE } from '@/utils/menu';
 
 interface Props {
   change: () => void;
@@ -197,11 +198,16 @@ const Access = (props: Props) => {
       key: 'stream',
       ellipsis: true,
       align: 'center',
-      render: (text: any, record: any) => (
-        <span>
-          上行: {String(record?.upstream)}, 下行: {String(record?.downstream)}
-        </span>
-      ),
+      render: (text: any, record: any) => {
+        const list = [];
+        if (record?.upstream) {
+          list.push('上行');
+        }
+        if (record?.downstream) {
+          list.push('下行');
+        }
+        return <span>{list.join(',')}</span>;
+      },
     },
     {
       title: '说明',
@@ -304,7 +310,8 @@ const Access = (props: Props) => {
               <Button
                 type="primary"
                 onClick={() => {
-                  const tab: any = window.open(`${origin}/#/link/Type/Save/:id`);
+                  const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
+                  const tab: any = window.open(`${origin}/#${url}`);
                   tab!.onTabSaveSuccess = (value: any) => {
                     if (value.status === 200) {
                       queryNetworkList(props.data?.id || access?.provider);
@@ -360,7 +367,8 @@ const Access = (props: Props) => {
                     暂无数据
                     <a
                       onClick={() => {
-                        const tab: any = window.open(`${origin}/#/link/Type/Save/:id`);
+                        const url = getMenuPathByCode(MENUS_CODE['link/Type/Detail']);
+                        const tab: any = window.open(`${origin}/#${url}`);
                         tab!.onTabSaveSuccess = (value: any) => {
                           if (value.status === 200) {
                             queryNetworkList(props.data?.id || access?.provider);
@@ -539,44 +547,32 @@ const Access = (props: Props) => {
               </div>
             </div>
             <div className={styles.config}>
-              <div className={styles.title}>配置概览</div>
-              <Descriptions column={1}>
-                <Descriptions.Item label="接入方式">
-                  {props.data?.name || providers.find((i) => i.id === access?.provider)?.name}
-                </Descriptions.Item>
-                {(props.data?.description ||
-                  providers.find((i) => i.id === access?.provider)?.description) && (
-                  <Descriptions.Item>
-                    <span style={{ color: 'rgba(0,0,0,0.55)' }}>
-                      {props.data?.description ||
-                        providers.find((i) => i.id === access?.provider)?.description}
-                    </span>
-                  </Descriptions.Item>
-                )}
-                <Descriptions.Item label="消息协议">
-                  {procotolList.find((i) => i.id === procotolCurrent)?.name || ''}
-                </Descriptions.Item>
-                {procotolList.find((i) => i.id === procotolCurrent)?.description && (
-                  <Descriptions.Item style={{ color: 'rgba(0,0,0,0.55)' }}>
-                    <span style={{ color: 'rgba(0,0,0,0.55)' }}>
-                      {procotolList.find((i) => i.id === procotolCurrent)?.description || ''}
-                    </span>
-                  </Descriptions.Item>
-                )}
-                <Descriptions.Item label="网络组件">
-                  {(networkList.find((i) => i.id === networkCurrent)?.addresses || []).map(
-                    (item: any) => (
-                      <div key={item.address}>
-                        <Badge
-                          color={item.health === -1 ? 'red' : 'green'}
-                          text={item.address}
-                          style={{ marginLeft: '20px' }}
-                        />
-                      </div>
-                    ),
-                  )}
-                </Descriptions.Item>
-              </Descriptions>
+              <div className={styles.title}>接入方式</div>
+              <div>
+                {props.data?.name || providers.find((i) => i.id === access?.provider)?.name}
+              </div>
+              {(props.data?.description ||
+                providers.find((i) => i.id === access?.provider)?.description) && (
+                <span>
+                  {props.data?.description ||
+                    providers.find((i) => i.id === access?.provider)?.description}
+                </span>
+              )}
+              <div className={styles.title}>消息协议</div>
+              <div>{procotolList.find((i) => i.id === procotolCurrent)?.name || ''}</div>
+              <ReactMarkdown>{config?.document}</ReactMarkdown>
+              <div className={styles.title}>网络组件</div>
+              {(networkList.find((i) => i.id === networkCurrent)?.addresses || []).map(
+                (item: any) => (
+                  <div key={item.address}>
+                    <Badge
+                      color={item.health === -1 ? 'red' : 'green'}
+                      text={item.address}
+                      style={{ marginLeft: '20px' }}
+                    />
+                  </div>
+                ),
+              )}
               {config?.routes && config?.routes?.length > 0 && (
                 <div>
                   <Table

+ 2 - 2
src/pages/link/AccessConfig/index.tsx

@@ -71,7 +71,7 @@ const AccessConfig = () => {
           onSearch={(data: any) => {
             const dt = {
               pageSize: 10,
-              terms: [...data.terms],
+              terms: [...data?.terms],
             };
             handleSearch(dt);
           }}
@@ -90,7 +90,7 @@ const AccessConfig = () => {
           </Button>
         </div>
         <Row gutter={[16, 16]} style={{ marginTop: 10 }}>
-          {dataSource.data.map((item: any) => (
+          {(dataSource?.data || []).map((item: any) => (
             <Col key={item.name} span={12}>
               <Card hoverable>
                 <div className={styles.box}>

src/pages/link/Type/Save/index.less → src/pages/link/Type/Detail/index.less


+ 1 - 1
src/pages/link/Type/Save/index.tsx

@@ -21,7 +21,7 @@ import { createForm, onFieldValueChange } from '@formily/core';
 import { Button, Card, message } from 'antd';
 import styles from './index.less';
 import { useAsyncDataSource } from '@/utils/util';
-import { service } from '..';
+import { service } from '../index';
 import _ from 'lodash';
 import FAutoComplete from '@/components/FAutoComplete';
 import { Store } from 'jetlinks-store';

+ 1 - 1
src/pages/link/Type/index.tsx

@@ -26,7 +26,7 @@ export const service = new Service('network/config');
  */
 const pageJump = (id?: string) => {
   // 跳转详情
-  history.push(`${getMenuPathByParams(MENUS_CODE['link/Type/Save'], id)}`);
+  history.push(`${getMenuPathByParams(MENUS_CODE['link/Type/Detail'], id)}`);
 };
 
 const Network = () => {

+ 5 - 2
src/pages/system/Department/index.tsx

@@ -185,7 +185,7 @@ export default observer(() => {
         'x-validator': [
           {
             required: true,
-            message: '请输入名称',
+            message: '请选择上级部门',
           },
         ],
         'x-component-props': {
@@ -272,7 +272,10 @@ export default observer(() => {
           const response = await service.queryOrgThree({
             paging: false,
             ...params,
-            sorts: [{ name: 'createTime', order: 'desc' }],
+            sorts: [
+              { name: 'createTime', order: 'desc' },
+              { name: 'sortIndex', order: 'asc' },
+            ],
           });
           setTreeData(response.result);
           return {

+ 7 - 5
src/pages/system/Menu/Detail/buttons.tsx

@@ -153,7 +153,7 @@ export default (props: ButtonsProps) => {
         id: 'page.system.menu.describe',
         defaultMessage: '备注说明',
       }),
-      dataIndex: 'describe',
+      dataIndex: 'description',
       // render: (_, row) => () => {
       //   console.log(row)
       //   return (<> {row.describe || '-'}</>)
@@ -263,14 +263,16 @@ export default (props: ButtonsProps) => {
         visible={visible}
         title={handleTitle()}
         onOk={() => {
-          saveData();
+          if (!disabled) {
+            saveData();
+          }
         }}
         onCancel={() => {
           resetForm();
           setVisible(false);
         }}
       >
-        <Form form={form} labelCol={{ span: 4 }} wrapperCol={{ span: 20 }}>
+        <Form form={form} layout={'vertical'}>
           <Form.Item
             name="id"
             label={intl.formatMessage({
@@ -296,7 +298,7 @@ export default (props: ButtonsProps) => {
               },
             ]}
           >
-            <Input disabled={!!(!disabled && id)} />
+            <Input disabled={!!(disabled || id)} />
           </Form.Item>
           <Form.Item
             name="name"
@@ -331,7 +333,7 @@ export default (props: ButtonsProps) => {
             </Form.Item>
           </Form.Item>
           <Form.Item
-            name="describe"
+            name="description"
             label={intl.formatMessage({
               id: 'pages.table.describe',
               defaultMessage: '描述',

+ 59 - 34
src/pages/system/Menu/Detail/edit.tsx

@@ -9,7 +9,6 @@ import {
   Radio,
   Row,
   Select,
-  Switch,
   Tooltip,
   TreeSelect,
 } from 'antd';
@@ -67,7 +66,11 @@ export default (props: EditProps) => {
   const saveData = async () => {
     const formData = await form.validateFields();
     if (formData) {
-      const response: any = !props.data.id
+      // formData.options = {
+      //   switch: show,
+      // };
+
+      const response: any = !formData.id
         ? await service.save(formData)
         : await service.update(formData);
       if (response.status === 200) {
@@ -84,12 +87,15 @@ export default (props: EditProps) => {
   };
 
   useEffect(() => {
-    console.log(props);
     if (form && props.basePath) {
       form.setFieldsValue({
         url: props.basePath,
       });
     }
+    queryPermissions({ paging: false });
+    queryMenuThree({ paging: false });
+    queryAssetsType();
+    /* eslint-disable */
   }, []);
 
   // const filterThree = (e: any) => {
@@ -103,13 +109,6 @@ export default (props: EditProps) => {
   // };
 
   useEffect(() => {
-    queryPermissions({ paging: false });
-    queryMenuThree({ paging: false });
-    queryAssetsType();
-    /* eslint-disable */
-  }, []);
-
-  useEffect(() => {
     if (form) {
       form.setFieldsValue({
         ...props.data,
@@ -118,6 +117,10 @@ export default (props: EditProps) => {
       setAccessSupport(props.data.accessSupport ? props.data.accessSupport.value : 'unsupported');
     }
     setDisabled(!!props.data.id);
+
+    if (props.data.options) {
+      setShow(props.data.options.switch);
+    }
     /* eslint-disable */
   }, [props.data]);
 
@@ -134,7 +137,11 @@ export default (props: EditProps) => {
                 required={true}
                 rules={[{ required: true, message: '请上传图标' }]}
               >
-                <UploadImage disabled={disabled} style={{ width: 140, height: 130 }} />
+                <UploadImage
+                  types={['image/png']}
+                  disabled={disabled}
+                  style={{ width: 140, height: 130 }}
+                />
               </Form.Item>
             </Col>
             <Col span={21}>
@@ -149,7 +156,7 @@ export default (props: EditProps) => {
                     required={true}
                     rules={[{ required: true, message: '请输入名称' }]}
                   >
-                    <Input disabled={disabled} />
+                    <Input disabled={disabled} placeholder={'请输入名称'} />
                   </Form.Item>
                 </Col>
                 <Col span={12}>
@@ -162,7 +169,7 @@ export default (props: EditProps) => {
                     required={true}
                     rules={[{ required: true, message: '请输入编码' }]}
                   >
-                    <Input disabled={disabled} />
+                    <Input disabled={disabled} placeholder={'请输入编码'} />
                   </Form.Item>
                 </Col>
               </Row>
@@ -180,7 +187,7 @@ export default (props: EditProps) => {
                       { max: 120, message: '最多可输入120字符' },
                     ]}
                   >
-                    <Input disabled={disabled} />
+                    <Input disabled={disabled} placeholder={'请输入页面地址'} />
                   </Form.Item>
                 </Col>
                 <Col span={12}>
@@ -197,27 +204,36 @@ export default (props: EditProps) => {
                       },
                     ]}
                   >
-                    <InputNumber style={{ width: '100%' }} disabled={disabled} />
+                    <InputNumber
+                      style={{ width: '100%' }}
+                      disabled={disabled}
+                      placeholder={'请输入排序'}
+                    />
                   </Form.Item>
                 </Col>
               </Row>
             </Col>
+            <Col span={24}>
+              <Form.Item name={'describe'} label={'说明'}>
+                <Input.TextArea rows={4} maxLength={200} showCount placeholder={'请输入说明'} />
+              </Form.Item>
+            </Col>
           </Row>
         </Card>
         <Card style={{ marginTop: 24 }}>
           <Title
             title={'权限配置'}
-            toolbarRender={
-              <Switch
-                disabled={disabled}
-                checkedChildren="开启"
-                unCheckedChildren="关闭"
-                checked={show}
-                onChange={(checked) => {
-                  setShow(checked);
-                }}
-              />
-            }
+            // toolbarRender={
+            //   <Switch
+            //     disabled={disabled}
+            //     checkedChildren="开启"
+            //     unCheckedChildren="关闭"
+            //     checked={show}
+            //     onChange={(checked) => {
+            //       setShow(checked);
+            //     }}
+            //   />
+            // }
           />
           {show && (
             <Row gutter={[0, 10]}>
@@ -226,6 +242,7 @@ export default (props: EditProps) => {
                   label={'数据权限控制'}
                   tooltip={'此菜单页面数据所对应的资产类型'}
                   name={'accessSupport'}
+                  required
                 >
                   <Radio.Group
                     onChange={(e) => {
@@ -247,10 +264,14 @@ export default (props: EditProps) => {
                   </Radio.Group>
                 </Form.Item>
                 {accessSupport === 'support' && (
-                  <Form.Item name={'assetType'}>
+                  <Form.Item
+                    name={'assetType'}
+                    rules={[{ required: true, message: '请选择资产类型' }]}
+                  >
                     <Select
                       style={{ width: 500 }}
                       disabled={disabled}
+                      placeholder={'请选择资产类型'}
                       options={
                         assetsType
                           ? assetsType.map((item: any) => ({ label: item.name, value: item.id }))
@@ -260,11 +281,15 @@ export default (props: EditProps) => {
                   </Form.Item>
                 )}
                 {accessSupport === 'indirect' && (
-                  <Form.Item name={'indirectMenus'}>
+                  <Form.Item
+                    name={'indirectMenus'}
+                    rules={[{ required: true, message: '请选择关联菜单' }]}
+                  >
                     <TreeSelect
                       style={{ width: 400 }}
                       disabled={disabled}
                       multiple
+                      placeholder={'请选择关联菜单'}
                       fieldNames={{ label: 'name', value: 'id' }}
                       treeData={menuThree}
                     />
@@ -289,12 +314,6 @@ export default (props: EditProps) => {
                   />
                   {/*</Form.Item>*/}
                 </Form.Item>
-                <Form.Item hidden name={'id'}>
-                  <Input />
-                </Form.Item>
-                <Form.Item hidden name={'parentId'}>
-                  <Input />
-                </Form.Item>
               </Col>
             </Row>
           )}
@@ -314,6 +333,12 @@ export default (props: EditProps) => {
             })}
           </Button>
         </Card>
+        <Form.Item hidden name={'id'}>
+          <Input />
+        </Form.Item>
+        <Form.Item hidden name={'parentId'}>
+          <Input />
+        </Form.Item>
       </Form>
     </div>
   );

+ 9 - 11
src/pages/system/Menu/Detail/index.tsx

@@ -43,7 +43,7 @@ export default () => {
   useEffect(() => {
     queryDetail();
     /* eslint-disable */
-  }, []);
+  }, [location]);
 
   return (
     <PageContainer
@@ -68,16 +68,14 @@ export default () => {
       }}
     >
       {tabKey === 'detail' ? (
-        <div style={{ padding: '16px 24px' }}>
-          <BaseDetail
-            data={{
-              ...data,
-              parentId: pId,
-            }}
-            basePath={params.get('basePath')}
-            onLoad={queryDetail}
-          />
-        </div>
+        <BaseDetail
+          data={{
+            ...data,
+            parentId: pId,
+          }}
+          basePath={params.get('basePath')}
+          onLoad={queryDetail}
+        />
       ) : (
         <Buttons data={data} onLoad={queryDetail} />
       )}

+ 6 - 0
src/pages/system/Menu/service.ts

@@ -11,6 +11,12 @@ class Service extends BaseService<MenuItem> {
   queryMenuThree = (data: any) => request(`${this.uri}/_all/tree`, { method: 'POST', data });
 
   /**
+   * 当前用户权限菜单
+   * @param data
+   */
+  queryOwnThree = (data: any) => request(`${this.uri}/user-own/tree`, { method: 'POST', data });
+
+  /**
    * 查询权限管理
    * @param data
    */

+ 43 - 38
src/pages/system/Role/Edit/Permission/Allocate/MenuPermission.tsx

@@ -99,7 +99,6 @@ const MenuPermission = (props: Props) => {
                     enabled: e.target.checked,
                   };
                 });
-                console.log(e.target.checked);
                 props.change({
                   ...value,
                   check: e.target.checked ? 1 : 3, // 1: 全选 2: 只选了部分 3: 一个都没选
@@ -112,44 +111,50 @@ const MenuPermission = (props: Props) => {
             </Checkbox>
           </div>
           <div>
-            <Checkbox.Group
-              name={value?.id}
-              value={_.map(
-                (value?.buttons || []).filter((i: any) => i?.enabled),
-                'id',
-              )}
-              onChange={(data: CheckboxValueType[]) => {
-                const buttons = value.buttons.map((i: any) => {
-                  return {
-                    ...i,
-                    enabled: data.includes(i.id),
+            {value.id === 'menu-permission' ? (
+              <span style={{ fontWeight: value.id === 'menu-permission' ? 600 : 400 }}>
+                权限数据
+              </span>
+            ) : (
+              <Checkbox.Group
+                name={value?.id}
+                value={_.map(
+                  (value?.buttons || []).filter((i: any) => i?.enabled),
+                  'id',
+                )}
+                onChange={(data: CheckboxValueType[]) => {
+                  const buttons = value.buttons.map((i: any) => {
+                    return {
+                      ...i,
+                      enabled: data.includes(i.id),
+                    };
+                  });
+                  const clen = (value?.children || []).filter((i: any) => i.check !== 3).length;
+                  let check: number = 3;
+                  if (data.length + clen === 0) {
+                    check = 3;
+                  } else if (
+                    data.length + clen <
+                    value?.buttons.length + (value?.children.length || 0)
+                  ) {
+                    check = 2;
+                  } else {
+                    check = 1;
+                  }
+                  const d = {
+                    ...value,
+                    check,
+                    buttons: [...buttons],
                   };
-                });
-                const clen = (value?.children || []).filter((i: any) => i.check !== 3).length;
-                let check: number = 3;
-                if (data.length + clen === 0) {
-                  check = 3;
-                } else if (
-                  data.length + clen <
-                  value?.buttons.length + (value?.children.length || 0)
-                ) {
-                  check = 2;
-                } else {
-                  check = 1;
-                }
-                const d = {
-                  ...value,
-                  check,
-                  buttons: [...buttons],
-                };
-                props.change(d);
-              }}
-              options={(value?.buttons || []).map((i: any) => ({
-                label: i.name,
-                value: i.id,
-                key: i.id,
-              }))}
-            />
+                  props.change(d);
+                }}
+                options={(value?.buttons || []).map((i: any) => ({
+                  label: i.name,
+                  value: i.id,
+                  key: i.id,
+                }))}
+              />
+            )}
           </div>
         </div>
         <div

src/pages/system/Role/Edit/Permission/Allocate/index.less → src/pages/system/Role/Detail/Permission/Allocate/index.less


src/pages/system/Role/Edit/Permission/Allocate/index.tsx → src/pages/system/Role/Detail/Permission/Allocate/index.tsx


src/pages/system/Role/Edit/Permission/index.less → src/pages/system/Role/Detail/Permission/index.less


+ 28 - 4
src/pages/system/Role/Edit/Permission/index.tsx

@@ -1,5 +1,5 @@
 import { Button, Card, Col, Form, Input, message, Row } from 'antd';
-import Allocate from '@/pages/system/Role/Edit/Permission/Allocate';
+import Allocate from '@/pages/system/Role/Detail/Permission/Allocate';
 import { useEffect, useState } from 'react';
 import { history, useParams } from 'umi';
 import { service } from '@/pages/system/Role';
@@ -38,6 +38,29 @@ const Permission = () => {
     }
   }, [params, params.id]);
 
+  // const getDataList: any = (data1: any[]) => {
+  //   if (Array.isArray(data1) && data1.length > 0) {
+  //     return data1.map((item) => {
+  //       const check = item.check;
+  //       delete item.check;
+  //       return {
+  //         ...item,
+  //         granted: check === 1 || check === 2,
+  //         children: item?.children ? getDataList(item.children) : [],
+  //       };
+  //     });
+  //   }
+  //   return [];
+  // };
+  /**
+   * 扁平化树数组
+   */
+  const flattenArray: any = (arr: any[]) => {
+    return arr.reduce((result, item) => {
+      return result.concat(item, Array.isArray(item.children) ? flattenArray(item.children) : []);
+    }, []);
+  };
+
   const getDataList: any = (data1: any[]) => {
     if (Array.isArray(data1) && data1.length > 0) {
       return data1.map((item) => {
@@ -45,8 +68,8 @@ const Permission = () => {
         delete item.check;
         return {
           ...item,
-          granted: check !== 3,
-          children: item?.children ? getDataList(item.children) : [],
+          granted: check === 1,
+          children: item?.children,
         };
       });
     }
@@ -63,9 +86,10 @@ const Permission = () => {
           name: values?.name,
           description: values?.description || '',
         });
+        const list = getDataList(flattenArray([...values.permission?.children]) || []) || [];
         service
           .saveGrantTree('role', params?.id, {
-            menus: getDataList([...values.permission?.children]),
+            menus: list.filter((item: any) => item.granted) || [],
           })
           .subscribe((resp) => {
             if (resp.status === 200) {

src/pages/system/Role/Edit/UserManage/BindUser.tsx → src/pages/system/Role/Detail/UserManage/BindUser.tsx


src/pages/system/Role/Edit/UserManage/index.tsx → src/pages/system/Role/Detail/UserManage/index.tsx


+ 2 - 2
src/pages/system/Role/Edit/index.tsx

@@ -2,8 +2,8 @@ import { observer } from '@formily/react';
 import { PageContainer } from '@ant-design/pro-layout';
 import { useState } from 'react';
 import { history } from 'umi';
-import UserManage from '@/pages/system/Role/Edit/UserManage';
-import Permission from '@/pages/system/Role/Edit/Permission';
+import UserManage from '@/pages/system/Role/Detail/UserManage';
+import Permission from '@/pages/system/Role/Detail/Permission';
 import { useIntl } from '@@/plugin-locale/localeExports';
 
 const RoleEdit = observer(() => {

+ 2 - 1
src/pages/system/Role/index.tsx

@@ -11,6 +11,7 @@ import { Link, useLocation } from 'umi';
 import { Store } from 'jetlinks-store';
 import SystemConst from '@/utils/const';
 import { CurdModel } from '@/components/BaseCrud/model';
+import { getMenuPathByParams, MENUS_CODE } from '@/utils/menu';
 
 export const service = new Service('role');
 
@@ -82,7 +83,7 @@ const Role: React.FC = observer(() => {
       valueType: 'option',
       width: 200,
       render: (text, record) => [
-        <Link to={`/system/role/edit/${record.id}`} key="link">
+        <Link to={`${getMenuPathByParams(MENUS_CODE['system/Role/Detail'], record.id)}`} key="link">
           <Tooltip
             title={intl.formatMessage({
               id: 'pages.data.option.edit',

+ 2 - 7
src/pages/user/Login/index.tsx

@@ -1,6 +1,6 @@
 import { Checkbox, message, Spin } from 'antd';
 import React, { useEffect, useRef, useState } from 'react';
-import { history, Link } from 'umi';
+import { Link } from 'umi';
 import styles from './index.less';
 import Token from '@/utils/token';
 import Service from '@/pages/user/Login/service';
@@ -42,15 +42,10 @@ const Login: React.FC = () => {
 
   /** 此方法会跳转到 redirect 参数所在的位置 */
   const goto = () => {
-    if (!history) return;
     setTimeout(() => {
-      const { query } = history.location;
-      const { redirect } = query as {
-        redirect: string;
-      };
       // history.push(redirect || '/');
       // 用于触发app中的render,生成路由
-      window.location.href = redirect || '/';
+      window.location.href = '/';
       setLoading(false);
     }, 10);
   };

+ 4 - 1
src/utils/menu/index.ts

@@ -156,7 +156,10 @@ export const getMenuPathByCode = (code: string): string => {
  */
 export const getMenuPathByParams = (code: string, id?: string, regStr: string = ':id') => {
   const menusData = getMenuPathByCode(code);
-  return id ? menusData.replace(regStr, id) : menusData;
+  if (!menusData) {
+    console.warn('menusData is', menusData);
+  }
+  return id && menusData ? menusData.replace(regStr, id) : menusData;
 };
 
 export default getRoutes;

+ 2 - 2
src/utils/menu/router.ts

@@ -38,7 +38,6 @@ export const MENUS_CODE = {
   'link/Protocol/Debug': 'link/Protocol/Debug',
   'link/Protocol': 'link/Protocol',
   'link/Type': 'link/Type',
-  'link/Type/Save': 'link/Type/Save',
   'link/AccessConfig': 'link/AccessConfig',
   'log/Access': 'log/Access',
   'log/System': 'log/System',
@@ -59,7 +58,7 @@ export const MENUS_CODE = {
   'system/Menu': 'system/Menu',
   'system/OpenAPI': 'system/OpenAPI',
   'system/Permission': 'system/Permission',
-  'system/Role/Edit': 'system/Role/Edit',
+  'system/Role/Detail': 'system/Role/Detail',
   'system/Role': 'system/Role',
   'system/Tenant/Detail/Assets': 'system/Tenant/Detail/Assets',
   'system/Tenant/Detail/Info': 'system/Tenant/Detail/Info',
@@ -101,6 +100,7 @@ export const getDetailNameByCode = {
   'device/Instance/Detail': '设备详情',
   'device/Firmware/Detail': '固件详情',
   'system/Department/Detail': '部门详情',
+  'system/Role/Detail': '权限配置',
   'link/Type/Detail': '网络组件详情',
   'link/AccessConfig/Detail': '配置详情',
 };