Explorar el Código

feat: 完善初始化页面

xieyonghong hace 3 años
padre
commit
fdd361f1b5

+ 1 - 1
src/app.tsx

@@ -343,7 +343,7 @@ export function patchRoutes(routes: any) {
 }
 
 export function render(oldRender: any) {
-  if (history.location.pathname !== loginPath && history.location.pathname !== bindPath) {
+  if (![loginPath, bindPath].includes(history.location.pathname)) {
     // SystemConfigService.getAMapKey().then((res) => {
     //   if (res && res.status === 200 && res.result) {
     //     localStorage.setItem(SystemConst.AMAP_KEY, res.result.apiKey);

+ 1 - 1
src/components/FSelectDevice/index.tsx

@@ -53,7 +53,7 @@ const FSelectDevice = connect((props: Props) => {
       }),
       dataIndex: 'registryTime',
       width: '200px',
-      render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : '/'),
+      render: (text: any) => (text ? moment(text).format('YYYY-MM-DD HH:mm:ss') : ''),
       sorter: true,
     },
     {

+ 1 - 0
src/pages/device/Firmware/Task/Save/index.tsx

@@ -89,6 +89,7 @@ const Save = (props: Props) => {
     });
     if (resp.status === 200) {
       onlyMessage('保存成功!');
+      props.save();
     } else {
       onlyMessage('保存失败!', 'error');
     }

+ 45 - 5
src/pages/init-home/components/basis.tsx

@@ -1,16 +1,56 @@
 import { UploadImage } from '@/components';
 import { Col, Form, Input, Row, Select } from 'antd';
-import { useEffect } from 'react';
+import { useEffect, forwardRef, useImperativeHandle } from 'react';
+import { service } from '../index'
 
 interface Props {
-  getData: Function;
+  getData?: Function;
 }
 
-const Basis = (props: Props) => {
+const Basis = forwardRef((props: Props, ref) => {
   const [form] = Form.useForm();
 
+  const saveData = () => {
+    return new Promise(async resolve => {
+      const formData = await form.validateFields().catch(() => {
+        resolve(false)
+      })
+      if (formData) {
+        const item = [
+          {
+            scope: 'basis',
+            properties: {
+              ...formData,
+              apikey: '',
+            },
+          },
+          {
+            scope: 'api',
+            properties: {
+              api: formData.apikey,
+            },
+          },
+        ];
+        const res = await service.save(item)
+        if (res.status === 200) {
+          resolve(true)
+        } else {
+          resolve(false)
+        }
+      } else {
+        resolve(false)
+      }
+    })
+  }
+
+  useImperativeHandle(ref, () => ({
+    save: saveData
+  }))
+
   useEffect(() => {
-    props.getData(form);
+    if (props.getData) {
+      props.getData(form);
+    }
   }, []);
 
   return (
@@ -84,5 +124,5 @@ const Basis = (props: Props) => {
       </Row>
     </Form>
   );
-};
+});
 export default Basis;

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 13 - 653
src/pages/init-home/components/data/RoleData.ts


+ 65 - 69
src/pages/init-home/components/data/index.tsx

@@ -1,81 +1,77 @@
-import { useEffect, useState } from 'react';
+import {forwardRef, useImperativeHandle, useState} from 'react';
 import { service } from '../../index';
 import Save from './save';
-interface Props {
-  isTrigger: boolean;
-  onChange: (state: boolean) => void;
-}
 
-const Data = (props: Props) => {
+const Data = forwardRef((_, ref) => {
   const [flag, setFlag] = useState<boolean>(false);
   const [visible, setVisible] = useState<boolean>(false);
   const [values, setValues] = useState<any>({});
 
-  const handleChange = async () => {
-    if (Object.keys(values).length === 0) {
-      props.onChange(true);
-      return;
-    }
-    // 新增网络组件
-    const network = await service.saveNetwork({
-      type: 'MQTT_SERVER',
-      shareCluster: true,
-      name: 'MQTT网络组件',
-      configuration: {
-        host: '0.0.0.0',
-        secure: false,
-        port: values.port,
-        publicHost: values.publicHost,
-        publicPort: values.publicPort,
-      },
-    });
-    // 保存协议
-    const protocol = await service.saveProtocol();
-    let protocolItem: any = undefined;
-    if (protocol.status === 200) {
-      const proid = await service.getProtocol();
-      if (proid.status === 200) {
-        protocolItem = (proid?.result || []).find((it: any) => it.name === 'JetLinks官方协议');
+  const handleChange = () => {
+    return new Promise(async (resolve) => {
+      if (!Object.keys(values).length) {
+        return resolve(true)
       }
-    }
-    // 新增设备接入网关
-    const accessConfig = await service.saveAccessConfig({
-      name: 'MQTT类型设备接入网关',
-      provider: 'mqtt-server-gateway',
-      protocol: protocolItem?.id,
-      transport: 'MQTT',
-      channel: 'network',
-      channelId: network?.result?.id,
-    });
-    // 新增产品
-    const product = await service.saveProduct({
-      name: 'MQTT产品',
-      messageProtocol: protocolItem?.id,
-      protocolName: protocolItem?.name,
-      transportProtocol: 'MQTT',
-      deviceType: 'device',
-      accessId: accessConfig.result?.id,
-      accessName: accessConfig.result?.name,
-      accessProvider: 'mqtt-server-gateway',
-    });
-    // 新增设备
-    const device = await service.saveDevice({
-      name: 'MQTT设备',
-      productId: product?.result?.id,
-      productName: product?.result?.name,
-    });
-    if (device.status === 200) {
-      props.onChange(true);
-    } else {
-      props.onChange(false);
-    }
+      try {
+        // 新增网络组件
+        const network = await service.saveNetwork({
+          type: 'MQTT_SERVER',
+          shareCluster: true,
+          name: 'MQTT网络组件',
+          configuration: {
+            host: '0.0.0.0',
+            secure: false,
+            port: values.port,
+            publicHost: values.publicHost,
+            publicPort: values.publicPort,
+          },
+        });
+        // 保存协议
+        const protocol = await service.saveProtocol();
+        let protocolItem: any = undefined;
+        if (protocol.status === 200) {
+          const proid = await service.getProtocol();
+          if (proid.status === 200) {
+            protocolItem = (proid?.result || []).find((it: any) => it.name === 'JetLinks官方协议');
+          }
+        }
+        // 新增设备接入网关
+        const accessConfig = await service.saveAccessConfig({
+          name: 'MQTT类型设备接入网关',
+          provider: 'mqtt-server-gateway',
+          protocol: protocolItem?.id,
+          transport: 'MQTT',
+          channel: 'network',
+          channelId: network?.result?.id,
+        });
+        // 新增产品
+        const product = await service.saveProduct({
+          name: 'MQTT产品',
+          messageProtocol: protocolItem?.id,
+          protocolName: protocolItem?.name,
+          transportProtocol: 'MQTT',
+          deviceType: 'device',
+          accessId: accessConfig.result?.id,
+          accessName: accessConfig.result?.name,
+          accessProvider: 'mqtt-server-gateway',
+        });
+        // 新增设备
+        const device = await service.saveDevice({
+          name: 'MQTT设备',
+          productId: product?.result?.id,
+          productName: product?.result?.name,
+        });
+        resolve(device.status === 200)
+      } catch (e) {
+        console.log(e)
+        resolve(false)
+      }
+    })
   };
 
-  useEffect(() => {
-    if (props.isTrigger) {
-      handleChange();
-    }
-  }, [props.isTrigger]);
+  useImperativeHandle(ref, () => ({
+    save: handleChange
+  }))
 
   return (
     <div>
@@ -105,6 +101,6 @@ const Data = (props: Props) => {
       )}
     </div>
   );
-};
+});
 
 export default Data;

+ 1 - 0
src/pages/init-home/components/data/save/index.tsx

@@ -171,4 +171,5 @@ const Save = (props: Props) => {
     </Modal>
   );
 };
+
 export default Save;

+ 10 - 9
src/pages/init-home/components/menu.tsx

@@ -1,13 +1,8 @@
 import {useEffect, useRef, useState, forwardRef, useImperativeHandle} from "react";
 import BaseMenu from '@/pages/system/Menu/Setting/baseMenu'
 import {service} from '../index'
-import { observable } from '@formily/reactive';
 
-export const MenuDataModel = observable<{menuData: any[]}>({
-  menuData: []
-});
-
-const Menu = forwardRef((_, ref) => {
+const Menu = forwardRef((props: { onChange?: (menu: any) => void}, ref) => {
 
   const [count, setCount] = useState(0)
   const menuRef = useRef<any[]>()
@@ -50,14 +45,20 @@ const Menu = forwardRef((_, ref) => {
       const newTree = filterMenu(resp.result.map((item: any) => item.id), BaseMenu)
       const _count = menuCount(newTree)
       menuRef.current = newTree
-      MenuDataModel.menuData = newTree
       setCount(_count)
     }
   }
 
   useImperativeHandle(ref, () => ({
-    save: async () => {
-      console.log(menuRef.current)
+    save: () => {
+      return new Promise((resolve) => {
+        if (props.onChange) {
+          props.onChange(menuRef.current)
+        }
+        service.updateMenus(menuRef.current).then(res => {
+          resolve(res.status === 200)
+        })
+      })
     }
   }))
 

+ 50 - 14
src/pages/init-home/components/role.tsx

@@ -1,40 +1,76 @@
 import {Checkbox} from 'antd'
 import { useState, forwardRef, useImperativeHandle} from "react";
 import classNames from "classnames";
-import RoleData, {roleKeysType, ROLEKEYS} from "./data/RoleData"
+import RoleMenuData, {roleKeysType, ROLEKEYS, RoleData } from "./data/RoleData"
+import {service} from "@/pages/init-home";
 import '../index.less';
-import {MenuDataModel} from "@/pages/init-home/components/menu";
 
 const Role = forwardRef((_, ref) => {
 
   const [ keys, setKeys ] = useState<roleKeysType[]>([])
 
-  const findMenuByRole = (menu: any[], code: string): boolean => {
-    return menu.some(item => {
+  const findMenuByRole = (menu: any[], code: string): any => {
+    let _item = null
+    menu.some(item => {
       if (item.code === code) {
+        _item = item
         return true
       }
 
       if (item.children) {
-        return findMenuByRole(item.children, code)
+        const childrenItem = findMenuByRole(item.children, code)
+        if (childrenItem) {
+          _item = childrenItem
+          return true
+        }
+        return false
       }
 
-      return false
+      return null
     })
+    return _item
   }
 
   useImperativeHandle(ref, () => ({
     save: async () => {
-      console.log(keys)
-    // 获取当前选中的角色
-      const newRole = keys.map(item => {
-
-        const _roleData = (RoleData[item] as []).filter((roleItem: any) => {
-          return findMenuByRole(MenuDataModel.menuData, roleItem.code)
+      return new Promise((resolve) => {
+        if (!keys.length) {
+          return resolve(true)
+        }
+        let Count = 0
+        keys.forEach(async (item, index) => {
+          const _itemData = RoleData[item]
+          // 添加该角色
+          const res = await service.addRole(_itemData)
+          if (res.status === 200) {
+            const menuTree = await service.getRoleMenu(res.result.id)
+            if (menuTree.status === 200) {
+              const _roleData = (RoleMenuData[item] as []).filter((roleItem: any) => {
+                const _menu = findMenuByRole(menuTree.result, roleItem.code)
+                if (_menu) {
+                  roleItem.id = _menu.id
+                  roleItem.parentId = _menu.parentId
+                  roleItem.createTime = _menu.createTime
+                  return true
+                }
+                return false
+              })
+              //更新权限
+              const roleRes = await service.updateRoleMenu(res.result.id, {menus: _roleData})
+              if (roleRes.status === 200) {
+                Count += 1
+              }
+              if (index === keys.length - 1) {
+                resolve(Count === keys.length)
+              }
+            } else if (index === keys.length - 1){
+              resolve(Count === keys.length)
+            }
+          } else if (index === keys.length - 1){
+            resolve(Count === keys.length)
+          }
         })
-        return { menus: _roleData }
       })
-      console.log(newRole)
     }
   }), [keys])
 

+ 29 - 13
src/pages/init-home/index.less

@@ -22,12 +22,12 @@
 
       .left {
         width: 30px;
-        height: 100%;
+        height: 1000px;
       }
 
       .right {
-        width: calc(100% - 50px);
-        height: 1200px;
+        width: calc(100% - 70px);
+        // height: 1200px;
 
         .collapseTitle {
           display: flex;
@@ -44,6 +44,22 @@
         }
       }
     }
+
+    ::-webkit-scrollbar {
+      width: 12px;
+    }
+
+    /* 滚动槽 */
+    ::-webkit-scrollbar-track {
+      background: #f2f2f2;
+      border-radius: 8px;
+    }
+
+    /* 滚动条滑块 */
+    ::-webkit-scrollbar-thumb {
+      background: #cecece;
+      border-radius: 8px;
+    }
   }
 }
 
@@ -53,25 +69,25 @@
     gap: 24px;
 
     .role-item {
+      position: relative;
       display: flex;
       flex-direction: column;
       justify-content: space-between;
-      padding: 24px;
-      border: 1px solid rgba(245, 245, 245, 1);
-      position: relative;
       margin-bottom: 30px;
+      padding: 24px;
       background-repeat: no-repeat;
       background-position: center;
       background-size: 370px;
+      border: 1px solid rgba(245, 245, 245, 1);
 
       .role-item-title {
         display: flex;
 
         > div {
           flex: 1 1 auto;
-          font-size: 16px;
-          font-weight: bold;
           padding-left: 90px;
+          font-weight: bold;
+          font-size: 16px;
         }
       }
 
@@ -82,29 +98,29 @@
       }
 
       &.role-image-1 {
-        background-image: url("/images/init-home/role1.png");
+        background-image: url('/images/init-home/role1.png');
       }
 
       &.role-image-2 {
-        background-image: url("/images/init-home/role2.png");
+        background-image: url('/images/init-home/role2.png');
       }
 
       &.role-image-3 {
-        background-image: url("/images/init-home/role3.png");
+        background-image: url('/images/init-home/role3.png');
       }
 
       .role-item-footer {
         position: absolute;
         bottom: -30px;
         left: 0;
-        text-align: center;
         width: 100%;
         color: #999;
         font-size: 12px;
+        text-align: center;
       }
 
       &.active {
-        background-color: #F5F5F5;
+        background-color: #f5f5f5;
       }
     }
   }

+ 115 - 50
src/pages/init-home/index.tsx

@@ -1,75 +1,140 @@
 import { TitleComponent } from '@/components';
-import { Button, Collapse, Steps } from 'antd';
+import {Button, Collapse, Spin} from 'antd';
 import styles from './index.less';
 import Basis from './components/basis';
 import Menu from './components/menu';
 import Role from './components/role';
 import Data from './components/data';
 import Service from './service';
+import { useHistory } from 'umi'
+import {useState, useRef, useEffect} from 'react';
+import BaseMenu from '@/pages/system/Menu/Setting/baseMenu'
 
 export const service = new Service();
 
 const InitHome = () => {
+  const [loadings, setLoadings] = useState<boolean>(false);
+  const [, setCurrent] = useState<number>(0);
+  const history = useHistory()
+
+  const cacheRef = useRef<Set<string>>()
+
+  const baseRef = useRef<{ save: any }>()
+  const menuRef = useRef<{ save: any }>()
+  const roleRef = useRef<{ save: any }>()
+  const dataRef = useRef<{ save: any }>()
+
+  const jump = () => {
+    history.push(BaseMenu[0].url)
+  }
+
+  useEffect(() => {
+    service.getInit().then(res => {
+      if (res.status === 200 && res.result.length) {
+        jump()
+      }
+    })
+  }, [])
+
   return (
     <div className={styles.init}>
       <TitleComponent data={'系统初始化'} />
       <div className={styles.box}>
         <div className={styles.container}>
-          <div className={styles.left}>
-            <Steps direction="vertical" current={1} percent={60} style={{ height: '100%' }}>
-              <Steps.Step />
-              <Steps.Step />
-              <Steps.Step />
-              <Steps.Step />
-            </Steps>
-          </div>
+          {/*<div className={styles.left}>*/}
+          {/*  <Steps direction="vertical" current={current} percent={60} style={{ height: '100%' }}>*/}
+          {/*    <Steps.Step />*/}
+          {/*    <Steps.Step />*/}
+          {/*    <Steps.Step />*/}
+          {/*    <Steps.Step />*/}
+          {/*  </Steps>*/}
+          {/*</div>*/}
           <div className={styles.right}>
-            <Collapse defaultActiveKey={['1', '2', '3', '4']}>
-              <Collapse.Panel
-                header={
-                  <div className={styles.collapseTitle}>
-                    基本信息
-                    <div className={styles.collapseDesc}>
-                      配置平台名称、登录背景图、主题色等基本信息
+            <Spin spinning={loadings}>
+              <Collapse defaultActiveKey={['1', '2', '3', '4']}>
+                <Collapse.Panel
+                  header={
+                    <div className={styles.collapseTitle}>
+                      基本信息
+                      <div className={styles.collapseDesc}>
+                        配置平台名称、登录背景图、主题色等基本信息
+                      </div>
                     </div>
-                  </div>
+                  }
+                  key="1"
+                >
+                  <Basis ref={baseRef} />
+                </Collapse.Panel>
+                <Collapse.Panel
+                  header={
+                    <div className={styles.collapseTitle}>
+                      菜单初始化<div className={styles.collapseDesc}>初始化菜单数据</div>
+                    </div>
+                  }
+                  key="2"
+                >
+                  <Menu ref={menuRef} />
+                </Collapse.Panel>
+                <Collapse.Panel
+                  header={
+                    <div className={styles.collapseTitle}>
+                      角色初始化<div className={styles.collapseDesc}>初始化内置角色与权限数据</div>
+                    </div>
+                  }
+                  key="3"
+                >
+                  <Role ref={roleRef} />
+                </Collapse.Panel>
+                <Collapse.Panel
+                  header={
+                    <div className={styles.collapseTitle}>
+                      初始数据<div className={styles.collapseDesc}>初始化设备接入示例数据</div>
+                    </div>
+                  }
+                  key="4"
+                >
+                  <Data ref={dataRef} />
+                </Collapse.Panel>
+              </Collapse>
+            </Spin>
+            <Button
+              type="primary"
+              style={{ marginTop: 20 }}
+              loading={loadings}
+              onClick={async () => {
+                setLoadings(true);
+                setCurrent(0);
+                if (!cacheRef.current?.has('base')) {
+                  const baseRes = await baseRef.current?.save()
+                  if (!baseRes) { return  setLoadings(false) }
+                  cacheRef.current?.add('base')
                 }
-                key="1"
-              >
-                <Basis getData={() => {}} />
-              </Collapse.Panel>
-              <Collapse.Panel
-                header={
-                  <div className={styles.collapseTitle}>
-                    菜单初始化<div className={styles.collapseDesc}>初始化菜单数据</div>
-                  </div>
+
+                if (!cacheRef.current?.has('menu')) {
+                  const menuRes = await menuRef.current?.save()
+                  if (!menuRes) { return  setLoadings(false) }
+                  cacheRef.current?.add('menu')
                 }
-                key="2"
-              >
-                <Menu />
-              </Collapse.Panel>
-              <Collapse.Panel
-                header={
-                  <div className={styles.collapseTitle}>
-                    角色初始化<div className={styles.collapseDesc}>初始化内置角色与权限数据</div>
-                  </div>
+
+                if (!cacheRef.current?.has('role')) {
+                  const roleRes = await roleRef.current?.save()
+                  if (!roleRes) { return  setLoadings(false) }
+                  cacheRef.current?.add('role')
                 }
-                key="3"
-              >
-                <Role />
-              </Collapse.Panel>
-              <Collapse.Panel
-                header={
-                  <div className={styles.collapseTitle}>
-                    初始数据<div className={styles.collapseDesc}>初始化设备接入示例数据</div>
-                  </div>
+
+                if (!cacheRef.current?.has('data')) {
+                  const dataRes = await dataRef.current?.save()
+                  if (!dataRes) { return  setLoadings(false) }
+                  cacheRef.current?.add('data')
                 }
-                key="4"
-              >
-                <Data isTrigger={false} onChange={() => {}} />
-              </Collapse.Panel>
-            </Collapse>
-            <Button type="primary" style={{ marginTop: 20 }}>
+                //  记录当前
+                service.saveInit().then(res => {
+                  if (res.status === 200) {
+                    jump()
+                  }
+                })
+              }}
+            >
               确认
             </Button>
           </div>

+ 21 - 1
src/pages/init-home/service.ts

@@ -46,7 +46,27 @@ class Service extends BaseService<any> {
       data,
     });
   getPermissionAll = () =>
-    request(`${SystemConst.API_BASE}/permission/_query/no-paging?paging=false`,);
+    request(`${SystemConst.API_BASE}/permission/_query/no-paging?paging=false`);
+
+  // 更新全部菜单
+  updateMenus = (data: any) => request(`${SystemConst.API_BASE}/menu/_all`, { method: 'PATCH', data });
+
+  // 添加角色
+  addRole = (data: any) =>
+    request(`/${SystemConst.API_BASE}/role`, { method: 'POST', data })
+
+  // 更新权限菜单
+  getRoleMenu = (id: string) =>
+    request(`/${SystemConst.API_BASE}/menu/role/${id}/_grant/tree`, { method: 'GET' })
+
+  // 更新权限菜单
+  updateRoleMenu = (id: string, data: any) =>
+    request(`/${SystemConst.API_BASE}/menu/role/${id}/_grant`, { method: 'PUT', data })
+
+//  记录初始化
+  saveInit = () => request(`/${SystemConst.API_BASE}/user/settings/init`, { method: 'POST', data: { init: true} })
+//  获取初始化
+  getInit = () => request(`/${SystemConst.API_BASE}/user/settings/init`, { method: 'GET' })
 }
 
 export default Service;

+ 176 - 168
src/pages/media/Device/Playback/index.tsx

@@ -2,7 +2,7 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import LivePlayer from '@/components/Player';
 import { useCallback, useEffect, useRef, useState } from 'react';
-import {Calendar, Empty, List, Spin, Tooltip} from 'antd';
+import { Calendar, Empty, List, Spin, Tooltip } from 'antd';
 import { useLocation } from 'umi';
 import Service from './service';
 import './index.less';
@@ -62,7 +62,7 @@ const IconNode = (props: IconNodeType) => {
   return (
     <a
       onClick={() => {
-        console.log(props,status)
+        console.log(props, status);
         if (props.type === 'local') {
           if (status === 2) {
             // 已下载,进行跳转
@@ -86,7 +86,7 @@ export default () => {
   const [type, setType] = useState('local');
   const [historyList, setHistoryList] = useState<recordsItemType[]>([]);
   const [time, setTime] = useState<Moment | undefined>(undefined);
-  const [loading, setLoading] = useState(false)
+  const [loading, setLoading] = useState(false);
   const [cloudTime, setCloudTime] = useState<any>();
   const location = useLocation();
   const player = useRef<any>();
@@ -104,7 +104,7 @@ export default () => {
     setPlayStatus(0);
     setUrl('');
     if (deviceId && channelId && date) {
-      setLoading(true)
+      setLoading(true);
       const params = {
         startTime: date.format('YYYY-MM-DD 00:00:00'),
         endTime: date.format('YYYY-MM-DD 23:59:59'),
@@ -115,7 +115,7 @@ export default () => {
           ...params,
           includeFiles: false,
         });
-        setLoading(false)
+        setLoading(false);
         let newList: recordsItemType[] = serviceResp.result;
 
         if (serviceResp.status === 200 && serviceResp.result) {
@@ -123,11 +123,13 @@ export default () => {
           newList = localResp.result.map((item: recordsItemType) => {
             return {
               ...item,
-              isServer: serviceResp.result.length ? serviceResp.result.some(
-                (serverFile: any) =>
-                  item.startTime >= serverFile.streamStartTime &&
-                  serverFile.streamEndTime <= item.endTime,
-              ) : false,
+              isServer: serviceResp.result.length
+                ? serviceResp.result.some(
+                    (serverFile: any) =>
+                      item.startTime >= serverFile.streamStartTime &&
+                      serverFile.streamEndTime <= item.endTime,
+                  )
+                : false,
             };
           });
           setHistoryList(newList);
@@ -135,7 +137,7 @@ export default () => {
           setHistoryList(newList);
         }
       } else {
-        setLoading(false)
+        setLoading(false);
         setHistoryList([]);
       }
     }
@@ -145,33 +147,39 @@ export default () => {
    * 查询云端视频
    * @param date
    */
-  const queryServiceRecords = useCallback(async (date: Moment) => {
-    setPlayStatus(0);
-    setUrl('');
-    if (deviceId && channelId && date) {
-      setLoading(true)
-      const params = {
-        startTime: date.format('YYYY-MM-DD 00:00:00'),
-        endTime: date.format('YYYY-MM-DD 23:59:59'),
-        includeFiles: true,
-      };
+  const queryServiceRecords = useCallback(
+    async (date: Moment) => {
+      setPlayStatus(0);
+      setUrl('');
+      if (deviceId && channelId && date) {
+        setLoading(true);
+        const params = {
+          startTime: date.format('YYYY-MM-DD 00:00:00'),
+          endTime: date.format('YYYY-MM-DD 23:59:59'),
+          includeFiles: true,
+        };
 
-      const resp = await service.recordsInServerFiles(deviceId, channelId, params);
-      setLoading(false)
-      if (resp.status === 200) {
-        setHistoryList(resp.result);
+        const resp = await service.recordsInServerFiles(deviceId, channelId, params);
+        setLoading(false);
+        if (resp.status === 200) {
+          setHistoryList(resp.result);
+        }
       }
-    }
-  }, [deviceId, channelId])
+    },
+    [deviceId, channelId],
+  );
 
-  const cloudView = useCallback((startTime: number, endTime: number) => {
-    setType('cloud');
-    setCloudTime({
-      startTime,
-      endTime,
-    });
-    queryServiceRecords(time!);
-  }, [time]);
+  const cloudView = useCallback(
+    (startTime: number, endTime: number) => {
+      setType('cloud');
+      setCloudTime({
+        startTime,
+        endTime,
+      });
+      queryServiceRecords(time!);
+    },
+    [time],
+  );
 
   const downloadClick = async (item: recordsItemType) => {
     const downloadUrl = service.downLoadFile(item.id);
@@ -250,49 +258,47 @@ export default () => {
         </div>
         <div className={'playback-right'}>
           <Spin spinning={loading}>
-
-
-          <Tooltip
-            // title={<>云端:存储在服务器中 本地:存储在设备本地</>}
-            title={
-              <>
-                <div>云端:存储在服务器中</div>
-                <div>本地:存储在设备本地</div>
-              </>
-            }
-            placement="topLeft"
-          >
-            <div>
-              类型: <QuestionCircleOutlined />
-            </div>
-          </Tooltip>
-          <RadioCard
-            model={'singular'}
-            value={type}
-            itemStyle={{ minWidth: 0, width: '100%' }}
-            onSelect={(key: string) => {
-              setType(key);
-              console.log(key);
-              if (key === 'cloud') {
-                queryServiceRecords(time!);
-              } else {
-                queryLocalRecords(time!);
+            <Tooltip
+              // title={<>云端:存储在服务器中 本地:存储在设备本地</>}
+              title={
+                <>
+                  <div>云端:存储在服务器中</div>
+                  <div>本地:存储在设备本地</div>
+                </>
               }
-            }}
-            options={[
-              {
-                label: '云端',
-                value: 'cloud',
-                imgUrl: require('/public/images/media/cloud.png'),
-              },
-              {
-                label: '本地',
-                value: 'local',
-                imgUrl: require('/public/images/local.png'),
-              },
-            ]}
-          />
-          {/* <Select
+              placement="topLeft"
+            >
+              <div>
+                类型: <QuestionCircleOutlined />
+              </div>
+            </Tooltip>
+            <RadioCard
+              model={'singular'}
+              value={type}
+              itemStyle={{ minWidth: 0, width: '100%' }}
+              onSelect={(key: string) => {
+                setType(key);
+                console.log(key);
+                if (key === 'cloud') {
+                  queryServiceRecords(time!);
+                } else {
+                  queryLocalRecords(time!);
+                }
+              }}
+              options={[
+                {
+                  label: '云端',
+                  value: 'cloud',
+                  imgUrl: require('/public/images/media/cloud.png'),
+                },
+                {
+                  label: '本地',
+                  value: 'local',
+                  imgUrl: require('/public/images/local.png'),
+                },
+              ]}
+            />
+            {/* <Select
             value={type}
             options={[
               { label: '云端', value: 'cloud' },
@@ -308,101 +314,103 @@ export default () => {
               }
             }}
           /> */}
-          <div className={'playback-calendar'}>
-            <Calendar
-              value={time}
-              onChange={(date) => {
-                setTime(date);
-                if (type === 'cloud') {
-                  queryServiceRecords(date);
-                } else {
-                  queryLocalRecords(date);
-                }
-              }}
-              disabledDate={(currentDate) => currentDate > moment(new Date())}
-              fullscreen={false}
-            />
-          </div>
-          <div className={classNames('playback-list', { 'no-list': !historyList.length })}>
-            {historyList && historyList.length ? (
-              <List
-                className={'playback-list-items'}
-                itemLayout="horizontal"
-                dataSource={historyList}
-                renderItem={(item) => {
-                  const _startTime = item.startTime || item.mediaStartTime;
-                  const startTime = moment(item.startTime || item.mediaStartTime).format(
-                    'HH:mm:ss',
-                  );
-                  const endTime = moment(item.endTime || item.mediaEndTime).format('HH:mm:ss');
-                  let title = '下载到云端';
-
-                  if (type === 'local') {
-                    if (item.isServer) {
-                      title = '查看';
-                    }
+            <div className={'playback-calendar'}>
+              <Calendar
+                value={time}
+                onChange={(date) => {
+                  setTime(date);
+                  if (type === 'cloud') {
+                    queryServiceRecords(date);
                   } else {
-                    title = '下载录像文件';
+                    queryLocalRecords(date);
                   }
-                  return (
-                    <List.Item
-                      actions={[
-                        <Tooltip
-                          key="play-btn"
-                          title={
-                            _startTime === playNowTime.current && playStatus === 1 ? '暂停' : '播放'
-                          }
-                        >
-                          <a
-                            onClick={() => {
-                              if (playStatus === 0 || _startTime !== playNowTime.current) {
-                                if (playTimeNode.current) {
-                                  playTimeNode.current.playByStartTime(_startTime);
-                                }
-                              } else if (playStatus == 1 && _startTime === playNowTime.current) {
-                                if (player.current.getVueInstance) {
-                                  player.current.getVueInstance().pause();
-                                }
-                              } else if (playStatus == 2 && _startTime === playNowTime.current) {
-                                if (player.current.getVueInstance) {
-                                  player.current.getVueInstance().play();
-                                }
-                              }
-                            }}
+                }}
+                disabledDate={(currentDate) => currentDate > moment(new Date())}
+                fullscreen={false}
+              />
+            </div>
+            <div className={classNames('playback-list', { 'no-list': !historyList.length })}>
+              {historyList && historyList.length ? (
+                <List
+                  className={'playback-list-items'}
+                  itemLayout="horizontal"
+                  dataSource={historyList}
+                  renderItem={(item) => {
+                    const _startTime = item.startTime || item.mediaStartTime;
+                    const startTime = moment(item.startTime || item.mediaStartTime).format(
+                      'HH:mm:ss',
+                    );
+                    const endTime = moment(item.endTime || item.mediaEndTime).format('HH:mm:ss');
+                    let title = '下载到云端';
+
+                    if (type === 'local') {
+                      if (item.isServer) {
+                        title = '查看';
+                      }
+                    } else {
+                      title = '下载录像文件';
+                    }
+                    return (
+                      <List.Item
+                        actions={[
+                          <Tooltip
+                            key="play-btn"
+                            title={
+                              _startTime === playNowTime.current && playStatus === 1
+                                ? '暂停'
+                                : '播放'
+                            }
                           >
-                            {_startTime === playNowTime.current && playStatus === 1 ? (
-                              <PauseCircleOutlined />
-                            ) : (
-                              <PlayCircleOutlined />
-                            )}
-                          </a>
-                        </Tooltip>,
+                            <a
+                              onClick={() => {
+                                if (playStatus === 0 || _startTime !== playNowTime.current) {
+                                  if (playTimeNode.current) {
+                                    playTimeNode.current.playByStartTime(_startTime);
+                                  }
+                                } else if (playStatus == 1 && _startTime === playNowTime.current) {
+                                  if (player.current.getVueInstance) {
+                                    player.current.getVueInstance().pause();
+                                  }
+                                } else if (playStatus == 2 && _startTime === playNowTime.current) {
+                                  if (player.current.getVueInstance) {
+                                    player.current.getVueInstance().play();
+                                  }
+                                }
+                              }}
+                            >
+                              {_startTime === playNowTime.current && playStatus === 1 ? (
+                                <PauseCircleOutlined />
+                              ) : (
+                                <PlayCircleOutlined />
+                              )}
+                            </a>
+                          </Tooltip>,
 
-                        <Tooltip key={'download'} title={title}>
-                          <IconNode
-                            type={type}
-                            item={item}
-                            onCloudView={cloudView}
-                            onDownLoad={() => {
-                              downloadClick(item);
-                            }}
-                          />
-                        </Tooltip>,
-                      ]}
-                    >
-                      <div style={{ textAlign: 'center', paddingLeft: 10 }}>
-                        {`${startTime}`} ~ {`${endTime}`}
-                      </div>
-                    </List.Item>
-                  );
-                }}
-              >
-                <div></div>
-              </List>
-            ) : (
-              <Empty />
-            )}
-          </div>
+                          <Tooltip key={'download'} title={title}>
+                            <IconNode
+                              type={type}
+                              item={item}
+                              onCloudView={cloudView}
+                              onDownLoad={() => {
+                                downloadClick(item);
+                              }}
+                            />
+                          </Tooltip>,
+                        ]}
+                      >
+                        <div style={{ textAlign: 'center', paddingLeft: 10 }}>
+                          {`${startTime}`} ~ {`${endTime}`}
+                        </div>
+                      </List.Item>
+                    );
+                  }}
+                >
+                  <div></div>
+                </List>
+              ) : (
+                <Empty />
+              )}
+            </div>
           </Spin>
         </div>
       </div>

+ 1 - 1
src/pages/rule-engine/Alarm/Config/Save/input.tsx

@@ -106,7 +106,7 @@ const InputSave = (props: Props) => {
       state: {
         title: '状态',
         type: 'string',
-        required: true,
+        // required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Switch',
         default: false,

+ 1 - 1
src/pages/rule-engine/Alarm/Config/Save/output.tsx

@@ -103,7 +103,7 @@ const OutputSave = (props: Props) => {
       state: {
         title: '状态',
         type: 'string',
-        required: true,
+        // required: true,
         'x-decorator': 'FormItem',
         'x-component': 'Switch',
         default: false,

+ 37 - 27
src/pages/system/Menu/Setting/baseMenu.ts

@@ -588,36 +588,46 @@ export default [
                 url: '/iot/link/Channel/Modbus',
                 icon: 'icon-changjingliandong',
                 permissions: [],
-                buttons: [
-                  {
-                    id: 'update',
-                    name: '编辑',
-                    permissions: [{ permission: 'modbus-master', actions: ['query', 'save'] }],
-                  },
-                  {
-                    id: 'action',
-                    name: '启/禁用',
-                    permissions: [{ permission: 'modbus-master', actions: ['query', 'save'] }],
-                  },
-                  {
-                    id: 'view',
-                    name: '设备接入',
-                    permissions: [{ permission: 'modbus-master', actions: ['query', 'save'] }],
-                  },
-                  {
-                    id: 'delete',
-                    name: '删除',
-                    permissions: [{ permission: 'modbus-master', actions: ['query', 'delete'] }],
-                  },
-                  {
-                    id: 'add',
-                    name: '新增',
-                    permissions: [{ permission: 'modbus-master', actions: ['query', 'save'] }],
-                  },
-                ],
               },
             ],
           },
+          {
+            code: 'device/Firmware',
+            name: '远程升级',
+            parentId: '1-4',
+            id: '1-4-9',
+            url: '/iot/link/firmware',
+            icon: 'icon-wangluozujian',
+            permissions: [
+              {permission: "firmware-manager", actions: ["query", "save", "delete"]},
+              {permission: "firmware-upgrade-task-manager", actions: ["query", "save", "delete", "deploy"]},
+              {permission: "device-product", actions: ["query"]},
+              {permission: "device-api", actions: ["query"]},
+            ],
+            buttons: [
+              {
+                id: 'update',
+                name: '编辑',
+                permissions: [{ permission: 'firmware-upgrade-task-manager', actions: ['save'] }],
+              },
+              {
+                id: 'action',
+                name: '启/禁用',
+                permissions: [{ permission: 'firmware-upgrade-task-manager', actions: ['deploy'] }],
+              },
+              {
+                id: 'delete',
+                name: '删除',
+                permissions: [{ permission: 'firmware-upgrade-task-manager', actions: ['delete'] }],
+              },
+              {
+                id: 'add',
+                name: '新增',
+                permissions: [{ permission: 'firmware-upgrade-task-manager', actions: ['save'] }],
+              },
+            ],
+          },
+
         ],
       },
 

+ 3 - 3
src/pages/system/Menu/Setting/index.tsx

@@ -7,7 +7,7 @@ import {
 } from '@ant-design/icons';
 import Tree from './tree';
 import './index.less';
-import {Button, message, Tooltip} from 'antd';
+import { Button, message, Tooltip } from 'antd';
 import BaseTreeData from './baseMenu';
 import { useEffect, useState } from 'react';
 import { HTML5Backend } from 'react-dnd-html5-backend';
@@ -169,11 +169,11 @@ export default observer(() => {
       service.updateMenus(MenuSettingModel.menuData).then((res) => {
         setLoading(false);
         if (res.status === 200) {
-          message.success('操作成功')
+          message.success('操作成功');
         }
       });
     } else {
-      message.warning('请配置系统菜单')
+      message.warning('请配置系统菜单');
     }
   };
 

+ 15 - 5
src/pages/user/Login/index.tsx

@@ -1,5 +1,5 @@
 import { Button, Checkbox, Divider, message, Spin } from 'antd';
-import React, { useEffect, useRef, useState } from 'react';
+import React, { useEffect, useRef, useState, useMemo } from 'react';
 import { Link } from 'umi';
 import styles from './index.less';
 import Token from '@/utils/token';
@@ -33,14 +33,16 @@ const Login: React.FC = () => {
         ...initialState,
         currentUser: userInfo,
       });
+      return userInfo
     }
+    return null
   };
 
   const loginRef = useRef<Partial<LoginParam>>({});
-  const loginForm = createForm({
+  const loginForm = useMemo(() => createForm({
     validateFirst: true,
     initialValues: loginRef.current,
-  });
+  }), [captcha]);
 
   const [loading, setLoading] = useState<boolean>(false);
 
@@ -158,7 +160,15 @@ const Login: React.FC = () => {
       {
         next: async (userInfo) => {
           Token.set(userInfo.token);
-          await fetchUserInfo();
+          const userRef: any = await fetchUserInfo();
+          if (userRef?.user?.username === 'admin') {
+            const initRef = await Service.initPage()
+            if (initRef.status === 200 && !initRef.result.length) {
+              window.location.href = '/#/init-home';
+              setLoading(false);
+              return
+            }
+          }
           // goto();
           window.location.href = '/';
           setLoading(false);
@@ -174,7 +184,7 @@ const Login: React.FC = () => {
           // setLoading(false);
         },
         complete: () => {
-          getCode();
+          // getCode();
           // setLoading(false);
         },
       },

+ 1 - 0
src/pages/user/Login/service.ts

@@ -60,6 +60,7 @@ const Service = {
     request(`/${SystemConst.API_BASE}/user/detail`, {
       method: 'GET',
     }),
+  initPage: () => request(`/${SystemConst.API_BASE}/user/settings/init`, { method: 'GET' })
 };
 
 export default Service;