Commit 84678965 authored by 邓晓峰's avatar 邓晓峰

public: bate version

parents 249c1f7b 736484df
Pipeline #24753 failed with stages
in 8 seconds
......@@ -2,7 +2,7 @@ import { readdirSync } from 'fs';
import { join } from 'path';
// utils must build before core
const headPkgs = ['card'];
const headPkgs = ['Empty'];
const tailPkgs = readdirSync(join(__dirname, 'packages')).filter(
(pkg) => pkg.charAt(0) !== '.' && !headPkgs.includes(pkg),
);
......
......@@ -95,19 +95,19 @@ export default {
'/components': [
{
title: '布局',
children: ['card'],
children: ['Empty'],
},
{
title: '通用',
children: ['card'],
children: ['ImageSelect', 'QuotaSelect', 'TimeRangePicker', 'MqttView'],
},
{
title: '数据录入',
children: ['card'],
children: ['Empty'],
},
{
title: '数据展示',
children: ['card'],
children: ['DeviceTree', 'RealTimeInfo', 'HistoryInfo'],
},
],
},
......
......@@ -118,8 +118,20 @@
"publishConfig": {
"registry": "https://g.civnet.cn:4873"
},
"size-limit": [],
"size-limit": [
{
"path": "packages/Empty/dist/**/*.js",
"limit": "100 s",
"webpack": false,
"running": false
}
],
"dependencies": {
"cross-spawn": "^7.0.3"
"@wisdom-components/Empty": "^1.0.1",
"classnames": "^2.2.6",
"cross-spawn": "^7.0.3",
"highcharts": "^9.0.1",
"highcharts-react-official": "^3.0.0",
"mqtt-client": "^1.0.11"
}
}
# `@wisdom-components/DeviceTree`
> TODO: description
## Usage
```
const devicetree = require('@wisdom-components/DeviceTree');
// TODO: DEMONSTRATE API
```
## API
api 参考 Antd Tree 组件 https://ant.design/components/tree-cn/
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(key 在整个树范围内唯一) | array<{key, title, children, [disabled, selectable]}> | - |
| onSearch | 搜索框输入事件的回调,会返回搜索框输入信息 | function(value){ } | - |
{
"name": "@wisdom-components/devicetree",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: DeviceTree - 设备树
nav:
title: 组件
path: /components
group:
path: /
---
# DeviceTree 设备树
基础业务组件
- 允许单设备选择
- 允许多设备选择
- 允许搜索设备树
## 何时使用
- 在设备树选择时。
## 代码演示
<code src="./demos/Basic.js">
## API
api 参考 Antd Tree 组件 https://ant.design/components/tree-cn/
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| prefix | 搜索框的前置图标 | ReactNode | 搜索 icon |
| placeholder | 搜索框占位符 | string | 搜索设备名称 |
| treeData | treeNodes 数据,如果设置则不需要手动构造 TreeNode 节点(key 在整个树范围内唯一) | array<{key, title, children, [disabled, selectable]}> | - |
| onSearch | 搜索框输入事件的回调,会返回搜索框输入信息 | function(value){ } | - |
import React, { useEffect, useState } from 'react';
import PandaDeviceTree from '../index';
import request from 'umi-request';
const Demo = () => {
const [treeData, setTreeData] = useState([]);
const [expandedKeys, setExpandedKeys] = useState(null);
const [checkedKeys, setCheckedKeys] = useState([]);
const [selectedKeys, setSelectedKeys] = useState([]);
const [autoExpandParent, setAutoExpandParent] = useState(true);
const fetchData = (params = {}) => {
request(baseUrl + '/Publish/Monitor/Device/DeviceTree', {
method: 'post',
data: {
PageIndex: 1,
PageSize: 500,
deviceTypes: '二供泵房,二供机组',
getChild: true,
userID: 1,
queryInfo: params.queryInfo || '',
sortFields: '',
direction: '',
isTop: true,
},
}).then(function (response) {
let data = response.data
? response.data.list && response.data.list.length > 0
? response.data.list[0].DeviceList
: []
: [];
setTreeData(handleData(data));
});
};
const handleData = (data) => {
data.map((item) => {
item.title = item.DeviceName;
item.key = item.StationID;
item.children = handleData(item.children);
});
return data;
};
useEffect(() => {
fetchData();
}, []);
const onExpand = (expandedKeysValue) => {
console.log('onExpand', expandedKeysValue);
setExpandedKeys(expandedKeysValue);
setAutoExpandParent(false);
};
const onCheck = (checkedKeysValue) => {
console.log('onCheck', checkedKeysValue);
setCheckedKeys(checkedKeysValue);
};
const onSelect = (selectedKeysValue, info) => {
console.log('onSelect', info);
setSelectedKeys(selectedKeysValue);
};
const onSearch = (e) => {
if (e.type == 'keydown') {
fetchData({ queryInfo: e.target.value });
}
console.log(e.type, 'e.type');
console.log(e.target.value, 'e.target.value');
};
return (
<PandaDeviceTree
onSearch={onSearch}
checkable
onExpand={onExpand}
expandedKeys={expandedKeys}
autoExpandParent={autoExpandParent}
onCheck={onCheck}
checkedKeys={checkedKeys}
onSelect={onSelect}
selectedKeys={selectedKeys}
treeData={treeData}
/>
);
};
export default Demo;
const baseUrl = 'http://192.168.10.150:8777';
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Input, Tree, Divider, ConfigProvider } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import Empty from '@wisdom-components/Empty';
import './index.less';
const DeviceTree = (props) => {
const { prefix, placeholder, treeData, onSearch } = props;
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('device-tree');
return (
<div className={classNames(prefixCls)}>
<Input
prefix={prefix}
placeholder={placeholder}
bordered={false}
onChange={onSearch}
onPressEnter={onSearch}
/>
<Divider />
{!!treeData.length && <Tree {...props} />}
{!treeData.length && <Empty />}
</div>
);
};
DeviceTree.defaultProps = {
prefix: <SearchOutlined />,
placeholder: '搜索设备名称',
treeData: [],
onSearch: () => {},
};
DeviceTree.propTypes = {
prefix: PropTypes.node,
placeholder: PropTypes.string,
treeData: PropTypes.array,
onSearch: PropTypes.func,
};
export default DeviceTree;
@import (reference) '~antd/es/style/themes/default';
@device-tree-prefix-cls: ~'@{ant-prefix}-device-tree';
.@{device-tree-prefix-cls} {
padding: 5px;
.ant-divider {
margin: 6px 0 12px 0;
}
}
# `@wisdom-components/Empty`
> TODO: description
## Usage
```
const empty = require('@wisdom-components/Empty');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/empty",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: Empty - 空状态
nav:
title: 组件
path: /components
group:
path: /
---
# Empty 空状态
空状态时的展示占位图。
## 何时使用
- 当目前没有数据时,用于显式的用户提示。
- 初始化场景时的引导创建流程。
## 代码演示
<code src="./demos/Basic.js">
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| description | 自定义描述内容 | ReactNode | - |
| image | 设置显示图片,为 string 时表示自定义图片地址 | ReactNode | - |
| theme | 设置主题 | string | 可选值:light/dark, 默认 light |
| size | 设置图片尺寸 | string | 可选值:small/middle/large, 默认 middle |
| statusCode | 状态码 | string | 可选值:0/-1/-2, 默认 0 |
| imageStyle | 图片样式 | CSSProperties | - |
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 168 155" style="enable-background:new 0 0 168 155;" xml:space="preserve">
<style type="text/css">
.st0{opacity:0.5;}
.st1{fill:#AEBACC;}
.st2{fill:#7D889E;}
.st3{fill:#748091;}
.st4{fill:#FFFFFF;}
.st5{fill:url(#SVGID_1_);}
.st6{fill:url(#SVGID_2_);}
</style>
<g class="st0">
<g>
<ellipse class="st1" cx="128.7" cy="116.5" rx="38.7" ry="38.5"/>
<ellipse class="st2" cx="142.9" cy="104.3" rx="5.4" ry="5.4"/>
<ellipse class="st2" cx="150.8" cy="115.9" rx="2.5" ry="2.1"/>
<ellipse class="st2" cx="139" cy="133.8" rx="4.2" ry="4.3"/>
<path class="st1" d="M143.2,80.6c0.8-1.1,1.6-2.2,2.4-3.4c0.3,1,1.5,3.9,4.6,5.9c3.8,2.5,7.7,1.9,8.6,1.8
c-0.8,1.6-1.7,3.2-2.5,4.8C152,86.8,147.6,83.7,143.2,80.6z"/>
<path class="st1" d="M164.1,130.7c1,0.6,2.4,1.9,3.4,2.6c-0.9,0.3-2.4,1.7-4.3,4.2c-2.3,3.1-2.3,5.4-2.1,6.1c-1.5-0.7-3-1.4-4.4-2
C159.5,138,161.2,134.3,164.1,130.7z"/>
<ellipse transform="matrix(0.497 -0.8678 0.8678 0.497 5.8072 172.8094)" class="st2" cx="152" cy="81.4" rx="2.4" ry="7.8"/>
<ellipse transform="matrix(0.5031 -0.8642 0.8642 0.5031 -37.9388 210.9669)" class="st2" cx="164.5" cy="138.5" rx="6" ry="2"/>
</g>
<g>
<circle class="st1" cx="41.6" cy="30.5" r="30.5"/>
<path class="st2" d="M12,38c-6.1,4.7-7.3,8-6.7,9.8c0.6,1.8,4,3.7,11.7,3.8c7.7,0.1,17.4-1.7,27.3-5C66,39.3,77.7,28.3,76.3,24
c-0.5-1.4-2.5-2.4-5.7-3L68,15.1c6.4,0.7,12,2.9,13.4,7.2l0,0C84.8,32.5,65.7,45,46.1,51.6c-10.2,3.4-20.2,5.3-28.4,5.3
c-0.2,0-0.4,0-0.6,0C5.2,56.8,1.4,52.8,0.2,49.4c-1.1-3.3,1.5-10.1,10.9-17.4L12,38z"/>
</g>
<g>
<path class="st3" d="M63,136.3c1.3,3.2,2.4,5.1,3.4,6.2c1,1.2,2.8,2.5,3.9,5.2c0.2,0.4,0.3,0.6,0.4,1c0.8,2.7-0.5,4.5,0.5,5.6
c0.2,0.2,0.5,0.5,1.3,0.6c3.9,0,7.7,0,11.6,0c0.5-1.1,1.1-2.5,1.5-4.1c0.7-2.5,0.8-4.8,0.8-6.5c0.4-0.1,0.8-0.2,1.1-0.2
c0.6,0,1.1,0.1,1.5,0.2c0.3,0.1,0.6,0.1,0.7,0.2c0.2,1.7,0.4,3.5,0.5,5.3c0,0.5-0.1,1.6,0.2,2.9c0.2,0.9,0.5,1.7,0.7,2.2
c4.3,0,8.5,0,12.8,0c0.2-0.1,0.6-0.3,0.9-0.9c0.3-0.6,0.2-1.3,0.2-1.5c-0.1-0.8,0.3-2.1,1-4.8c0.8-3.1,2.6-4.3,4-7.2
c0.9-1.8,1.9-4.2,2.5-8.2C96.6,133.6,79,135,63,136.3z"/>
<path class="st3" d="M65.1,99c-5.5,6-9.4,11.4-12,15.3c-8.6,13-8.3,18.3-7.9,20.7c0.2,1.3,1,5.9,4.1,7.3c3.8,1.6,8.7-2.6,11-4.5
c11.8-10,15.3-30.8,16.5-40.7C72.8,97.8,69,98.4,65.1,99z"/>
<path class="st4" d="M66.8,97.7c-2.1,2.6-5.4,7.2-7.1,13.9c-0.9,3.4-3.7,14.4,2.7,23.9c4.1,6.1,10.1,8.6,12.1,9.4
c11.8,4.7,29.4,2.1,36.5-9.5c5.9-9.8,1.8-21.8-0.2-27.6c-2.2-6.4-5.4-11.1-7.7-14C91,95,78.9,96.4,66.8,97.7z"/>
<path class="st3" d="M101,98.5c2.4,3.3,4.2,6.9,5.5,10.7c1.8,5.1,5.4,15.8,0.6,23.7c-4.2,7-13.3,9.5-20.3,9.5
c-3.8,0-7.4-0.7-10.5-1.9c-4.4-1.7-7.7-4.3-10-7.7c-5.3-7.8-2.9-17.1-2.1-20.2c1-3.8,2.7-7.4,5.1-10.7l27-3L101,98.5 M103.1,93.7
c-12.1,1.3-24.2,2.7-36.3,4c-2.1,2.6-5.4,7.2-7.1,13.9c-0.9,3.4-3.7,14.4,2.7,23.9c4.1,6.1,10.1,8.6,12.1,9.4
c3.7,1.5,7.9,2.2,12.2,2.2c9.5,0,19.4-3.7,24.3-11.7c5.9-9.8,1.8-21.8-0.2-27.6C108.7,101.3,105.5,96.6,103.1,93.7L103.1,93.7z"/>
<path class="st3" d="M64,111.1c4.3-0.7,12.4-3,17.4-3.4c10.1-0.8,19.3-0.5,27.2,0.3c0-4.5,0-9,0-13.5c-14,0-28,0-42,0
C66.8,99.5,63.7,106.1,64,111.1z"/>
<path class="st4" d="M35.7,58.1c-1.8,4.4-2.5,8.5-2.5,8.5c-0.6,3.4-0.6,6.3-0.5,8.4c0.6,11.1,6.4,17.6,12.2,21.8
c10.6,7.7,75.4,17,88.7-12.8c4.2-9.4,0.3-21.3-0.6-24.8c-4-16.3-16.3-25.1-20.2-27.6c-19.2-12.4-45.4-8.7-60.5,3.6
C42.4,43.1,37,54.8,35.7,58.1z"/>
<path class="st3" d="M85.6,28.6c9.1,0,17.6,2.4,24.6,6.9c4.3,2.8,14.8,10.8,18.2,24.8c0.1,0.4,0.2,0.9,0.4,1.4
c1.1,4,3.6,13.5,0.5,20.4c-7,15.4-30.6,17.8-44,17.8c-17.7,0-34-4-37.9-6.8c-6.5-4.8-9.9-10.8-10.3-18.3c-0.1-2.5,0-5,0.5-7.4
c0,0,0.7-3.7,2.2-7.6c0.5-1.3,5.6-13.3,15.2-21C63,32.4,74.4,28.6,85.6,28.6 M85.6,24c-12.5,0-24.8,4.2-33.4,11.2
C42.4,43.1,37,54.8,35.7,58.1c-1.8,4.4-2.5,8.5-2.5,8.5c-0.6,3.4-0.6,6.3-0.5,8.4c0.6,11.1,6.4,17.6,12.2,21.8
c5.1,3.7,22.4,7.7,40.6,7.7c20,0,41.2-4.9,48.2-20.5c4.2-9.4,0.3-21.3-0.6-24.8c-4-16.3-16.3-25.1-20.2-27.6
C104.5,26.4,95,24,85.6,24L85.6,24z"/>
<path class="st3" d="M105.3,27.8c5.7-3.6,13.1-3.1,18.1,1.2c5.3,4.6,6.9,12.5,3.7,19C119.9,41.3,112.6,34.5,105.3,27.8z"/>
<path class="st3" d="M63.3,29.2c-5.7-3.6-13.1-3.1-18.1,1.2c-5.3,4.6-6.9,12.5-3.7,19C48.8,42.7,56.1,36,63.3,29.2z"/>
<path class="st3" d="M55.2,68.4c0.1-1.5,0.7-6.4,4.8-10c2.9-2.5,8.4-5.1,12.5-2.8c2,1.1,2.9,3,3.2,3.7c2.1,4.6,0.4,11.1-3.9,14.7
c-3.3,2.7-8.4,4.1-12.2,2.1c-0.6-0.3-2.9-1.5-3.9-4.1C55.1,70.5,55.1,69.1,55.2,68.4z"/>
<path class="st3" d="M115.1,68.4c-0.1-1.5-0.7-6.4-4.8-10c-2.9-2.5-8.4-5.1-12.5-2.8c-2,1.1-2.9,3-3.2,3.7
c-2.1,4.6-0.4,11.1,3.9,14.7c3.3,2.7,8.4,4.1,12.2,2.1c0.6-0.3,2.9-1.5,3.9-4.1C115.2,70.5,115.2,69.1,115.1,68.4z"/>
<path class="st3" d="M82.3,71.9c-0.2-0.5,0.1-1.2,0.7-1.3c1-0.2,2.3-0.4,3.8-0.4c1.7,0,2.6,0,2.9,0.5c0.5,1-1.2,3.9-3.6,4
C84.6,74.8,83,73.7,82.3,71.9z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="106.1071" y1="81.2148" x2="109.9657" y2="81.2148">
<stop offset="0" style="stop-color:#566277"/>
<stop offset="1" style="stop-color:#7D889E"/>
</linearGradient>
<rect x="106.1" y="75.9" class="st5" width="3.9" height="10.6"/>
<path class="st3" d="M103.7,114.3c-4.9-6.4-3.9-13.9-3.4-17.3c0.7-4.9,3.3-14.8,8.1-15.2c3.9-0.3,7.4,5.9,8.2,7.4
c6.6,11.9,3.1,29.7-3.2,31.3C109.3,121.5,104.6,115.5,103.7,114.3z"/>
<circle class="st4" cx="69.8" cy="61.9" r="2.9"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="91.1882" y1="62.8381" x2="121.0263" y2="62.8381">
<stop offset="0" style="stop-color:#566277"/>
<stop offset="1" style="stop-color:#7D889E"/>
</linearGradient>
<path class="st6" d="M106.1,51.4c6.3,0,11.5,5.2,11.5,11.5s-5.2,11.5-11.5,11.5c-6.3,0-11.5-5.2-11.5-11.5S99.8,51.4,106.1,51.4
M106.1,47.9c-8.2,0-14.9,6.7-14.9,14.9s6.7,14.9,14.9,14.9S121,71.1,121,62.8S114.3,47.9,106.1,47.9L106.1,47.9z"/>
<circle class="st4" cx="100.5" cy="61.6" r="4.2"/>
<path class="st3" d="M85.8,82.7c-1.6,0-3.2-0.3-4.8-1c-0.7-0.3-1.1-1.1-0.8-1.8c0.3-0.7,1.1-1.1,1.8-0.8c1.3,0.6,2.7,0.8,4.1,0.7
c3.2-0.1,5.5-1.9,6.5-2.9c0.6-0.5,1.4-0.5,2,0c0.5,0.6,0.5,1.4,0,2c-1.3,1.3-4.2,3.5-8.3,3.7C86.1,82.7,86,82.7,85.8,82.7z"/>
</g>
</g>
</svg>
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.1.2, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 168 155" style="enable-background:new 0 0 168 155;" xml:space="preserve">
<style type="text/css">
.st0{fill:#EFF3F6;}
.st1{fill:#D5DFEB;}
.st2{fill:#D3DFEC;}
.st3{fill:#FFFFFF;}
.st4{fill:url(#SVGID_1_);}
.st5{fill:url(#SVGID_2_);}
</style>
<g>
<ellipse class="st0" cx="128.7" cy="116.5" rx="38.7" ry="38.5"/>
<ellipse class="st1" cx="142.9" cy="104.3" rx="5.4" ry="5.4"/>
<ellipse class="st1" cx="150.8" cy="115.9" rx="2.5" ry="2.1"/>
<ellipse class="st1" cx="139" cy="133.8" rx="4.2" ry="4.3"/>
<path class="st0" d="M143.2,80.6c0.8-1.1,1.6-2.2,2.4-3.4c0.3,1,1.5,3.9,4.6,5.9c3.8,2.5,7.7,1.9,8.6,1.8c-0.8,1.6-1.7,3.2-2.5,4.8
C152,86.8,147.6,83.7,143.2,80.6z"/>
<path class="st0" d="M164.1,130.7c1,0.6,2.4,1.9,3.4,2.6c-0.9,0.3-2.4,1.7-4.3,4.2c-2.3,3.1-2.3,5.4-2.1,6.1c-1.5-0.7-3-1.4-4.4-2
C159.5,138,161.2,134.3,164.1,130.7z"/>
<ellipse transform="matrix(0.497 -0.8678 0.8678 0.497 5.8072 172.8094)" class="st1" cx="152" cy="81.4" rx="2.4" ry="7.8"/>
<ellipse transform="matrix(0.5031 -0.8642 0.8642 0.5031 -37.9388 210.9669)" class="st1" cx="164.5" cy="138.5" rx="6" ry="2"/>
</g>
<g>
<circle class="st0" cx="41.6" cy="30.5" r="30.5"/>
<path class="st1" d="M12,38c-6.1,4.7-7.3,8-6.7,9.8c0.6,1.8,4,3.7,11.7,3.8c7.7,0.1,17.4-1.7,27.3-5C66,39.3,77.7,28.3,76.3,24
c-0.5-1.4-2.5-2.4-5.7-3L68,15.1c6.4,0.7,12,2.9,13.4,7.2l0,0C84.8,32.5,65.7,45,46.1,51.6c-10.2,3.4-20.2,5.3-28.4,5.3
c-0.2,0-0.4,0-0.6,0C5.2,56.8,1.4,52.8,0.2,49.4c-1.1-3.3,1.5-10.1,10.9-17.4L12,38z"/>
</g>
<g>
<path class="st2" d="M63,136.3c1.3,3.2,2.4,5.1,3.4,6.2c1,1.2,2.8,2.5,3.9,5.2c0.2,0.4,0.3,0.6,0.4,1c0.8,2.7-0.5,4.5,0.5,5.6
c0.2,0.2,0.5,0.5,1.3,0.6c3.9,0,7.7,0,11.6,0c0.5-1.1,1.1-2.5,1.5-4.1c0.7-2.5,0.8-4.8,0.8-6.5c0.4-0.1,0.8-0.2,1.1-0.2
c0.6,0,1.1,0.1,1.5,0.2c0.3,0.1,0.6,0.1,0.7,0.2c0.2,1.7,0.4,3.5,0.5,5.3c0,0.5-0.1,1.6,0.2,2.9c0.2,0.9,0.5,1.7,0.7,2.2
c4.3,0,8.5,0,12.8,0c0.2-0.1,0.6-0.3,0.9-0.9c0.3-0.6,0.2-1.3,0.2-1.5c-0.1-0.8,0.3-2.1,1-4.8c0.8-3.1,2.6-4.3,4-7.2
c0.9-1.8,1.9-4.2,2.5-8.2C96.6,133.6,79,135,63,136.3z"/>
<path class="st2" d="M65.1,99c-5.5,6-9.4,11.4-12,15.3c-8.6,13-8.3,18.3-7.9,20.7c0.2,1.3,1,5.9,4.1,7.3c3.8,1.6,8.7-2.6,11-4.5
c11.8-10,15.3-30.8,16.5-40.7C72.8,97.8,69,98.4,65.1,99z"/>
<path class="st3" d="M66.8,97.7c-2.1,2.6-5.4,7.2-7.1,13.9c-0.9,3.4-3.7,14.4,2.7,23.9c4.1,6.1,10.1,8.6,12.1,9.4
c11.8,4.7,29.4,2.1,36.5-9.5c5.9-9.8,1.8-21.8-0.2-27.6c-2.2-6.4-5.4-11.1-7.7-14C91,95,78.9,96.4,66.8,97.7z"/>
<path class="st2" d="M101,98.5c2.4,3.3,4.2,6.9,5.5,10.7c1.8,5.1,5.4,15.8,0.6,23.7c-4.2,7-13.3,9.5-20.3,9.5
c-3.8,0-7.4-0.7-10.5-1.9c-4.4-1.7-7.7-4.3-10-7.7c-5.3-7.8-2.9-17.1-2.1-20.2c1-3.8,2.7-7.4,5.1-10.7l27-3L101,98.5 M103.1,93.7
c-12.1,1.3-24.2,2.7-36.3,4c-2.1,2.6-5.4,7.2-7.1,13.9c-0.9,3.4-3.7,14.4,2.7,23.9c4.1,6.1,10.1,8.6,12.1,9.4
c3.7,1.5,7.9,2.2,12.2,2.2c9.5,0,19.4-3.7,24.3-11.7c5.9-9.8,1.8-21.8-0.2-27.6C108.7,101.3,105.5,96.6,103.1,93.7L103.1,93.7z"/>
<path class="st2" d="M64,111.1c4.3-0.7,12.4-3,17.4-3.4c10.1-0.8,19.3-0.5,27.2,0.3c0-4.5,0-9,0-13.5c-14,0-28,0-42,0
C66.8,99.5,63.7,106.1,64,111.1z"/>
<path class="st3" d="M35.7,58.1c-1.8,4.4-2.5,8.5-2.5,8.5c-0.6,3.4-0.6,6.3-0.5,8.4c0.6,11.1,6.4,17.6,12.2,21.8
c10.6,7.7,75.4,17,88.7-12.8c4.2-9.4,0.3-21.3-0.6-24.8c-4-16.3-16.3-25.1-20.2-27.6c-19.2-12.4-45.4-8.7-60.5,3.6
C42.4,43.1,37,54.8,35.7,58.1z"/>
<path class="st2" d="M85.6,28.6c9.1,0,17.6,2.4,24.6,6.9c4.3,2.8,14.8,10.8,18.2,24.8c0.1,0.4,0.2,0.9,0.4,1.4
c1.1,4,3.6,13.5,0.5,20.4c-7,15.4-30.6,17.8-44,17.8c-17.7,0-34-4-37.9-6.8c-6.5-4.8-9.9-10.8-10.3-18.3c-0.1-2.5,0-5,0.5-7.4
c0,0,0.7-3.7,2.2-7.6c0.5-1.3,5.6-13.3,15.2-21C63,32.4,74.4,28.6,85.6,28.6 M85.6,24c-12.5,0-24.8,4.2-33.4,11.2
C42.4,43.1,37,54.8,35.7,58.1c-1.8,4.4-2.5,8.5-2.5,8.5c-0.6,3.4-0.6,6.3-0.5,8.4c0.6,11.1,6.4,17.6,12.2,21.8
c5.1,3.7,22.4,7.7,40.6,7.7c20,0,41.2-4.9,48.2-20.5c4.2-9.4,0.3-21.3-0.6-24.8c-4-16.3-16.3-25.1-20.2-27.6
C104.5,26.4,95,24,85.6,24L85.6,24z"/>
<path class="st2" d="M105.3,27.8c5.7-3.6,13.1-3.1,18.1,1.2c5.3,4.6,6.9,12.5,3.7,19C119.9,41.3,112.6,34.5,105.3,27.8z"/>
<path class="st2" d="M63.3,29.2c-5.7-3.6-13.1-3.1-18.1,1.2c-5.3,4.6-6.9,12.5-3.7,19C48.8,42.7,56.1,36,63.3,29.2z"/>
<path class="st2" d="M55.2,68.4c0.1-1.5,0.7-6.4,4.8-10c2.9-2.5,8.4-5.1,12.5-2.8c2,1.1,2.9,3,3.2,3.7c2.1,4.6,0.4,11.1-3.9,14.7
c-3.3,2.7-8.4,4.1-12.2,2.1c-0.6-0.3-2.9-1.5-3.9-4.1C55.1,70.5,55.1,69.1,55.2,68.4z"/>
<path class="st2" d="M115.1,68.4c-0.1-1.5-0.7-6.4-4.8-10c-2.9-2.5-8.4-5.1-12.5-2.8c-2,1.1-2.9,3-3.2,3.7
c-2.1,4.6-0.4,11.1,3.9,14.7c3.3,2.7,8.4,4.1,12.2,2.1c0.6-0.3,2.9-1.5,3.9-4.1C115.2,70.5,115.2,69.1,115.1,68.4z"/>
<path class="st2" d="M82.3,71.9c-0.2-0.5,0.1-1.2,0.7-1.3c1-0.2,2.3-0.4,3.8-0.4c1.7,0,2.6,0,2.9,0.5c0.5,1-1.2,3.9-3.6,4
C84.6,74.8,83,73.7,82.3,71.9z"/>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="106.1071" y1="81.2148" x2="109.9657" y2="81.2148">
<stop offset="0" style="stop-color:#9AABC5"/>
<stop offset="1" style="stop-color:#D5DFEB"/>
</linearGradient>
<rect x="106.1" y="75.9" class="st4" width="3.9" height="10.6"/>
<path class="st2" d="M103.7,114.3c-4.9-6.4-3.9-13.9-3.4-17.3c0.7-4.9,3.3-14.8,8.1-15.2c3.9-0.3,7.4,5.9,8.2,7.4
c6.6,11.9,3.1,29.7-3.2,31.3C109.3,121.5,104.6,115.5,103.7,114.3z"/>
<circle class="st3" cx="69.8" cy="61.9" r="2.9"/>
<linearGradient id="SVGID_2_" gradientUnits="userSpaceOnUse" x1="91.1882" y1="62.8381" x2="121.0263" y2="62.8381">
<stop offset="0" style="stop-color:#9AABC5"/>
<stop offset="1" style="stop-color:#D5DFEB"/>
</linearGradient>
<path class="st5" d="M106.1,51.4c6.3,0,11.5,5.2,11.5,11.5s-5.2,11.5-11.5,11.5c-6.3,0-11.5-5.2-11.5-11.5S99.8,51.4,106.1,51.4
M106.1,47.9c-8.2,0-14.9,6.7-14.9,14.9s6.7,14.9,14.9,14.9S121,71.1,121,62.8S114.3,47.9,106.1,47.9L106.1,47.9z"/>
<circle class="st3" cx="100.5" cy="61.6" r="4.2"/>
<path class="st2" d="M85.8,82.7c-1.6,0-3.2-0.3-4.8-1c-0.7-0.3-1.1-1.1-0.8-1.8c0.3-0.7,1.1-1.1,1.8-0.8c1.3,0.6,2.7,0.8,4.1,0.7
c3.2-0.1,5.5-1.9,6.5-2.9c0.6-0.5,1.4-0.5,2,0c0.5,0.6,0.5,1.4,0,2c-1.3,1.3-4.2,3.5-8.3,3.7C86.1,82.7,86,82.7,85.8,82.7z"/>
</g>
</svg>
import React from 'react';
import { Button } from 'antd';
import PandaEmpty from '../index';
class Demo extends React.Component {
render() {
return (
<div style={{ width: '100%' }}>
<PandaEmpty size={'small'} />
<br />
<PandaEmpty />
<br />
<PandaEmpty size={'large'} />
<br />
<PandaEmpty size={'small'} statusCode={'-2'} />
<br />
<PandaEmpty statusCode={'-1'}>
<Button type="primary">刷新试试</Button>
</PandaEmpty>
<br />
<div style={{ width: '100%', padding: '32px 0', backgroundColor: '#282D3B' }}>
<PandaEmpty theme={'dark'} statusCode={'-1'}></PandaEmpty>
</div>
</div>
);
}
}
export default Demo;
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { ConfigProvider } from 'antd';
import noDataLight from './assets/noDataLight.svg';
import noDataDark from './assets/noDataDark.svg';
import errorLight from './assets/errorLight.png';
import errorDark from './assets/errorDark.png';
import './index.less';
const Empty = ({ description, image, theme, size, statusCode, imageStyle, children }) => {
const alt = description ? description : 'empty';
const des = description ? description : DESC_DATA[`${statusCode}`];
const imageSrc = image ? image : IMAGE_DATA[theme][statusCode == '0' ? 0 : 1];
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('panda-empty');
let imageNode = null;
if (typeof image === 'string') {
imageNode = <img alt={alt} src={imageSrc} />;
} else {
imageNode = image;
}
return (
<div
className={classNames(prefixCls, {
small: size === 'small',
middle: size === 'middle',
large: size === 'large',
})}
>
<div className={`${prefixCls}-image`} style={imageStyle}>
{imageNode}
</div>
{des && <div className={`${prefixCls}-des`}>{des}</div>}
{children && <div className={`${prefixCls}-footer`}>{children}</div>}
</div>
);
};
Empty.defaultProps = {
description: '',
image: '',
theme: 'light',
size: 'middle',
statusCode: '0',
imageStyle: {},
};
Empty.propTypes = {
description: PropTypes.node, // 自定义描述内容
image: PropTypes.node, // 设置显示图片,为 string 时表示自定义图片地址
theme: PropTypes.string, // 设置主题 light dark
size: PropTypes.string, // 设置图片尺寸 small middle large
statusCode: PropTypes.string, // 状态码
imageStyle: PropTypes.object, // 图片样式
};
export default Empty;
const DESC_DATA = {
0: '咦~暂时没有查询到相关数据呢~',
'-1': '参数错误~请刷新试试或等服务恢复~',
'-2': '服务器处理异常~请刷新试试或等服务恢复~',
};
const IMAGE_DATA = {
light: [noDataLight, errorLight],
dark: [noDataDark, errorDark],
};
@import (reference) '~antd/es/style/themes/default';
@panda-empty-prefix-cls: ~'@{ant-prefix}-panda-empty';
.@{panda-empty-prefix-cls} {
font-size: 14px;
text-align: center;
&-image {
margin-bottom: 10px;
img {
height: 100%;
}
}
&-des {
color: #90a0bb;
font-weight: 400;
}
&-footer {
margin-top: 16px;
}
&.small {
margin: 8px 0;
.@{panda-empty-prefix-cls}-image {
height: 92px;
}
}
&.middle {
margin: 32px 0;
.@{panda-empty-prefix-cls}-image {
height: 124px;
}
}
&.large {
margin: 32px 0;
.@{panda-empty-prefix-cls}-image {
height: 156px;
}
}
}
# `@wisdom-components/HistoryInfo`
> TODO: description
## Usage
```
const historyinfo = require('@wisdom-components/HistoryInfo');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/historyinfo",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: HistoryInfo - 历史数据查看
nav:
title: 组件
path: /components
group:
path: /
---
# HistoryInfo 历史数据查看
基础业务组件
- 查看任意时间段的历史数据
- 允许同期对比任意历史数据
- 允许过滤异常值
- 允许指定时间间隔的数据抽稀
## 何时使用
- 以图表或表格形式,查看历史数据时。
## 代码演示
<code src="./demos/Basic.js">
## API
api 参考 Antd Table 组件 https://ant.design/components/table-cn/#API
| 参数 | 说明 | 类型 | 默认值 |
| ------------ | ---------------------------------------------- | ------------------ | -------- |
| title | 标题 | string | 指标曲线 |
| columns | 表格列的配置描述 | array | [ ] |
| dataSource | 表格数据源 | array | [ ] |
| tableProps | 表格其他 props | object | { } |
| chartOptions | 曲线的 options | object | { } |
| onChange | 选择时间设置或曲线设置的回调,会返回修改后的值 | function(value){ } | - |
import React, { useEffect, useState } from 'react';
import request from 'umi-request';
import moment from 'moment';
import HistoryInfo from '../index';
const Demo = () => {
const [data, setData] = useState([]);
const [series, setSeries] = useState([]);
const [columns, setColumns] = useState([
{
title: '采集时间',
dataIndex: 'time',
key: 'time',
},
]);
useEffect(() => {
fetchData(initialParams);
}, []);
const fetchData = (params = {}) => {
request(baseUrl + '/Publish/Monitor/Device/SensorsDataForStation', {
method: 'post',
data: params,
}).then((res) => {
let resData = response.data;
let columnsData = resData.map((item, index) => {
return {
title: `${item.EquipmentName}-${item.sensorName}${
item.unit ? '(' + item.unit + ')' : ''
}`,
dataIndex: `value${index + 1}`,
key: `value${index + 1}`,
};
});
let tableData = resData[0].dataModel.map((item, index) => ({ key: index, time: item.PT }));
tableData.forEach((item, i) => {
resData.forEach((child, index) => {
child.dataModel.forEach((value, j) => {
if (i === j) item[`value${index + 1}`] = value.PV;
});
});
});
setData(tableData);
setColumns([...columns, ...columnsData]);
handleSeries();
});
};
const handleSeries = () => {
let resData = response.data;
let seriesData = [];
resData.forEach((item) => {
let data = [];
let obj = {};
obj.name = item.EquipmentName + item.sensorName;
item.dataModel.forEach((child) => {
data.push({
x: moment(child.PT).valueOf(),
y: child.PV,
});
});
obj.data = data;
seriesData.push(obj);
});
setSeries(seriesData);
};
const onChangeParams = (value = []) => {
let params = initialParams;
const { dateRange, ignoreOutliers, zoom, unit } = value;
let requestArr = [];
dateRange.forEach((item) => {
let param = {
...params,
stream: params.stream.map((child) => ({
...child,
dateFrom: item.dateFrom,
dateTo: item.dateTo,
})),
ignoreOutliers,
zoom,
unit,
};
requestArr.push(fetchData(param));
});
Promise.all(requestArr).then((values) => {
console.log(values);
});
};
return (
<HistoryInfo
title={'指标曲线'}
columns={columns}
dataSource={data}
chartOptions={{ series }}
tableProps={{ bordered: true }}
onChange={onChangeParams}
/>
);
};
export default Demo;
const baseUrl = 'http://192.168.10.150:8777';
const initialParams = {
stream: [
{
stationCode: 'EGBF00000006',
sensors: '出水瞬时流量,今日供水量,今日用电量',
pointVersions: '二供泵房',
dateFrom: '2021-03-15 16:01:21',
dateTo: '2021-03-16 16:01:21',
},
{
stationCode: 'EGJZ00007117',
sensors: '进水压力,出水压力,泵1状态',
pointVersions: '二供机组',
dateFrom: '2021-03-15 16:01:21',
dateTo: '2021-03-16 16:01:21',
},
],
ignoreOutliers: false, // 过滤异常值
isVertical: false, // 是否展示竖表
zoom: '', // 数据抽稀
unit: '', // 数据抽稀 min h
};
const response = {
code: 0,
msg: 'Ok',
data: [
{
stationCode: 'EGBF00000006',
sensorName: '出水瞬时流量',
dataModel: [
{
PV: 8.38,
PT: '2021-03-15 16:03:49',
},
{
PV: 8.67,
PT: '2021-03-15 16:08:50',
},
{
PV: 9.32,
PT: '2021-03-15 16:18:21',
},
{
PV: 9.65,
PT: '2021-03-15 16:23:23',
},
{
PV: 9.63,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 2,
EquipmentName: '温州嵇师村低压区智慧集成泵房',
unit: 'm³/h',
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
{
stationCode: 'EGBF00000006',
sensorName: '今日供水量',
dataModel: [
{
PV: 179,
PT: '2021-03-15 16:03:49',
},
{
PV: 179,
PT: '2021-03-15 16:08:50',
},
{
PV: 181,
PT: '2021-03-15 16:18:21',
},
{
PV: 182,
PT: '2021-03-15 16:23:23',
},
{
PV: 182,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 2,
EquipmentName: '温州嵇师村低压区智慧集成泵房',
unit: 'm³',
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
{
stationCode: 'EGBF00000006',
sensorName: '今日用电量',
dataModel: [
{
PV: 34,
PT: '2021-03-15 16:03:49',
},
{
PV: 34,
PT: '2021-03-15 16:08:50',
},
{
PV: 35,
PT: '2021-03-15 16:18:21',
},
{
PV: 35,
PT: '2021-03-15 16:23:23',
},
{
PV: 35,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 0,
EquipmentName: '温州嵇师村低压区智慧集成泵房',
unit: 'kWh',
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
{
stationCode: 'EGJZ00007117',
sensorName: '进水压力',
dataModel: [
{
PV: 0.08,
PT: '2021-03-15 16:04:21',
},
{
PV: 0.09,
PT: '2021-03-15 16:09:22',
},
{
PV: 0.09,
PT: '2021-03-15 16:18:21',
},
{
PV: 0.08,
PT: '2021-03-15 16:23:23',
},
{
PV: 0.08,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 2,
EquipmentName: '温州嵇师村低压区智慧集成泵房-低区',
unit: 'MPa',
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
{
stationCode: 'EGJZ00007117',
sensorName: '出水压力',
dataModel: [
{
PV: 0.33,
PT: '2021-03-15 16:04:21',
},
{
PV: 0.34,
PT: '2021-03-15 16:09:22',
},
{
PV: 0.33,
PT: '2021-03-15 16:18:21',
},
{
PV: 0.32,
PT: '2021-03-15 16:23:23',
},
{
PV: 0.32,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 2,
EquipmentName: '温州嵇师村低压区智慧集成泵房-低区',
unit: 'MPa',
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
{
stationCode: 'EGJZ00007117',
sensorName: '泵1状态',
dataModel: [
{
PV: 1,
PT: '2021-03-15 16:04:21',
},
{
PV: 1,
PT: '2021-03-15 16:09:22',
},
{
PV: 1,
PT: '2021-03-15 16:18:21',
},
{
PV: 1,
PT: '2021-03-15 16:23:23',
},
{
PV: 1,
PT: '2021-03-15 16:28:24',
},
],
decimalPoint: 0,
EquipmentName: '温州嵇师村低压区智慧集成泵房-低区',
unit: null,
normalData: [],
dataModelAbnormal: [],
alarmHisList: [],
AreaCode: null,
SensorType: null,
Min_Data: null,
Avg_Data: null,
MinDatas: [],
MaxDatas: [],
},
],
};
This diff is collapsed.
@import (reference) '~antd/es/style/themes/default';
@history-info-prefix-cls: ~'@{ant-prefix}-history-info';
.@{history-info-prefix-cls} {
&-content {
padding: 10px 0 0 0;
}
&-time {
display: flex;
align-items: center;
margin-bottom: 20px;
white-space: nowrap;
.@{history-info-prefix-cls}-label {
letter-spacing: 27px;
}
.@{history-info-prefix-cls}-label:after {
right: -20px;
}
.ant-radio-group,
.ant-select {
margin-right: 16px;
}
.anticon-plus-circle {
margin-left: 10px;
color: @primary-color;
font-size: 16px;
cursor: pointer;
}
}
&-cover {
display: flex;
align-items: center;
margin-bottom: 20px;
white-space: nowrap;
}
&-label {
position: relative;
width: 80px;
}
&-label:after {
position: absolute;
top: 0;
right: 7px;
content: ':';
}
&-connect {
margin: 0 10px;
}
&-connect.first {
display: none;
}
&-contrast-list {
display: flex;
align-items: center;
}
}
# `@wisdom-components/ImageSelect`
> TODO: description
## Usage
```
const imageselect = require('@wisdom-components/ImageSelect');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/imageselect",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: ImageSelect - 图片选择
nav:
title: 组件
path: /components
group:
path: /
---
# ImageSelect 图片选择
基础业务组件
- 允许单文件夹下图片选择
- 允许多文件夹下图片选择
- 允许搜索、自动定位选中图片
## 何时使用
- 在系统图片选择时。
- 在设备图例选择时。
## 代码演示
<code src="./demos/Basic.js">
## API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| src | 封面图片地址 | string | - |
| url | 默认 url 地址 | string | window.location.origin |
| dataSource | 模态框数据源 | array | - |
| width | 模态框宽度 | number | 700 |
| title | 模态框标题 | string | 点击选择图片文件 |
| onSearch | 搜索框输入事件的回调,会返回搜索框输入信息 | function(value){ } | - |
| onSelect | 选中模态框的图片后的回调,会返回选中图片信息 | function(value){ } | - |
import React from 'react';
import PandaImageSelect from '../index';
import request from 'umi-request';
class Demo extends React.Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
Authorization: '',
};
}
onSelect = (value) => {
console.log(value, 'PandaImageSelect-value');
};
fetchData = (params = {}) => {
const _this = this;
const { Authorization } = this.state;
request(baseUrl + '/Publish/OMS/FileCenter/GetImageOrderByPath', {
headers: { Authorization },
method: 'get',
params: {
path: 'assets/images/appMenu',
maxLength: 279.1525423728813,
fileName: params.fileName ? params.fileName : '',
},
}).then(function (response) {
_this.setState({
dataSource: response.data,
});
});
};
// 获取token
componentDidMount() {
const _this = this;
request(baseUrl + '/Publish/GateWay/OMS/OMSLogin', {
method: 'post',
requestType: 'form',
data: {
userName: 'omsa',
password: 'pandaomsa',
},
})
.then(function (response) {
if (response.data.token) {
const { access_token, token_type } = response.data.token;
_this.setState(
{
Authorization: token_type + ' ' + access_token,
},
() => {
_this.fetchData();
},
);
}
})
.catch(function (error) {
console.log(error);
});
}
render() {
return (
<PandaImageSelect
onSearch={(params) => this.fetchData(params)}
dataSource={this.state.dataSource}
onSelect={(value) => this.onSelect(value)}
url={baseUrl}
/>
);
}
}
export default Demo;
const baseUrl = 'http://192.168.10.150:8777'; // window.location.origin
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Input, Image, Modal, Row, Col, ConfigProvider } from 'antd';
import Empty from '@wisdom-components/Empty';
import { PlusOutlined, SearchOutlined } from '@ant-design/icons';
import './index.less';
const ImageSelect = ({ src, url, width, title, dataSource, onSearch, onSelect }) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('image-select');
const baseUrl = url ? url : window.location.origin;
const imgList = dataSource.map((item) => {
return {
list: item.list,
text: item.text,
};
});
const [visible, setVisible] = useState(false);
const [imgSrc, setImgSrc] = useState(src);
// 展示模态框
const showModal = () => {
setVisible(true);
};
// 关闭模态框
const handleCancel = () => {
setVisible(false);
};
// 搜索框内容变化时的回调
const onChange = (e) => {
if (e.target.value === '') onPressEnter(e);
};
// 搜索框按下回车的回调
const onPressEnter = (e) => {
onSearch({ fileName: e.target.value });
};
// 选中模态框中的图片
const handleSelect = (e, item) => {
setImgSrc(item.value);
onSelect && onSelect({ ...item, url: baseUrl + item.value });
handleCancel();
};
const renderImageList = (data = []) => {
return (
<Row gutter={[0, 24]}>
{!!data &&
data.map((item, index) => (
<Col
span={4}
key={item.value + index}
className={classNames(`${prefixCls}-modal-item-wrap`, {
selected: imgSrc === item.value,
})}
onClick={(e) => handleSelect(e, item)}
>
<Image
className={classNames(`${prefixCls}-modal-item-image`)}
preview={false}
src={baseUrl + item.value}
title={item.text}
/>
<div className={classNames(`${prefixCls}-modal-item-text`)}>
{item.text ? item.text.split('.')[0] : item.text}
</div>
</Col>
))}
</Row>
);
};
return (
<div className={classNames(prefixCls)}>
<div className={classNames(`${prefixCls}-wrap`)} onClick={showModal}>
{!!imgSrc && (
<Image
className={classNames(`${prefixCls}-cover`)}
preview={false}
src={baseUrl + imgSrc}
/>
)}
{!imgSrc && (
<div>
<PlusOutlined />
<p>点击选择图片</p>
</div>
)}
</div>
<Modal
width={width}
centered
title={
<div className={classNames(`${prefixCls}-modal-header`)}>
<div className={classNames(`${prefixCls}-modal-title`)}>{title}</div>
<Input
className={classNames(`${prefixCls}-modal-search`)}
placeholder="搜索图片关键词"
allowClear
bordered={false}
prefix={<SearchOutlined />}
onChange={onChange}
onPressEnter={onPressEnter}
/>
</div>
}
footer={null}
visible={visible}
onCancel={handleCancel}
className={classNames(`${prefixCls}-modal`)}
>
{!!imgList.length &&
imgList.map((item, index) => {
return (
<div key={item.text + index} className={classNames(`${prefixCls}-modal-folder`)}>
<div className={classNames(`${prefixCls}-modal-path-title`)}>{item.text}</div>
<div>{renderImageList(item.list)}</div>
</div>
);
})}
{!imgList.length && <Empty />}
</Modal>
</div>
);
};
ImageSelect.defaultProps = {
src: '', // 封面图片地址
url: '', // 默认url地址
dataSource: [], // 模态框数据源
width: 700, // 模态框宽度
title: '点击选择图片文件', // 模态框宽度
onSearch: function (v) {}, // 搜索框输入事件的回调
onSelect: function (v) {}, // 模态框选中的图片的回调
};
ImageSelect.propTypes = {
src: PropTypes.string, // 封面图片地址
url: PropTypes.string, // 默认url地址
dataSource: PropTypes.array, // 模态框数据源
width: PropTypes.number, // 模态框宽度
title: PropTypes.string, // 模态框标题
onSearch: PropTypes.func, // 搜索框输入事件的回调
onSelect: PropTypes.func, // 模态框选中的图片的回调
};
export default ImageSelect;
@import (reference) '~antd/es/style/themes/default';
@image-select-prefix-cls: ~'@{ant-prefix}-image-select';
.@{image-select-prefix-cls} {
&-wrap {
display: flex;
align-items: center;
justify-content: center;
width: 100px;
height: 100px;
padding: 5px;
overflow: hidden;
text-align: center;
background-color: rgba(250, 251, 252, 1);
border: 1px solid rgba(219, 225, 234, 1);
cursor: pointer;
p {
font-size: 12px;
}
}
&-cover {
width: 88px;
height: 88px;
}
}
.@{image-select-prefix-cls}-modal {
&-header {
display: flex;
align-items: center;
justify-content: space-between;
padding-right: 30px;
}
&-title {
color: #222222;
font-weight: 500;
font-size: 16px;
}
&-search {
width: 313px;
height: 29px;
background: #eef1f9;
border-radius: 15px;
.ant-input-prefix {
color: #5a6c8a;
}
input {
background: #eef1f9;
}
}
&-folder {
border-top: 1px solid #ccd6e1;
}
&-folder:first-of-type {
border: none;
.@{image-select-prefix-cls}-modal-path-title {
margin-top: 0;
}
}
&-path-title {
margin: 20px 0;
color: #242323;
font-size: 16px;
}
&-item-wrap {
text-align: center;
border: 1px solid transparent;
}
&-item-wrap:hover {
background: linear-gradient(0deg, #f1f4fb 0%, #ffffff 100%);
border: 1px solid #b8d6fb;
border-radius: 2px;
cursor: pointer;
}
&-item-image {
width: 40px;
height: 40px;
}
&.selected {
background: linear-gradient(0deg, #f1f4fb 0%, #ffffff 100%);
border: 1px solid #b8d6fb;
border-radius: 2px;
}
&-item-text {
font-size: 12px;
white-space: nowrap;
}
.ant-modal-body {
max-height: 600px;
overflow-y: auto;
}
}
# `@wisdom-components/MqttView`
> TODO: description
## Usage
```
const mqttView = require('@wisdom-components/MqttView');
// TODO: DEMONSTRATE API
```
'use strict';
const mqttView = require('..');
describe('@wisdom-components/MqttView', () => {
it('needs tests');
});
{
"name": "@wisdom-components/mqttview",
"version": "1.0.0",
"description": "> TODO: description",
"author": "lijiwen <961370825@qq.com>",
"homepage": "",
"license": "ISC",
"main": "lib/MqttView.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: MqttView - Mqtt连接
nav:
title: 组件
path: /components
group:
path: /
---
# PandaMqttView Mqtt 连接
<code src="./demos/Base.js">
## API
| 参数 | 说明 | 类型 | 默认值 |
| ----------- | ------------------- | ----------------------------- | ------------ |
| mqttIP | Mqtt 连接 IP 和端口 | string | - |
| mqttPath | Mqtt 连接路径 | string | /mqtt |
| mqttSsl | 是否加密 | bool | false |
| siteCode | 站点编码 | string | - |
| devices | 设备编码集合 | array | [] |
| callback | 订阅的回调 | function(value,code,topic){ } | function(){} |
| controlback | 控制的回调 | function(value,code,topic){ } | function(){} |
## 方法
| 名称 | 描述 |
| --- | --- |
| saveWaterMqtt() | Mqtt 连接并订阅数据 |
| disSaveWaconnect() | Mqtt 连接断开 |
| onSendWaMessageArrived(userName,password,callbackGuid,devicecode,tag,type,val,controlMode) | Mqtt 消息发送 |
import React from 'react';
import { Button } from 'antd';
import PandaMqttView from '../index';
class MqttDemo extends React.Component {
constructor(props) {
super(props);
this.state = {
flag: true,
};
this.mqttView = new PandaMqttView({
mqttIP: 'emqttd10.panda-water.cn:443',
mqttPath: '/mqtt',
mqttSsl: true,
siteCode: 'site_shsys656',
devices: ['QSJZ00000001', 'SHBF00000001', 'SHJZ00000001'],
callback: () => {
this.setState({ flag: false });
},
controlback: () => {},
});
}
saveMqtt() {
this.mqttView.saveWaterMqtt();
}
destroyMqtt() {
this.mqttView.disSaveWaconnect();
this.setState({ flag: true });
}
render() {
return (
<>
<Button
type="primary"
key={'Button1'}
disabled={!this.state.flag}
onClick={this.saveMqtt.bind(this)}
>
Mqtt连接
</Button>
<Button
type="primary"
key={'Button2'}
disabled={this.state.flag}
onClick={this.destroyMqtt.bind(this)}
style={{ marginLeft: '10px' }}
>
Mqtt销毁
</Button>
</>
);
}
}
export default MqttDemo;
import React, { useEffect } from 'react';
import MqttClient from 'mqtt-client';
import PropTypes from 'prop-types';
class MqttView {
constructor(props) {
this.mqttIP = props.mqttIP;
this.mqttPath = props.mqttPath;
this.mqttSsl = props.mqttSsl;
this.siteCode = props.siteCode;
this.devices = props.devices;
this.callback = props.callback;
this.controlback = props.controlback;
this.flag = false;
}
//Mqtt第一步
saveWaterMqtt() {
const hostname = this.mqttIP.split(':')[0],
port = this.mqttIP.split(':')[1] * 1,
clientId = 'PandaWeb-' + this.createGuid(),
timeout = 50,
keepAlive = 60,
cleanSession = true,
ssl = this.mqttSsl,
path = this.mqttPath,
userName = 'admin',
password = 'public';
this.saveWaCount = 0;
this.saveWaClient = new MqttClient.Client(hostname, port, path, clientId);
this.saveWaOptions = {
invocationContext: {
host: hostname,
port: port,
path: path,
clientId: clientId,
},
timeout: timeout,
keepAliveInterval: keepAlive,
cleanSession: cleanSession,
useSSL: ssl,
userName: userName,
password: password,
onSuccess: this.onSaveWaConnect.bind(this),
onFailure: function (e) {
console.log(e);
},
};
//注册连接断开处理事件
this.saveWaClient.onConnectionLost = this.onSaveWaConnectionLost.bind(this);
//注册消息接收处理事件
this.saveWaClient.onMessageArrived = this.onSaveWaMessageArrived.bind(this);
this.saveWaClient.connect(this.saveWaOptions);
}
//消息接收方法
onSaveWaMessageArrived(message) {
this.onMessageArrived(message, 'saveWaType');
}
//主题建立连接
onSaveWaConnect() {
this.flag = true;
this.devices.forEach((item) => {
if (item) {
var saveWaTopic = this.siteCode + '/' + item.replace(/[#+]/g, '@');
this.saveWaClient.subscribe(saveWaTopic);
}
});
this.devices.forEach((item) => {
if (item) {
this.saveWaClient.subscribe(
'callback/control/' + this.siteCode + '/' + item.replace(/[#+]/g, '@') + '/#',
);
}
});
}
//主题断开连接
disSaveWaconnect() {
if (this.saveWaClient) {
this.flag = false;
// this.saveWaClient.disconnect();
if (this.saveWaClient.isConnected()) this.saveWaClient.disconnect();
if (this.saveWatester) {
clearInterval(this.saveWatester);
this.saveWatester = null;
}
}
}
//主题掉线重连接
onSaveWaConnectionLost() {
if (this.flag) this.saveWaClient.connect(this.saveWaOptions);
this.saveWatester = setInterval(() => {
if (this.saveWaClient.isConnected()) {
clearInterval(this.saveWatester);
this.saveWatester = null;
} else {
this.saveWaClient.connect(this.saveWaOptions);
}
}, 1000);
}
//消息接收
onMessageArrived(message, infoType) {
var topic = message.topic;
var code = topic.split('/')[topic.split('/').length - 1];
if (topic.indexOf('callback/control/' + this.siteCode) > -1) {
this.controlback(message.payloadString, code, topic);
return false;
}
this.callback(message.payloadString, code, topic);
}
//消息发送
onSendWaMessageArrived(
userName,
password,
callbackGuid,
devicecode,
tag,
type,
val,
controlMode,
) {
var info = {
userName: userName,
password: password,
callbackGuid: callbackGuid,
type: '',
operateType: type,
controlValue: val || String(val) != '' ? val : '',
sitecode: this.siteCode,
devicecode: devicecode,
controlMode: controlMode ? controlMode : '',
tag: tag,
};
var message = new MqttView.Message(JSON.stringify(info));
message.destinationName = 'setdata/' + this.siteCode + '/' + devicecode;
this.saveWaClient.send(message);
}
//生成GUID
createGuid() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'
.replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == 'x' ? r : (r & 0x3) | 0x8;
return v.toString(16);
})
.toUpperCase();
}
}
MqttView.propTypes = {
mqttIP: PropTypes.string,
mqttPath: PropTypes.string,
mqttSsl: PropTypes.bool,
siteCode: PropTypes.string,
devices: PropTypes.array,
callback: PropTypes.func,
controlback: PropTypes.func,
};
MqttView.defaultProps = {
mqttIP: '',
mqttPath: '/mqtt',
mqttSsl: false,
siteCode: '',
devices: [],
callback: () => {},
controlback: () => {},
};
export default MqttView;
# `@wisdom-components/QuotaSelect`
> TODO: description
## Usage
```
const quotaselect = require('@wisdom-components/QuotaSelect');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/quotaselect",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: QuotaSelect - 指标选择
nav:
title: 组件
path: /components
group:
path: /
---
# QuotaSelect 指标选择
基础业务组件
- 允许对分类指标进行选择
- 允许限制选择最大指标数
- 允许搜索指标
## 何时使用
- 在指标选择时。
## 代码演示
<code src="./demos/Basic.js">
## API
指标选择按钮 props 参考 https://ant.design/components/button-cn/#API
已选指标树 props 参考 https://ant.design/components/tree-cn/#API
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| buttonProps | 指标选择按钮 | object | { } |
| width | 模态框宽度 | number | 900 |
| title | 模态框标题 | string | 选择显示字段 |
| cancelText | 模态框取消文本 | string | 取消选择 |
| placeholder | 搜索框占位符 | string | 搜索关键词 |
| searchPrefix | 搜索框前置图标 | ReactNode | SearchOutlined |
| targetValue | 指标类型 | string | 重要指标 |
| maximum | 最多可选择指标数量 | number | 0 |
| dataSource | 指标列表数据源 | array | [ ] |
| selectData | 已选指标数据 | array | [ ] |
| treeProps | 已选指标树 props | object | { } |
| onModalCancel | 点击模态框取消按钮的回调 | function(value){ } | - |
| onModalOk | 模态框点击确定回调 | function(value){ } | - |
| onSearch | 搜索框输入事件的回调,会返回搜索框输入信息 | function(value){ } | - |
| onRadioChange | 指标类型切换的回调 | function(value){ } | - |
| onCancelSelect | 点击已选指标树的删除按钮的回调 | function(value){ } | - |
import React, { useEffect, useState } from 'react';
import request from 'umi-request';
import { UnorderedListOutlined, SearchOutlined } from '@ant-design/icons';
import PandaQuotaSelect from '../index';
const Demo = () => {
const [quotaList, setQuotaList] = useState([]);
const [selectData, setSelectData] = useState([]);
const fetchData = () => {
request(baseUrl + '/Publish/Monitor/Device/GetQuotaList', {
method: 'get',
params: {
accountName: '二供泵房',
addrSchemeID: '',
},
})
.then(function (response) {
if (response.code == '0') {
let curData = response.data.map((item) => ({
title: item.Name,
key: item.Name,
checked: false,
...item,
}));
setQuotaList(curData);
}
})
.catch(function (error) {
console.log(error);
});
};
useEffect(() => {
fetchData();
}, []);
const onModalOk = () => {
console.log('onModalOk');
};
const onModalCancel = () => {
let newQuotaList = [...quotaList];
newQuotaList.forEach((item) => {
item.checked = false;
});
setQuotaList(newQuotaList);
setSelectData([]);
console.log('onModalCancel');
};
const onCheckboxChange = (e) => {
let curData = [...selectData];
let newQuotaList = [...quotaList];
let data = curData.filter((item) => item.title === e.target.value);
if (data.length) {
curData = curData.filter((item) => item.title !== e.target.value);
} else {
curData.push({ key: e.target.value, title: e.target.value });
}
newQuotaList.forEach((item) => {
if (item.title === e.target.value) item.checked = !item.checked;
});
setSelectData(curData);
setQuotaList(newQuotaList);
console.log(e.target.value, 'onCheckboxChange');
};
const onSearch = (e) => {
if (e.type === 'keydown') {
console.log(e.target.value, 'onSearch');
}
};
const onRadioChange = (e) => {
console.log(e, 'onRadioChange');
};
const onCancelSelect = ({ title }) => {
let curData = [...selectData];
let newQuotaList = [...quotaList];
let data = curData.filter((item) => item.title !== title);
newQuotaList.forEach((item) => {
if (item.title === title) item.checked = !item.checked;
});
setSelectData(data);
setQuotaList(newQuotaList);
console.log(title, 'onCancelSelect');
};
const onDragEnter = (info) => {
console.log(info, 'onDragEnter');
};
const onDrop = (info) => {
console.log(info, 'onDrop');
const dropKey = info.node.key;
const dragKey = info.dragNode.key;
const dropPos = info.node.pos.split('-');
const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
const loop = (data, key, callback) => {
for (let i = 0; i < data.length; i++) {
if (data[i].key === key) {
return callback(data[i], i, data);
}
if (data[i].children) {
loop(data[i].children, key, callback);
}
}
};
const data = [...selectData];
let dragObj;
loop(data, dragKey, (item, index, arr) => {
arr.splice(index, 1);
dragObj = item;
});
if (!info.dropToGap) {
loop(data, dropKey, (item) => {
item.children = item.children || [];
item.children.unshift(dragObj);
});
} else if (
(info.node.props.children || []).length > 0 && // Has children
info.node.props.expanded && // Is expanded
dropPosition === 1 // On the bottom gap
) {
loop(data, dropKey, (item) => {
item.children = item.children || [];
item.children.unshift(dragObj);
});
} else {
let ar;
let i;
loop(data, dropKey, (item, index, arr) => {
ar = arr;
i = index;
});
if (dropPosition === -1) {
ar.splice(i, 0, dragObj);
} else {
ar.splice(i + 1, 0, dragObj);
}
}
setSelectData(data);
};
return (
<PandaQuotaSelect
buttonProps={{
icon: <UnorderedListOutlined />,
children: `指标选择(${selectData.length})`,
}}
onModalCancel={onModalCancel}
onModalOk={onModalOk}
maximum={3}
targetValue={'emphasis'}
dataSource={quotaList}
selectData={selectData}
onSearch={onSearch}
searchPrefix={<SearchOutlined />}
onRadioChange={onRadioChange}
onCheckboxChange={onCheckboxChange}
onCancelSelect={onCancelSelect}
treeProps={{
draggable: true,
onDragEnter,
onDrop,
}}
/>
);
};
export default Demo;
const baseUrl = 'http://192.168.10.150:8777';
import React, { useContext, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Button, Input, Radio, Modal, Checkbox, Row, Col, Tree, ConfigProvider } from 'antd';
import Empty from '@wisdom-components/Empty';
import { ExclamationCircleFilled, SearchOutlined, CloseOutlined } from '@ant-design/icons';
import './index.less';
const { TreeNode } = Tree;
const QuotaSelect = ({
buttonProps,
width,
title,
cancelText,
onModalOk,
onModalCancel,
searchPrefix,
placeholder,
targetValue,
maximum,
dataSource,
selectData,
onSearch,
onRadioChange,
onCheckboxChange,
onCancelSelect,
treeProps,
}) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('quota-select');
const [visible, setVisible] = useState(false);
// 展示模态框
const showModal = () => {
setVisible(true);
};
// 关闭模态框
const handleCancel = (e) => {
if (e.target.innerHTML === cancelText) {
onModalCancel && onModalCancel();
} else {
setVisible(false);
}
};
const onOk = () => {
onModalOk && onModalOk();
setVisible(false);
};
return (
<div className={classNames(prefixCls)}>
<Button {...buttonProps} onClick={showModal} />
<Modal
centered
width={width}
title={title}
cancelText={cancelText}
visible={visible}
onCancel={handleCancel}
onOk={onOk}
className={classNames(`${prefixCls}-modal`)}
>
<div className={classNames(`${prefixCls}-modal-wrap`)}>
<div className={classNames(`${prefixCls}-modal-left`)}>
<div className={classNames(`${prefixCls}-modal-select-wrap`)}>
<Input
className={classNames(`${prefixCls}-modal-search`)}
placeholder={placeholder}
bordered={false}
prefix={searchPrefix}
onChange={onSearch}
onPressEnter={onSearch}
/>
<div className={classNames(`${prefixCls}-modal-target`)}>
<div>指标:</div>
<Radio.Group onChange={onRadioChange} defaultValue={targetValue}>
<Radio.Button value="emphasis">重点指标</Radio.Button>
<Radio.Button value="all">全部</Radio.Button>
</Radio.Group>
</div>
<div
className={classNames(`${prefixCls}-modal-select`, {
warning: !(selectData.length < maximum),
})}
>
{selectData.length < maximum ? (
<>
<ExclamationCircleFilled />
<div>已选择 {selectData.length} 个指标</div>
</>
) : (
<>
<ExclamationCircleFilled />
<div>已达上限,最多选择 {maximum} 个指标</div>
</>
)}
</div>
</div>
<div className={classNames(`${prefixCls}-modal-option-wrap`)}>
{!dataSource.length && <Empty />}
<Row gutter={[0, 6]}>
{!!dataSource.length &&
dataSource.map((item) => (
<Col span={8} key={item.key}>
<Checkbox
value={item.title}
checked={item.checked}
disabled={
(selectData.length > maximum || selectData.length === maximum) &&
!item.checked
}
onChange={onCheckboxChange}
>
{item.title}
</Checkbox>
</Col>
))}
</Row>
</div>
</div>
<div className={classNames(`${prefixCls}-modal-right`)}>
<div className={classNames(`${prefixCls}-modal-number`)}>
已选:{selectData.length}/{maximum}
</div>
<div className={classNames(`${prefixCls}-modal-tree`)}>
<Tree {...treeProps}>
{selectData.map((item) => (
<TreeNode
key={item.key}
title={
<div className={classNames(`${prefixCls}-modal-tree-title`)}>
<div>{item.title}</div>
<CloseOutlined onClick={() => onCancelSelect(item)} />
</div>
}
/>
))}
</Tree>
</div>
</div>
</div>
</Modal>
</div>
);
};
QuotaSelect.defaultProps = {
buttonProps: {},
width: 900,
title: '选择显示字段',
cancelText: '取消选择',
placeholder: '搜索关键词',
searchPrefix: <SearchOutlined />,
targetValue: 'emphasis',
maximum: 0,
dataSource: [],
selectData: [],
treeProps: {},
onModalCancel: () => {},
onModalOk: () => {},
onSearch: () => {},
onRadioChange: () => {},
onCancelSelect: () => {},
};
QuotaSelect.propTypes = {
buttonProps: PropTypes.object,
width: PropTypes.number,
title: PropTypes.string,
cancelText: PropTypes.string,
placeholder: PropTypes.string,
searchPrefix: PropTypes.node,
targetValue: PropTypes.string,
maximum: PropTypes.number,
dataSource: PropTypes.array,
selectData: PropTypes.array,
treeProps: PropTypes.object,
onModalCancel: PropTypes.func,
onModalOk: PropTypes.func,
onSearch: PropTypes.func,
onRadioChange: PropTypes.func,
onCancelSelect: PropTypes.func,
};
export default QuotaSelect;
@import (reference) '~antd/es/style/themes/default';
@quota-select-prefix-cls: ~'@{ant-prefix}-quota-select';
.@{quota-select-prefix-cls} {
}
.@{quota-select-prefix-cls}-modal {
&-wrap {
display: flex;
height: 500px;
}
&-left {
flex: 7;
border-right: 1px solid rgba(0, 0, 0, 0.2);
.empty {
margin: 128px 0;
}
}
&-right {
flex: 3;
}
&-select-wrap {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px 0 0;
}
&-search {
width: 160px;
background-color: #eef1f9;
border-radius: 15px;
.ant-input-prefix {
color: #5a6c8a;
}
}
&-search:hover {
background-color: #eef1f9;
}
&-target {
display: flex;
align-items: center;
margin: 0 10px 0 10px;
white-space: nowrap;
}
&-select {
display: flex;
align-items: center;
white-space: nowrap;
span {
margin-right: 6px;
color: #52c41a;
font-size: 16px;
}
}
&-select.warning {
color: #f0495d;
span {
color: #f0495d;
}
}
&-option-wrap {
height: calc(100% - 52px);
margin-top: 20px;
overflow-y: auto;
}
&-number {
margin-bottom: 10px;
padding: 4px 0 10px 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.2);
}
&-tree {
height: calc(100% - 47px);
overflow-y: auto;
.ant-tree-treenode,
.ant-tree-node-content-wrapper {
width: 100%;
}
}
&-tree-title {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
}
}
# `@wisdom-components/RealTimeInfo`
> TODO: description
## Usage
```
const realtimeinfo = require('@wisdom-components/RealTimeInfo');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/realtimeinfo",
"version": "1.0.0",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: RealTimeInfo - 实时数据查看
nav:
title: 组件
path: /components
group:
path: /
---
# RealTimeInfo 实时数据查看
基础业务组件
- 查看指标名称对应的实时数据
- 允许对重点指标和全部指标筛选
- 允许搜索指标名称
## 何时使用
- 实时指标监控,查看实时指标数据时。
## 代码演示
<code src="./demos/Basic.js">
## API
api 参考 Antd Table 组件 https://ant.design/components/table-cn/#API
| 参数 | 说明 | 类型 | 默认值 |
| ------------- | ------------------------------------------ | ------------------ | -------------- |
| guid | 采集编码 | string | - |
| placeholder | 搜索框占位符 | string | 输入指标名称等 |
| targetValue | 选中‘重要指标’或者‘全部’ | string | emphasis |
| onSearch | 搜索框输入事件的回调,会返回搜索框输入信息 | function(value){ } | - |
| onRadioChange | 指标选择事件的回调,会返回选中的指标值 | function(value){ } | - |
import React, { useEffect, useState } from 'react';
import Empty from '@wisdom-components/Empty';
import RealTimeInfo from '../index';
import request from 'umi-request';
const Demo = () => {
const [data, setData] = useState([]);
const [targetValue, setTargetValue] = useState('emphasis');
const [guid, setGuid] = useState('');
const [updateTime, setUpdateTime] = useState('');
const GetPointAddressEntry = (params = {}) => {
return request(baseUrl + '/Publish/Monitor/PointAddress/GetPointAddressEntry', {
method: 'get',
params: {
versionID: 4,
},
});
};
const GetSensorType = (params = {}) => {
return request(baseUrl + '/Publish/Monitor/Scada/GetSensorType', {
method: 'get',
params: {},
});
};
const GetDeviceRealInfo = (params = {}) => {
return request(baseUrl + '/Publish/Monitor/Device/DeviceRealInfo', {
method: 'post',
data: {
userID: '1',
pageIndex: 1,
pageSize: 20,
isFocus: false,
accountFieldParams: [{ AName: '二供泵房' }, { AName: '二供机组' }],
equipmentCode: 'EGBF00000001',
},
});
};
const fetchData = async () => {
let data1 = await GetPointAddressEntry();
let data2 = await GetSensorType();
let data3 = await GetDeviceRealInfo();
setUpdateTime(data3.data.list[0].PT);
setGuid(data3.data.list[0].Child[0].GUID);
handleData(data1.data, data2.data, data3.data);
};
const handleData = (data1 = [], data2 = [], data3 = []) => {
let newData = data1.map((item, index) => {
return {
id: item.ID,
key: item.ID,
index: index + 1,
name: item.Name,
value: 0,
unit: '--',
type: '--',
time: data3.list[0].PT,
...item,
};
});
newData.forEach((item) => {
let curData1 = data2.filter((child) => child.ID == item.SensorTypeID);
let curData2 = data3.list[0].DataList.filter((child) => child.PAID == item.ID);
if (curData1.length) {
item.unit = curData1[0].Unit || '--';
item.type = curData1[0].Name || '--';
}
if (curData2.length) {
item.value = curData2[0].PV || '--';
}
});
setData(newData);
};
useEffect(() => {
fetchData();
}, []);
const onSearch = (e) => {
console.log(e.type, e.target.value);
};
const onRadioChange = (e) => {
setTargetValue(e.target.value);
console.log('value', e.target.value);
};
return (
<RealTimeInfo
onSearch={onSearch}
onRadioChange={onRadioChange}
targetValue={targetValue}
guid={guid}
updateTime={updateTime}
bordered
columns={columns}
dataSource={data}
pagination={{ pageSize: 50 }}
scroll={{ y: 500 }}
locale={{ emptyText: <Empty /> }}
/>
);
};
export default Demo;
const baseUrl = 'http://192.168.10.150:8777';
const columns = [
{
title: '序号',
dataIndex: 'index',
},
{
title: '指标名称',
dataIndex: 'name',
width: 150,
},
{
title: '最新指标',
dataIndex: 'value',
render: (text) => <a>{text}</a>,
},
{
title: '单位',
dataIndex: 'unit',
},
{
title: '指标类型',
dataIndex: 'type',
},
{
title: '更新时间',
dataIndex: 'time',
},
];
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Input, Radio, Table, ConfigProvider } from 'antd';
import './index.less';
const RealTimeInfo = (props) => {
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('realtime-info');
const { placeholder, targetValue, guid, updateTime, onSearch, onRadioChange } = props;
return (
<div className={classNames(prefixCls)}>
<div className={classNames(`${prefixCls}-search-wrap`)}>
<div className={classNames(`${prefixCls}-search`)}>
<div className={classNames(`${prefixCls}-label`)}>搜索:</div>
<Input placeholder={placeholder} onChange={onSearch} onPressEnter={onSearch} />
</div>
<div className={classNames(`${prefixCls}-target`)}>
<div className={classNames(`${prefixCls}-label`)}>指标:</div>
<Radio.Group onChange={onRadioChange} defaultValue={targetValue}>
<Radio.Button value="emphasis">重点指标</Radio.Button>
<Radio.Button value="all">全部</Radio.Button>
</Radio.Group>
</div>
</div>
<div className={classNames(`${prefixCls}-code-wrap`)}>
<div>采集编码:{guid}</div>
<div>更新时间:{updateTime}</div>
</div>
<Table {...props} />
</div>
);
};
RealTimeInfo.defaultProps = {
placeholder: '输入指标名称等',
guid: '--',
targetValue: 'emphasis',
updateTime: '--',
onSearch: () => {},
onRadioChange: () => {},
};
RealTimeInfo.propTypes = {
placeholder: PropTypes.string,
guid: PropTypes.string,
targetValue: PropTypes.string,
updateTime: PropTypes.string,
onSearch: PropTypes.func,
onRadioChange: PropTypes.func,
};
export default RealTimeInfo;
@import (reference) '~antd/es/style/themes/default';
@realtime-info-prefix-cls: ~'@{ant-prefix}-realtime-info';
.@{realtime-info-prefix-cls} {
padding: 10px;
&-search-wrap {
display: flex;
margin-bottom: 20px;
}
&-search,
&-target {
display: flex;
align-items: center;
margin-right: 20px;
}
&-label {
white-space: nowrap;
}
&-code-wrap {
display: flex;
justify-content: space-between;
margin-bottom: 20px;
}
}
# `@wisdom-components/TimeRangePicker`
> TODO: description
## Usage
```
const timeRangePicker = require('@wisdom-components/TimeRangePicker');
// TODO: DEMONSTRATE API
```
{
"name": "@wisdom-components/timerangepicker",
"version": "1.0.1",
"description": "> TODO: description",
"author": "tuqian <webtuqian@163.com>",
"homepage": "",
"license": "ISC",
"main": "lib/index.js",
"directories": {
"lib": "lib",
"test": "__tests__"
},
"files": [
"lib"
],
"publishConfig": {
"registry": "https://g.civnet.cn:4873/"
},
"repository": {
"type": "git",
"url": "https://g.civnet.cn:8443/ReactWeb5/wisdom-components.git"
},
"scripts": {
"test": "echo \"Error: run tests from root\" && exit 1"
}
}
---
title: TimeRangePicker - 自定义类型时间选择
nav:
title: 组件
path: /components
group:
path: /
---
# TimeRangePicker 自定义类型时间选择
基础业务组件
- 支持默认时间的点击
- 支持无定义的类型过滤
- 支持自定义时间选项的精度
## 何时使用
- 需要自定义类型的时间选择时。
## 代码演示
<code src="./demos/Basic.js">
## API
自定义时间选择 api 参考 Antd DatePicker 组件 https://ant.design/components/date-picker-cn/#API
| 参数 | 说明 | 类型 | 默认值 |
| ------------ | ----------------------------------- | ------------------ | --------------------- |
| layout | 布局 | string | horizontal / vertical |
| value | 值 | string | - |
| defaultValue | 默认选中的值 | string | - |
| width | layout 值为 vertical 时,下拉框宽度 | number | 120 |
| dataSource | 数据源 | array | - |
| timeProps | 自定义时间选择 api | object | { } |
| onChange | 选择事件的回调,会返回选中的时间项 | function(value){ } | - |
import React, { useState } from 'react';
import TimeRangePicker from '../index';
import moment from 'moment';
const Demo = () => {
const [dateValue, setDateValue] = useState(null);
const [timeValue, setTimeValue] = useState(null);
const [data, setData] = useState({ startDate: '', endDate: '' });
const [time, setTime] = useState({ startTime: '', endTime: '' });
const onDateChange = (key, data = []) => {
let start = '',
end = '';
switch (key) {
case 'all':
break;
case 'yesterday':
start = moment().subtract(1, 'days').format(startFormat);
end = moment().subtract(1, 'days').format(endFormat);
break;
case 'today':
start = moment().format(startFormat);
end = moment().format(endFormat);
break;
case 'thisWeek':
start = moment().subtract(6, 'days').format(startFormat);
end = moment().format(endFormat);
break;
case 'lastWeek':
start = moment().subtract(14, 'days').format(startFormat);
end = moment().subtract(7, 'days').format(endFormat);
break;
case 'thisMonth':
start = moment().startOf('month').format(startFormat);
end = moment().endOf('month').format(endFormat);
break;
case 'lastMonth':
start = moment()
.month(moment().month() - 1)
.startOf('month')
.format(startFormat);
end = moment()
.month(moment().month() - 1)
.endOf('month')
.format(endFormat);
break;
case 'thisYear':
start = moment().startOf('year').format(startFormat);
end = moment().endOf('year').format(endFormat);
break;
case 'lastYear':
start = moment()
.year(moment().year() - 1)
.startOf('year')
.format(startFormat);
end = moment()
.year(moment().year() - 1)
.endOf('year')
.format(endFormat);
break;
case 'customer':
if (data && data.length > 0) {
start = moment(data[0]).format(startFormat);
end = moment(data[1]).format(endFormat);
}
break;
}
setDateValue(key);
setData({ startDate: start, endDate: end });
};
const onTimeChange = (key, data = []) => {
let start = '',
end = '';
switch (key) {
case 'oneHour':
start = moment().subtract(1, 'hour').format(timeFormat);
end = moment().format(timeFormat);
break;
case 'fourHour':
start = moment().subtract(4, 'hour').format(timeFormat);
end = moment().format(timeFormat);
break;
case 'twelveHours':
start = moment().subtract(12, 'hour').format(timeFormat);
end = moment().format(timeFormat);
break;
case 'roundClock':
start = moment().subtract(24, 'hour').format(timeFormat);
end = moment().format(timeFormat);
break;
case 'yesterday':
start = moment().subtract(1, 'days').format(startFormat);
end = moment().subtract(1, 'days').format(endFormat);
break;
case 'customer':
if (data && data.length > 0) {
start = moment(data[0]).format(timeFormat);
end = moment(data[1]).format(timeFormat);
}
break;
}
setTimeValue(key);
setTime({ startTime: start, endTime: end });
};
return (
<>
<h3>horizontal</h3>
<TimeRangePicker
value={dateValue}
onChange={onDateChange}
defaultValue={dateList[0].key}
dataSource={dateList}
layout={'horizontal'}
/>
<br />
<h3>vertical</h3>
<TimeRangePicker
value={dateValue}
onChange={onDateChange}
defaultValue={dateList[0].key}
dataSource={dateList}
layout={'vertical'}
/>
<br />
<div>
startDate{data.startDate}
<br />
endDate{data.endDate}
</div>
<br />
<TimeRangePicker
value={timeValue}
onChange={onTimeChange}
dataSource={timeList}
timeProps={{ showTime: true }}
/>
<br />
<div>
startTime{time.startTime}
<br />
endTime{time.endTime}
</div>
</>
);
};
export default Demo;
const startFormat = 'YYYY-MM-DD 00:00:00';
const endFormat = 'YYYY-MM-DD 23:59:59';
const timeFormat = 'YYYY-MM-DD kk:mm:ss';
const dateList = [
{
key: 'all',
name: '全部',
},
{
key: 'yesterday',
name: '昨天',
},
{
key: 'today',
name: '今天',
},
{
key: 'thisWeek',
name: '本周',
},
{
key: 'lastWeek',
name: '上周',
},
{
key: 'thisMonth',
name: '本月',
},
{
key: 'lastMonth',
name: '上月',
},
{
key: 'thisYear',
name: '今年',
},
{
key: 'lastYear',
name: '去年',
},
{
key: 'customer',
name: '自定义',
},
];
const timeList = [
{
key: 'oneHour',
name: '近1小时',
},
{
key: 'fourHour',
name: '近4小时',
},
{
key: 'twelveHours',
name: '近12小时',
},
{
key: 'roundClock',
name: '近24小时',
},
{
key: 'yesterday',
name: '昨天',
},
{
key: 'customer',
name: '自定义',
},
];
import React, { useContext, useEffect, useState } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
import { Select, DatePicker, Radio, ConfigProvider } from 'antd';
import Empty from '@wisdom-components/empty';
import './index.less';
const { RangePicker } = DatePicker;
const { Option } = Select;
const TimeRangePicker = ({
layout,
value,
defaultValue,
width,
dataSource,
timeProps,
onChange,
}) => {
const [visible, setVisible] = useState(false);
const [visibleKey, setVisibleKey] = useState(null);
const { getPrefixCls } = useContext(ConfigProvider.ConfigContext);
const prefixCls = getPrefixCls('time-range-picker');
useEffect(() => {
let data = dataSource.filter((item) => item.name === CUSTOMER_NAME);
setVisibleKey(data.length > 0 ? data[0].key : null);
}, []);
const onRadioChange = (e) => {
setVisible(e.target.value === visibleKey);
onChange && onChange(e.target.value);
};
const onTimeChange = (dates) => {
onChange && onChange(visibleKey, dates);
};
const onSelectChange = (value) => {
setVisible(value === visibleKey);
onChange && onChange(value);
};
const renderHorizontal = () => {
return (
<Radio.Group value={value} defaultValue={defaultValue} onChange={onRadioChange}>
{dataSource.map((item) => (
<Radio.Button key={item.key} value={item.key}>
{item.name}
</Radio.Button>
))}
</Radio.Group>
);
};
const renderVertical = () => {
return !!dataSource.length ? (
<Select
value={value}
defaultValue={defaultValue}
style={{ width: width }}
onChange={onSelectChange}
>
{dataSource.map((item) => (
<Option key={item.key} value={item.key}>
{item.name}
</Option>
))}
</Select>
) : (
renderEmpty()
);
};
const renderEmpty = () => {
return (
<ConfigProvider renderEmpty={() => <Empty size={'small'} description={'暂无数据'} />}>
<Select style={{ width: width }} />
</ConfigProvider>
);
};
return (
<div className={classNames(prefixCls)}>
{layout === 'horizontal' && renderHorizontal()}
{layout === 'vertical' && renderVertical()}
{visible && (
<RangePicker
className={classNames(`${prefixCls}-customer`)}
onChange={onTimeChange}
{...timeProps}
/>
)}
</div>
);
};
TimeRangePicker.defaultProps = {
layout: 'horizontal',
value: '',
defaultValue: '',
width: 120,
dataSource: [],
timeProps: {},
onChange: function (v) {},
};
TimeRangePicker.propTypes = {
layout: PropTypes.string,
value: PropTypes.string,
defaultValue: PropTypes.string,
width: PropTypes.number,
dataSource: PropTypes.array,
timeProps: PropTypes.object,
onChange: PropTypes.func,
};
export default TimeRangePicker;
const CUSTOMER_NAME = '自定义';
@import (reference) '~antd/es/style/themes/default';
@time-range-picker-prefix-cls: ~'@{ant-prefix}-time-range-picker';
.@{time-range-picker-prefix-cls} {
&-customer {
margin-left: 10px;
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment