Browse Source

(+) gateway

master
yinweiwen 2 years ago
parent
commit
55ef3f5d2e
  1. 18
      code/web/client/assets/color.less
  2. 3
      code/web/client/src/app.js
  3. 2
      code/web/client/src/index.js
  4. 9
      code/web/client/src/layout/components/header/index.js
  5. 1
      code/web/client/src/layout/containers/no-match/index.js
  6. 17
      code/web/client/src/layout/index.js
  7. 4
      code/web/client/src/sections/auth/containers/login.js
  8. 48
      code/web/client/src/sections/gateway/actions/gateway.js
  9. 7
      code/web/client/src/sections/gateway/actions/index.js
  10. 86
      code/web/client/src/sections/gateway/containers/ConfigModal.js
  11. 135
      code/web/client/src/sections/gateway/containers/Gateway.js
  12. 5
      code/web/client/src/sections/gateway/containers/index.js
  13. 15
      code/web/client/src/sections/gateway/index.js
  14. 16
      code/web/client/src/sections/gateway/nav-item.js
  15. 5
      code/web/client/src/sections/gateway/reducers/index.js
  16. 12
      code/web/client/src/sections/gateway/routes.js
  17. 3
      code/web/client/src/sections/gateway/style.less
  18. 3
      code/web/client/src/sections/search/containers/search.js
  19. 4
      code/web/client/src/sections/search/containers/searchRes.js
  20. 2
      code/web/client/src/sections/search/containers/toplogo.js
  21. 6
      code/web/client/src/sections/search/style.less
  22. 13
      code/web/client/src/utils/webapi.js
  23. BIN
      code/web/node_modules.rar

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

@ -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-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:active {background: color(~`colorPalette("@{calendar-input-bg}", 1)`);}
.ant-menu-item-danger.ant-menu-item:active {background: #fff1f0;}
.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:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{calendar-input-bg}", 1)`);}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: #fff1f0;}
.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-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;}
@ -1727,15 +1727,15 @@ 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-checked {background-color: @primary-color;}
.ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);}
.ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{table-header-cell-split-color}", 4)`);border-color: #ffadd2;}
.ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{alert-warning-border-color}", 1)`);border-color: #ffadd2;}
.ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{table-header-cell-split-color}", 4)`);border-color: #ffadd2;}
.ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{alert-warning-border-color}", 1)`);border-color: #ffadd2;}
.ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{calendar-input-bg}", 1)`);border-color: #ffa39e;}
.ant-tag-red {color: #cf1322;background: #fff1f0;border-color: #ffa39e;}
.ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;}
.ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;}
.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("@{shadow-color}", 1)`);border-color: #ffd591;}
.ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;}
.ant-tag-yellow {color: #d4b106;background: #feffe6;border-color: #fffb8f;}
.ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;}
@ -1743,15 +1743,15 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;}
.ant-tag-cyan {color: #08979c;background: #e6fffb;border-color: #87e8de;}
.ant-tag-cyan-inverse {color: #fff;background: #13c2c2;border-color: #13c2c2;}
.ant-tag-lime {color: #7cb305;background: #fcffe6;border-color: #eaff8f;}
.ant-tag-lime {color: #7cb305;background: color(~`colorPalette("@{dropdown-menu-submenu-disabled-bg}", 1)`);border-color: #eaff8f;}
.ant-tag-lime-inverse {color: #fff;background: #a0d911;border-color: #a0d911;}
.ant-tag-green {color: #389e0d;background: #f6ffed;border-color: #b7eb8f;}
.ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;}
.ant-tag-blue {color: #096dd9;background: color(~`colorPalette("@{transfer-item-hover-bg}", 1)`);border-color: #91d5ff;}
.ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;}
.ant-tag-geekblue {color: #1d39c4;background: #f0f5ff;border-color: #adc6ff;}
.ant-tag-geekblue {color: #1d39c4;background: color(~`colorPalette("@{badge-text-color}", 1)`);border-color: #adc6ff;}
.ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;}
.ant-tag-purple {color: #531dab;background: #f9f0ff;border-color: #d3adf7;}
.ant-tag-purple {color: #531dab;background: color(~`colorPalette("@{table-header-sort-bg}", 2)`);border-color: #d3adf7;}
.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-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;}

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

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

2
code/web/client/src/index.js

@ -4,4 +4,4 @@ import React from 'react';
import { render } from 'react-dom';
import App from './app';
render((<App projectName="安心云" />), document.getElementById('App'));
render((<App projectName="运维中心" />), document.getElementById('App'));

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

@ -8,7 +8,7 @@ import lightVars from '$themes/light.json';
import exampleVars from '$themes/example.json'
import styles from './style.css';
import {
MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined,SmileOutlined
MenuFoldOutlined, MenuUnfoldOutlined, UserOutlined, LogoutOutlined, SmileOutlined,ApartmentOutlined
} from '@ant-design/icons';
const themeMap = {
@ -61,7 +61,7 @@ const Header = props => {
</span>
<div className={styles['header-title']} style={{}}>
<Link to="/">
安心云4.0
DevOps
</Link>
{/* <span>{user.orgName}</span> */}
</div>
@ -72,6 +72,11 @@ const Header = props => {
selectedKeys={[current]} style={{ border: 0 }}
onClick={handelClick}
>
<Menu.SubMenu key="devops" title={<div style={{ margin: '0 8px' }}>运维工具</div>} >
<Menu.Item key="gatewayProfile" icon={<ApartmentOutlined />} >
<Link to="/gateway_profile">网关配置</Link>
</Menu.Item>
</Menu.SubMenu>
<Menu.SubMenu key="theme" title={<div style={{ margin: '0 8px' }}>主题切换</div>} >
<Menu.Item key="themeLight" >
<span>亮色风格</span>

1
code/web/client/src/layout/containers/no-match/index.js

@ -4,6 +4,7 @@ import React from 'react';
import moment from 'moment'
const NoMatch = props => {
console.log("404")
return (
<div style={{ textAlign: 'center', padding: 120 }}>
<p style={{ fontSize: 80, lineHeight: 1.5 }}>404</p>

17
code/web/client/src/layout/index.js

@ -60,6 +60,10 @@ const Root = props => {
}
flat(routes);
combineRoutes.push({
key: "nomatch",
component: NoMatch
})
return combineRoutes;
}
@ -90,7 +94,7 @@ const Root = props => {
let actions = {
layout: layoutActions
}
for (let s of sections) {
if (!s.key) console.warn('请给你的section添加一个key值,section name:' + s.name);
for (let r of s.routes) {
@ -147,6 +151,9 @@ const Root = props => {
component={route.component}
/>
)))
console.log(combineRoutes)
console.log(outerRoutes)
}, [])
return (
@ -161,12 +168,10 @@ const Root = props => {
history={history}
routes={innnerRoutes}
>
{combineRoutes}
<Switch>
{combineRoutes}
</Switch>
</Layout>
<Route
path={'*'}
component={NoMatch}
/>
</Switch>
</div>
</ConnectedRouter>

4
code/web/client/src/sections/auth/containers/login.js

@ -27,7 +27,7 @@ const Login = props => {
useEffect(() => {
if (user && user.authorized) {
dispatch(push('/search'));
dispatch(push('/'));
}
}, [user])
@ -56,7 +56,7 @@ const Login = props => {
height: 410,
padding: 30,
}}>
<p style={{ fontSize: 21, fontWeight: 'bold', textAlign: 'center' }}>安心云 4.0</p>
<p style={{ fontSize: 21, fontWeight: 'bold', textAlign: 'center' }}>DevOps</p>
<Form onKeyDown={enterHandler}>
<div style={{ fontSize: 10, fontWeight: 'bold' }}>用户名</div>
<FormItem>

48
code/web/client/src/sections/gateway/actions/gateway.js

@ -0,0 +1,48 @@
'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_GATEWAY_LIST',
url: ApiTable.getGateways,
msg: { error: '获取网关配置列表失败' },
reducer: { name: 'gateways' }
})
}
export function add(data) {
return dispatch => basicAction({
type: 'post',
dispatch: dispatch,
data: data,
actionType: 'POST_GATEWAY_CONFIG',
url: ApiTable.addGatewayConfig,
msg: { option: '添加网关配置' }
})
}
export function edit(data, gatewayId) {
return dispatch => basicAction({
type: 'put',
dispatch: dispatch,
data: data,
actionType: 'EDIT_GATEWAY_CONFIG',
url: `${ApiTable.editGatewayConfig.replace('{gatewayId}', gatewayId)}`,
msg: { option: '编辑网关配置' }
})
}
export function del(gatewayId) {
return dispatch => basicAction({
type: 'del',
dispatch: dispatch,
actionType: 'DEL_GATEWAY_CONFIG',
url: ApiTable.delGatewayConfig.replace('{gatewayId}', gatewayId),
msg: { option: '删除网关配置' }
})
}

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

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

86
code/web/client/src/sections/gateway/containers/ConfigModal.js

@ -0,0 +1,86 @@
import React, { useEffect, useRef } from 'react';
import { connect } from 'react-redux';
import { Spin, Button, Modal, Form, Switch } from 'antd';
import ProForm, { ProFormText, ProFormSelect, ProFormDateTimePicker } from '@ant-design/pro-form';
import { useState } from 'react';
const ConfigModal = (props) => {
const { dispatch, actions, visible, close, editData, types, catalogs } = props
const formRef = useRef()
const { gateway } = actions
return <Modal
title={`${editData ? '编辑' : '新增'}配置`}
visible={visible}
onOk={() => {
formRef.current.validateFields()
.then(v => {
dispatch(editData ? gateway.edit(v, editData.id) : gateway.add(v))
.then(res => {
if (res.success) {
dispatch(gateway.list())
close()
}
})
})
}}
onCancel={() => {
close()
}}
>
<ProForm
formRef={formRef}
autoFocusFirstInput
labelCol={{ span: 4 }}
wrapperCol={{ span: 18 }}
initialValues={
editData ?
editData :
{
excuteTime: '00:00',
isEnable: true
}
}
submitter={false}
formKey='config-form'
>
<ProFormText
label="任务名称"
name={'name'}
placeholder="请输入名称"
required
rules={[{ required: true, message: '请输入名称' }]}
/>
<ProFormSelect
options={catalogs}
cacheForSwr
name="catalog"
label="分类"
required
rules={[{ required: true, message: '请选择任务分类' }]}
/>
<ProFormSelect
options={types}
cacheForSwr
name="type"
label="类型"
required
rules={[{ required: true, message: '请选择任务类型' }]}
/>
<ProFormDateTimePicker
label="截止日期"
name="deadlineAt"
/>
</ProForm>
</Modal>
}
function mapStateToProps(state) {
const { auth, global, } = state;
return {
user: auth.user,
actions: global.actions
};
}
export default connect(mapStateToProps)(ConfigModal);

135
code/web/client/src/sections/gateway/containers/Gateway.js

@ -0,0 +1,135 @@
import React, { useState,useEffect,useRef } from 'react';
import { connect } from 'react-redux';
import { Spin,Button, Card, Input } from 'antd';
import '../style.less';
import { push } from 'react-router-redux'
import ProTable, { TableDropdown } from '@ant-design/pro-table';
const GatewayMode = ["tcp", "udp", "mqtt", "http", "dtu"]
const InMode = GatewayMode
const OutMode = GatewayMode
const ProtocolTypes = ["DeviceA", "DeviceB", "DeviceC", "DeviceD"]
const Gateway = (props) => {
const { dispatch, actions, user, loading, gateways } = props
const { gateway } = actions;
const [configModalVis, setConfigModalVis] = useState(false)
const [editData, setEditData] = useState(null)
useEffect(() => {
// dispatch(task.getTaskList())
}, [])
const actionRef = useRef();
const columns = [
{
dataIndex: 'id',
valueType: 'indexBorder',
width: 48,
}, {
title: '名称',
dataIndex: 'name',
ellipsis: true
},
{
title: '输入模式',
dataIndex: 'in_mode',
filters: true,
onFilter: true,
valueType: 'select',
valueEnum: InMode,
},
{
title: '输出模式',
dataIndex: 'out_mode',
filters: true,
onFilter: true,
valueType: 'select',
valueEnum: OutMode,
}, {
title: '输入配置',
dataIndex: 'in_config'
}, {
title: '输出配置',
dataIndex: 'out_config'
}, {
title: '协议',
dataIndex: 'protocol',
valueType: 'select',
valueEnum: ProtocolTypes
}, {
title: '协议信息',
dataIndex: 'protocol_info'
}, {
title: '操作',
valueType: 'option',
key: 'option',
render: (txt, row, _, action) => [
<Button type="primary"
onClick={() => {
setConfigModalVis(true)
setEditData({ ...row })
}}
>编辑</Button>
],
}
]
return (
<Spin tip="biubiubiu~" spinning={loading}>
<ProTable
columns={columns}
actionRef={actionRef}
toolbar={{
settings: []
}}
search={false}
dataSource={gateways}
request={async (params) => {
const query = {
limit: params.pageSize,
offset: params.pageSize * (params.current - 1)
}
const res = await dispatch(gateway.list(query));
return {
...res,
total: res.payload.data ? res.payload.data.count : 0
}
}}
options={false}
toolBarRender={() => [
<Button type="primary" key="primary" onClick={() => { setConfigModalVis(true) }}>
添加配置
</Button>,
]}
>
</ProTable>
{
configModalVis ?
<ConfigModal
visible={true}
close={() => {
setConfigModalVis(false)
setEditData(null)
}}
editData={editData}
/> : ''
}
</Spin>
)
}
function mapStateToProps(state) {
const { auth, global } = state;
return {
user: auth.user,
actions: global.actions
};
}
export default connect(mapStateToProps)(Gateway);

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

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

15
code/web/client/src/sections/gateway/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: 'gateway',
name: '网关配置',
reducers: reducers,
routes: routes,
actions: actions,
getNavItem: getNavItem
};

16
code/web/client/src/sections/gateway/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="gateway" icon={<SettingOutlined />} title={'网关配置'}>
<Menu.Item key="gateway_profile">
<Link to="/gateway_profile/list">配置清单</Link>
</Menu.Item>
</SubMenu>
);
}

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

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

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

@ -0,0 +1,12 @@
'use strict';
import { Gateway} from './containers';
export default [{
type: 'inner',
route: {
path: '/gateway_profile',
key: 'gateway',
breadcrumb: '网关配置',
component: Gateway
}
}];

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

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

3
code/web/client/src/sections/search/containers/search.js

@ -26,6 +26,9 @@ const Search = (props) => {
return (
<div className="parent">
{/* <svg xmlns="http://www.w3.org/2000/svg" version="1.1" className='svgblock'>
<circle cx="100" cy="50" r="40" stroke="black" stroke-width="2" fill="red" />
</svg> */}
{toplogo}
<Input.Search
className="search"

4
code/web/client/src/sections/search/containers/searchRes.js

@ -114,11 +114,11 @@ const searchRes = (props) => {
}
>
<List.Item.Meta
title={<a href={item.link}>{getHighlightedText(item.title, keyword)}</a>}
title={<a href={item.link}> <div dangerouslySetInnerHTML={ { __html: item.title}} /></a>}
description={item.ext?.desc ? item.ext.desc : undefined}
// description={item.content}
/>
{getHighlightedText(item.content, keyword)}
<div dangerouslySetInnerHTML={ { __html: item.content}} />
</List.Item>
)}
/> : <Empty className='search-empty' />

2
code/web/client/src/sections/search/containers/toplogo.js

File diff suppressed because one or more lines are too long

6
code/web/client/src/sections/search/style.less

@ -16,6 +16,12 @@
// justify-content: center;
justify-content: flex-start;
align-items: center;
vertical-align:top;
}
.svgblock{
vertical-align:top;
}
.parent-top{

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

@ -2,11 +2,20 @@
import request from 'superagent';
export const ApiTable = {
login: 'login',
logout: 'logout',
login: 'v1/login',
logout: 'v1/logout',
search: 'v1/search/{wd}',
search: 'v1/search/{wd}',
//网关配置
getGateways: 'gateway/list',
addGatewayConfig: 'gateway',
getGatewayConfig: 'gateway/{gatewayId}',
editGatewayConfig: 'gateway/{gatewayId}',
delGatewayConfig: 'gateway/{gatewayId}',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members',
};

BIN
code/web/node_modules.rar

Binary file not shown.
Loading…
Cancel
Save