Просмотр исходного кода

feat: 系统设置,三方优化,设备管理仪表盘

wzyyy 3 лет назад
Родитель
Сommit
1cff888197

BIN
public/images/bind/dingtalk.png


BIN
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:''
   };
 };
 

+ 103 - 23
src/pages/device/DashBoard/index.tsx

@@ -1,6 +1,6 @@
 import { PageContainer } from '@ant-design/pro-layout';
 import { Card, Badge } from 'antd';
-import { useEffect, useState } from 'react';
+import { useEffect, useRef, useState } from 'react';
 import './index.less';
 import Service from './service';
 import encodeQuery from '@/utils/encodeQuery';
@@ -8,6 +8,10 @@ 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;
@@ -16,6 +20,11 @@ interface TopCardProps {
   topRender?: any;
   bottomRender: () => React.ReactNode;
 }
+
+type RefType = {
+  getValues: Function;
+};
+
 const service = new Service('device/instance');
 const TopCard = (props: TopCardProps) => {
   return (
@@ -53,6 +62,9 @@ const DeviceBoard = () => {
   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,
@@ -198,27 +210,56 @@ const DeviceBoard = () => {
     }
   };
 
-  const getEcharts = async (params: any) => {
-    // 请求数据
-    console.log(params);
-
-    setOptions({
-      xAxis: {
-        type: 'category',
-        boundaryGap: false,
-        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
-      },
-      yAxis: {
-        type: 'value',
-      },
-      series: [
+  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([
         {
-          data: [820, 932, 901, 934, 1290, 1330, 1320],
-          type: 'line',
-          areaStyle: {},
+          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(() => {
@@ -226,6 +267,7 @@ const DeviceBoard = () => {
     productStatus();
     getOnline();
     getDevice();
+    geo({});
   }, []);
 
   return (
@@ -284,12 +326,50 @@ const DeviceBoard = () => {
         <DashBoard
           title={'设备消息'}
           options={options}
+          // closeInitialParams={true}
+          ref={ref}
           height={500}
-          initialValues={{
-            test: '2',
-          }}
+          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>
   );

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

@@ -15,6 +15,11 @@ class Service extends BaseService<DeviceInstance> {
       method: 'POST',
       data,
     });
+  getGeo = (data?: any) =>
+    request(`/${SystemConst.API_BASE}/geo/object/device/_search/geo.json`, {
+      method: 'POST',
+      data,
+    });
 }
 
 export default Service;

+ 45 - 28
src/pages/system/Basis/index.tsx

@@ -9,10 +9,12 @@ 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);
@@ -49,8 +51,45 @@ 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 (
@@ -77,20 +116,9 @@ const Basis = () => {
                 name="navTheme"
                 rules={[{ required: true, message: '请选择主题色' }]}
               >
-                <Select
-                  onChange={() => {
-                    // setInitialState({
-                    //     ...initialState,
-                    //     settings:{
-                    //         navTheme:e,
-                    //         headerTheme:e,
-                    //         title:'hahahah'
-                    //     }
-                    // })
-                  }}
-                >
-                  <Select.Option value="light">light</Select.Option>
-                  <Select.Option value="dark">dark</Select.Option>
+                <Select>
+                  <Select.Option value="light">白色</Select.Option>
+                  <Select.Option value="dark">黑色</Select.Option>
                 </Select>
               </Form.Item>
               <Form.Item
@@ -106,7 +134,7 @@ const Basis = () => {
             <div style={{ marginBottom: 8 }}>系统logo</div>
             <Upload {...uploadProps}>
               {imageUrl ? (
-                <img src={imageUrl} alt="avatar" style={{ width: '100%' }} />
+                <img src={imageUrl} alt="avatar" style={{ width: '100%', height: '100%' }} />
               ) : (
                 uploadButton
               )}
@@ -118,18 +146,7 @@ const Basis = () => {
             type="primary"
             key="basis"
             onClick={async () => {
-              // setPassword(true);
-              const data = await form.validateFields();
-              if (data) {
-                if (imageUrl !== '') {
-                  console.log({
-                    ...data,
-                    imageUrl,
-                  });
-                } else {
-                  message.error('请上传图片');
-                }
-              }
+              save();
             }}
             isPermission={userPermission.update}
           >

+ 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;