Merge pull request #62 from jetlinks/dev-hub

fix: 运维管理 证书管理修复部分bug,远程升级修复部分bug
This commit is contained in:
胡彪 2023-03-17 09:57:39 +08:00 committed by GitHub
commit 886ed03b8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 734 additions and 718 deletions

View File

@ -225,9 +225,9 @@ const changeValue = (index: number, type: string) => {
}
};
const changeCheckbox = (index: number, type: string) => {
const changeCheckbox = async (index: number, type: string) => {
// console.log(1, getTargetData(index, type).check,getTargetData(index, type));
//使setTimeout
//Dom setTimeout await nextTick()
setTimeout(() => {
// console.log(2, getTargetData(index, type).check,getTargetData(index, type));
let startIndex = 0;
@ -235,8 +235,8 @@ const changeCheckbox = (index: number, type: string) => {
const currentCheck = getTargetData(index, type).check;
if (!currentCheck) return;
for (let i = index; i >= 0; i--) {
const preDatCheck = getTargetData(i, type).check;
if (!preDatCheck) {
const preDataCheck = getTargetData(i, type).check;
if (!preDataCheck) {
startIndex = i;
break;
}

View File

@ -1,206 +1,225 @@
<template>
<j-spin :spinning="spinning">
<pro-search :columns="columns" target="search" @search="handleSearch" />
<j-pro-table
ref="tableRef"
model="CARD"
:columns="columns"
:gridColumn="2"
:gridColumns="[1, 2]"
:request="queryPoint"
:defaultParams="defaultParams"
:params="params"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
onChange: onSelectChange,
}"
@cancelSelect="cancelSelect"
>
<template #headerTitle>
<j-space>
<PermissionButton
v-if="data?.provider !== 'OPC_UA'"
type="primary"
@click="handlAdd"
hasPermission="DataCollect/Collector:add"
>
<template #icon><AIcon type="PlusOutlined" /></template>
新增点位
</PermissionButton>
<j-scrollbar height="680">
<j-pro-table
ref="tableRef"
model="CARD"
:columns="columns"
:gridColumn="2"
:gridColumns="[1, 2]"
:request="queryPoint"
:defaultParams="defaultParams"
:params="params"
:rowSelection="{
selectedRowKeys: _selectedRowKeys,
onChange: onSelectChange,
}"
@cancelSelect="cancelSelect"
>
<template #headerTitle>
<j-space>
<PermissionButton
v-if="data?.provider !== 'OPC_UA'"
type="primary"
@click="handlAdd"
hasPermission="DataCollect/Collector:add"
>
<template #icon
><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'"
type="primary"
@click="handlScan"
hasPermission="DataCollect/Collector:add"
style="margin-top: 15px"
>
<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>
<j-checkbox
v-model:checked="checkAll"
@change="onCheckAllChange"
>全选</j-checkbox
>
</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>
</slot>
</template>
</j-dropdown>
</j-space>
<div
v-if="data?.provider === 'OPC_UA'"
style="margin-top: 15px"
>
<j-checkbox
v-model:checked="checkAll"
@change="onCheckAllChange"
>全选</j-checkbox
>
</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 }}
<template #action>
<div class="card-box-action">
<j-popconfirm
title="确定删除?"
@confirm="handlDelete(slotProps.id)"
>
<a><AIcon type="DeleteOutlined" /></a>
</j-popconfirm>
<a @click="handlEdit(slotProps)"
><AIcon type="FormOutlined"
/></a>
</div>
</slot>
</template>
<template #action>
<div class="card-box-action">
<j-popconfirm
title="确定删除?"
@confirm="handlDelete(slotProps.id)"
>
<a><AIcon type="DeleteOutlined" /></a>
</j-popconfirm>
<a @click="handlEdit(slotProps)"
><AIcon type="FormOutlined"
/></a>
</div>
</template>
<template #img>
<img
:src="
slotProps.provider === 'OPC_UA'
? opcImage
: modbusImage
"
/>
</template>
<template #content>
<div class="card-box-content">
<div class="card-box-content-left">
<div class="card-box-content-left-1">
</template>
<template #img>
<img
:src="
slotProps.provider === 'OPC_UA'
? opcImage
: modbusImage
"
/>
</template>
<template #content>
<div class="card-box-content">
<div class="card-box-content-left">
<div class="card-box-content-left-1">
<div
class="ard-box-content-left-1-title"
v-if="
propertyValue.has(slotProps.id)
"
>
<j-ellipsis
style="max-width: 150px"
>
{{
propertyValue.get(
slotProps.id,
)?.parseData[0] || 0
}}({{
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
class="ard-box-content-left-1-title"
v-if="propertyValue.has(slotProps.id)"
class="card-box-content-right-2"
>
<j-ellipsis style="max-width: 150px">
<p>
{{
propertyValue.get(slotProps.id)
?.parseData[0] || 0
}}({{
propertyValue.get(slotProps.id)
?.dataType
}})
</j-ellipsis>
?.hex || ''
}}
</p>
<p>
{{
moment(
propertyValue.get(
slotProps.id,
)?.timestamp,
).format('YYYY-MM-DD HH:mm:ss')
}}
</p>
</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
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
v-if="getRight1(slotProps)"
class="card-box-content-right-1"
>
<span>{{ getQuantity(slotProps) }}</span>
<span>{{ getAddress(slotProps) }}</span>
<span>{{ getScaleFactor(slotProps) }}</span>
</div>
<div class="card-box-content-right-2">
<span>{{ getText(slotProps) }}</span>
<span>{{ getInterval(slotProps) }}</span>
<div class="card-box-content-right">
<div
v-if="getRight1(slotProps)"
class="card-box-content-right-1"
>
<span>{{
getQuantity(slotProps)
}}</span>
<span>{{ getAddress(slotProps) }}</span>
<span>{{
getScaleFactor(slotProps)
}}</span>
</div>
<div class="card-box-content-right-2">
<span>{{ getText(slotProps) }}</span>
<span>{{
getInterval(slotProps)
}}</span>
</div>
</div>
</div>
</div>
</template>
</PointCardBox>
</template>
</j-pro-table>
</template>
</PointCardBox>
</template>
</j-pro-table>
</j-scrollbar>
<SaveModBus
v-if="visible.saveModBus"
:data="current"

View File

@ -53,7 +53,7 @@ const handleChange = async (info: UploadChangeParam) => {
if (info.file.status === 'done') {
loading.value = false;
const result = info.file.response?.result;
const api = await querySystemApi(['paths']);
const api: any = await querySystemApi(['paths']);
const path = api.result[0]?.properties
? api.result[0]?.properties['base-path']
: '';
@ -88,6 +88,8 @@ watch(
.upload-box {
:deep(.ant-btn) {
width: 110px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.upload-text {
margin: 0 10px;

View File

@ -78,7 +78,16 @@
</j-form-item>
</j-col>
<j-col :span="12"
><j-form-item label="签名" v-bind="validateInfos.sign">
><j-form-item v-bind="validateInfos.sign">
<template #label>
签名
<j-tooltip title="请输入本地文件进行签名加密后的值">
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</template>
<j-input
placeholder="请输入签名"
v-model:value="formData.sign" /></j-form-item
@ -148,14 +157,14 @@
title="确认删除吗?"
ok-text="确认"
cancel-text="取消"
@confirm="removeUser(propertie)"
@confirm="removeList(propertie)"
>
<AIcon type="DeleteOutlined" />
</j-popconfirm>
</j-form-item>
</div>
<j-form-item class="formRef-form-item-add">
<j-button type="dashed" block @click="addUser">
<j-button type="dashed" block @click="addList">
<AIcon type="PlusOutlined" />
添加
</j-button>
@ -198,13 +207,13 @@ const dynamicValidateForm = reactive<{ properties: Properties[] }>({
properties: [],
});
const removeUser = (item: Properties) => {
const removeList = (item: Properties) => {
let index = dynamicValidateForm.properties.indexOf(item);
if (index !== -1) {
dynamicValidateForm.properties.splice(index, 1);
}
};
const addUser = () => {
const addList = () => {
dynamicValidateForm.properties.push({
id: '',
value: '',
@ -226,8 +235,9 @@ const props = defineProps({
const emit = defineEmits(['change']);
const id = props.data.id;
const VersionOrder = props.data.versionOrder;
const formData = ref({
const formData: any = ref({
name: '',
productId: undefined,
version: '',
@ -239,7 +249,7 @@ const formData = ref({
description: '',
});
const extraValue = ref({});
const extraValue: any = ref({});
const validatorSign = async (_: Record<string, any>, value: string) => {
const { signMethod, url } = formData.value;
@ -252,18 +262,16 @@ const validatorSign = async (_: Record<string, any>, value: string) => {
}
};
const validatorVersionOrder = async (_: Record<string, any>, value: string) => {
const { signMethod, productId } = formData.value;
if (value && !!signMethod && productId) {
const res = await validateVersion(productId, value);
if (res.status === 200) {
if (id && props.data.versionOrder === value) {
formData.value.versionOrder = '';
} else {
Promise.reject(res.result ? ['版本序号已存在'] : '');
if (id && VersionOrder === value) {
return Promise.resolve();
} else {
const { signMethod, productId } = formData.value;
if (value && !!signMethod && productId) {
const res = await validateVersion(productId, value);
if (res.status === 200) {
return Promise.reject(res.result ? '版本序号已存在' : '');
}
}
} else {
return Promise.resolve();
}
};
@ -278,7 +286,6 @@ const { resetFields, validate, validateInfos } = useForm(
version: [
{ required: true, message: '请输入版本号' },
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
{ validator: validatorVersionOrder, trigger: 'blur' },
],
versionOrder: [
{ required: true, message: '请输入版本序号' },
@ -298,13 +305,13 @@ const filterOption = (input: string, option: any) => {
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const onSubmit = async () => {
const { properties } = await formRef.value?.validate();
const handleOk = async () => {
const { properties }: any = await formRef.value?.validate();
validate()
.then(async (res) => {
const product = productOptions.value.find(
(item) => item?.value === res.productId,
.then(async (res: any) => {
const product: any = productOptions.value.find(
(item: any) => item?.value === res.productId,
);
const productName = product?.label || props.data?.url;
const size = extraValue.value?.length || props.data?.size;
@ -317,9 +324,9 @@ const onSubmit = async () => {
};
loading.value = true;
const response = !id
? await save(params)
: await update({ ...props.data, ...params });
if (response.status === 200) {
? await save(params).catch(() => {})
: await update({ ...props.data, ...params }).catch(() => {});
if (response?.status === 200) {
message.success('操作成功');
emit('change', true);
}
@ -330,9 +337,6 @@ const onSubmit = async () => {
});
};
const handleOk = () => {
onSubmit();
};
const handleCancel = () => {
emit('change', false);
};
@ -347,8 +351,8 @@ onMounted(() => {
paging: false,
terms: [{ column: 'state', value: 1 }],
sorts: [{ name: 'createTime', order: 'desc' }],
}).then((resp) => {
productOptions.value = resp.result.map((item) => ({
}).then((resp: any) => {
productOptions.value = resp.result.map((item: any) => ({
value: item.id,
label: item.name,
}));

View File

@ -22,7 +22,7 @@
@cancel="handleCancel"
@ok="handleOk"
>
<Search
<pro-search
:columns="columns"
target="search"
@search="handleSearch"
@ -44,6 +44,7 @@
>
<template #headerTitle>
<j-checkbox
v-if="checkAllData.length !== 0"
v-model:checked="state.checkAll"
:indeterminate="state.indeterminate"
@change="onCheckAllChange"
@ -194,7 +195,11 @@ const onCheckAllChange = (e: any) => {
_selectedRowKeys.value = state.checkedList;
};
const onSelectChange = (record: T[], selected: boolean, selectedRows: T[]) => {
const onSelectChange = (
record: T[any],
selected: boolean,
selectedRows: T[any],
) => {
_selectedRowKeys.value = selected
? [...getSetRowKey(selectedRows)]
: _selectedRowKeys.value.filter((item: T) => item !== record?.id);

View File

@ -132,7 +132,7 @@ const firmwareId = route.query.id;
const productId = route.query.productId;
const view = props.data.view;
const formData = ref({
const formData: any = ref({
name: '',
mode: undefined,
responseTimeoutSeconds: '',
@ -187,8 +187,8 @@ onMounted(() => {
paging: false,
terms: [{ column: 'state', value: 1 }],
sorts: [{ name: 'createTime', order: 'desc' }],
}).then((resp) => {
productOptions.value = resp.result.map((item) => ({
}).then((resp: any) => {
productOptions.value = resp.result.map((item: any) => ({
value: item.id,
label: item.name,
}));

View File

@ -1,6 +1,6 @@
<template>
<page-container>
<Search :columns="columns" target="search" @search="handleSearch" />
<pro-search :columns="columns" target="search" @search="handleSearch" />
<j-pro-table
ref="tableRef"
model="TABLE"

View File

@ -1,7 +1,11 @@
<template>
<page-container>
<div>
<Search :columns="columns" target="search" @search="handleSearch" />
<pro-search
:columns="columns"
target="search"
@search="handleSearch"
/>
<j-pro-table
ref="tableRef"
model="TABLE"
@ -246,11 +250,12 @@ onMounted(() => {
queryProduct({
paging: false,
sorts: [{ name: 'name', order: 'desc' }],
}).then((resp) => {
const list = resp.result.filter((it) => {
terms: [{ column: 'state', value: 1 }], // todo
}).then((resp: any) => {
const list = resp.result.filter((it: any) => {
return _.map(it?.features || [], 'id').includes('supportFirmware');
});
productOptions.value = list.map((item) => ({
productOptions.value = list.map((item: any) => ({
label: item.name,
value: item.id,
}));

View File

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

View File

@ -108,50 +108,52 @@
</j-row> </j-form
></j-col>
<j-col :span="8">
<div class="doc">
<h1>操作指引</h1>
<div>
1CTWing端创建产品设备以及一个第三方应用
</div>
<div>
2CTWing端配置产品/设备/分组级订阅订阅方URL地址请填写:
<div style="word-wrap: break-word">
{{
`${origin}/api/ctwing/${randomString()}/notify`
}}
<j-scrollbar height="500">
<div class="doc">
<h1>操作指引</h1>
<div>
1CTWing端创建产品设备以及一个第三方应用
</div>
<div>
2CTWing端配置产品/设备/分组级订阅订阅方URL地址请填写:
<div style="word-wrap: break-word">
{{
`${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 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>
</j-scrollbar>
</j-col>
</j-row>
</div>
@ -179,8 +181,12 @@
新增
</PermissionButton>
</div>
<div class="card-item">
<j-row :gutter="[24, 24]" v-if="procotolList.length > 0">
<j-scrollbar height="500">
<j-row
:gutter="[24, 24]"
v-if="procotolList.length > 0"
style="margin-right: 10px"
>
<j-col
:span="8"
v-for="item in procotolList"
@ -195,7 +201,7 @@
</j-col>
</j-row>
<j-empty v-else description="暂无数据" />
</div>
</j-scrollbar>
</div>
</div>
<div v-if="current === 2" class="card-last">
@ -362,26 +368,24 @@ const formData = ref<Form>({
const current = ref(0);
const stepCurrent = ref(0);
const steps = ref(['接入配置', '消息协议', '完成']);
const procotolList = ref([]);
const procotolList: any = ref([]);
const allProcotolList = ref([]);
const procotolCurrent = ref('');
const procotolCurrent: any = ref('');
const procotolChange = (id: string) => {
procotolCurrent.value = id;
};
const procotolSearch = (value: string) => {
if (value) {
const list = allProcotolList.value.filter((i) => {
return (
i.name &&
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
);
});
procotolList.value = list;
} else {
procotolList.value = allProcotolList.value;
}
procotolList.value = value
? allProcotolList.value.filter(
(i: any) =>
i.name &&
i.name
.toLocaleLowerCase()
.includes(value.toLocaleLowerCase()),
)
: allProcotolList.value;
};
const saveData = async () => {
@ -411,7 +415,7 @@ const saveData = async () => {
};
const queryProcotolList = async (id: string, params = {}) => {
const resp = await getProtocolList(ProtocolMapping.get(id), {
const resp: any = await getProtocolList(ProtocolMapping.get(id), {
...params,
'sorts[0].name': 'createTime',
'sorts[0].order': 'desc',
@ -424,10 +428,10 @@ const queryProcotolList = async (id: string, params = {}) => {
const addProcotol = () => {
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`,
);
tab.onTabSaveSuccess = (value) => {
tab.onTabSaveSuccess = (value: any) => {
if (value.success) {
procotolCurrent.value = value.result?.id;
queryProcotolList(props.provider?.id);
@ -486,12 +490,7 @@ watch(
}
.steps-box {
min-height: 400px;
.card-item {
padding-right: 5px;
max-height: 480px;
overflow-y: auto;
overflow-x: hidden;
}
.card-last {
padding-right: 5px;
overflow-y: auto;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,39 +1,35 @@
<template>
<j-spin :spinning="loading">
<div>
<j-textarea
:rows="4"
@change="textChange"
v-model:value="keystoreBase64"
:placeholder="placeholder"
/>
<j-upload
accept=".pem"
listType="text"
:action="NETWORK_CERTIFICATE_UPLOAD"
:headers="{
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
}"
:showUploadList="false"
@change="handleChange"
<j-textarea
:rows="4"
@change="textChange"
v-model:value="keystoreBase64"
:placeholder="placeholder"
/>
<j-upload
accept=".pem"
listType="text"
:action="NETWORK_CERTIFICATE_UPLOAD"
:headers="{
[TOKEN_KEY]: LocalStore.get(TOKEN_KEY),
}"
:showUploadList="false"
@change="handleChange"
>
<j-button style="margin-top: 10px">
<AIcon type="UploadOutlined" />
上传文件</j-button
>
<j-button style="margin-top: 10px">
<upload-outlined />
上传文件</j-button
>
</j-upload>
</div>
</j-upload>
</j-spin>
</template>
<script setup lang="ts" name="CertificateFile">
import { UploadOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import type { UploadChangeParam } from 'ant-design-vue';
import { LocalStore } from '@/utils/comm';
import { TOKEN_KEY } from '@/utils/variable';
import { NETWORK_CERTIFICATE_UPLOAD } from '@/api/link/certificate';
import type { UploadProps } from 'ant-design-vue';
const emit = defineEmits(['update:modelValue', 'change']);

View File

@ -25,7 +25,6 @@
</j-radio-button>
</j-radio-group>
</j-form-item>
<j-form-item
label="证书名称"
v-bind="validateInfos.name"
@ -154,9 +153,9 @@ const onSubmit = () => {
loading.value = true;
const response =
id === ':id'
? await save(params)
: await update({ ...params, id });
if (response.status === 200) {
? await save(params).catch(() => {})
: await update({ ...params, id }).catch(() => {});
if (response?.status === 200) {
message.success('操作成功');
router.push('/iot/link/certificate');
}
@ -180,9 +179,9 @@ const handleChange = (info: UploadChangeParam) => {
const detail = async (id: string) => {
if (id !== ':id') {
loading.value = true;
const res = await queryDetail(id);
const res: any = await queryDetail(id);
if (res.success) {
const result = res.result as FormDataType;
const result: any = res.result;
const type = result.type.value as TypeObjType;
formData.value = {
...result,
@ -207,12 +206,6 @@ detail(id);
height: 100%;
}
}
.form-upload-button {
margin-top: 10px;
}
.form-submit {
background-color: @primary-color !important;
}
}
.doc {

View File

@ -1,7 +1,11 @@
<template>
<page-container>
<div>
<Search :columns="columns" target="search" @search="handleSearch" />
<pro-search
:columns="columns"
target="search"
@search="handleSearch"
/>
<j-pro-table
ref="tableRef"
model="TABLE"

View File

@ -135,11 +135,17 @@ const changeType = (value: Array<string>) => {
const onSubmit = async () => {
const data: any = await formRef.value?.validate();
loading.value = true;
const response = !id
const response: any = !id
? await save(data).catch(() => {})
: await update({ ...props.data, ...data }).catch(() => {});
if (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;
};

View File

@ -36,6 +36,7 @@
show-search
:filter-option="filterOption"
@change="changeType"
:disabled="!!NetworkType"
/>
</j-form-item>
</j-col>
@ -778,14 +779,15 @@
height: 400px;
"
>
<MonacoEditor
<j-monaco-editor
theme="vs"
v-model:modelValue="
v-model:value="
cluster
.configuration
.parserConfiguration
.script
"
language="javascript"
/>
</div>
</j-form-item>
@ -981,6 +983,7 @@ import {
resourcesCurrent,
supports,
certificates,
start,
} from '@/api/link/type';
import {
FormStates,
@ -996,7 +999,6 @@ import {
import { cloneDeep } from 'lodash-es';
import type { FormData2Type, FormDataType } from '../type';
import { Store } from 'jetlinks-store';
import MonacoEditor from '@/components/MonacoEditor/index.vue';
const route = useRoute();
const NetworkType = route.query.type as string;
@ -1146,13 +1148,21 @@ const saveData = async () => {
: { ...formData.value, ...formRef2Data };
loading.value = true;
const resp =
const resp: any =
id === ':id'
? await save(params).catch(() => {})
: await update({ ...params, id }).catch(() => {});
if (resp?.status === 200) {
message.success('操作成功!');
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;
};

View File

@ -81,17 +81,17 @@
},
]"
>
<div class="form-label">
<template #label>
API Host
<span class="form-label-required">*</span>
<j-tooltip>
<template #title>
<p>调用流媒体接口时请求的服务地址</p>
</template>
<AIcon type="QuestionCircleOutlined" />
<j-tooltip
title="调用流媒体接口时请求的服务地址"
>
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</div>
</template>
<j-input
placeholder="请输入API Host"
v-model:value="formData.configuration.apiHost"
@ -135,19 +135,17 @@
},
]"
>
<div class="form-label">
<template #label>
RTP IP
<span class="form-label-required">*</span>
<j-tooltip>
<template #title>
<p>
视频设备将流推送到该IP地址下部分设备仅支持IP地址建议全是用IP地址
</p>
</template>
<AIcon type="QuestionCircleOutlined" />
<j-tooltip
title="视频设备将流推送到该IP地址下部分设备仅支持IP地址建议全是用IP地址"
>
<AIcon
type="QuestionCircleOutlined"
style="margin-left: 2px"
/>
</j-tooltip>
</div>
</template>
<j-input
placeholder="请输入RTP IP"
v-model:value="formData.configuration.rtpIp"
@ -250,7 +248,6 @@
<j-form-item>
<j-button
v-if="view === 'false'"
class="form-submit"
html-type="submit"
type="primary"
@click.prevent="onSubmit"
@ -302,7 +299,7 @@ const formData = ref<FormDataType>({
});
const onSubmit = async () => {
let data = await formRef.value?.validate();
let data: any = await formRef.value?.validate();
let params = { ...data };
const { configuration } = data;
if (configuration.dynamicRtpPort) {
@ -327,8 +324,8 @@ const onSubmit = async () => {
const detail = async (id: string) => {
loading.value = true;
const resp = await queryProviders();
options.value = resp.result.map((item) => ({
const resp: any = await queryProviders();
options.value = resp.result.map((item: any) => ({
value: item.id,
label: item.name,
}));
@ -368,9 +365,6 @@ watch(
<style lang="less" scoped>
.form {
.form-submit {
background-color: @primary-color !important;
}
.form-item-checked {
padding: 0;
padding-top: 35px;
@ -381,10 +375,6 @@ watch(
.form-label {
height: 30px;
padding-bottom: 8px;
.form-label-required {
color: red !important;
margin: 0 4px 0 -2px;
}
}
}
</style>

View File

@ -1,7 +1,11 @@
<template>
<page-container>
<div>
<Search :columns="columns" target="search" @search="handleSearch" />
<pro-search
:columns="columns"
target="search"
@search="handleSearch"
/>
<j-pro-table
ref="tableRef"
@ -211,8 +215,6 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
if (res.success) {
message.success('操作成功');
tableRef.value?.reload();
} else {
message.error('操作失败!');
}
},
},
@ -231,8 +233,6 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
if (res.success) {
message.success('操作成功');
tableRef.value.reload();
} else {
message.error('操作失败!');
}
},
},

View File

@ -1,19 +1,17 @@
export interface Configuration = {
secret: string,
apiHost: string,
apiPort: number,
rtpIp: string,
rtpPort: number,
dynamicRtpPort: boolean,
dynamicRtpPortRange?: array,
dynamicRtpPortRange0?: number,
dynamicRtpPortRange1?: number,
};
export type FormDataType = {
export interface Configuration {
secret: string;
apiHost: string;
apiPort: number | string;
rtpIp: string | string;
rtpPort: number | string;
dynamicRtpPort: boolean;
dynamicRtpPortRange?: array<any>;
dynamicRtpPortRange0?: number | string | undefined;
dynamicRtpPortRange1?: number | string | undefined;
}
export interface FormDataType {
name: string;
provider: undefined;
provider: string | undefined;
configuration: Configuration;
id?: string;
};
}