浏览代码

fix(merge): merge wzy

lind 3 年之前
父节点
当前提交
5b7fc4a5bb

+ 4 - 4
config/proxy.ts

@@ -12,11 +12,11 @@ export default {
       // target: 'http://192.168.32.44:8844/',
       // ws: 'ws://192.168.32.44:8844/',
       // 开发环境
-      target: 'http://120.79.18.123:8844/',
-      ws: 'ws://120.79.18.123:8844/',
+      // target: 'http://120.79.18.123:8844/',
+      // ws: 'ws://120.79.18.123:8844/',
       // 测试环境
-      // target: 'http://120.77.179.54:8844/',
-      // ws: 'ws://120.77.179.54:8844/',
+      target: 'http://120.77.179.54:8844/',
+      ws: 'ws://120.77.179.54:8844/',
       // target: 'http://192.168.66.5:8844/',
       // ws: 'ws://192.168.66.5:8844/',
       // ws: 'ws://demo.jetlinks.cn/jetlinks',

二进制
public/images/bind/dingtalk.png


二进制
public/images/bind/wechat-webapp.png


+ 15 - 2
src/app.tsx

@@ -43,13 +43,24 @@ export async function getInitialState(): Promise<{
     }
     return undefined;
   };
+  const getSettings = async () => {
+    try {
+      const res = await Service.settingDetail(['basis']);
+      return res.result;
+    } catch (error) {
+      history.push(loginPath);
+    }
+    return undefined;
+  };
+
   // 如果是登录页面,不执行
   if (history.location.pathname !== loginPath && history.location.pathname !== bindPath) {
     const currentUser = await fetchUserInfo();
+    const settings = await getSettings();
     return {
       fetchUserInfo,
       currentUser,
-      settings: {},
+      settings: settings?.[0].properties,
     };
   }
   // 链接websocket
@@ -189,6 +200,7 @@ export const request: RequestConfig = {
 
 // ProLayout 支持的api https://procomponents.ant.design/components/layout
 export const layout: RunTimeLayoutConfig = ({ initialState }) => {
+  console.log({ ...initialState });
   return {
     navTheme: 'light',
     headerTheme: 'light',
@@ -238,7 +250,8 @@ export const layout: RunTimeLayoutConfig = ({ initialState }) => {
     // 自定义 403 页面
     // unAccessible: <div>unAccessible</div>,
     ...initialState?.settings,
-    title: '',
+    // title: '',
+    // logo:''
   };
 };
 

+ 0 - 9
src/pages/account/Center/edit/passwordEdit.tsx

@@ -210,19 +210,10 @@ const PasswordEdit = (props: Props) => {
       onOk={async () => {
         const value: { oldPassword: string; newPassword: string; confirmPassword: string } =
           await form.submit();
-        console.log(value);
         props.save({
           oldPassword: value.oldPassword,
           newPassword: value.newPassword,
         });
-        // if (props.data.id) {
-        //   const resp = await service.resetPassword(props.data.id, value.confirmPassword);
-        //   if (resp.status === 200) {
-        //     message.success('操作成功');
-        //     props.close();
-        //   }
-        // }
-        // props.close();
       }}
     >
       <Form form={form} layout="vertical">

+ 1 - 0
src/pages/account/Center/index.tsx

@@ -320,6 +320,7 @@ const Center = () => {
         <PasswordEdit
           save={(item: any) => {
             savePassword(item);
+            setPassword(false);
           }}
           visible={password}
           close={() => {

+ 49 - 0
src/pages/device/DashBoard/index.less

@@ -0,0 +1,49 @@
+.device-dash-board {
+  .top-card-items {
+    margin-bottom: 12px;
+
+    .top-card-item {
+      display: flex;
+      flex-direction: column;
+      justify-content: space-between;
+      width: 25%;
+      padding: 6px 24px;
+      border: 1px solid #e3e3e3;
+
+      .top-card-top {
+        display: flex;
+        padding: 12px 0;
+
+        .top-card-top-left {
+          width: 80px;
+        }
+
+        .top-card-top-right {
+          .top-card-total {
+            font-weight: bold;
+            font-size: 20px;
+            line-height: 50px;
+          }
+        }
+
+        .top-card-top-charts {
+          display: flex;
+          flex-direction: column;
+          width: 100%;
+
+          .top-card-top-charts-total {
+            font-weight: bold;
+            font-size: 18px;
+          }
+        }
+      }
+
+      .top-card-bottom {
+        display: flex;
+        justify-content: space-between;
+        padding: 12px 0;
+        border-top: 1px solid #e3e3e3;
+      }
+    }
+  }
+}

+ 377 - 0
src/pages/device/DashBoard/index.tsx

@@ -0,0 +1,377 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import { Badge, Card } from 'antd';
+import { useEffect, useRef, useState } from 'react';
+import './index.less';
+import Service from './service';
+import encodeQuery from '@/utils/encodeQuery';
+import { useRequest } from 'umi';
+import DashBoard from '@/components/DashBoard';
+import type { EChartsOption } from 'echarts';
+import Echarts from '@/components/DashBoard/echarts';
+import moment from 'moment';
+import { AMap } from '@/components';
+import { Marker } from 'react-amap';
+import { EnvironmentOutlined } from '@ant-design/icons';
+
+interface TopCardProps {
+  isEcharts: boolean;
+  title: string;
+  total?: number | string;
+  topRender?: any;
+  bottomRender: () => React.ReactNode;
+}
+
+type RefType = {
+  getValues: Function;
+};
+
+const service = new Service('device/instance');
+const TopCard = (props: TopCardProps) => {
+  return (
+    <div className={'top-card-item'}>
+      {props.isEcharts ? (
+        <div className={'top-card-top'}>
+          <div className={'top-card-top-charts'}>
+            <div>{props.title}</div>
+            <div className={'top-card-top-charts-total'}>{props.total}</div>
+            <div style={{ height: 45, width: '100%' }}>{props.topRender}</div>
+          </div>
+        </div>
+      ) : (
+        <div className={'top-card-top'}>
+          <div className={'top-card-top-left'}></div>
+          <div className={'top-card-top-right'}>
+            <div className={'top-card-title'}>{props.title}</div>
+            <div className={'top-card-total'}>{props.total}</div>
+          </div>
+        </div>
+      )}
+
+      <div className={'top-card-bottom'}>{props.bottomRender()}</div>
+    </div>
+  );
+};
+
+const DeviceBoard = () => {
+  const [deviceOnline, setDeviceOnline] = useState(0);
+  const [deviceOffline, setDeviceOffline] = useState(0);
+  const [productPublish, setProductPublish] = useState(0);
+  const [productUnPublish, setProductUnPublish] = useState(0);
+  const [options, setOptions] = useState<EChartsOption>({});
+  const [onlineOptions, setOnlineOptions] = useState<EChartsOption>({});
+  const [yesterdayCount, setYesterdayCount] = useState(0);
+  const [deviceOptions, setDeviceOptions] = useState<EChartsOption>({});
+  const [month, setMonth] = useState(0);
+  const [point, setPoint] = useState([]);
+
+  const ref = useRef<RefType>();
+
+  const { data: deviceTotal } = useRequest(service.deviceCount, {
+    formatResult: (res) => res.result,
+  });
+  const { data: productTotal } = useRequest(service.productCount, {
+    defaultParams: [{}],
+    formatResult: (res) => res.result,
+  });
+
+  //设备数量
+  const deviceStatus = async () => {
+    const onlineRes = await service.deviceCount(encodeQuery({ terms: { state: 'online' } }));
+    if (onlineRes.status === 200) {
+      setDeviceOnline(onlineRes.result);
+    }
+    const offlineRes = await service.deviceCount(encodeQuery({ terms: { state: 'offline' } }));
+    if (offlineRes.status === 200) {
+      setDeviceOffline(offlineRes.result);
+    }
+  };
+
+  //产品数量
+  const productStatus = async () => {
+    const pusblish = await service.productCount({
+      terms: [
+        {
+          column: 'state',
+          value: '1',
+        },
+      ],
+    });
+    if (pusblish.status === 200) {
+      setProductPublish(pusblish.result);
+    }
+    const unpublish = await service.productCount({
+      terms: [
+        {
+          column: 'state',
+          value: '0',
+        },
+      ],
+    });
+    if (unpublish.status === 200) {
+      setProductUnPublish(unpublish.result);
+    }
+  };
+
+  //当前在线
+  const getOnline = async () => {
+    const res = await service.dashboard([
+      {
+        dashboard: 'device',
+        object: 'status',
+        measurement: 'record',
+        dimension: 'aggOnline',
+        group: 'aggOnline',
+        params: {
+          state: 'online',
+          limit: 15,
+          from: 'now-15d',
+          time: '1d',
+        },
+      },
+    ]);
+    if (res.status === 200) {
+      const x = res.result.map((item: any) => item.data.timeString);
+      const y = res.result.map((item: any) => item.data.value);
+      setYesterdayCount(y?.[1]);
+      setOnlineOptions({
+        xAxis: {
+          type: 'category',
+          data: x,
+          show: false,
+        },
+        yAxis: {
+          type: 'value',
+          show: false,
+        },
+        series: [
+          {
+            data: y,
+            type: 'bar',
+          },
+        ],
+      });
+    }
+  };
+  //今日设备消息量
+  const getDevice = async () => {
+    const res = await service.dashboard([
+      {
+        dashboard: 'device',
+        object: 'message',
+        measurement: 'quantity',
+        dimension: 'agg',
+        group: 'today',
+        params: {
+          time: '1h',
+          format: 'yyyy-MM-dd HH:mm:ss',
+          limit: 24,
+          from: 'now-1d',
+        },
+      },
+      {
+        dashboard: 'device',
+        object: 'message',
+        measurement: 'quantity',
+        dimension: 'agg',
+        group: 'thisMonth',
+        params: {
+          time: '1M',
+          format: 'yyyy-MM',
+          limit: 1,
+          from: 'now-1M',
+        },
+      },
+    ]);
+    if (res.status === 200) {
+      const thisMonth = res.result.find((item: any) => item.group === 'thisMonth').data.value;
+      setMonth(thisMonth);
+      const today = res.result.filter((item: any) => item.group !== 'thisMonth');
+      const x = today.map((item: any) => item.data.timeString);
+      const y = today.map((item: any) => item.data.value);
+      setDeviceOptions({
+        xAxis: {
+          type: 'category',
+          boundaryGap: false,
+          show: false,
+          data: x,
+        },
+        yAxis: {
+          type: 'value',
+          show: false,
+        },
+        series: [
+          {
+            data: y,
+            type: 'line',
+            areaStyle: {},
+          },
+        ],
+      });
+    }
+  };
+
+  const getEcharts = async () => {
+    const data = ref.current!.getValues();
+    if (data) {
+      console.log(Math.ceil((data.time.end - data.time.start) / (1 * 24 * 3600 * 1000) + 1));
+      const res = await service.dashboard([
+        {
+          dashboard: 'device',
+          object: 'message',
+          measurement: 'quantity',
+          dimension: 'agg',
+          group: 'device_msg',
+          params: {
+            time: '1d',
+            format: 'yyyy.MM.dd',
+            limit: Math.ceil((data.time.end - data.time.start) / (1 * 24 * 3600 * 1000) + 1),
+            from: moment(data.time.start).format('yyyy-MM-DD'),
+            to: moment(data.time.end).format('yyyy-MM-DD'),
+          },
+        },
+      ]);
+      if (res.status === 200) {
+        const x = res.result.map((item: any) => item.data.timeString);
+        const y = res.result.map((item: any) => item.data.value);
+        setOptions({
+          xAxis: {
+            type: 'category',
+            boundaryGap: false,
+            data: x,
+          },
+          yAxis: {
+            type: 'value',
+          },
+          series: [
+            {
+              data: y,
+              type: 'line',
+              areaStyle: {},
+            },
+          ],
+        });
+      }
+    }
+  };
+  //地图数据
+  const geo = async (data?: any) => {
+    const res = await service.getGeo(data);
+    if (res.status === 200) {
+      console.log(res.result.features);
+      setPoint(res.result.features);
+    }
+  };
+
+  useEffect(() => {
+    deviceStatus();
+    productStatus();
+    getOnline();
+    getDevice();
+    geo({});
+  }, []);
+
+  return (
+    <PageContainer>
+      <div className={'device-dash-board'}>
+        <Card className={'top-card-items'} bodyStyle={{ display: 'flex', gap: 12 }}>
+          <TopCard
+            title={'产品数量'}
+            total={productTotal}
+            isEcharts={false}
+            bottomRender={() => (
+              <>
+                <Badge status="success" text="已发布" />
+                <span style={{ padding: '0 4px' }}>{productPublish}</span>
+                <Badge status="error" text="未发布" />{' '}
+                <span style={{ padding: '0 4px' }}>{productUnPublish}</span>
+              </>
+            )}
+          />
+          <TopCard
+            title={'设备数量'}
+            total={deviceTotal}
+            isEcharts={false}
+            bottomRender={() => (
+              <>
+                <Badge status="success" text="在线" />{' '}
+                <span style={{ padding: '0 4px' }}>{deviceOnline}</span>
+                <Badge status="error" text="离线" />{' '}
+                <span style={{ padding: '0 4px' }}>{deviceOffline}</span>
+              </>
+            )}
+          />
+          <TopCard
+            title={'当前在线'}
+            total={22}
+            isEcharts={true}
+            topRender={
+              <div style={{ height: 56 }}>
+                <Echarts options={onlineOptions} />
+              </div>
+            }
+            bottomRender={() => <>昨日在线:{yesterdayCount} </>}
+          />
+          <TopCard
+            title={'今日设备消息量'}
+            total={2221}
+            isEcharts={true}
+            topRender={
+              <div style={{ height: 56 }}>
+                <Echarts options={deviceOptions} />
+              </div>
+            }
+            bottomRender={() => <>当月设备消息量:{month} </>}
+          />
+        </Card>
+        <DashBoard
+          title={'设备消息'}
+          options={options}
+          // closeInitialParams={true}
+          ref={ref}
+          height={500}
+          defaultTime={'week'}
+          onParamsChange={getEcharts}
+        />
+        <Card style={{ marginTop: 10 }}>
+          <div>设备分布</div>
+          <div>
+            <AMap
+              AMapUI
+              style={{
+                height: 500,
+                width: '100%',
+              }}
+            >
+              {point.map((item: any) => (
+                //@ts-ignore
+                <Marker
+                  position={{
+                    longitude: item.geometry.coordinates?.[0],
+                    latitude: item.geometry.coordinates?.[1],
+                  }}
+                >
+                  <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+                    <div
+                      style={{
+                        backgroundColor: '#666666',
+                        color: 'white',
+                        textAlign: 'center',
+                        marginBottom: 5,
+                      }}
+                    >
+                      {item.properties.deviceName}
+                    </div>
+                    <div>
+                      <EnvironmentOutlined style={{ color: 'blue', fontSize: 22 }} />
+                    </div>
+                  </div>
+                </Marker>
+              ))}
+            </AMap>
+          </div>
+        </Card>
+      </div>
+    </PageContainer>
+  );
+};
+export default DeviceBoard;

+ 25 - 0
src/pages/device/DashBoard/service.ts

@@ -0,0 +1,25 @@
+import BaseService from '@/utils/BaseService';
+import { request } from 'umi';
+import type { DeviceInstance } from '@/pages/device/Instance/typings';
+import SystemConst from '@/utils/const';
+
+class Service extends BaseService<DeviceInstance> {
+  deviceCount = (data?: any) => request(`${this.uri}/_count`, { methods: 'GET', params: data });
+  productCount = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/device-product/_count`, {
+      method: 'POST',
+      data,
+    });
+  dashboard = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/dashboard/_multi`, {
+      method: 'POST',
+      data,
+    });
+  getGeo = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/geo/object/device/_search/geo.json`, {
+      method: 'POST',
+      data,
+    });
+}
+
+export default Service;

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

@@ -224,6 +224,7 @@ const Modbus = () => {
         <div style={{ display: 'flex' }}>
           <div>
             <Tabs
+              style={{ height: 600 }}
               tabPosition={'left'}
               defaultActiveKey={opcId}
               onChange={(e) => {

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

@@ -240,6 +240,7 @@ const Opcua = () => {
         <div style={{ display: 'flex' }}>
           <div>
             <Tabs
+              style={{ height: 600 }}
               tabPosition={'left'}
               defaultActiveKey={opcId}
               onChange={(e) => {

+ 10 - 1
src/pages/link/Channel/Modbus/Access/bindDevice/index.tsx

@@ -28,14 +28,20 @@ const BindDevice = (props: Props) => {
     {
       title: '设备ID',
       dataIndex: 'id',
+      width: 200,
+      ellipsis: true,
     },
     {
       title: '设备名称',
       dataIndex: 'name',
+      width: 200,
+      ellipsis: true,
     },
     {
       title: '产品名称',
       dataIndex: 'productName',
+      width: 200,
+      ellipsis: true,
     },
     {
       title: '注册时间',
@@ -80,7 +86,10 @@ const BindDevice = (props: Props) => {
       <SearchComponent
         field={columns}
         target="bindDevice"
-        defaultParam={[{ column: 'id$modbus-master$not', value: props.id }]}
+        defaultParam={[
+          { column: 'id$modbus-master$not', value: props.id },
+          { column: 'productId$dev-protocol', value: 'modbus-tcp' },
+        ]}
         onSearch={(data) => {
           // 重置分页数据
           actionRef.current?.reset?.();

+ 5 - 0
src/pages/link/Channel/Modbus/Access/index.less

@@ -8,6 +8,11 @@
     .ant-tabs-content-holder {
       width: 1px;
     }
+
+    .ant-tabs > .ant-tabs-nav .ant-tabs-nav-more,
+    .ant-tabs > div > .ant-tabs-nav .ant-tabs-nav-more {
+      display: none;
+    }
   }
 }
 

+ 1 - 0
src/pages/link/Channel/Modbus/Access/index.tsx

@@ -235,6 +235,7 @@ const Access = () => {
           <div style={{ display: 'flex' }}>
             <div>
               <Tabs
+                style={{ height: 600 }}
                 tabPosition={'left'}
                 defaultActiveKey={deviceId}
                 onChange={(e) => {

+ 17 - 0
src/pages/link/Channel/Modbus/index.tsx

@@ -47,6 +47,8 @@ const Modbus = () => {
     {
       title: '端口',
       dataIndex: 'port',
+      search: false,
+      valueType: 'digit',
     },
     {
       title: '状态',
@@ -54,6 +56,21 @@ const Modbus = () => {
       renderText: (state) => (
         <Badge text={state?.text} status={state?.value === 'disabled' ? 'error' : 'success'} />
       ),
+      valueType: 'select',
+      valueEnum: {
+        disabled: {
+          text: intl.formatMessage({
+            id: 'pages.data.option.disabled',
+            defaultMessage: '禁用',
+          }),
+          status: 'disabled',
+        },
+        enabled: {
+          text: '正常',
+          status: 'enabled',
+        },
+      },
+      filterMultiple: false,
     },
     {
       title: '操作',

+ 2 - 0
src/pages/link/Channel/Opcua/Access/bindDevice/index.tsx

@@ -33,6 +33,8 @@ const BindDevice = (props: Props) => {
     {
       title: '设备名称',
       dataIndex: 'name',
+      width: 200,
+      ellipsis: true,
     },
     {
       title: '产品名称',

+ 5 - 0
src/pages/link/Channel/Opcua/Access/index.less

@@ -8,6 +8,11 @@
     .ant-tabs-content-holder {
       width: 1px;
     }
+
+    .ant-tabs > .ant-tabs-nav .ant-tabs-nav-more,
+    .ant-tabs > div > .ant-tabs-nav .ant-tabs-nav-more {
+      display: none;
+    }
   }
 }
 

+ 8 - 2
src/pages/link/Channel/Opcua/Access/index.tsx

@@ -36,6 +36,7 @@ const Access = () => {
   const [data, setData] = useState<any>([]);
   const [subscribeTopic] = useSendWebsocketMessage();
   const [propertyValue, setPropertyValue] = useState<any>({});
+  const [bindDeviceId, setBindDeviceId] = useState<any>('');
   const wsRef = useRef<any>();
 
   const columns: ProColumns<any>[] = [
@@ -102,10 +103,11 @@ const Access = () => {
               defaultMessage: '确认禁用?',
             }),
             onConfirm: async () => {
+              console.log(record);
               if (record.state.value === 'disable') {
-                await service.enablePoint(record.deviceId, [record.id]);
+                await service.enablePoint(bindDeviceId, [record.id]);
               } else {
-                await service.stopPoint(record.deviceId, [record.id]);
+                await service.stopPoint(bindDeviceId, [record.id]);
               }
               message.success(
                 intl.formatMessage({
@@ -169,6 +171,7 @@ const Access = () => {
       if (res.status === 200) {
         setDeviceId(res.result[0]?.deviceId);
         setProductId(res.result[0]?.productId);
+        setBindDeviceId(res.result[0]?.id);
         setParam({
           terms: [{ column: 'deviceId', value: res.result[0]?.deviceId }],
         });
@@ -234,10 +237,13 @@ const Access = () => {
               <Tabs
                 tabPosition={'left'}
                 defaultActiveKey={deviceId}
+                style={{ height: 600 }}
                 onChange={(e) => {
                   setDeviceId(e);
                   const items = bindList.find((item: any) => item.deviceId === e);
+                  console.log(items);
                   setProductId(items?.productId);
+                  setBindDeviceId(items?.id);
                   setParam({
                     terms: [{ column: 'deviceId', value: e }],
                   });

+ 38 - 9
src/pages/link/Channel/Opcua/index.tsx

@@ -52,9 +52,27 @@ const Opcua = () => {
     {
       title: '状态',
       dataIndex: 'state',
+      valueType: 'select',
       renderText: (state) => (
         <Badge text={state?.text} status={state?.value === 'disabled' ? 'error' : 'success'} />
       ),
+      valueEnum: {
+        disabled: {
+          text: intl.formatMessage({
+            id: 'pages.data.option.disabled',
+            defaultMessage: '禁用',
+          }),
+          status: 'disabled',
+        },
+        enabled: {
+          text: intl.formatMessage({
+            id: 'pages.data.option.enabled',
+            defaultMessage: '启用',
+          }),
+          status: 'enabled',
+        },
+      },
+      filterMultiple: false,
     },
     {
       title: '操作',
@@ -93,17 +111,28 @@ const Opcua = () => {
             }),
             onConfirm: async () => {
               if (record.state.value === 'disabled') {
-                await service.enable(record.id);
+                const res = await service.enable(record.id);
+                if (res.status === 200) {
+                  message.success(
+                    intl.formatMessage({
+                      id: 'pages.data.option.success',
+                      defaultMessage: '操作成功!',
+                    }),
+                  );
+                  actionRef.current?.reload();
+                }
               } else {
-                await service.disable(record.id);
+                const res = await service.disable(record.id);
+                if (res.status === 200) {
+                  message.success(
+                    intl.formatMessage({
+                      id: 'pages.data.option.success',
+                      defaultMessage: '操作成功!',
+                    }),
+                  );
+                  actionRef.current?.reload();
+                }
               }
-              message.success(
-                intl.formatMessage({
-                  id: 'pages.data.option.success',
-                  defaultMessage: '操作成功!',
-                }),
-              );
-              actionRef.current?.reload();
             },
           }}
           isPermission={permission.action}

+ 101 - 77
src/pages/system/Basis/index.tsx

@@ -1,4 +1,4 @@
-import { Card, Form, Input, Select, Upload, message } from 'antd';
+import { Card, Form, Input, message, Select, Upload } from 'antd';
 import { useModel } from '@@/plugin-model/useModel';
 import { useEffect, useState } from 'react';
 import usePermissions from '@/hooks/permission';
@@ -8,10 +8,13 @@ import Token from '@/utils/token';
 import SystemConst from '@/utils/const';
 import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
 import styles from './index.less';
+import { PageContainer } from '@ant-design/pro-layout';
+import Service from './service';
 
 const Basis = () => {
+  const service = new Service();
   const [form] = Form.useForm();
-  const { initialState } = useModel('@@initialState');
+  const { initialState, setInitialState } = useModel('@@initialState');
   const { permission: userPermission } = usePermissions('system/Basis');
   const [imageUrl, setImageUrl] = useState<string>('');
   const [loading, setLoading] = useState(false);
@@ -48,89 +51,110 @@ const Basis = () => {
     </div>
   );
 
+  const detail = async (data: any) => {
+    const res = await service.detail(data);
+    if (res.status === 200) {
+      setImageUrl(res.result?.[0].properties.logo);
+      form.setFieldsValue(res.result?.[0].properties);
+      setInitialState({
+        ...initialState,
+        settings: {
+          ...res.result?.[0].properties,
+        },
+      });
+    }
+  };
+  const save = async () => {
+    const formData = await form.validateFields();
+    if (formData && imageUrl !== '') {
+      const item = [
+        {
+          scope: 'basis',
+          properties: {
+            ...formData,
+            headerTheme: formData.navTheme,
+            logo: imageUrl,
+          },
+        },
+      ];
+      const res = await service.save(item);
+      if (res.status === 200) {
+        message.success('保存成功');
+        detail(['basis']);
+      }
+    } else {
+      message.error('请上传图片');
+    }
+  };
+
   useEffect(() => {
     console.log(initialState);
+    detail(['basis']);
   }, []);
 
   return (
-    <Card>
-      <div
-        style={{
-          display: 'flex',
-          alignItems: 'flex-start',
-          justifyContent: 'flex-start',
-        }}
-      >
-        <div style={{ width: 400 }}>
-          <Form layout="vertical" form={form}>
-            <Form.Item
-              label="系统名称"
-              name="title"
-              rules={[{ required: true, message: '请输入系统名称' }]}
-            >
-              <Input />
-            </Form.Item>
-            <Form.Item
-              label="主题色"
-              name="navTheme"
-              rules={[{ required: true, message: '请选择主题色' }]}
-            >
-              <Select
-                onChange={() => {
-                  // setInitialState({
-                  //     ...initialState,
-                  //     settings:{
-                  //         navTheme:e,
-                  //         headerTheme:e,
-                  //         title:'hahahah'
-                  //     }
-                  // })
-                }}
+    <PageContainer>
+      <Card>
+        <div
+          style={{
+            display: 'flex',
+            alignItems: 'flex-start',
+            justifyContent: 'flex-start',
+          }}
+        >
+          <div style={{ width: 400 }}>
+            <Form layout="vertical" form={form}>
+              <Form.Item
+                label="系统名称"
+                name="title"
+                rules={[{ required: true, message: '请输入系统名称' }]}
+              >
+                <Input />
+              </Form.Item>
+              <Form.Item
+                label="主题色"
+                name="navTheme"
+                rules={[{ required: true, message: '请选择主题色' }]}
+              >
+                <Select>
+                  <Select.Option value="light">白色</Select.Option>
+                  <Select.Option value="dark">黑色</Select.Option>
+                </Select>
+              </Form.Item>
+              <Form.Item
+                label="高德API Key"
+                name="apikey"
+                tooltip="配置后平台可调用高德地图GIS服务"
               >
-                <Select.Option value="light">light</Select.Option>
-                <Select.Option value="dark">dark</Select.Option>
-              </Select>
-            </Form.Item>
-            <Form.Item label="高德API Key" name="apikey" tooltip="配置后平台可调用高德地图GIS服务">
-              <Input />
-            </Form.Item>
-          </Form>
+                <Input />
+              </Form.Item>
+            </Form>
+          </div>
+          <div className={styles.content}>
+            <div style={{ marginBottom: 8 }}>系统logo</div>
+            <Upload {...uploadProps}>
+              {imageUrl ? (
+                <img src={imageUrl} alt="avatar" style={{ width: '100%', height: '100%' }} />
+              ) : (
+                uploadButton
+              )}
+            </Upload>
+          </div>
         </div>
-        <div className={styles.content}>
-          <div style={{ marginBottom: 8 }}>系统logo</div>
-          <Upload {...uploadProps}>
-            {imageUrl ? (
-              <img src={imageUrl} alt="avatar" style={{ width: '100%' }} />
-            ) : (
-              uploadButton
-            )}
-          </Upload>
+        <div>
+          <PermissionButton
+            type="primary"
+            key="basis"
+            onClick={async () => {
+              save();
+            }}
+            isPermission={userPermission.update}
+          >
+            保存
+          </PermissionButton>
         </div>
-      </div>
-      <div>
-        <PermissionButton
-          type="primary"
-          key="basis"
-          onClick={async () => {
-            // setPassword(true);
-            const data = await form.validateFields();
-            if (data) {
-              if (imageUrl !== '') {
-                console.log({
-                  ...data,
-                  imageUrl,
-                });
-              } else {
-                message.error('请上传图片');
-              }
-            }
-          }}
-          isPermission={userPermission.update}
-        >
-          保存
-        </PermissionButton>
-      </div>
-    </Card>
+      </Card>
+    </PageContainer>
   );
 };
 

+ 18 - 0
src/pages/system/Basis/service.ts

@@ -0,0 +1,18 @@
+import BaseService from '@/utils/BaseService';
+import { request } from 'umi';
+import SystemConst from '@/utils/const';
+
+class Service extends BaseService<any> {
+  save = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/system/config/scope/_save`, {
+      method: 'POST',
+      data,
+    });
+  detail = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/system/config/scopes`, {
+      method: 'POST',
+      data,
+    });
+}
+
+export default Service;

+ 11 - 10
src/pages/user/Login/index.tsx

@@ -1,4 +1,4 @@
-import { Button, Checkbox, message, Spin } from 'antd';
+import { Button, Checkbox, Divider, message, Spin } from 'antd';
 import React, { useEffect, useRef, useState } from 'react';
 import { Link } from 'umi';
 import styles from './index.less';
@@ -14,7 +14,6 @@ import SystemConst from '@/utils/const';
 import { useIntl } from '@@/plugin-locale/localeExports';
 import { SelectLang } from '@@/plugin-locale/SelectLang';
 import Footer from '@/components/Footer';
-import { DingdingOutlined, WechatOutlined } from '@ant-design/icons';
 
 const Login: React.FC = () => {
   const [captcha, setCaptcha] = useState<{ key?: string; base64?: string }>({});
@@ -22,6 +21,10 @@ const Login: React.FC = () => {
   const { initialState, setInitialState } = useModel('@@initialState');
   const intl = useIntl();
 
+  const iconMap = new Map();
+  iconMap.set('dingtalk', require('/public/images/bind/dingtalk.png'));
+  iconMap.set('wechat-webapp', require('/public/images/bind/wechat-webapp.png'));
+
   const fetchUserInfo = async () => {
     const userInfo = (await initialState?.fetchUserInfo?.()) as UserInfo;
     if (userInfo) {
@@ -195,9 +198,11 @@ const Login: React.FC = () => {
                       defaultMessage: '登录',
                     })}
                   </Submit>
-                  <div style={{ marginTop: 10 }}>
-                    <div>其他方式登录</div>
-                    <div>
+                  <div style={{ marginTop: 20 }}>
+                    <Divider plain style={{ height: 12 }}>
+                      <div style={{ color: '#807676d9', fontSize: 12 }}>其他方式登录</div>
+                    </Divider>
+                    <div style={{ position: 'relative', bottom: '10px' }}>
                       {bindings.map((item: any) => (
                         <Button
                           type="link"
@@ -211,11 +216,7 @@ const Login: React.FC = () => {
                             };
                           }}
                         >
-                          {item.type === 'dingtalk' ? (
-                            <DingdingOutlined style={{ color: '#009BF5', fontSize: '20px' }} />
-                          ) : (
-                            <WechatOutlined style={{ color: '#2AAE67', fontSize: '20px' }} />
-                          )}
+                          <img src={iconMap.get(item.type)} />
                         </Button>
                       ))}
                     </div>

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

@@ -52,6 +52,11 @@ const Service = {
       method: 'GET',
       params,
     }),
+  settingDetail: (data?: any) =>
+    request(`/${SystemConst.API_BASE}/system/config/scopes`, {
+      method: 'POST',
+      data,
+    }),
 };
 
 export default Service;

+ 1 - 0
src/utils/menu/router.ts

@@ -22,6 +22,7 @@ export enum MENUS_CODE {
   'device/Location' = 'device/Location',
   'device/Product/Save' = 'device/Product/Save',
   'device/Product' = 'device/Product',
+  'device/DashBoard' = 'device/DashBoard',
   'device/components/Alarm/Edit' = 'device/components/Alarm/Edit',
   'device/components/Alarm/Record' = 'device/components/Alarm/Record',
   'device/components/Alarm/Setting' = 'device/components/Alarm/Setting',