wenlele
2 years ago
9 changed files with 726 additions and 143 deletions
@ -0,0 +1,251 @@ |
|||
'use strict'; |
|||
import React, { useEffect, useState, useRef } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { push } from 'react-router-redux'; |
|||
import { Popconfirm, Button, Popover, Skeleton, Form, Tag, Modal, Collapse, Input } from '@douyinfe/semi-ui'; |
|||
import { IconSearch, IconHelpCircle, IconAlertCircle, IconMinusCircle, IconPlusCircle, IconPlus, IconRefresh } from '@douyinfe/semi-icons'; |
|||
import { SkeletonScreen } from "$components"; |
|||
|
|||
const AddModel = props => { |
|||
const { dispatch, user, error, actions, close, editionData, searchValue } = props |
|||
const { edition } = actions |
|||
const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 |
|||
const [limits, setLimits] = useState()//每页实际条数 |
|||
const [detailV, setDetailV] = useState(false) |
|||
const [property, setProperty] = useState([{ key: 'hb', value: 'ture' }]) |
|||
const [attributeValue, setattributeValue] = useState({}) |
|||
const [type, setType] = useState('groups') //类型的判断 |
|||
const [isEditor, setIsEditor] = useState(false) |
|||
const [appearType, setAppearType] = useState('mqtt') |
|||
const api = useRef() |
|||
|
|||
useEffect(() => { |
|||
if(editionData){ |
|||
|
|||
} |
|||
|
|||
}, []) |
|||
|
|||
|
|||
|
|||
return ( |
|||
<> |
|||
<Modal |
|||
title={editionData ? '编辑插件' : '新增插件'} |
|||
// okText={okText} |
|||
visible={true} |
|||
onOk={() => { |
|||
api.current.validate().then(r => { |
|||
console.log(r); |
|||
r.ctx_res = JSON.parse(r.ctx_res) |
|||
// delete r.type |
|||
if (dataToModal?.id) { |
|||
dispatch(edition.putedge(dataToModal?.id, r)).then(res => { |
|||
console.log(res) |
|||
if (res.success) { |
|||
close() |
|||
} |
|||
}) |
|||
} else { |
|||
dispatch(edition.postAddPlugIn(searchValue, r)).then(res => {//查询网关列表 |
|||
if (res.success) { |
|||
console.log(res); |
|||
} |
|||
}) |
|||
} |
|||
|
|||
}) |
|||
}} |
|||
width={560} |
|||
onCancel={() => close()} |
|||
> |
|||
<Form |
|||
getFormApi={(formApi) => (api.current = formApi)} |
|||
layout="horizontal" |
|||
labelAlign="right" |
|||
labelWidth="104px" |
|||
allowEmpty={true} |
|||
style={{ display: 'flex', flexDirection: 'column' }} |
|||
onValueChange={(_, v) => { |
|||
console.log(v); |
|||
if (v?.type) { |
|||
setType(v.type) |
|||
} |
|||
if (v.ctx_type) { |
|||
setAppearType(v.ctx_type) |
|||
} |
|||
|
|||
}} |
|||
> |
|||
<Form.Input |
|||
field='name' |
|||
label='名称:' |
|||
labelPosition="left" |
|||
placeholder='请输入名称' |
|||
style={{ width: 240, marginBottom: 12 }} |
|||
initValue={dataToModal?.name || ''} |
|||
rules={[{ required: true, message: "请输入设备名称" }]} |
|||
/> |
|||
<Form.Select |
|||
field="type" |
|||
label='类型:' |
|||
labelPosition="left" |
|||
placeholder='请选择类型' |
|||
style={{ width: 240, marginBottom: 12 }} |
|||
initValue={dataToModal?.softwareVer || type} |
|||
rules={[{ required: true, message: "请选择类型" }]} |
|||
> |
|||
{[{ name: '上报', value: 'groups' }, { name: '计算', value: 'antenna' }].map(v => <Form.Select.Option key={v.value} value={v.value}>{v.name}</Form.Select.Option>)} |
|||
</Form.Select> |
|||
<Form.TextArea |
|||
field='description' |
|||
label='描述:' |
|||
labelPosition="left" |
|||
placeholder='' |
|||
style={{ width: 300, marginBottom: 12 }} |
|||
initValue={dataToModal?.name || ''} |
|||
autosize |
|||
rows={1} |
|||
/> |
|||
<Form.Checkbox |
|||
field='enable' |
|||
label='是否启动:' |
|||
labelPosition="left" |
|||
style={{ marginBottom: 12 }} |
|||
initValue={dataToModal?.name || true} |
|||
/> |
|||
{type == 'groups' ? |
|||
<> |
|||
<Form.Select |
|||
field="ctx_type" |
|||
label='上报类型:' |
|||
labelPosition="left" |
|||
placeholder='请选择上报类型' |
|||
style={{ width: 240, marginBottom: 12 }} |
|||
initValue={'' || 'mqtt'} |
|||
rules={[{ required: true, message: "请选择上报类型" }]} |
|||
> |
|||
{[{ name: 'mqtt', value: 'mqtt' }, { name: 'http', value: 'http' }, { name: 'db', value: 'db' }].map(v => <Form.Select.Option key={v.value} value={v.value}>{v.name}</Form.Select.Option>)} |
|||
</Form.Select> |
|||
<Form.Select |
|||
field="ctx_res_lang" |
|||
label='脚本语言:' |
|||
labelPosition="left" |
|||
placeholder='请选择脚本语言' |
|||
style={{ width: 240, marginBottom: 12 }} |
|||
initValue={'' || 'Lua'} |
|||
rules={[{ required: true, message: "请选择脚本语言" }]} |
|||
> |
|||
{[{ name: 'Lua', value: 'Lua' },].map(v => <Form.Select.Option key={v.value} value={v.value}>{v.name}</Form.Select.Option>)} |
|||
</Form.Select> |
|||
<Form.Select |
|||
field="ctx_res_type" |
|||
label='脚本类型:' |
|||
labelPosition="left" |
|||
placeholder='请选择脚本类型' |
|||
style={{ width: 240, marginBottom: 12 }} |
|||
initValue={'' || 'INLINE'} |
|||
rules={[{ required: true, message: "请选择脚本类型" }]} |
|||
> |
|||
{[{ name: 'INLINE', value: 'INLINE' }].map(v => <Form.Select.Option key={v.value} value={v.value}>{v.name}</Form.Select.Option>)} |
|||
</Form.Select> |
|||
<div style={{ display: 'flex' }}> |
|||
<div style={{ width: 104, height: 44, padding: '6px 16px 6px 0', textIndent: 28, fontWeight: 600, color: 'var(--semi-color-text-0)', fontSize: 14 }}>参数:</div> |
|||
<div> |
|||
{appearType == 'mqtt' ? |
|||
<> |
|||
<Form.Input field='ctx_param_ur' label='URL(必填):' labelPosition="left" |
|||
placeholder='127.0.0.1:1883' style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_clientid' label='ClientlD:' labelPosition="left" placeholder='请输入ClientlD' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_username' label='用户名:' labelPosition="left" placeholder='请输入用户名' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_password' label='密码:' labelPosition="left" placeholder='请输入密码' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_topic' label='默认上报主题:' labelPosition="left" placeholder='请输入主题' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_qos' label='默认上报QoS:' labelPosition="left" placeholder='0' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
</> |
|||
: appearType == 'http' ? |
|||
<> |
|||
<Form.Input field='ctx_param_url' label='请求地址:' labelPosition="left" |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_method' label='请求方法:' labelPosition="left" placeholder='post' |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || ''} |
|||
/> |
|||
<Form.Input field='ctx_param_headers' label='默认请求头:' labelPosition="left" |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || JSON.stringify({})} |
|||
/> |
|||
</> |
|||
: <> |
|||
<Form.Input field='ctx_param_type' label='数据库类型:' labelPosition="left" |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || JSON.stringify({})} |
|||
/> |
|||
<Form.Input field='ctx_param_conn' label='连接字符串:' labelPosition="left" |
|||
style={{ width: 180, marginBottom: 12 }} initValue={dataToModal?.name || JSON.stringify({})} |
|||
/> |
|||
</> |
|||
|
|||
} |
|||
|
|||
</div> |
|||
</div> |
|||
<Form.TextArea |
|||
field='ctx_res' |
|||
label={<>脚本<Button theme='borderless' type='secondary' style={{ width: 46, height: 32 }} onClick={() => setIsEditor(!isEditor)}>{isEditor ? '完成' : '编辑'}</Button></>} |
|||
labelPosition="left" |
|||
style={{ width: 300, marginBottom: 12 }} |
|||
initValue={dataToModal?.name || JSON.stringify([])} |
|||
autosize |
|||
disabled={!isEditor} |
|||
/> |
|||
<Form.Input |
|||
field='ctx_cron' |
|||
label='定时任务:' |
|||
labelPosition="left" |
|||
style={{ width: 180, marginBottom: 12 }} |
|||
initValue={dataToModal?.name || ''} |
|||
/> |
|||
</> |
|||
: <> |
|||
<Button style={{ width: 182, marginBottom: 10, marginLeft: 104, color: '#0000009c', border: '1px solid #00000040', }}>获取平台计算和分组配置</Button> |
|||
<Form.TextArea |
|||
field='ctx_group_content' |
|||
label='计算配置:' |
|||
labelPosition="left" |
|||
placeholder='' |
|||
style={{ width: 300, marginBottom: 12 }} |
|||
initValue={dataToModal?.name || JSON.stringify([])} |
|||
autosize |
|||
rules={[{ required: true, message: "请输入计算配置" }]} |
|||
/> |
|||
</> |
|||
} |
|||
|
|||
</Form> |
|||
</Modal> |
|||
</> |
|||
|
|||
); |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
error: auth.error, |
|||
actions: global.actions, |
|||
apiRoot: global.apiRoot, |
|||
isRequesting: auth.isRequesting |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(AddModel); |
@ -1,5 +1,6 @@ |
|||
'use strict'; |
|||
import EditionManage from './administer'; |
|||
import GatewayManage from './gateway'; |
|||
import PlugIn from './plugIn.jsx'; |
|||
|
|||
export { EditionManage, GatewayManage }; |
|||
export { EditionManage, GatewayManage,PlugIn }; |
@ -0,0 +1,283 @@ |
|||
'use strict'; |
|||
import React, { useEffect, useState, useRef } from 'react'; |
|||
import { connect } from 'react-redux'; |
|||
import { push } from 'react-router-redux'; |
|||
import { Popconfirm, Button, Popover, Skeleton, Table, Tag, Input, Pagination, Select } from '@douyinfe/semi-ui'; |
|||
import { IconSearch } from '@douyinfe/semi-icons'; |
|||
import { SkeletonScreen } from "$components"; |
|||
import GatewayStatusModal from './gatewayStatusModal' |
|||
import PlugInModel from '../components/plugInModel' |
|||
import { UnControlled as CodeMirror } from 'react-codemirror2' |
|||
import moment from 'moment' |
|||
|
|||
|
|||
|
|||
const PlugIn = props => { |
|||
const { dispatch, user, error, actions } = props |
|||
const { edition } = actions; |
|||
const [query, setQuery] = useState({ limit: 10, page: 0 }); //页码信息 |
|||
const [tableData, setTableData] = useState([]); |
|||
const [limits, setLimits] = useState(0)//每页实际条数 |
|||
const [detailV, setDetailV] = useState(false) |
|||
const [dataToModal, setDataToModal] = useState(null) |
|||
const [addModel, setAddModel] = useState(false) |
|||
const [editionData, setEditionData] = useState() |
|||
const [searchValue, setSearchValue] = useState('') //选择的序列号 |
|||
const [selectData, setSelectData] = useState([]) //序列号 |
|||
|
|||
|
|||
const [instance, setInstance] = useState(null); |
|||
const [codeData, setCodeData] = useState(''); |
|||
|
|||
|
|||
useEffect(() => { |
|||
|
|||
|
|||
dispatch(edition.getGateways({})).then(res => {//查询网关列表 |
|||
if (res.success) { |
|||
let data = res.payload.data?.data?.map(r => ({ name: r.serialNo, value: r.id })) |
|||
setSelectData(data) |
|||
setSearchValue(data[0]?.value) |
|||
getTableData({ id: data[0]?.value, limit: 10, page: 0 }) |
|||
} |
|||
}) |
|||
|
|||
}, []) |
|||
|
|||
const getTableData = (obj) => { |
|||
let { id, limit, page } = obj |
|||
dispatch(edition.getPlugInList(id, { limit, offset: limit * page })).then(res => { |
|||
if (res.success) { |
|||
setTableData(res.payload.data.data); |
|||
setLimits(res.payload.data.total); |
|||
} |
|||
}) |
|||
|
|||
|
|||
} |
|||
|
|||
const ableDevice = (id, v) => { |
|||
let ableObj = { "enabled": v } |
|||
// dispatch(edition.ableGateway(id, ableObj)).then(r => { |
|||
// if (r.success) { |
|||
// getTableData(query) |
|||
// } |
|||
// }) |
|||
} |
|||
|
|||
const aStyle = { color: '#40a9ff', cursor: 'pointer' } |
|||
const columns = [ |
|||
{ |
|||
title: "插件ID", |
|||
dataIndex: "ID", |
|||
key: "ID", |
|||
}, { |
|||
title: "类型", |
|||
dataIndex: "Type", |
|||
key: "Type", |
|||
render: (text, r) => { |
|||
let type = { group: '分组计算', antenna: '上报' } |
|||
return r.Type ? type[r.Type] : "" |
|||
} |
|||
}, { |
|||
title: " 插件名称", |
|||
dataIndex: "Name", |
|||
key: "Name", |
|||
}, { |
|||
title: "插件描述", |
|||
dataIndex: "Desctription", |
|||
key: "Desctription", |
|||
render: (text, r) => { |
|||
return r.Desctription || '--' |
|||
} |
|||
}, { |
|||
title: "是否启用", |
|||
dataIndex: "Enable", |
|||
key: "Enable", |
|||
render: (text, r) => { |
|||
return r.Enable == true ? '启用' : '禁用' |
|||
} |
|||
}, { |
|||
title: "操作", |
|||
dataIndex: "action", |
|||
key: "action", |
|||
render: (text, r) => { |
|||
return <div style={{ width: 90 }}> |
|||
<a style={{ ...aStyle, marginLeft: 15, marginRight: 15 }} onClick={() => { |
|||
setAddModel(true) |
|||
setEditionData(r.ID) |
|||
}}>编辑</a> |
|||
<Popconfirm |
|||
title="提示" |
|||
position='leftBottom' |
|||
content={<div style={{ width: 150 }}>确认删除该网关插件吗?</div>} |
|||
onConfirm={() => { |
|||
dispatch(edition.getPlugInList(id, { limit, offset: limit * page })).then(res => { |
|||
if (res.success) { |
|||
setTableData(res.payload.data.data); |
|||
setLimits(res.payload.data.total); |
|||
} |
|||
}) |
|||
}}> |
|||
<a style={aStyle}>删除</a> |
|||
</Popconfirm> |
|||
</div> |
|||
} |
|||
} |
|||
]; |
|||
|
|||
|
|||
|
|||
return ( |
|||
<> |
|||
<div> |
|||
<video |
|||
id="gatewayBanner" |
|||
autoPlay |
|||
loop |
|||
muted |
|||
style={{ width: "100%", objectFit: "cover", height: 171 }} |
|||
src="/assets/videos/gateway_banner.mp4" |
|||
type="video/mp4" |
|||
/> |
|||
<div style={{ position: "absolute", top: 12 }}> |
|||
<div |
|||
style={{ |
|||
fontSize: 22, |
|||
paddingTop: 15, |
|||
marginLeft: 21, |
|||
}} |
|||
> |
|||
插件管理 |
|||
</div> |
|||
<div |
|||
style={{ |
|||
fontSize: 14, |
|||
paddingTop: 18, |
|||
marginLeft: 20, |
|||
}} |
|||
> |
|||
对单个网关设备进行插件管理,根据所选序列号,展示对应网关的插件。 |
|||
</div> |
|||
<div |
|||
style={{ |
|||
fontSize: 14, |
|||
marginTop: 28, |
|||
marginLeft: 21, |
|||
width: 89, |
|||
height: 32, |
|||
lineHeight: "32px", |
|||
textAlign: "center", |
|||
backgroundColor: "#D9EAFF", |
|||
color: "#1859C1", |
|||
cursor: "pointer", |
|||
}} |
|||
onClick={() => { |
|||
setAddModel(true) |
|||
}} |
|||
> |
|||
新增插件 |
|||
</div> |
|||
</div> |
|||
</div> |
|||
<div style={{ |
|||
width: "100%", |
|||
background: "#FFFFFF", |
|||
borderRadius: 3, |
|||
padding: "8px 20px", |
|||
marginTop: 20, |
|||
}}> |
|||
<div style={{ display: 'flex', alignItems: 'center', marginBottom: 16 }}> |
|||
序列号: |
|||
<Select |
|||
maxHeight={240} |
|||
// filter={true} |
|||
// defaultValue={selectData[0]?.value} |
|||
value={searchValue} |
|||
placeholder="请选择网关序列号" |
|||
dropdownMatchSelectWidth={true} |
|||
style={{ width: 186 }} |
|||
onSelect={(value, option) => { |
|||
console.log(value, option); |
|||
setSearchValue(value) |
|||
getTableData({ id: value, limit: 1, page: 0 }) |
|||
}} |
|||
> |
|||
{selectData?.map(r => <Select.Option value={r.value} key={r.value}>{r.name}</Select.Option>)} |
|||
</Select> |
|||
<Button |
|||
style={{ marginLeft: 20, width: 60, height: 32, borderRadius: 3, backgroundColor: '#0b57dcb3' }} |
|||
theme="solid" |
|||
type="primary" |
|||
onClick={() => { |
|||
getTableData({ limit: 10, page: 0, name: searchValue }) |
|||
setQuery({ limit: 10, page: 0 }) |
|||
}}>查询</Button> |
|||
</div> |
|||
|
|||
<Table |
|||
columns={columns} |
|||
dataSource={tableData} |
|||
bordered={false} |
|||
empty="暂无插件" |
|||
style={{ |
|||
padding: "0px 20px", |
|||
}} |
|||
pagination={false} |
|||
/> |
|||
|
|||
{limits > 0 ? <div |
|||
style={{ |
|||
display: "flex", |
|||
justifyContent: "flex-end", |
|||
padding: "20px 20px", |
|||
}} |
|||
> |
|||
<span style={{ lineHeight: "30px" }}> |
|||
共{limits}个设备 |
|||
</span> |
|||
<Pagination |
|||
total={limits} |
|||
showSizeChanger |
|||
currentPage={query.page + 1} |
|||
pageSizeOpts={[10, 20, 30, 40]} |
|||
onChange={(page, pageSize) => { |
|||
setQuery({ limit: pageSize, page: page - 1 }) |
|||
getTableData({ limit: pageSize, page: page - 1, name: searchValue }) |
|||
}} |
|||
/> |
|||
</div> : ""} |
|||
</div> |
|||
|
|||
|
|||
{ |
|||
addModel ? |
|||
<PlugInModel |
|||
editionData={editionData} |
|||
searchValue={searchValue} |
|||
close={() => { |
|||
setAddModel(false); |
|||
setEditionData('') |
|||
getTableData({ limit: 10, page: 0, id: searchValue }); |
|||
setQuery({ limit: 10, page: 0 }) |
|||
}} |
|||
/> |
|||
: "" |
|||
} |
|||
</> |
|||
|
|||
); |
|||
} |
|||
|
|||
function mapStateToProps (state) { |
|||
const { auth, global } = state; |
|||
return { |
|||
user: auth.user, |
|||
error: auth.error, |
|||
actions: global.actions, |
|||
apiRoot: global.apiRoot, |
|||
isRequesting: auth.isRequesting |
|||
} |
|||
} |
|||
|
|||
export default connect(mapStateToProps)(PlugIn); |
@ -1,28 +1,31 @@ |
|||
'use strict'; |
|||
|
|||
export const ApiTable = { |
|||
login: 'v1/login', |
|||
logout: 'v1/logout', |
|||
login: 'v1/login', |
|||
logout: 'v1/logout', |
|||
|
|||
crossCheck: 'cross_token/check', |
|||
getGateways: 'v1/edges', |
|||
ableGateway: 'v1/edge/{id}/enable', |
|||
getGatewayStatus: 'v1/edge/{id}/metrics', |
|||
delGateway: 'v1/edge/{id}', |
|||
crossCheck: 'cross_token/check', |
|||
getGateways: 'v1/edges', |
|||
ableGateway: 'v1/edge/{id}/enable', |
|||
getGatewayStatus: 'v1/edge/{id}/metrics', |
|||
delGateway: 'v1/edge/{id}', |
|||
|
|||
gatewaySsh: 'v1/edge/{id}/ssh', |
|||
rebootGateway: 'v1/edge/{id}/reboot', |
|||
restartGateway: 'v1/edge/{id}/restart', |
|||
postedge:'v1/edge', //新增网关
|
|||
putedge:"v1/edge/{edgeId}", //修改网关
|
|||
gatewaySsh: 'v1/edge/{id}/ssh', |
|||
rebootGateway: 'v1/edge/{id}/reboot', |
|||
restartGateway: 'v1/edge/{id}/restart', |
|||
postedge: 'v1/edge', //新增网关
|
|||
putedge: "v1/edge/{edgeId}", //修改网关
|
|||
|
|||
getVersions: 'v1/versions', //查询网关版本信息
|
|||
deleteVersion: 'v1/version/{versionId}', //删除网关版本
|
|||
postVersion: 'v1/version', //新增网关版本
|
|||
getPlugInList: 'v1/edge/{id}/plugins', //插件列表
|
|||
postAddPlugIn:'v1/edge/{id}/plugin/new' |
|||
|
|||
getVersions: 'v1/versions', //查询网关版本信息
|
|||
deleteVersion: 'v1/version/{versionId}', //删除网关版本
|
|||
postVersion: 'v1/version', //新增网关版本
|
|||
}; |
|||
|
|||
export const RouteTable = { |
|||
apiRoot: '/api/root', |
|||
fileUpload: '/_upload/new', |
|||
cleanUpUploadTrash: '/_upload/cleanup', |
|||
apiRoot: '/api/root', |
|||
fileUpload: '/_upload/new', |
|||
cleanUpUploadTrash: '/_upload/cleanup', |
|||
}; |
Loading…
Reference in new issue