sun-chaochao пре 3 година
родитељ
комит
a4337cbe9e

+ 8 - 12
src/components/DashBoard/baseCard.tsx

@@ -1,30 +1,26 @@
-import Header from './header';
 import type { HeaderProps } from './header';
-import Echarts from './echarts';
+import Header from './header';
 import type { EchartsProps } from './echarts';
+import Echarts from './echarts';
 import Style from './index.less';
 import classNames from 'classnames';
 
 interface BaseCardProps extends HeaderProps, EchartsProps {
   height: number;
-  classNames?: string;
+  className?: string;
 }
 
 export default (props: BaseCardProps) => {
+  const { height, className, options, ...formProps } = props;
   return (
     <div
-      className={classNames(Style['dash-board-echarts'], props.classNames)}
+      className={classNames(Style['dash-board-echarts'], className)}
       style={{
-        height: props.height || 200,
+        height: height || 200,
       }}
     >
-      <Header
-        title={props.title}
-        onParamsChange={props.onParamsChange}
-        extraParams={props.extraParams}
-        initialValues={props.initialValues}
-      />
-      <Echarts options={props.options} />
+      <Header {...formProps} />
+      <Echarts options={options} />
     </div>
   );
 };

+ 13 - 64
src/components/DashBoard/header.tsx

@@ -1,7 +1,8 @@
-import React, { useEffect, useState } from 'react';
+import React from 'react';
 import Style from './index.less';
-import { Col, DatePicker, Form, Input, Radio, Row } from 'antd';
-import moment from 'moment';
+import { Col, Form, Row } from 'antd';
+import type { TimeType } from './timePicker';
+import RangePicker from './timePicker';
 
 export interface HeaderProps {
   title: string;
@@ -15,28 +16,17 @@ export interface HeaderProps {
     Children: React.ReactNode;
   };
   initialValues?: any;
+  defaultTime?: TimeType;
 }
 
 export default (props: HeaderProps) => {
   const [form] = Form.useForm();
-  const [radioValue, setRadioValue] = useState<string | undefined>('today');
 
-  const change = async () => {
-    const data = await form.getFieldsValue();
-    data.time = [data.time[0].valueOf(), data.time[1].valueOf()];
+  const change = async (data: any) => {
     if (props.onParamsChange) {
       props.onParamsChange(data);
     }
   };
-  const setTime = (sTime: number, eTime: number) => {
-    form.setFieldsValue({ time: [moment(sTime), moment(eTime)] });
-  };
-
-  useEffect(() => {
-    setTime(moment().startOf('day').valueOf(), new Date().getTime());
-    setRadioValue('today');
-    change();
-  }, []);
 
   return (
     <div className={Style.header}>
@@ -48,8 +38,8 @@ export default (props: HeaderProps) => {
           layout={'inline'}
           preserve={false}
           initialValues={props.initialValues}
-          onValuesChange={() => {
-            change();
+          onValuesChange={(_, allValue) => {
+            change(allValue);
           }}
         >
           <Row style={{ width: '100%' }}>
@@ -59,52 +49,11 @@ export default (props: HeaderProps) => {
               </Col>
             )}
             <Col span={props.extraParams ? 18 : 24}>
-              <Form.Item noStyle>
-                <Input.Group compact>
-                  <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
-                    <Radio.Group
-                      defaultValue="day"
-                      buttonStyle="solid"
-                      value={radioValue}
-                      onChange={(e) => {
-                        let startTime: any = 0;
-                        const endTime = moment(new Date());
-                        const value = e.target.value;
-                        if (value === 'today') {
-                          startTime = moment().startOf('day').valueOf();
-                        } else if (value === 'week') {
-                          startTime = moment().subtract(6, 'days').valueOf();
-                        } else if (value === 'month') {
-                          startTime = moment().subtract(29, 'days').valueOf();
-                        } else {
-                          startTime = moment().subtract(365, 'days').valueOf();
-                        }
-                        setRadioValue(value);
-                        form.setFieldsValue({ time: [moment(startTime), moment(endTime)] });
-                        change();
-                      }}
-                    >
-                      <Radio.Button value="today">当天</Radio.Button>
-                      <Radio.Button value="week">近一周</Radio.Button>
-                      <Radio.Button value="month">近一月</Radio.Button>
-                      <Radio.Button value="year">近一年</Radio.Button>
-                    </Radio.Group>
-                    <Form.Item name={'time'} noStyle>
-                      {
-                        // @ts-ignore
-                        <DatePicker.RangePicker
-                          showTime
-                          onChange={(date: any) => {
-                            if (date) {
-                              setRadioValue(undefined);
-                            }
-                          }}
-                        />
-                      }
-                    </Form.Item>
-                  </div>
-                </Input.Group>
-              </Form.Item>
+              <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
+                <Form.Item noStyle name={'time'}>
+                  <RangePicker defaultTime={props.defaultTime} />
+                </Form.Item>
+              </div>
             </Col>
           </Row>
         </Form>

+ 1 - 3
src/components/DashBoard/index.less

@@ -4,8 +4,6 @@
   width: 100%;
   padding: 24px;
   background-color: #fff;
-  border: 1px solid #f0f0f0;
-  border-radius: 2px;
 }
 .header {
   display: flex;
@@ -26,5 +24,5 @@
   flex-grow: 1;
   width: 100%;
   height: 0;
-  margin: 12px;
+  margin-top: 12px;
 }

+ 95 - 0
src/components/DashBoard/timePicker.tsx

@@ -0,0 +1,95 @@
+import type { DatePickerProps } from 'antd';
+import { DatePicker, Radio } from 'antd';
+import moment from 'moment';
+import { useEffect, useState } from 'react';
+
+export enum TimeKey {
+  'today' = 'today',
+  'week' = 'week',
+  'month' = 'month',
+  'year' = 'year',
+}
+
+export type TimeType = keyof typeof TimeKey;
+
+interface ExtraTimePickerProps extends Omit<DatePickerProps, 'onChange' | 'value'> {
+  onChange?: (value: number[]) => void;
+  value?: number[];
+  defaultTime?: TimeType;
+}
+
+export const getTimeByType = (type: TimeType) => {
+  switch (type) {
+    case TimeKey.week:
+      return moment().subtract(6, 'days').valueOf();
+    case TimeKey.month:
+      return moment().startOf('month').valueOf();
+    case TimeKey.year:
+      return moment().subtract(365, 'days').valueOf();
+    default:
+      return moment().startOf('day').valueOf();
+  }
+};
+
+export default (props: ExtraTimePickerProps) => {
+  const [radioValue, setRadioValue] = useState<TimeType | undefined>(undefined);
+
+  const { value, onChange, ...extraProps } = props;
+
+  const change = (startTime: number, endTime: number) => {
+    if (onChange) {
+      onChange([startTime, endTime]);
+    }
+  };
+
+  const timeChange = (type: TimeType) => {
+    const endTime = moment(new Date()).valueOf();
+    const startTime: number = getTimeByType(type);
+    setRadioValue(type);
+    change(startTime, endTime);
+  };
+
+  useEffect(() => {
+    timeChange(props.defaultTime || TimeKey.today);
+  }, []);
+
+  return (
+    <>
+      {
+        // @ts-ignore
+        <DatePicker.RangePicker
+          {...extraProps}
+          value={
+            value && [
+              moment(value && value[0] ? value[0] : new Date()),
+              moment(value && value[1] ? value[1] : new Date()),
+            ]
+          }
+          onChange={(rangeValue) => {
+            setRadioValue(undefined);
+            if (rangeValue && rangeValue.length === 2) {
+              change(rangeValue[0]!.valueOf(), rangeValue[1]!.valueOf());
+            }
+          }}
+          renderExtraFooter={() => (
+            <div style={{ padding: '12px 0' }}>
+              <Radio.Group
+                defaultValue="day"
+                buttonStyle="solid"
+                value={radioValue}
+                onChange={(e) => {
+                  timeChange(e.target.value);
+                }}
+              >
+                <Radio.Button value={TimeKey.today}>当天</Radio.Button>
+                <Radio.Button value={TimeKey.week}>近一周</Radio.Button>
+                <Radio.Button value={TimeKey.month}>近一月</Radio.Button>
+                <Radio.Button value={TimeKey.year}>近一年</Radio.Button>
+              </Radio.Group>
+            </div>
+          )}
+        />
+      }
+    </>
+  );
+};

+ 1 - 0
src/pages/device/components/Metadata/Base/Edit/index.tsx

@@ -704,6 +704,7 @@ const Edit = observer((props: Props) => {
                 edit: {
                   type: 'void',
                   'x-component': 'Editable.Popover',
+                  // 'x-reactions': '{{(field)=>field.title=field.query(".edit.name").get("value")||field.title}}',
                   title: '指标数据',
                   'x-reactions': {
                     dependencies: ['.edit.name'],

+ 1 - 1
src/pages/device/components/Metadata/Import/index.tsx

@@ -181,7 +181,7 @@ const Import = (props: Props) => {
     const data = (await form.submit()) as any;
 
     if (data.metadata === 'alink') {
-      service.convertMetadata('to', 'alink', data.import).subscribe({
+      service.convertMetadata('from', 'alink', data.import).subscribe({
         next: async (meta) => {
           message.success('导入成功');
           await service.modify(param.id, { metadata: JSON.stringify(meta) });

+ 71 - 0
src/pages/link/DashBoard/index.tsx

@@ -0,0 +1,71 @@
+import { PageContainer } from '@ant-design/pro-layout';
+import DashBoard from '@/components/DashBoard';
+import { Radio } from 'antd';
+import { useState } from 'react';
+import type { EChartsOption } from 'echarts';
+
+export default () => {
+  const [options, setOptions] = useState<EChartsOption>({});
+
+  const getEcharts = async (data: any) => {
+    console.log(data);
+    setOptions({
+      xAxis: {
+        type: 'category',
+        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
+      },
+      yAxis: {
+        type: 'value',
+      },
+      series: [
+        {
+          data: [150, 230, 224, 218, 135, 147, 260],
+          type: 'line',
+        },
+      ],
+    });
+  };
+
+  return (
+    <PageContainer>
+      <div>
+        <div>
+          <DashBoard
+            title={'网络流量'}
+            initialValues={{ test: false }}
+            height={400}
+            extraParams={{
+              key: 'test',
+              Children: (
+                <Radio.Group buttonStyle={'solid'}>
+                  <Radio.Button value={true}>上行</Radio.Button>
+                  <Radio.Button value={false}>下行</Radio.Button>
+                </Radio.Group>
+              ),
+            }}
+            defaultTime={'week'}
+            options={options}
+            onParamsChange={getEcharts}
+          />
+        </div>
+        <div style={{ display: 'flex' }}>
+          <DashBoard
+            title={'CPU使用率趋势'}
+            initialValues={{ test: false }}
+            height={400}
+            defaultTime={'week'}
+            options={options}
+            onParamsChange={getEcharts}
+          />
+          <DashBoard
+            title={'JVM内存使用率趋势'}
+            height={400}
+            defaultTime={'week'}
+            options={options}
+            onParamsChange={getEcharts}
+          />
+        </div>
+      </div>
+    </PageContainer>
+  );
+};

+ 4 - 0
src/pages/media/DashBoard/index.less

@@ -31,4 +31,8 @@
       }
     }
   }
+
+  .media-dash-board-body {
+    border: 1px solid #f0f0f0;
+  }
 }

+ 2 - 1
src/pages/media/DashBoard/index.tsx

@@ -6,7 +6,7 @@ import React, { useEffect, useState } from 'react';
 import Service from './service';
 import './index.less';
 import encodeQuery from '@/utils/encodeQuery';
-import { EChartsOption } from 'echarts';
+import type { EChartsOption } from 'echarts';
 
 interface TopCardProps {
   url: string;
@@ -122,6 +122,7 @@ export default () => {
           />
         </Card>
         <DashBoard
+          className={'media-dash-board-body'}
           title={'播放数量(人次)'}
           options={options}
           height={500}

+ 20 - 0
src/pages/notice/Config/Detail/doc/Webhook.tsx

@@ -0,0 +1,20 @@
+import './index.less';
+
+const Webhook = () => {
+  return (
+    <div className={'doc'}>
+      <h1>1. 概述</h1>
+      <div>
+        webhook是一个接收HTTP请求的URL(本平台默认只支持HTTP
+        POST请求),实现了Webhook的第三方系统可以基于该URL订阅本平台系统信息,本平台按配置把特定的事件结果推送到指定的地址,便于系统做后续处理。
+      </div>
+      <h1>2.通知配置说明</h1>
+      <h2>1. Webhook</h2>
+      <div>Webhook地址。</div>
+
+      <h2>2. 请求头</h2>
+      <div>支持根据系统提供的接口设置不同的请求头。如 Accept-Language 、Content-Type</div>
+    </div>
+  );
+};
+export default Webhook;

+ 2 - 1
src/pages/notice/Config/Detail/index.tsx

@@ -34,6 +34,7 @@ import Email from '@/pages/notice/Config/Detail/doc/Email';
 import { PermissionButton } from '@/components';
 import usePermissions from '@/hooks/permission';
 import FAutoComplete from '@/components/FAutoComplete';
+import Webhook from './doc/Webhook';
 
 export const docMap = {
   weixin: {
@@ -54,7 +55,7 @@ export const docMap = {
     embedded: <Email />,
   },
   webhook: {
-    http: <div>webhook</div>,
+    http: <Webhook />,
   },
 };
 

+ 18 - 0
src/pages/notice/Template/Detail/doc/Webhook.tsx

@@ -0,0 +1,18 @@
+import './index.less';
+
+const Webhook = () => {
+  return (
+    <div className="doc">
+      <h1>1. 概述</h1>
+      <div>
+        通知模板结合通知配置为告警消息通知提供支撑。通知模板只能调用同一类型的通知配置服务。
+      </div>
+      <h1>2.模板配置说明</h1>
+      <div>
+        1、请求体 请求体中的数据来自于发送通知时指定的所有变量,也可通过自定义的方式进行变量配置。
+        使用webhook通知时,系统会将该事件通过您指定的URL地址,以POST方式发送。
+      </div>
+    </div>
+  );
+};
+export default Webhook;

+ 2 - 1
src/pages/notice/Template/Detail/index.tsx

@@ -47,6 +47,7 @@ import FAutoComplete from '@/components/FAutoComplete';
 import { PermissionButton } from '@/components';
 import usePermissions from '@/hooks/permission';
 import FMonacoEditor from '@/components/FMonacoEditor';
+import Webhook from './doc/Webhook';
 
 export const docMap = {
   weixin: {
@@ -67,7 +68,7 @@ export const docMap = {
     embedded: <Email />,
   },
   webhook: {
-    http: <div>webhook</div>,
+    http: <Webhook />,
   },
 };
 

+ 8 - 0
src/pages/rule-engine/Alarm/Config/index.tsx

@@ -207,6 +207,7 @@ const Config = () => {
             const resp = await service.getDataExchange('consume');
             if (resp.status === 200) {
               f.setInitialValues(resp.result?.config.config);
+              f.setValuesIn('id', resp.result?.id);
             }
           });
         },
@@ -222,6 +223,7 @@ const Config = () => {
             const resp = await service.getDataExchange('producer');
             if (resp.status === 200) {
               f.setInitialValues(resp.result?.config.config);
+              f.setValuesIn('id', resp.result?.id);
             }
           });
         },
@@ -282,6 +284,10 @@ const Config = () => {
   const ioSchema: ISchema = {
     type: 'object',
     properties: {
+      id: {
+        'x-component': 'Input',
+        'x-hidden': true,
+      },
       kafka: {
         title: 'kafka地址',
         type: 'string',
@@ -351,6 +357,7 @@ const Config = () => {
       config: {
         config: inputConfig,
       },
+      id: inputConfig.id,
       sourceType: 'kafka',
       exchangeType: 'producer',
     });
@@ -359,6 +366,7 @@ const Config = () => {
         sourceType: 'kafka',
         config: outputConfig,
       },
+      id: outputConfig.id,
       sourceType: 'kafka',
       exchangeType: 'consume',
     });

+ 1 - 0
src/pages/rule-engine/Alarm/Config/typing.d.ts

@@ -6,6 +6,7 @@ type LevelItem = {
 };
 
 type IOConfigItem = {
+  id?: string;
   address: string;
   topic: string;
   username: string;

+ 3 - 8
src/pages/system/DataSource/Management/DataTable.tsx

@@ -2,12 +2,11 @@ import { Form, FormGrid, FormItem, Input, Password, Select } from '@formily/antd
 import { createForm } from '@formily/core';
 import type { ISchema } from '@formily/react';
 import { createSchemaField } from '@formily/react';
-import { message, Modal } from 'antd';
-import { service } from '@/pages/system/DataSource';
+import { Modal } from 'antd';
 
 interface Props {
   close: () => void;
-  reload: () => void;
+  save: (data: any) => void;
   data: any;
 }
 
@@ -59,11 +58,7 @@ const DataTable = (props: Props) => {
 
   const handleSave = async () => {
     const data: any = await form.submit();
-    const response: any = props.data?.id ? await service.update(data) : await service.save(data);
-    if (response.status === 200) {
-      message.success('保存成功');
-      props.reload();
-    }
+    props.save(data);
   };
 
   return (

+ 20 - 0
src/pages/system/DataSource/Management/index.less

@@ -0,0 +1,20 @@
+.tables {
+  :global {
+    .ant-tree-treenode .ant-tree-node-selected {
+      background-color: white;
+    }
+  }
+  .treeTitle {
+    display: flex;
+    justify-content: space-between;
+    width: 100%;
+    .options {
+      display: none;
+    }
+  }
+  .treeTitle:hover {
+    .options {
+      display: block;
+    }
+  }
+}

+ 60 - 52
src/pages/system/DataSource/Management/index.tsx

@@ -1,5 +1,5 @@
 import { PageContainer } from '@ant-design/pro-layout';
-import { Card, Col, Input, Popconfirm, Row, Tree } from 'antd';
+import { Card, Col, Input, message, Popconfirm, Row, Space, Tree } from 'antd';
 import { useEffect, useRef, useState } from 'react';
 import { service } from '@/pages/system/DataSource';
 import { useIntl, useLocation } from 'umi';
@@ -10,6 +10,7 @@ import usePermissions from '@/hooks/permission';
 import SearchComponent from '@/components/SearchComponent';
 import ProTable from '@jetlinks/pro-table';
 import DataTable from './DataTable';
+import styles from './index.less';
 
 const Management = () => {
   const location = useLocation<{ id: string }>();
@@ -139,59 +140,63 @@ const Management = () => {
               onSearch={() => {}}
               style={{ width: '100%', marginBottom: 10 }}
             />
-            <Tree
-              showLine
-              defaultExpandAll
-              height={500}
-              selectedKeys={[...defaultSelectedKeys]}
-              onSelect={(selectedKeys) => {
-                if (!selectedKeys.includes('tables')) {
-                  setDefaultSelectedKeys([...selectedKeys]);
-                }
-              }}
-            >
-              <Tree.TreeNode
-                title={() => {
-                  return (
-                    <div style={{ display: 'flex', justifyContent: 'space-between', width: 230 }}>
-                      <div>数据源名称</div>
-                      <div>
-                        <PlusOutlined
-                          onClick={() => {
-                            setCurrent({});
-                            setVisible(true);
-                          }}
-                        />
-                      </div>
-                    </div>
-                  );
+            <div className={styles.tables}>
+              <Tree
+                showLine
+                showIcon
+                defaultExpandAll
+                height={500}
+                selectedKeys={[...defaultSelectedKeys]}
+                onSelect={(selectedKeys) => {
+                  if (!selectedKeys.includes('tables')) {
+                    setDefaultSelectedKeys([...selectedKeys]);
+                  }
                 }}
-                key={'tables'}
               >
-                {rdbList.map((item) => (
-                  <Tree.TreeNode
-                    key={item.name}
-                    title={() => {
-                      return (
-                        <div
-                          style={{ display: 'flex', justifyContent: 'space-between', width: 200 }}
-                        >
-                          <div>{item.name}</div>
-                          <div>
-                            <PlusOutlined
-                              onClick={() => {
-                                setCurrent(item);
-                                setVisible(true);
-                              }}
-                            />
-                          </div>
+                <Tree.TreeNode
+                  title={() => {
+                    return (
+                      <div style={{ display: 'flex', justifyContent: 'space-between', width: 230 }}>
+                        <div>数据源名称</div>
+                        <div>
+                          <PlusOutlined
+                            onClick={() => {
+                              setCurrent({});
+                              setVisible(true);
+                            }}
+                          />
                         </div>
-                      );
-                    }}
-                  />
-                ))}
-              </Tree.TreeNode>
-            </Tree>
+                      </div>
+                    );
+                  }}
+                  key={'tables'}
+                >
+                  {rdbList.map((item) => (
+                    <Tree.TreeNode
+                      key={item.name}
+                      title={() => {
+                        return (
+                          <div className={styles.treeTitle}>
+                            <div className={styles.title}>{item.name}</div>
+                            <div className={styles.options}>
+                              <Space>
+                                <EditOutlined
+                                  onClick={() => {
+                                    setCurrent(item);
+                                    setVisible(true);
+                                  }}
+                                />
+                                <DeleteOutlined />
+                              </Space>
+                            </div>
+                          </div>
+                        );
+                      }}
+                    />
+                  ))}
+                </Tree.TreeNode>
+              </Tree>
+            </div>
           </Col>
           <Col span={18}>
             <div>
@@ -248,7 +253,10 @@ const Management = () => {
       {visible && (
         <DataTable
           data={current}
-          reload={() => {
+          save={(data) => {
+            rdbList.push(data);
+            setRdbList([...rdbList]);
+            message.success('操作成功!');
             setVisible(false);
           }}
           close={() => {

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

@@ -44,6 +44,7 @@ export enum MENUS_CODE {
   'link/Protocol' = 'link/Protocol',
   'link/Type' = 'link/Type',
   'link/AccessConfig' = 'link/AccessConfig',
+  'link/DashBoard' = 'link/DashBoard',
   'Log' = 'Log',
   'media/Cascade' = 'media/Cascade',
   'media/Cascade/Save' = 'media/Cascade/Save',