fix: 运维管理 设备接入网关修复部分bug

This commit is contained in:
jackhoo_98 2023-03-16 15:59:01 +08:00
parent 62592b6e83
commit 05045768d2
10 changed files with 599 additions and 580 deletions

View File

@ -1,206 +1,225 @@
<template> <template>
<j-spin :spinning="spinning"> <j-spin :spinning="spinning">
<pro-search :columns="columns" target="search" @search="handleSearch" /> <pro-search :columns="columns" target="search" @search="handleSearch" />
<j-pro-table <j-scrollbar height="680">
ref="tableRef" <j-pro-table
model="CARD" ref="tableRef"
:columns="columns" model="CARD"
:gridColumn="2" :columns="columns"
:gridColumns="[1, 2]" :gridColumn="2"
:request="queryPoint" :gridColumns="[1, 2]"
:defaultParams="defaultParams" :request="queryPoint"
:params="params" :defaultParams="defaultParams"
:rowSelection="{ :params="params"
selectedRowKeys: _selectedRowKeys, :rowSelection="{
onChange: onSelectChange, selectedRowKeys: _selectedRowKeys,
}" onChange: onSelectChange,
@cancelSelect="cancelSelect" }"
> @cancelSelect="cancelSelect"
<template #headerTitle> >
<j-space> <template #headerTitle>
<PermissionButton <j-space>
v-if="data?.provider !== 'OPC_UA'" <PermissionButton
type="primary" v-if="data?.provider !== 'OPC_UA'"
@click="handlAdd" type="primary"
hasPermission="DataCollect/Collector:add" @click="handlAdd"
> hasPermission="DataCollect/Collector:add"
<template #icon><AIcon type="PlusOutlined" /></template> >
新增点位 <template #icon
</PermissionButton> ><AIcon type="PlusOutlined"
/></template>
新增点位
</PermissionButton>
<PermissionButton <PermissionButton
v-if="data?.provider === 'OPC_UA'"
type="primary"
@click="handlScan"
hasPermission="DataCollect/Collector:add"
>
<template #icon
><AIcon type="PlusOutlined"
/></template>
扫描
</PermissionButton>
<j-dropdown v-if="data?.provider === 'OPC_UA'">
<j-button
>批量操作 <AIcon type="DownOutlined"
/></j-button>
<template #overlay>
<j-menu>
<j-menu-item>
<PermissionButton
hasPermission="DataCollect/Collector:update"
@click="handlBatchUpdate()"
>
<template #icon
><AIcon type="FormOutlined"
/></template>
编辑
</PermissionButton>
</j-menu-item>
<j-menu-item>
<PermissionButton
hasPermission="DataCollect/Collector:delete"
:popConfirm="{
title: `确定删除?`,
onConfirm: () => handlDelete(),
}"
>
<template #icon
><AIcon type="EditOutlined"
/></template>
删除
</PermissionButton>
</j-menu-item>
</j-menu>
</template>
</j-dropdown>
</j-space>
<div
v-if="data?.provider === 'OPC_UA'" v-if="data?.provider === 'OPC_UA'"
type="primary" style="margin-top: 15px"
@click="handlScan"
hasPermission="DataCollect/Collector:add"
> >
<template #icon><AIcon type="PlusOutlined" /></template> <j-checkbox
扫描 v-model:checked="checkAll"
</PermissionButton> @change="onCheckAllChange"
<j-dropdown v-if="data?.provider === 'OPC_UA'"> >全选</j-checkbox
<j-button >
>批量操作 <AIcon type="DownOutlined" </div>
/></j-button> </template>
<template #overlay> <template #card="slotProps">
<j-menu> <PointCardBox
<j-menu-item> :showStatus="true"
<PermissionButton :value="slotProps"
hasPermission="DataCollect/Collector:update" @click="handleClick"
@click="handlBatchUpdate()" :active="_selectedRowKeys.includes(slotProps.id)"
> class="card-box"
<template #icon :status="getState(slotProps).value"
><AIcon type="FormOutlined" :statusText="getState(slotProps)?.text"
/></template> :statusNames="Object.fromEntries(colorMap.entries())"
编辑 >
</PermissionButton> <template #title>
</j-menu-item> <slot name="title">
<j-menu-item> <div class="card-box-title">
<PermissionButton {{ slotProps.name }}
hasPermission="DataCollect/Collector:delete" </div>
:popConfirm="{ </slot>
title: `确定删除?`,
onConfirm: () => handlDelete(),
}"
>
<template #icon
><AIcon type="EditOutlined"
/></template>
删除
</PermissionButton>
</j-menu-item>
</j-menu>
</template> </template>
</j-dropdown> <template #action>
</j-space> <div class="card-box-action">
<div <j-popconfirm
v-if="data?.provider === 'OPC_UA'" title="确定删除?"
style="margin-top: 15px" @confirm="handlDelete(slotProps.id)"
> >
<j-checkbox <a><AIcon type="DeleteOutlined" /></a>
v-model:checked="checkAll" </j-popconfirm>
@change="onCheckAllChange" <a @click="handlEdit(slotProps)"
>全选</j-checkbox ><AIcon type="FormOutlined"
> /></a>
</div>
</template>
<template #card="slotProps">
<PointCardBox
:showStatus="true"
:value="slotProps"
@click="handleClick"
:active="_selectedRowKeys.includes(slotProps.id)"
class="card-box"
:status="getState(slotProps).value"
:statusText="getState(slotProps)?.text"
:statusNames="Object.fromEntries(colorMap.entries())"
>
<template #title>
<slot name="title">
<div class="card-box-title">
{{ slotProps.name }}
</div> </div>
</slot> </template>
</template> <template #img>
<template #action> <img
<div class="card-box-action"> :src="
<j-popconfirm slotProps.provider === 'OPC_UA'
title="确定删除?" ? opcImage
@confirm="handlDelete(slotProps.id)" : modbusImage
> "
<a><AIcon type="DeleteOutlined" /></a> />
</j-popconfirm> </template>
<a @click="handlEdit(slotProps)" <template #content>
><AIcon type="FormOutlined" <div class="card-box-content">
/></a> <div class="card-box-content-left">
</div> <div class="card-box-content-left-1">
</template> <div
<template #img> class="ard-box-content-left-1-title"
<img v-if="
:src=" propertyValue.has(slotProps.id)
slotProps.provider === 'OPC_UA' "
? opcImage >
: modbusImage <j-ellipsis
" style="max-width: 150px"
/> >
</template> {{
<template #content> propertyValue.get(
<div class="card-box-content"> slotProps.id,
<div class="card-box-content-left"> )?.parseData[0] || 0
<div class="card-box-content-left-1"> }}({{
propertyValue.get(
slotProps.id,
)?.dataType
}})
</j-ellipsis>
</div>
<span v-else>--</span>
<a
v-if="
getAccessModes(
slotProps,
).includes('write')
"
@click.stop="clickEdit(slotProps)"
><AIcon type="EditOutlined"
/></a>
<a
v-if="
getAccessModes(
slotProps,
).includes('read')
"
@click.stop="clickRedo(slotProps)"
><AIcon type="RedoOutlined"
/></a>
</div>
<div <div
class="ard-box-content-left-1-title"
v-if="propertyValue.has(slotProps.id)" v-if="propertyValue.has(slotProps.id)"
class="card-box-content-right-2"
> >
<j-ellipsis style="max-width: 150px"> <p>
{{ {{
propertyValue.get(slotProps.id) propertyValue.get(slotProps.id)
?.parseData[0] || 0 ?.hex || ''
}}({{ }}
propertyValue.get(slotProps.id) </p>
?.dataType <p>
}}) {{
</j-ellipsis> moment(
propertyValue.get(
slotProps.id,
)?.timestamp,
).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</div> </div>
<span v-else>--</span>
<a
v-if="
getAccessModes(slotProps).includes(
'write',
)
"
@click.stop="clickEdit(slotProps)"
><AIcon type="EditOutlined"
/></a>
<a
v-if="
getAccessModes(slotProps).includes(
'read',
)
"
@click.stop="clickRedo(slotProps)"
><AIcon type="RedoOutlined"
/></a>
</div> </div>
<div
v-if="propertyValue.has(slotProps.id)"
class="card-box-content-right-2"
>
<p>
{{
propertyValue.get(slotProps.id)
?.hex || ''
}}
</p>
<p>
{{
moment(
propertyValue.get(slotProps.id)
?.timestamp,
).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</div>
</div>
<div class="card-box-content-right"> <div class="card-box-content-right">
<div <div
v-if="getRight1(slotProps)" v-if="getRight1(slotProps)"
class="card-box-content-right-1" class="card-box-content-right-1"
> >
<span>{{ getQuantity(slotProps) }}</span> <span>{{
<span>{{ getAddress(slotProps) }}</span> getQuantity(slotProps)
<span>{{ getScaleFactor(slotProps) }}</span> }}</span>
</div> <span>{{ getAddress(slotProps) }}</span>
<div class="card-box-content-right-2"> <span>{{
<span>{{ getText(slotProps) }}</span> getScaleFactor(slotProps)
<span>{{ getInterval(slotProps) }}</span> }}</span>
</div>
<div class="card-box-content-right-2">
<span>{{ getText(slotProps) }}</span>
<span>{{
getInterval(slotProps)
}}</span>
</div>
</div> </div>
</div> </div>
</div> </template>
</template> </PointCardBox>
</PointCardBox> </template>
</template> </j-pro-table>
</j-pro-table> </j-scrollbar>
<SaveModBus <SaveModBus
v-if="visible.saveModBus" v-if="visible.saveModBus"
:data="current" :data="current"

View File

@ -48,7 +48,7 @@ const checkedChange = (id: string) => {
overflow: hidden; overflow: hidden;
background: url('/public/images/access.png') no-repeat; background: url('/public/images/access.png') no-repeat;
background-size: 100% 100%; background-size: 100% 100%;
min-height: 105px; height: 120px;
.title { .title {
width: calc(100% - 88px); width: calc(100% - 88px);
@ -94,7 +94,6 @@ const checkedChange = (id: string) => {
position: relative; position: relative;
color: #2f54eb; color: #2f54eb;
border-color: #2f54eb; border-color: #2f54eb;
.checked-icon { .checked-icon {
display: block; display: block;
} }

View File

@ -108,50 +108,52 @@
</j-row> </j-form </j-row> </j-form
></j-col> ></j-col>
<j-col :span="8"> <j-col :span="8">
<div class="doc"> <j-scrollbar height="500">
<h1>操作指引</h1> <div class="doc">
<div> <h1>操作指引</h1>
1CTWing端创建产品设备以及一个第三方应用 <div>
</div> 1CTWing端创建产品设备以及一个第三方应用
<div> </div>
2CTWing端配置产品/设备/分组级订阅订阅方URL地址请填写: <div>
<div style="word-wrap: break-word"> 2CTWing端配置产品/设备/分组级订阅订阅方URL地址请填写:
{{ <div style="word-wrap: break-word">
`${origin}/api/ctwing/${randomString()}/notify` {{
}} `${origin}/api/ctwing/${randomString()}/notify`
}}
</div>
</div>
<div class="image">
<j-image width="100%" :src="img1" />
</div>
<div>
3IOT端创建类型为CTWing的设备接入网关
</div>
<div>
4IOT端创建产品选中接入方式为CTWing,填写CTWing平台中的产品IDMaster-APIkey
</div>
<div class="image">
<j-image width="100%" :src="img2" />
</div>
<div>
5IOT端添加设备为每一台设备设置唯一的IMEI需与CTWing平台中填写的值一致
</div>
<div class="image">
<j-image width="100%" :src="img3" />
</div>
<h1>设备接入网关配置说明</h1>
<div>
1.请将CTWing的AEP平台-应用管理中的App
Key和App Secret复制到当前页面
</div>
<div class="image">
<j-image width="100%" :src="img4" />
</div>
<h1>其他说明</h1>
<div>
1.在IOT端启用设备时若CTWing平台没有与之对应的设备则将在CTWing端自动创建新设备
</div> </div>
</div> </div>
<div class="image"> </j-scrollbar>
<j-image width="100%" :src="img1" />
</div>
<div>
3IOT端创建类型为CTWing的设备接入网关
</div>
<div>
4IOT端创建产品选中接入方式为CTWing,填写CTWing平台中的产品IDMaster-APIkey
</div>
<div class="image">
<j-image width="100%" :src="img2" />
</div>
<div>
5IOT端添加设备为每一台设备设置唯一的IMEI需与CTWing平台中填写的值一致
</div>
<div class="image">
<j-image width="100%" :src="img3" />
</div>
<h1>设备接入网关配置说明</h1>
<div>
1.请将CTWing的AEP平台-应用管理中的App
Key和App Secret复制到当前页面
</div>
<div class="image">
<j-image width="100%" :src="img4" />
</div>
<h1>其他说明</h1>
<div>
1.在IOT端启用设备时若CTWing平台没有与之对应的设备则将在CTWing端自动创建新设备
</div>
</div>
</j-col> </j-col>
</j-row> </j-row>
</div> </div>
@ -179,8 +181,12 @@
新增 新增
</PermissionButton> </PermissionButton>
</div> </div>
<div class="card-item"> <j-scrollbar height="500">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
v-if="procotolList.length > 0"
style="margin-right: 10px"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
@ -195,7 +201,7 @@
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty v-else description="暂无数据" />
</div> </j-scrollbar>
</div> </div>
</div> </div>
<div v-if="current === 2" class="card-last"> <div v-if="current === 2" class="card-last">
@ -362,26 +368,24 @@ const formData = ref<Form>({
const current = ref(0); const current = ref(0);
const stepCurrent = ref(0); const stepCurrent = ref(0);
const steps = ref(['接入配置', '消息协议', '完成']); const steps = ref(['接入配置', '消息协议', '完成']);
const procotolList = ref([]); const procotolList: any = ref([]);
const allProcotolList = ref([]); const allProcotolList = ref([]);
const procotolCurrent = ref(''); const procotolCurrent: any = ref('');
const procotolChange = (id: string) => { const procotolChange = (id: string) => {
procotolCurrent.value = id; procotolCurrent.value = id;
}; };
const procotolSearch = (value: string) => { const procotolSearch = (value: string) => {
if (value) { procotolList.value = value
const list = allProcotolList.value.filter((i) => { ? allProcotolList.value.filter(
return ( (i: any) =>
i.name && i.name &&
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase()) i.name
); .toLocaleLowerCase()
}); .includes(value.toLocaleLowerCase()),
procotolList.value = list; )
} else { : allProcotolList.value;
procotolList.value = allProcotolList.value;
}
}; };
const saveData = async () => { const saveData = async () => {
@ -411,7 +415,7 @@ const saveData = async () => {
}; };
const queryProcotolList = async (id: string, params = {}) => { const queryProcotolList = async (id: string, params = {}) => {
const resp = await getProtocolList(ProtocolMapping.get(id), { const resp: any = await getProtocolList(ProtocolMapping.get(id), {
...params, ...params,
'sorts[0].name': 'createTime', 'sorts[0].name': 'createTime',
'sorts[0].order': 'desc', 'sorts[0].order': 'desc',
@ -424,10 +428,10 @@ const queryProcotolList = async (id: string, params = {}) => {
const addProcotol = () => { const addProcotol = () => {
const url = menuStory.menus['link/Protocol']?.path; const url = menuStory.menus['link/Protocol']?.path;
const tab = window.open( const tab: any = window.open(
`${window.location.origin + window.location.pathname}#${url}?save=true`, `${window.location.origin + window.location.pathname}#${url}?save=true`,
); );
tab.onTabSaveSuccess = (value) => { tab.onTabSaveSuccess = (value: any) => {
if (value.success) { if (value.success) {
procotolCurrent.value = value.result?.id; procotolCurrent.value = value.result?.id;
queryProcotolList(props.provider?.id); queryProcotolList(props.provider?.id);
@ -486,12 +490,7 @@ watch(
} }
.steps-box { .steps-box {
min-height: 400px; min-height: 400px;
.card-item {
padding-right: 5px;
max-height: 480px;
overflow-y: auto;
overflow-x: hidden;
}
.card-last { .card-last {
padding-right: 5px; padding-right: 5px;
overflow-y: auto; overflow-y: auto;

View File

@ -29,23 +29,17 @@
}, },
]" ]"
> >
<div class="form-label"> <template #label>
接口地址 接口地址
<span <j-tooltip
class="form-label-required" title="同步物联网平台设备数据到OneNet"
>*</span
> >
<j-tooltip>
<template #title>
<p>
同步物联网平台设备数据到OneNet
</p>
</template>
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px"
/> />
</j-tooltip> </j-tooltip>
</div> </template>
<j-input <j-input
disabled disabled
v-model:value=" v-model:value="
@ -95,23 +89,17 @@
}, },
]" ]"
> >
<div class="form-label"> <template #label>
通知Token 通知Token
<span <j-tooltip
class="form-label-required" title="接收OneNet推送的Token地址"
>*</span
> >
<j-tooltip>
<template #title>
<p>
接收OneNet推送的Token地址
</p>
</template>
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px"
/> />
</j-tooltip> </j-tooltip>
</div> </template>
<j-input <j-input
v-model:value=" v-model:value="
formState.validateToken formState.validateToken
@ -131,20 +119,17 @@
}, },
]" ]"
> >
<div class="form-label"> <template #label>
aesKey aesKey
<j-tooltip> <j-tooltip
<template #title> title="OneNet端生成的消息加密key"
<p> >
OneNet
端生成的消息加密key
</p>
</template>
<AIcon <AIcon
type="QuestionCircleOutlined" type="QuestionCircleOutlined"
style="margin-left: 2px"
/> />
</j-tooltip> </j-tooltip>
</div> </template>
<j-input <j-input
v-model:value="formState.aesKey" v-model:value="formState.aesKey"
placeholder="请输入aesKey" placeholder="请输入aesKey"
@ -171,86 +156,88 @@
</j-row> </j-form </j-row> </j-form
></j-col> ></j-col>
<j-col :span="8"> <j-col :span="8">
<div class="doc"> <j-scrollbar height="500">
<h1>操作指引</h1> <div class="doc">
<div> <h1>操作指引</h1>
1OneNet端创建产品设备并配置HTTP推送 <div>
</div> 1OneNet端创建产品设备并配置HTTP推送
<div> </div>
2IOT端创建类型为OneNet的设备接入网关 <div>
</div> 2IOT端创建类型为OneNet的设备接入网关
<div> </div>
3IOT端创建产品选中接入方式为OneNet类型的设备接入网关填写Master-APIkeyOneNet端的产品Key <div>
</div> 3IOT端创建产品选中接入方式为OneNet类型的设备接入网关填写Master-APIkeyOneNet端的产品Key
<div class="image"> </div>
<j-image width="100%" :src="img5" /> <div class="image">
</div> <j-image width="100%" :src="img5" />
<div> </div>
4IOT端添加设备在设备实例页面为每一台设备设置唯一的IMEIIMSI码需与OneNet平台中的值一致 <div>
</div> 4IOT端添加设备在设备实例页面为每一台设备设置唯一的IMEIIMSI码需与OneNet平台中的值一致
<div class="image"> </div>
<j-image width="100%" :src="img6" /> <div class="image">
</div> <j-image width="100%" :src="img6" />
<h1>HTTP推送配置说明</h1> </div>
<div class="image"> <h1>HTTP推送配置说明</h1>
<j-image width="100%" :src="img" /> <div class="image">
</div> <j-image width="100%" :src="img" />
<div> </div>
HTTP推送配置路径应用开发&gt;数据推送 <div>
</div> HTTP推送配置路径应用开发&gt;数据推送
<j-descriptions </div>
bordered <j-descriptions
size="small" bordered
:column="1" size="small"
:labelStyle="{ width: '100px' }" :column="1"
> :labelStyle="{ width: '100px' }"
<j-descriptions-item label="参数"
>说明</j-descriptions-item
> >
<j-descriptions-item label="实例名称" <j-descriptions-item label="参数"
>推送实例的名称</j-descriptions-item >说明</j-descriptions-item
> >
<j-descriptions-item label="推送地址"> <j-descriptions-item label="实例名称"
用于接收OneNet推送设备数据的地址物联网平台地址: >推送实例的名称</j-descriptions-item
<div style="word-wrap: break-word"> >
{{ <j-descriptions-item label="推送地址">
`${origin}/api/one-net/${randomString()}/notify` 用于接收OneNet推送设备数据的地址物联网平台地址:
}} <div style="word-wrap: break-word">
</div> {{
</j-descriptions-item> `${origin}/api/one-net/${randomString()}/notify`
<j-descriptions-item label="Token"> }}
自定义token,可用于验证请求是否来自OneNet </div>
</j-descriptions-item> </j-descriptions-item>
<j-descriptions-item label="消息加密"> <j-descriptions-item label="Token">
采用AES加密算法对推送的数据进行数据加密AesKey为加密秘钥 自定义token,可用于验证请求是否来自OneNet
</j-descriptions-item> </j-descriptions-item>
</j-descriptions> <j-descriptions-item label="消息加密">
采用AES加密算法对推送的数据进行数据加密AesKey为加密秘钥
</j-descriptions-item>
</j-descriptions>
<h1>设备接入网关配置说明</h1> <h1>设备接入网关配置说明</h1>
<j-descriptions <j-descriptions
bordered bordered
size="small" size="small"
:column="1" :column="1"
:labelStyle="{ width: '100px' }" :labelStyle="{ width: '100px' }"
>
<j-descriptions-item label="参数"
>说明</j-descriptions-item
> >
<j-descriptions-item label="apiKey" <j-descriptions-item label="参数"
>OneNet平台中具体产品的Key</j-descriptions-item >说明</j-descriptions-item
> >
<j-descriptions-item label="通知Token"> <j-descriptions-item label="apiKey"
填写OneNet数据推送配置中设置的Token >OneNet平台中具体产品的Key</j-descriptions-item
</j-descriptions-item> >
<j-descriptions-item label="aesKey"> <j-descriptions-item label="通知Token">
若OneNet数据推送配置了消息加密此处填写OneNet端数据推送配置中设置的aesKey 填写OneNet数据推送配置中设置的Token
</j-descriptions-item> </j-descriptions-item>
</j-descriptions> <j-descriptions-item label="aesKey">
<h1>其他说明</h1> 若OneNet数据推送配置了消息加密此处填写OneNet端数据推送配置中设置的aesKey
<div> </j-descriptions-item>
1.在IOT端启用设备时若OneNet平台没有与之对应的设备则将在OneNet端自动创建新设备 </j-descriptions>
<h1>其他说明</h1>
<div>
1.在IOT端启用设备时若OneNet平台没有与之对应的设备则将在OneNet端自动创建新设备
</div>
</div> </div>
</div> </j-scrollbar>
</j-col> </j-col>
</j-row> </j-row>
</div> </div>
@ -278,8 +265,12 @@
新增 新增
</PermissionButton> </PermissionButton>
</div> </div>
<div class="card-item"> <j-scrollbar height="500">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
v-if="procotolList.length > 0"
style="margin-right: 10px"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
@ -294,7 +285,7 @@
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty v-else description="暂无数据" />
</div> </j-scrollbar>
</div> </div>
</div> </div>
<div v-if="current === 2" class="card-last"> <div v-if="current === 2" class="card-last">
@ -463,26 +454,24 @@ const formData = ref<Form>({
const current = ref(0); const current = ref(0);
const stepCurrent = ref(0); const stepCurrent = ref(0);
const steps = ref(['接入配置', '消息协议', '完成']); const steps = ref(['接入配置', '消息协议', '完成']);
const procotolList = ref([]); const procotolList: any = ref([]);
const allProcotolList = ref([]); const allProcotolList = ref([]);
const procotolCurrent = ref(''); const procotolCurrent: any = ref('');
const procotolChange = (id: string) => { const procotolChange = (id: string) => {
procotolCurrent.value = id; procotolCurrent.value = id;
}; };
const procotolSearch = (value: string) => { const procotolSearch = (value: string) => {
if (value) { procotolList.value = value
const list = allProcotolList.value.filter((i) => { ? allProcotolList.value.filter(
return ( (i: any) =>
i.name && i.name &&
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase()) i.name
); .toLocaleLowerCase()
}); .includes(value.toLocaleLowerCase()),
procotolList.value = list; )
} else { : allProcotolList.value;
procotolList.value = allProcotolList.value;
}
}; };
const saveData = async () => { const saveData = async () => {
@ -513,7 +502,7 @@ const saveData = async () => {
}; };
const queryProcotolList = async (id: string, params = {}) => { const queryProcotolList = async (id: string, params = {}) => {
const resp = await getProtocolList(ProtocolMapping.get(id), { const resp: any = await getProtocolList(ProtocolMapping.get(id), {
...params, ...params,
'sorts[0].name': 'createTime', 'sorts[0].name': 'createTime',
'sorts[0].order': 'desc', 'sorts[0].order': 'desc',
@ -526,10 +515,10 @@ const queryProcotolList = async (id: string, params = {}) => {
const addProcotol = () => { const addProcotol = () => {
const url = menuStory.menus['link/Protocol']?.path; const url = menuStory.menus['link/Protocol']?.path;
const tab = window.open( const tab: any = window.open(
`${window.location.origin + window.location.pathname}#${url}?save=true`, `${window.location.origin + window.location.pathname}#${url}?save=true`,
); );
tab.onTabSaveSuccess = (value) => { tab.onTabSaveSuccess = (value: any) => {
if (value.success) { if (value.success) {
procotolCurrent.value = value.result?.id; procotolCurrent.value = value.result?.id;
queryProcotolList(props.provider?.id); queryProcotolList(props.provider?.id);
@ -587,12 +576,6 @@ watch(
} }
.steps-box { .steps-box {
min-height: 400px; min-height: 400px;
.card-item {
padding-right: 5px;
max-height: 480px;
overflow-y: auto;
overflow-x: hidden;
}
.card-last { .card-last {
padding-right: 5px; padding-right: 5px;
overflow-y: auto; overflow-y: auto;

View File

@ -29,14 +29,18 @@
新增 新增
</PermissionButton> </PermissionButton>
</div> </div>
<div class="card-item"> <j-scrollbar height="500">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0"> <j-row
:gutter="[24, 24]"
v-if="networkList.length > 0"
style="margin-right: 10px"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in networkList" v-for="item in networkList"
:key="item.id" :key="item.id"
> >
<access-card <AccessCard
@checkedChange="checkedChange" @checkedChange="checkedChange"
:checked="networkCurrent" :checked="networkCurrent"
:data="{ :data="{
@ -96,11 +100,11 @@
</j-tooltip> </j-tooltip>
</div> </div>
</template> </template>
</access-card> </AccessCard>
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty v-else description="暂无数据" />
</div> </j-scrollbar>
</div> </div>
</div> </div>
<div <div
@ -250,9 +254,9 @@ const formRef = ref<FormInstance>();
const current = ref(0); const current = ref(0);
const stepCurrent = ref(0); const stepCurrent = ref(0);
const steps = ref(['网络组件', '完成']); const steps = ref(['网络组件', '完成']);
const networkCurrent = ref(''); const networkCurrent: any = ref('');
const networkList = ref([]); const networkList: any = ref([]);
const allNetworkList = ref([]); const allNetworkList: any = ref([]);
const onFinish = async (values: any) => { const onFinish = async (values: any) => {
const providerId = props.provider.id; const providerId = props.provider.id;
@ -288,15 +292,15 @@ const queryNetworkList = async (id: string, include: string, data = {}) => {
}; };
const networkSearch = (value: string) => { const networkSearch = (value: string) => {
if (value) { networkList.value = value
networkList.value = allNetworkList.value.filter( ? allNetworkList.value.filter(
(i: any) => (i: any) =>
i.name && i.name &&
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase()), i.name
); .toLocaleLowerCase()
} else { .includes(value.toLocaleLowerCase()),
networkList.value = allNetworkList.value; )
} : allNetworkList.value;
}; };
const saveData = async () => { const saveData = async () => {
@ -306,12 +310,12 @@ const saveData = async () => {
const addNetwork = () => { const addNetwork = () => {
const url = menuStory.menus['link/Type/Detail']?.path; const url = menuStory.menus['link/Type/Detail']?.path;
const tab = window.open( const tab: any = window.open(
`${window.location.origin + window.location.pathname}#${url}?type=${ `${window.location.origin + window.location.pathname}#${url}?type=${
NetworkTypeMapping.get(props.provider?.id) || '' NetworkTypeMapping.get(props.provider?.id) || ''
}`, }`,
); );
tab.onTabSaveSuccess = (value) => { tab.onTabSaveSuccess = (value: any) => {
if (value.success) { if (value.success) {
networkCurrent.value = value.result.id; networkCurrent.value = value.result.id;
queryNetworkList(props.provider?.id, networkCurrent.value || ''); queryNetworkList(props.provider?.id, networkCurrent.value || '');
@ -364,12 +368,6 @@ watch(
} }
.steps-box { .steps-box {
min-height: 400px; min-height: 400px;
.card-item {
padding-right: 5px;
max-height: 480px;
overflow-y: auto;
overflow-x: hidden;
}
.card-last { .card-last {
padding-right: 5px; padding-right: 5px;
overflow-y: auto; overflow-y: auto;
@ -405,9 +403,6 @@ watch(
} }
.config-right { .config-right {
padding: 20px; padding: 20px;
// color: rgba(0, 0, 0, 0.8);
// background: rgba(0, 0, 0, 0.04);
.config-right-item { .config-right-item {
margin-bottom: 10px; margin-bottom: 10px;

View File

@ -71,24 +71,17 @@
}, },
]" ]"
> >
<div> <template #label>
集群 集群
<span style="color: red; margin: 0 4px 0 -2px" <j-tooltip
>*</span title="共享配置:集群下所有节点共用同一配置,独立配置:集群下不同节点使用不同配置"
> >
<j-tooltip> <AIcon
<template #title> type="QuestionCircleOutlined"
<p> style="margin-left: 2px"
共享配置:集群下所有节点共用同一配置 />
</p>
<p>
独立配置:集群下不同节点使用不同配置
</p>
</template>
<AIcon type="QuestionCircleOutlined" />
</j-tooltip> </j-tooltip>
</div> </template>
<j-radio-group <j-radio-group
v-model:value="formState.shareCluster" v-model:value="formState.shareCluster"
> >
@ -218,20 +211,26 @@
:header="`#${index + 1}.节点`" :header="`#${index + 1}.节点`"
> >
<template #extra> <template #extra>
<AIcon type="DeleteOutlined" /> <AIcon
@click="removeCluster(cluster)"
type="DeleteOutlined"
/>
</template> </template>
<j-row :gutter="[24, 24]"> <j-row :gutter="[24, 24]">
<j-col :span="8"> <j-col :span="8">
<j-form-item <j-form-item
label="节点名称"
:name="[ :name="[
'cluster', 'cluster',
index, index,
'clusterNodeId', 'clusterNodeId',
]" ]"
:rules="{
required: true,
message:
'请选择节点名称',
}"
> >
<div class="form-label">
节点名称
</div>
<j-select <j-select
v-model:value=" v-model:value="
cluster.clusterNodeId cluster.clusterNodeId
@ -527,22 +526,22 @@ import { getResourcesCurrent, getClusters } from '@/api/link/accessConfig';
import { update, save } from '@/api/link/accessConfig'; import { update, save } from '@/api/link/accessConfig';
interface Form2 { interface Form2 {
clusterNodeId: string; clusterNodeId: string | undefined;
port: string; port: string | undefined;
host: string; host: string | undefined;
publicPort: string; publicPort: string | undefined;
publicHost: string; publicHost: string | undefined;
id: number; id: number;
} }
interface FormState { interface FormState {
domain: string; domain: string | undefined;
sipId: string; sipId: string | undefined;
shareCluster: boolean; shareCluster: boolean;
hostPort: { hostPort: {
port: string; port: string | undefined;
host: string; host: string | undefined;
publicPort: string; publicPort: string | undefined;
publicHost: string; publicHost: string | undefined;
}; };
} }
@ -576,14 +575,14 @@ const formData = ref({
description: '', description: '',
}); });
let formState = ref<FormState>({ let formState = ref<FormState>({
domain: '', domain: undefined,
sipId: '', sipId: undefined,
shareCluster: true, shareCluster: true,
hostPort: { hostPort: {
port: '', port: undefined,
host: '0.0.0.0', host: '0.0.0.0',
publicPort: '', publicPort: undefined,
publicHost: '', publicHost: undefined,
}, },
}); });
@ -610,11 +609,11 @@ const removeCluster = (item: Form2) => {
const addCluster = () => { const addCluster = () => {
const id = Date.now(); const id = Date.now();
dynamicValidateForm.cluster.push({ dynamicValidateForm.cluster.push({
clusterNodeId: '', clusterNodeId: undefined,
port: '', port: undefined,
host: '', host: undefined,
publicPort: '', publicPort: undefined,
publicHost: '', publicHost: undefined,
id, id,
}); });
activeKey.value = [...activeKey.value, id.toString()]; activeKey.value = [...activeKey.value, id.toString()];
@ -665,16 +664,7 @@ const saveData = () => {
id === ':id' ? await save(params) : await update({ ...params, id }); id === ':id' ? await save(params) : await update({ ...params, id });
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
// if (params.get('save')) {
// if ((window as any).onTabSaveSuccess) {
// if (resp.result) {
// (window as any).onTabSaveSuccess(resp.result);
// setTimeout(() => window.close(), 300);
// }
// }
// } else {
history.back(); history.back();
// }
} }
}); });
}; };
@ -709,14 +699,14 @@ onMounted(() => {
getResourcesCurrent().then((resp) => { getResourcesCurrent().then((resp) => {
if (resp.status === 200) { if (resp.status === 200) {
sipListConst = resp.result; sipListConst = resp.result;
sipListOption.value = sipListConst.map((i) => ({ sipListOption.value = sipListConst.map((i: any) => ({
value: i.host, value: i.host,
label: i.host, label: i.host,
})); }));
sipList.value = sipListConst sipList.value = sipListConst
.find((i) => i.host === '0.0.0.0') .find((i: any) => i.host === '0.0.0.0')
?.portList.map((i) => { ?.portList.map((i: any) => {
return { return {
value: JSON.stringify({ value: JSON.stringify({
host: '0.0.0.0', host: '0.0.0.0',
@ -728,9 +718,9 @@ onMounted(() => {
} }
}); });
getClusters().then((resp) => { getClusters().then((resp: any) => {
if (resp.status === 200) { if (resp.status === 200) {
const list = resp.result.map((i) => ({ const list = resp.result.map((i: any) => ({
value: i.id, value: i.id,
label: i.name, label: i.name,
})); }));

View File

@ -25,8 +25,12 @@
新增 新增
</PermissionButton> </PermissionButton>
</div> </div>
<div class="card-item"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="networkList.length > 0"> <j-row
:gutter="[24, 24]"
v-if="networkList.length > 0"
style="margin-right: 10px"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in networkList" v-for="item in networkList"
@ -88,7 +92,7 @@
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty v-else description="暂无数据" />
</div> </j-scrollbar>
</div> </div>
<div class="steps-box" v-else-if="current === 1"> <div class="steps-box" v-else-if="current === 1">
<div class="alert"> <div class="alert">
@ -111,8 +115,12 @@
新增 新增
</PermissionButton> </PermissionButton>
</div> </div>
<div class="card-item"> <j-scrollbar height="480">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0"> <j-row
:gutter="[24, 24]"
v-if="procotolList.length > 0"
style="margin-right: 10px"
>
<j-col <j-col
:span="8" :span="8"
v-for="item in procotolList" v-for="item in procotolList"
@ -127,7 +135,7 @@
</j-col> </j-col>
</j-row> </j-row>
<j-empty v-else description="暂无数据" /> <j-empty v-else description="暂无数据" />
</div> </j-scrollbar>
</div> </div>
<div class="steps-box" v-else> <div class="steps-box" v-else>
<div <div
@ -169,97 +177,111 @@
</j-form> </j-form>
</j-col> </j-col>
<j-col :span="12"> <j-col :span="12">
<div class="config-right"> <j-scrollbar height="600">
<div class="config-right-item"> <div class="config-right">
<div class="config-right-item-title"> <div class="config-right-item">
接入方式 <div class="config-right-item-title">
接入方式
</div>
<div class="config-right-item-context">
{{ provider.name }}
</div>
<div class="config-right-item-context">
{{ provider.description }}
</div>
</div> </div>
<div class="config-right-item-context"> <div class="config-right-item">
{{ provider.name }} <div class="config-right-item-title">
</div> 消息协议
<div class="config-right-item-context"> </div>
{{ provider.description }} <div class="config-right-item-context">
</div> {{
</div> procotolList.find(
<div class="config-right-item"> (i: any) =>
<div class="config-right-item-title"> i.id ===
消息协议 procotolCurrent,
</div> ).name
<div class="config-right-item-context"> }}
{{ </div>
procotolList.find( <div
(i) => i.id === procotolCurrent, class="config-right-item-context"
).name v-if="config.document"
}}
</div>
<div
class="config-right-item-context"
v-if="config.document"
>
<Markdown :source="config.document" />
</div>
</div>
<div
class="config-right-item"
v-if="getNetworkCurrent()"
>
<div class="config-right-item-title">
网络组件
</div>
<div
v-for="i in getNetworkCurrentData()"
:key="i.address"
>
<j-badge
:color="getColor(i)"
:text="i.address"
/>
</div>
</div>
<div
class="config-right-item"
v-if="
config.routes &&
config.routes.length > 0
"
>
<div class="config-right-item-title">
{{
data.provider ===
'mqtt-server-gateway' ||
data.provider ===
'mqtt-client-gateway'
? 'topic'
: 'URL信息'
}}
</div>
<j-table
:pagination="false"
:rowKey="generateUUID()"
:datj-source="config.routes || []"
bordered
:columns="
config.id === 'MQTT'
? columnsMQTT
: columnsHTTP
"
:scroll="{ y: 300 }"
>
<template
#bodyCell="{ column, text, record }"
> >
<template <Markdown
v-if=" :source="config.document"
column.dataIndex === />
'stream' </div>
</div>
<div
class="config-right-item"
v-if="getNetworkCurrent()"
>
<div class="config-right-item-title">
网络组件
</div>
<div
v-for="i in getNetworkCurrentData()"
:key="i.address"
>
<j-badge
:color="getColor(i)"
:text="i.address"
/>
</div>
</div>
<div
class="config-right-item"
v-if="
config.routes &&
config.routes.length > 0
"
>
<div class="config-right-item-title">
{{
data.provider ===
'mqtt-server-gateway' ||
data.provider ===
'mqtt-client-gateway'
? 'topic'
: 'URL信息'
}}
</div>
<j-scrollbar height="200">
<j-table
:pagination="false"
:rowKey="generateUUID()"
:data-source="
config.routes || []
" "
bordered
:columns="
config.id === 'MQTT'
? columnsMQTT
: columnsHTTP
"
:scroll="{ y: 300 }"
> >
{{ getStream(record) }} <template
</template> #bodyCell="{
</template> column,
</j-table> text,
record,
}"
>
<template
v-if="
column.dataIndex ===
'stream'
"
>
{{ getStream(record) }}
</template>
</template>
</j-table>
</j-scrollbar>
</div>
</div> </div>
</div> </j-scrollbar>
</j-col> </j-col>
</j-row> </j-row>
</div> </div>
@ -444,6 +466,8 @@ const getNetworkCurrentData = () =>
const getColor = (i: any) => (i.health === -1 ? 'red' : 'green'); const getColor = (i: any) => (i.health === -1 ? 'red' : 'green');
const getStream = (record: any) => { const getStream = (record: any) => {
console.log(222, record);
let stream = ''; let stream = '';
if (record.upstream && record.downstream) stream = '上行、下行'; if (record.upstream && record.downstream) stream = '上行、下行';
else if (record.upstream) stream = '上行'; else if (record.upstream) stream = '上行';
@ -493,19 +517,16 @@ const saveData = () => {
protocol: procotolCurrent.value, protocol: procotolCurrent.value,
channel: 'network', // channel: 'network', //
channelId: networkCurrent.value, channelId: networkCurrent.value,
provider: props.provider.id,
transport:
props.provider?.id === 'child-device'
? 'Gateway'
: ProtocolMapping.get(props.provider.id),
}; };
const resp = const resp =
id === ':id' id === ':id'
? await save(params) ? await save(params)
: await update({ : await update({ ...params, id });
...params,
id,
provider: props.provider.id,
transport:
props.provider?.id === 'child-device'
? 'Gateway'
: ProtocolMapping.get(props.provider.id),
});
if (resp.status === 200) { if (resp.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
history.back(); history.back();
@ -549,7 +570,6 @@ const next = async () => {
rowSpan: 0, rowSpan: 0,
}; };
const list = config.value?.routes || []; const list = config.value?.routes || [];
const arr = list.filter( const arr = list.filter(
(res: any) => res.group === record.group, (res: any) => res.group === record.group,
); );
@ -558,7 +578,6 @@ const next = async () => {
rowIndex === 0 || rowIndex === 0 ||
list[rowIndex - 1].group !== record.group; list[rowIndex - 1].group !== record.group;
isRowIndex && (obj.rowSpan = arr.length); isRowIndex && (obj.rowSpan = arr.length);
return obj; return obj;
}, },
}; };
@ -629,12 +648,6 @@ watch(
} }
.steps-box { .steps-box {
min-height: 400px; min-height: 400px;
.card-item {
padding-right: 5px;
max-height: 480px;
overflow-y: auto;
overflow-x: hidden;
}
.card-last { .card-last {
padding-right: 5px; padding-right: 5px;
overflow-y: auto; overflow-y: auto;

View File

@ -6,7 +6,6 @@
target="search" target="search"
@search="handleSearch" @search="handleSearch"
/> />
<j-pro-table <j-pro-table
ref="tableRef" ref="tableRef"
model="CARD" model="CARD"
@ -353,6 +352,12 @@ const handleSearch = (e: any) => {
}; };
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.table {
max-height: 700px;
overflow-y: auto;
overflow-x: hidden;
}
.tableCardDisabled { .tableCardDisabled {
width: 100%; width: 100%;
background: url('/images/access-config-diaabled.png') no-repeat; background: url('/images/access-config-diaabled.png') no-repeat;

View File

@ -135,11 +135,17 @@ const changeType = (value: Array<string>) => {
const onSubmit = async () => { const onSubmit = async () => {
const data: any = await formRef.value?.validate(); const data: any = await formRef.value?.validate();
loading.value = true; loading.value = true;
const response = !id const response: any = !id
? await save(data).catch(() => {}) ? await save(data).catch(() => {})
: await update({ ...props.data, ...data }).catch(() => {}); : await update({ ...props.data, ...data }).catch(() => {});
if (response?.status === 200) { if (response?.status === 200) {
emit('change', response?.status === 200); emit('change', response?.status === 200);
if ((window as any).onTabSaveSuccess) {
if (response.result?.id) {
(window as any).onTabSaveSuccess(response);
setTimeout(() => window.close(), 300);
}
}
} }
loading.value = false; loading.value = false;
}; };

View File

@ -36,6 +36,7 @@
show-search show-search
:filter-option="filterOption" :filter-option="filterOption"
@change="changeType" @change="changeType"
:disabled="!!NetworkType"
/> />
</j-form-item> </j-form-item>
</j-col> </j-col>
@ -981,6 +982,7 @@ import {
resourcesCurrent, resourcesCurrent,
supports, supports,
certificates, certificates,
start,
} from '@/api/link/type'; } from '@/api/link/type';
import { import {
FormStates, FormStates,
@ -1146,13 +1148,21 @@ const saveData = async () => {
: { ...formData.value, ...formRef2Data }; : { ...formData.value, ...formRef2Data };
loading.value = true; loading.value = true;
const resp = const resp: any =
id === ':id' id === ':id'
? await save(params).catch(() => {}) ? await save(params).catch(() => {})
: await update({ ...params, id }).catch(() => {}); : await update({ ...params, id }).catch(() => {});
if (resp?.status === 200) { if (resp?.status === 200) {
message.success('操作成功!'); message.success('操作成功!');
history.back(); history.back();
if ((window as any).onTabSaveSuccess) {
if (resp.result?.id) {
start(resp.result?.id).then(() => {
(window as any).onTabSaveSuccess(resp);
setTimeout(() => window.close(), 300);
});
}
}
} }
loading.value = false; loading.value = false;
}; };