Browse Source

factor管理界面

master
yinweiwen 2 years ago
parent
commit
19c3e234a0
  1. 20
      code/web/client/assets/color.less
  2. 3
      code/web/client/src/app.js
  3. 3
      code/web/client/src/layout/components/header/index.js
  4. 16
      code/web/client/src/sections/factor/actions/factors.js
  5. 7
      code/web/client/src/sections/factor/actions/index.js
  6. 145
      code/web/client/src/sections/factor/containers/ConfigModal.js
  7. 196
      code/web/client/src/sections/factor/containers/FactorProfile.js
  8. 5
      code/web/client/src/sections/factor/containers/index.js
  9. 4
      code/web/client/src/sections/factor/containers/models.js
  10. 15
      code/web/client/src/sections/factor/index.js
  11. 16
      code/web/client/src/sections/factor/nav-item.js
  12. 5
      code/web/client/src/sections/factor/reducers/index.js
  13. 12
      code/web/client/src/sections/factor/routes.js
  14. 3
      code/web/client/src/sections/factor/style.less
  15. 3
      code/web/client/src/utils/webapi.js

20
code/web/client/assets/color.less

@ -174,7 +174,7 @@ button::-moz-focus-inner,
[type='submit']::-moz-focus-inner {border-style: none;} [type='submit']::-moz-focus-inner {border-style: none;}
fieldset {border: 0;} fieldset {border: 0;}
legend {color: inherit;} legend {color: inherit;}
mark {background-color: color(~`colorPalette("@{table-header-sort-active-bg}", 2)`);} mark {background-color: #feffe6;}
::selection {color: #fff;background: @primary-color;} ::selection {color: #fff;background: @primary-color;}
.anticon {color: inherit;} .anticon {color: inherit;}
.ant-fade-enter, .ant-fade-appear {animation-fill-mode: both;} .ant-fade-enter, .ant-fade-appear {animation-fill-mode: both;}
@ -1147,10 +1147,10 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
.ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;} .ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;}
.ant-menu-item-danger.ant-menu-item {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:active {background: color(~`colorPalette("@{alert-info-border-color}", 4)`);} .ant-menu-item-danger.ant-menu-item:active {background: color(~`colorPalette("@{skeleton-color}", 1)`);}
.ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{alert-info-border-color}", 4)`);} .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{skeleton-color}", 1)`);}
.ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;} .ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;}
.ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;} .ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;}
.ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;} .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;}
@ -1727,17 +1727,17 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;} .ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;}
.ant-tag-checkable-checked {background-color: @primary-color;} .ant-tag-checkable-checked {background-color: @primary-color;}
.ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);} .ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);}
.ant-tag-pink {color: #c41d7f;background: #fff0f6;border-color: #ffadd2;} .ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{shadow-color}", 1)`);border-color: #ffadd2;}
.ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-magenta {color: #c41d7f;background: #fff0f6;border-color: #ffadd2;} .ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{shadow-color}", 1)`);border-color: #ffadd2;}
.ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{alert-info-border-color}", 4)`);border-color: #ffa39e;} .ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{skeleton-color}", 1)`);border-color: #ffa39e;}
.ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;} .ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;}
.ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;} .ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;}
.ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;} .ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;}
.ant-tag-orange {color: #d46b08;background: #fff7e6;border-color: #ffd591;} .ant-tag-orange {color: #d46b08;background: color(~`colorPalette("@{switch-bg}", 1)`);border-color: #ffd591;}
.ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;} .ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;}
.ant-tag-yellow {color: #d4b106;background: color(~`colorPalette("@{table-header-sort-active-bg}", 2)`);border-color: #fffb8f;} .ant-tag-yellow {color: #d4b106;background: #feffe6;border-color: #fffb8f;}
.ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;} .ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;}
.ant-tag-gold {color: #d48806;background: #fffbe6;border-color: #ffe58f;} .ant-tag-gold {color: #d48806;background: #fffbe6;border-color: #ffe58f;}
.ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;} .ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;}
@ -1749,9 +1749,9 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;} .ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;}
.ant-tag-blue {color: #096dd9;background: #e6f7ff;border-color: #91d5ff;} .ant-tag-blue {color: #096dd9;background: #e6f7ff;border-color: #91d5ff;}
.ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;} .ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;}
.ant-tag-geekblue {color: #1d39c4;background: color(~`colorPalette("@{slider-rail-background-color-hover}", 1)`);border-color: #adc6ff;} .ant-tag-geekblue {color: #1d39c4;background: color(~`colorPalette("@{disabled-color}", 1)`);border-color: #adc6ff;}
.ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;} .ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;}
.ant-tag-purple {color: #531dab;background: color(~`colorPalette("@{shadow-color-inverse}", 1)`);border-color: #d3adf7;} .ant-tag-purple {color: #531dab;background: #f9f0ff;border-color: #d3adf7;}
.ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;} .ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;}
.ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;} .ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;}
.ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;} .ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;}

3
code/web/client/src/app.js

@ -5,6 +5,7 @@ import Layout from './layout';
import Auth from './sections/auth'; import Auth from './sections/auth';
import Gateway from './sections/gateway'; import Gateway from './sections/gateway';
import Search from './sections/search'; import Search from './sections/search';
import Factor from './sections/factor';
const App = props => { const App = props => {
const { projectName } = props const { projectName } = props
@ -16,7 +17,7 @@ const App = props => {
return ( return (
<Layout <Layout
title={projectName} title={projectName}
sections={[Auth, Search, Gateway]} sections={[Auth, Search, Gateway,Factor]}
/> />
) )
} }

3
code/web/client/src/layout/components/header/index.js

@ -76,6 +76,9 @@ const Header = props => {
<Menu.Item key="gatewayProfile" icon={<ApartmentOutlined />} > <Menu.Item key="gatewayProfile" icon={<ApartmentOutlined />} >
<Link to="/gateway_profile">网关配置</Link> <Link to="/gateway_profile">网关配置</Link>
</Menu.Item> </Menu.Item>
<Menu.Item key="factorProfile" icon={<ApartmentOutlined />} >
<Link to="/factor_profile">监测因素</Link>
</Menu.Item>
</Menu.SubMenu> </Menu.SubMenu>
<Menu.SubMenu key="theme" title={<div style={{ margin: '0 8px' }}>主题切换</div>} > <Menu.SubMenu key="theme" title={<div style={{ margin: '0 8px' }}>主题切换</div>} >
<Menu.Item key="themeLight" > <Menu.Item key="themeLight" >

16
code/web/client/src/sections/factor/actions/factors.js

@ -0,0 +1,16 @@
'use strict';
import { basicAction } from '@peace/utils'
import { ApiTable } from '$utils'
export function list(query) {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
query: query,
actionType: 'GET_FACTOR_LIST',
url: ApiTable.getFactors,
msg: { error: '获取监测原型列表失败' },
reducer: { name: 'factors' }
})
}

7
code/web/client/src/sections/factor/actions/index.js

@ -0,0 +1,7 @@
'use strict';
import * as factor from './factors'
export default {
...factor
}

145
code/web/client/src/sections/factor/containers/ConfigModal.js

@ -0,0 +1,145 @@
import React, { useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import { Spin, Select, Divider, Space, Button, Modal, Input, Form, Switch, AutoComplete } from 'antd';
import ProForm, { ProFormText, ProFormSelect, ProFormTextArea } from '@ant-design/pro-form';
import { useState } from 'react';
import { InMode, OutMode, ProtocolTypes } from './models';
import { PlusOutlined } from '@ant-design/icons';
const { Option } = Select;
const ConfigModal = (props) => {
const { dispatch, actions, visible, close, editData } = props
const formRef = useRef()
const inputRef = useRef()
const { gateway } = actions
const [protocols, setProtocols] = useState([])
const [newProtocol, setNewProtocol] = useState('')
useEffect(() => {
setProtocols([]);
dispatch(gateway.getProtocols()).then(res => {
if (res.success) {
setProtocols(res.payload.data.sort())
}
})
}, [])
const onNewProtocolChange = (e) => {
setNewProtocol(e.target.value)
}
const addNewProtocol = (e) => {
e.preventDefault();
setProtocols([...protocols, newProtocol])
setNewProtocol('');
setTimeout(()=>{
inputRef.current?.focus();
})
}
const batchAdd=(v)=>{
}
return <Modal
title={`${editData ? '编辑' : '新增'}配置`}
visible={visible}
onOk={() => {
formRef.current.validateFields()
.then(v => {
v.id = editData?.id;
if (!v.in_config) {
v.in_config = '{}'
}
if (!v.out_config) {
v.out_config = '{}'
}
dispatch(editData ? gateway.edit([v]) : gateway.add([v]))
.then(res => {
if (res.success) {
dispatch(gateway.list())
close()
}
})
})
}}
onCancel={() => {
close()
}}
>
<ProForm
formRef={formRef}
autoFocusFirstInput
Layout={'vertical'}
initialValues={
editData ?
editData :
{
in_mode: InMode[0],
out_mode: OutMode[0],
protocol: ProtocolTypes[0],
}
}
submitter={false}
formKey='config-form'
grid={true}
>
<ProFormText name="name" label="名称" tooltip="最长为 24 位" placeholder="请输入名称" rules={[{ required: true, message: '请输入名称' }]} />
<ProFormSelect
colProps={{ span: 12 }}
label="输入模式"
name="in_mode"
valueEnum={InMode}
/>
<ProFormSelect
colProps={{ span: 12 }}
label="输出模式"
name="out_mode"
valueEnum={OutMode}
/>
<ProFormTextArea colProps={{ span: 24 }} name="in_config" label="输入配置" />
<ProFormTextArea colProps={{ span: 24 }} name="out_config" label="输出配置" />
<Form.Item name="protocol" label="协议" rules={[{ required: true }]}>
<Select
style={{ width: 300 }}
placeholder="选择协议"
dropdownRender={menu => (
<>
{menu}
<Divider style={{ margin: '8px 0' }} />
<Space style={{ padding: '0 8px 4px' }}>
<Input
placeholder="输入新增协议名"
ref={inputRef}
value={newProtocol}
onChange={onNewProtocolChange}
/>
<Button type="text" icon={<PlusOutlined />} onClick={addNewProtocol}>
添加项
</Button>
</Space>
</>
)}
>
{protocols.map(item => (
<Option key={item}>{item}</Option>
))}
</Select>
</Form.Item>
<ProFormTextArea colProps={{ span: 24 }}
name="protocol_info" label="协议信息"
/>
</ProForm>
</Modal>
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions,
};
}
export default connect(mapStateToProps)(ConfigModal);

196
code/web/client/src/sections/factor/containers/FactorProfile.js

@ -0,0 +1,196 @@
import React, { useState, useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import { Spin, Button, Card, Input, Space, Tag, Table } from 'antd';
import '../style.less';
import { push } from 'react-router-redux'
import ProTable, { TableDropdown } from '@ant-design/pro-table';
import { InMode, OutMode, ProtocolTypes } from './models';
import ConfigModal from './ConfigModal';
import { request } from 'superagent';
const FactorProfile = (props) => {
const { dispatch, actions, user, loading, factors, total } = props
const { factor } = actions;
const [configModalVis, setConfigModalVis] = useState(false)
const [editData, setEditData] = useState(null)
const [fdata, setFdata] = useState([])
const pageSize = 10
useEffect(() => {
// dispatch(factor.list())
}, [])
const actionRef = useRef();
const columns = [
{
dataIndex: 'id',
valueType: 'indexBorder',
width: 48,
}, {
title: '名称',
dataIndex: 'Name',
ellipsis: true
},
{
title: '编码',
dataIndex: 'Proto',
},
{
title: '监测项',
dataIndex: 'Factor_proto_item',
ellipsis: true,
render: (_, record) => (
<Space>
{record.Factor_proto_item.map(({ Name }) => (
<Tag key={Name}>
{Name}
</Tag>
))}
</Space>
),
}, {
title: '操作',
valueType: 'option',
key: 'option',
render: (txt, row, _, action) => [
<Button type="primary"
onClick={() => {
setConfigModalVis(true)
setEditData({ ...row })
}}
>详情</Button>
],
}
]
const requestList = async (params) => {
const ret = await dispatch(factor.list());
console.log(ret)
let tmp = []
tmp = ret.payload.data.data;
if (!!params.Name) {
tmp = tmp.filter(a => a.Name.includes(params.Name))
}
if (!!params.Proto) {
tmp = tmp.filter(a => a.Proto.includes(params.Proto))
}
if(!!params.Factor_proto_item){
tmp = tmp.filter(a => a.Factor_proto_item.some(d=>{
return d.Name.includes(params.Factor_proto_item) ||
d.Field_name.includes(params.Factor_proto_item) ||
d.Item_unit.includes(params.Factor_proto_item)
}))
}
let data = tmp.slice(params.pageSize * (params.current - 1), params.pageSize * params.current);
data = data.map(d => ({
...d,
key: d.Proto + d.Name
}))
setFdata(data)
return {
total: tmp.length,
data: data
}
const query = {
limit: params.pageSize,
offset: params.pageSize * (params.current - 1)
}
const res = await dispatch(factor.list(query));
return {
...res,
total: res.payload.data ? res.payload.data.total : 0
}
}
const expandRow = (record, index, indent, expanded) => {
let cols = []
if (expanded) {
cols = [{
title: '名称',
dataIndex: 'Name',
ellipsis: true
}, {
title: '字段',
dataIndex: 'Field_name',
ellipsis: true
}, {
title: '单位',
dataIndex: 'Item_unit',
ellipsis: true
},
]
return <Table columns={cols} dataSource={record.Factor_proto_item} pagination={false}></Table>
}
}
const [activeExpRow, setActiveExpRow] = React.useState();
return (
<div>
{/* <Spin tip="biubiubiu~" spinning={loading}> */}
<ProTable
columns={columns}
actionRef={actionRef}
toolbar={{
settings: []
}}
search={{
labelWidth: 'auto',
}}
dataSource={fdata}
request={requestList}
manualRequest={false}
options={false}
toolBarRender={() => [
<Button type="primary" key="primary" onClick={() => { setConfigModalVis(true) }}>
添加配置
</Button>,
]}
pagination={{
pageSize: pageSize,
onChange: (page) => console.log(page),
}}
expandedRowKeys={activeExpRow}
rowExpandable={(record) => { return true; }}
expandedRowRender={expandRow}
onExpand={
(expanded, record) => {
const keys = [];
if (expanded) {
keys.push(record.Proto + record.Name);
}
console.log(keys);
setActiveExpRow(keys);
}
}
>
</ProTable>
{
configModalVis ?
<ConfigModal
visible={true}
close={() => {
setConfigModalVis(false)
setEditData(null)
}}
editData={editData}
/> : ''
}
{/* </Spin> */}
</div>
)
}
function mapStateToProps(state) {
const { auth, global, factors } = state;
return {
user: auth.user,
actions: global.actions,
loading: factors.isRequesting,
factors: factors.data?.data ?? [],
};
}
export default connect(mapStateToProps)(FactorProfile);

5
code/web/client/src/sections/factor/containers/index.js

@ -0,0 +1,5 @@
'use strict';
import FactorProfile from './FactorProfile';
export { FactorProfile };

4
code/web/client/src/sections/factor/containers/models.js

@ -0,0 +1,4 @@
const GatewayMode = ["tcp", "udp", "mqtt", "http", "dtu"]
export const InMode = GatewayMode
export const OutMode = GatewayMode
export const ProtocolTypes = ["DeviceA", "DeviceB", "DeviceC", "DeviceD"]

15
code/web/client/src/sections/factor/index.js

@ -0,0 +1,15 @@
'use strict';
import reducers from './reducers';
import routes from './routes';
import actions from './actions';
import { getNavItem } from './nav-item';
export default {
key: 'factor',
name: '监测因素',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

16
code/web/client/src/sections/factor/nav-item.js

@ -0,0 +1,16 @@
import React from 'react';
import { Link } from 'react-router-dom';
import { Menu } from 'antd';
import { SettingOutlined } from '@ant-design/icons';
const SubMenu = Menu.SubMenu;
export function getNavItem(user, dispatch) {
return (
<SubMenu key="factor" icon={<SettingOutlined />} title={'监测因素'}>
<Menu.Item key="factor_profile">
<Link to="/factor_profile/list">监测因素列表</Link>
</Menu.Item>
</SubMenu>
);
}

5
code/web/client/src/sections/factor/reducers/index.js

@ -0,0 +1,5 @@
'use strict';
export default {
}

12
code/web/client/src/sections/factor/routes.js

@ -0,0 +1,12 @@
'use strict';
import { FactorProfile} from './containers';
export default [{
type: 'inner',
route: {
path: '/factor_profile',
key: 'factor',
breadcrumb: '监测因素管理',
component: FactorProfile
}
}];

3
code/web/client/src/sections/factor/style.less

@ -0,0 +1,3 @@
#example:hover {
font-size: larger;
}

3
code/web/client/src/utils/webapi.js

@ -17,6 +17,9 @@ export const ApiTable = {
delGatewayConfig: 'gateway/{gatewayId}', delGatewayConfig: 'gateway/{gatewayId}',
getGatewayProtocols: 'v1/deviceProxy/protocols', getGatewayProtocols: 'v1/deviceProxy/protocols',
// 监测因素
getFactors: 'v1/searchFactor',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members', getEnterprisesMembers: 'enterprises/{enterpriseId}/members',
}; };

Loading…
Cancel
Save