Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
1793218b6a
2
.npmrc
2
.npmrc
|
@ -1,2 +1,2 @@
|
||||||
always-auth=true
|
always-auth=true
|
||||||
registry=http://47.108.170.157:9013/
|
registry=https://registry.jetlinks.cn/
|
|
@ -23,14 +23,14 @@
|
||||||
"event-source-polyfill": "^1.0.31",
|
"event-source-polyfill": "^1.0.31",
|
||||||
"global": "^4.4.0",
|
"global": "^4.4.0",
|
||||||
"jetlinks-store": "^0.0.3",
|
"jetlinks-store": "^0.0.3",
|
||||||
|
"jetlinks-ui-components": "^1.0.0",
|
||||||
"js-cookie": "^3.0.1",
|
"js-cookie": "^3.0.1",
|
||||||
"less": "^4.1.3",
|
"less": "^4.1.3",
|
||||||
"less-loader": "^11.1.0",
|
"less-loader": "^11.1.0",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
"marked": "^4.2.12",
|
"marked": "^4.2.12",
|
||||||
"mavon-editor": "^2.10.4",
|
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"monaco-editor": "^0.24.0",
|
"monaco-editor": "^0.36.0",
|
||||||
"nrm": "^1.2.5",
|
"nrm": "^1.2.5",
|
||||||
"pinia": "^2.0.28",
|
"pinia": "^2.0.28",
|
||||||
"unplugin-auto-import": "^0.12.1",
|
"unplugin-auto-import": "^0.12.1",
|
||||||
|
|
|
@ -49,8 +49,10 @@ export const queryProduct = (data?: any) =>
|
||||||
export const queryDevice = () =>
|
export const queryDevice = () =>
|
||||||
server.get(`/device/instance/_query/no-paging?paging=false`);
|
server.get(`/device/instance/_query/no-paging?paging=false`);
|
||||||
|
|
||||||
export const validateVersion = (productId: string, versionOrder: number) =>
|
export const validateVersion = (
|
||||||
server.get(`/firmware/${productId}/${versionOrder}/exists`);
|
productId: string,
|
||||||
|
versionOrder: number | string,
|
||||||
|
) => server.get(`/firmware/${productId}/${versionOrder}/exists`);
|
||||||
|
|
||||||
export const queryDetailList = (data: Record<string, unknown>) =>
|
export const queryDetailList = (data: Record<string, unknown>) =>
|
||||||
server.post(`/device-instance/detail/_query`, data);
|
server.post(`/device-instance/detail/_query`, data);
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
import server from '@/utils/request';
|
||||||
|
|
||||||
|
export const dashboard = (data: object) =>
|
||||||
|
server.post(`/dashboard/_multi`, data);
|
||||||
|
export const productCount = (data: object) =>
|
||||||
|
server.post(`/device-product/_count`, data);
|
||||||
|
export const getGeo = (data: object) =>
|
||||||
|
server.post(`/geo/object/device/_search/geo.json`, data);
|
||||||
|
export const deviceCount = (data: object) =>
|
||||||
|
server.get(`/device/instance/_count`, data);
|
||||||
|
export const serverNode = () => server.get(`/dashboard/cluster/nodes`);
|
|
@ -1,16 +1,29 @@
|
||||||
import server from '@/utils/request'
|
import server from '@/utils/request'
|
||||||
|
import type { CascadeItem } from '@/views/media/Cascade/typings'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
// 列表
|
// 列表
|
||||||
list: (data: any, id: string) => server.post(`/media/gb28181-cascade/_query`, data),
|
list: (data: any) => server.post<any>(`/media/gb28181-cascade/_query`, data),
|
||||||
// 列表字段通道数量, 来自下面接口的total
|
// 列表字段通道数量, 来自下面接口的total
|
||||||
queryCount: (id: string) => server.post(`/media/gb28181-cascade/${id}/bindings/_query`),
|
queryCount: (id: string) => server.post<any>(`/media/gb28181-cascade/${id}/bindings/_query`),
|
||||||
// 详情
|
// 详情
|
||||||
detail: (id: string): any => server.get(`/media/gb28181-cascade/${id}`),
|
detail: (id: string): any => server.get(`/media/gb28181-cascade/${id}`),
|
||||||
// 新增
|
// 新增
|
||||||
save: (data: any) => server.post(`/media/gb28181-cascade`, data),
|
save: (data: any) => server.post(`/media/gb28181-cascade`, data),
|
||||||
// 修改
|
// 修改
|
||||||
update: (id: string, data: any) => server.put(`/media/gb28181-cascade/${id}`, data),
|
// update: (id: string, data: any) => server.put(`/media/gb28181-cascade/${id}`, data),
|
||||||
|
update: (data: any) => server.patch(`/media/gb28181-cascade`, data),
|
||||||
// 删除
|
// 删除
|
||||||
del: (id: string) => server.remove(`media/gb28181-cascade/${id}`),
|
del: (id: string) => server.remove(`media/gb28181-cascade/${id}`),
|
||||||
|
// 禁用
|
||||||
|
disabled: (id: string) => server.post<any>(`/media/gb28181-cascade/${id}/_disabled`),
|
||||||
|
// 启用
|
||||||
|
enabled: (id: string) => server.post<any>(`/media/gb28181-cascade/${id}/_enabled`),
|
||||||
|
|
||||||
|
// 新增/编辑
|
||||||
|
// 获取集群节点
|
||||||
|
clusters: () => server.get<any>(`/network/resources/clusters`),
|
||||||
|
// SIP本地地址
|
||||||
|
all: () => server.get<any>(`/network/resources/alive/_all`),
|
||||||
|
|
||||||
}
|
}
|
|
@ -18,4 +18,9 @@ export const getOrgList = (parmas?:any) => server.get('/organization/_query/no-p
|
||||||
/**
|
/**
|
||||||
* 搜索
|
* 搜索
|
||||||
*/
|
*/
|
||||||
export const query = (data:any) => server.post('/alarm/record/_query/',data);
|
export const query = (data:any) => server.post('/alarm/record/_query/',data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 告警处理
|
||||||
|
*/
|
||||||
|
export const handleLog = (data:any) => server.post('/alarm/record/_handle',data)
|
|
@ -7,4 +7,15 @@ export const save = (data: any) => server.post(`/scene`, data)
|
||||||
|
|
||||||
export const detail = (id: string) => server.get(`/scene/${id}`)
|
export const detail = (id: string) => server.get(`/scene/${id}`)
|
||||||
|
|
||||||
export const query = (data: any) => server.post('/scene/_query/',data);
|
export const query = (data: any) => server.post('/scene/_query/',data);
|
||||||
|
|
||||||
|
export const _delete = (id: string) => server.remove(`/scene/${id}/`);
|
||||||
|
|
||||||
|
export const _action = (id: string, type: '_disable' | '_enable') => server.put(`/scene/${id}/${type}`);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动触发
|
||||||
|
* @param id
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const _execute = (id: string) => server.post(`/scene/${id}/_execute`);
|
|
@ -48,7 +48,7 @@ const iconKeys = [
|
||||||
'ClockCircleOutlined',
|
'ClockCircleOutlined',
|
||||||
'PartitionOutlined',
|
'PartitionOutlined',
|
||||||
'ShareAltOutlined',
|
'ShareAltOutlined',
|
||||||
'playCircleOutlined',
|
'PlayCircleOutlined',
|
||||||
'RightOutlined',
|
'RightOutlined',
|
||||||
'FileTextOutlined',
|
'FileTextOutlined',
|
||||||
'UploadOutlined',
|
'UploadOutlined',
|
||||||
|
@ -60,6 +60,9 @@ const iconKeys = [
|
||||||
'RedoOutlined',
|
'RedoOutlined',
|
||||||
'VideoCameraOutlined',
|
'VideoCameraOutlined',
|
||||||
'HistoryOutlined',
|
'HistoryOutlined',
|
||||||
|
'ToolOutlined',
|
||||||
|
'FileOutlined',
|
||||||
|
'LikeOutlined'
|
||||||
]
|
]
|
||||||
|
|
||||||
const Icon = (props: {type: string}) => {
|
const Icon = (props: {type: string}) => {
|
||||||
|
|
|
@ -3,26 +3,26 @@
|
||||||
<template #title>
|
<template #title>
|
||||||
<div style="display: flex; justify-content: space-between; align-items: center;">
|
<div style="display: flex; justify-content: space-between; align-items: center;">
|
||||||
<div style="width: 150px;">配置元素</div>
|
<div style="width: 150px;">配置元素</div>
|
||||||
<AIcon type="CloseOutlined" @click="visible = false"/>
|
<AIcon type="CloseOutlined" @click="visible = false" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div style="max-width: 400px;">
|
<div style="max-width: 400px;">
|
||||||
<a-form layout="vertical" :model="_value">
|
<div class="ant-form-vertical">
|
||||||
<value-type-form v-model:value="_value" :name="[]" isSub key="sub"></value-type-form>
|
<value-type-form v-model:value="_value" :name="name" isSub key="sub"></value-type-form>
|
||||||
<a-form-item label="说明" name="description" :rules="[
|
<a-form-item label="说明" :name="name.concat(['description'])" :rules="[
|
||||||
{ max: 200, message: '最多可输入200个字符' },
|
{ max: 200, message: '最多可输入200个字符' },
|
||||||
]">
|
]">
|
||||||
<a-textarea v-model:value="_value.description" size="small"></a-textarea>
|
<a-textarea v-model:value="_value.description" size="small"></a-textarea>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<a-button type="dashed" block @click="visible = true">
|
<a-button type="dashed" block @click="visible = true">
|
||||||
配置元素<AIcon type="EditOutlined" class="item-icon"/>
|
配置元素
|
||||||
|
<AIcon type="EditOutlined" class="item-icon" />
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-popover>
|
</a-popover>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="ArrayParam">
|
<script setup lang="ts" name="ArrayParam">
|
||||||
import ValueTypeForm from '@/views/device/components/Metadata/Base/Edit/ValueTypeForm.vue';
|
import ValueTypeForm from '@/views/device/components/Metadata/Base/Edit/ValueTypeForm.vue';
|
||||||
|
@ -36,7 +36,7 @@ const props = defineProps({
|
||||||
default: () => ({ extends: {} })
|
default: () => ({ extends: {} })
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<(string | number)[]>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -47,7 +47,7 @@ const props = defineProps({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<(string| number)[]>,
|
||||||
required: true
|
required: true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -30,7 +30,7 @@ const props = defineProps({
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<(string| number)[]>,
|
||||||
default: () => ([]),
|
default: () => ([]),
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
|
@ -13,18 +13,18 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<a-form :model="_value[index]" layout="vertical">
|
<div class="ant-form-vertical">
|
||||||
<a-form-item label="Value" name="value" :rules="[
|
<a-form-item label="Value" :name="name.concat([index, 'value'])" :rules="[
|
||||||
{ required: true, message: '请输入Value' },
|
{ required: true, message: '请输入Value' },
|
||||||
]">
|
]">
|
||||||
<a-input v-model:value="_value[index].value" size="small"></a-input>
|
<a-input v-model:value="_value[index].value" size="small"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="Text" name="text" :rules="[
|
<a-form-item label="Text" :name="name.concat([index, 'text'])" :rules="[
|
||||||
{ required: true, message: '请输入Text' },
|
{ required: true, message: '请输入Text' },
|
||||||
]">
|
]">
|
||||||
<a-input v-model:value="_value[index].text" size="small"></a-input>
|
<a-input v-model:value="_value[index].text" size="small"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="item-edit" @click="handleEdit(index)">
|
<div class="item-edit" @click="handleEdit(index)">
|
||||||
{{ item.text || '枚举项配置' }}
|
{{ item.text || '枚举项配置' }}
|
||||||
|
@ -33,11 +33,13 @@
|
||||||
</a-popover>
|
</a-popover>
|
||||||
</div>
|
</div>
|
||||||
<div class="item-right">
|
<div class="item-right">
|
||||||
<AIcon type="DeleteOutlined" @click="handleDelete(index)"/>
|
<AIcon type="DeleteOutlined" @click="handleDelete(index)" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-button type="dashed" block @click="handleAdd">
|
<a-button type="dashed" block @click="handleAdd">
|
||||||
<template #icon><AIcon type="PlusOutlined" class="item-icon" /></template>
|
<template #icon>
|
||||||
|
<AIcon type="PlusOutlined" class="item-icon" />
|
||||||
|
</template>
|
||||||
新增枚举型
|
新增枚举型
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -57,20 +59,23 @@ const emit = defineEmits<Emits>()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: {
|
value: {
|
||||||
type: Object as PropType<EnumType[]>,
|
type: Object as PropType<EnumType[]>,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<(string | number)[]>,
|
||||||
default: () => ([])
|
default: () => ([])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const _value = ref<EnumType[]>([])
|
const _value = ref<EnumType[]>([])
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
_value.value = props.value
|
_value.value = props.value || ([{}])
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(_value,
|
watch(_value,
|
||||||
() => {
|
() => {
|
||||||
emit('update:value', _value.value)
|
emit('update:value', _value.value)
|
||||||
},
|
},
|
||||||
{ deep: true })
|
{ deep: true, immediate: true })
|
||||||
|
|
||||||
const editIndex = ref<number>(-1)
|
const editIndex = ref<number>(-1)
|
||||||
const handleEdit = (index: number) => {
|
const handleEdit = (index: number) => {
|
||||||
|
|
|
@ -13,27 +13,25 @@
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div style="max-width: 400px;">
|
<div style="max-width: 400px;" class="ant-form-vertical">
|
||||||
<a-form :model="_value[index]" layout="vertical">
|
<a-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||||
<a-form-item label="标识" name="id" :rules="[
|
{ required: true, message: '请输入标识' },
|
||||||
{ required: true, message: '请输入标识' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{
|
||||||
{
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
pattern: /^[a-zA-Z0-9_\-]+$/,
|
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
},
|
||||||
},
|
]">
|
||||||
]">
|
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
||||||
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<a-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||||
<a-form-item label="名称" name="name" :rules="[
|
{ required: true, message: '请输入名称' },
|
||||||
{ required: true, message: '请输入名称' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
]">
|
||||||
]">
|
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
||||||
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
</a-form-item>
|
||||||
</a-form-item>
|
<value-type-form v-model:value="_value[index].valueType" :name="name.concat([index, 'valueType'])" isSub
|
||||||
<value-type-form v-model:value="_value[index].valueType" :name="['valueType']" isSub
|
key="json_sub"></value-type-form>
|
||||||
key="json_sub"></value-type-form>
|
|
||||||
</a-form>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="item-edit" @click="handleEdit(index)">
|
<div class="item-edit" @click="handleEdit(index)">
|
||||||
|
@ -47,7 +45,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-button type="dashed" block @click="handleAdd">
|
<a-button type="dashed" block @click="handleAdd">
|
||||||
<template #icon><AIcon type="PlusOutlined" class="item-icon" /></template>
|
<template #icon>
|
||||||
|
<AIcon type="PlusOutlined" class="item-icon" />
|
||||||
|
</template>
|
||||||
添加参数
|
添加参数
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -65,6 +65,10 @@ const emit = defineEmits<Emits>()
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
value: {
|
value: {
|
||||||
type: Object as PropType<JsonType[]>,
|
type: Object as PropType<JsonType[]>,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<(string | number)[]>,
|
||||||
|
default: () => ([])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -15,8 +15,8 @@
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div>
|
<div>
|
||||||
<a-form :model="_value[index]" layout="vertical">
|
<div class="ant-form-vertical">
|
||||||
<a-form-item label="标识" name="id" :rules="[
|
<a-form-item label="标识" :name="name.concat([index, 'id'])" :rules="[
|
||||||
{ required: true, message: '请输入标识' },
|
{ required: true, message: '请输入标识' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
{
|
{
|
||||||
|
@ -26,19 +26,19 @@
|
||||||
]">
|
]">
|
||||||
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
<a-input v-model:value="_value[index].id" size="small"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="名称" name="name" :rules="[
|
<a-form-item label="名称" :name="name.concat([index, 'name'])" :rules="[
|
||||||
{ required: true, message: '请输入名称' },
|
{ required: true, message: '请输入名称' },
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
]">
|
]">
|
||||||
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
<a-input v-model:value="_value[index].name" size="small"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="指标值" name="value" :rules="[
|
<a-form-item label="指标值" :name="name.concat([index, 'value'])" :rules="[
|
||||||
{ required: true, message: '请输入指标值' },
|
{ required: true, message: '请输入指标值' },
|
||||||
{ validator: () => validateIndicator(_value[index]), message: '请输入指标值' }
|
{ validator: () => validateIndicator(_value[index]), message: '请输入指标值' }
|
||||||
]">
|
]">
|
||||||
<JIndicators v-model:value="_value[index]" :type="type" size="small" :enum="enum"/>
|
<JIndicators v-model:value="_value[index]" :type="type" size="small" :enum="enum" />
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div class="item-edit" @click="handleEdit(index)">
|
<div class="item-edit" @click="handleEdit(index)">
|
||||||
|
@ -52,7 +52,9 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<a-button type="dashed" block @click="handleAdd">
|
<a-button type="dashed" block @click="handleAdd">
|
||||||
<template #icon><AIcon type="PlusOutlined" class="item-icon" /></template>
|
<template #icon>
|
||||||
|
<AIcon type="PlusOutlined" class="item-icon" />
|
||||||
|
</template>
|
||||||
添加指标
|
添加指标
|
||||||
</a-button>
|
</a-button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -77,6 +79,10 @@ const props = defineProps({
|
||||||
enum: {
|
enum: {
|
||||||
type: Object,
|
type: Object,
|
||||||
default: () => ({})
|
default: () => ({})
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: Array as PropType<(string | number)[]>,
|
||||||
|
default: () => ([])
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ const props = defineProps({
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<(string| number)[]>,
|
||||||
default: () => ([])
|
default: () => ([])
|
||||||
},
|
},
|
||||||
id: String,
|
id: String,
|
||||||
|
|
|
@ -90,7 +90,7 @@ const insert = (val) => {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.value,
|
watch(() => props.modelValue,
|
||||||
(val) => {
|
(val) => {
|
||||||
instance.setValue(val)
|
instance.setValue(val)
|
||||||
})
|
})
|
||||||
|
|
|
@ -4,14 +4,14 @@
|
||||||
<a-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled">
|
<a-popconfirm v-bind="popConfirm" :disabled="!isPermission || props.disabled">
|
||||||
<a-tooltip v-if="tooltip" v-bind="tooltip">
|
<a-tooltip v-if="tooltip" v-bind="tooltip">
|
||||||
<slot v-if="noButton"></slot>
|
<slot v-if="noButton"></slot>
|
||||||
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
|
<a-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
</template>
|
</template>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" >
|
<a-button v-else v-bind="props" :disabled="_isPermission" >
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
|
@ -22,7 +22,7 @@
|
||||||
<template v-else-if="tooltip">
|
<template v-else-if="tooltip">
|
||||||
<a-tooltip v-bind="tooltip">
|
<a-tooltip v-bind="tooltip">
|
||||||
<slot v-if="noButton"></slot>
|
<slot v-if="noButton"></slot>
|
||||||
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
|
<a-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
|
@ -32,7 +32,7 @@
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<slot v-if="noButton"></slot>
|
<slot v-if="noButton"></slot>
|
||||||
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
|
<a-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
|
@ -42,7 +42,7 @@
|
||||||
</template>
|
</template>
|
||||||
<a-tooltip v-else title="没有权限">
|
<a-tooltip v-else title="没有权限">
|
||||||
<slot v-if="noButton"></slot>
|
<slot v-if="noButton"></slot>
|
||||||
<a-button v-else v-bind="_buttonProps" :disabled="_isPermission" :style="props.style">
|
<a-button v-else v-bind="props" :disabled="_isPermission" :style="props.style">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
<template #icon>
|
<template #icon>
|
||||||
<slot name="icon"></slot>
|
<slot name="icon"></slot>
|
||||||
|
@ -91,7 +91,7 @@ const props = defineProps({
|
||||||
...buttonProps()
|
...buttonProps()
|
||||||
})
|
})
|
||||||
|
|
||||||
const { tooltip, popConfirm, hasPermission, noButton, ..._buttonProps } = props;
|
// const { tooltip, popConfirm, hasPermission, noButton, ..._buttonProps } = props;
|
||||||
|
|
||||||
const permissionStore = usePermissionStore()
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
|
@ -103,8 +103,8 @@ const isPermission = computed(() => {
|
||||||
})
|
})
|
||||||
const _isPermission = computed(() =>
|
const _isPermission = computed(() =>
|
||||||
'hasPermission' in props && isPermission.value
|
'hasPermission' in props && isPermission.value
|
||||||
? 'disabled' in _buttonProps
|
? 'disabled' in props
|
||||||
? _buttonProps.disabled as boolean
|
? props.disabled as boolean
|
||||||
: false
|
: false
|
||||||
: true
|
: true
|
||||||
)
|
)
|
||||||
|
|
|
@ -158,7 +158,6 @@ const JTable = defineComponent<JTableProps>({
|
||||||
const pageSize = ref<number>(6)
|
const pageSize = ref<number>(6)
|
||||||
const total = ref<number>(0)
|
const total = ref<number>(0)
|
||||||
const loading = ref<boolean>(true)
|
const loading = ref<boolean>(true)
|
||||||
const loading1 = ref<boolean>(true)
|
|
||||||
|
|
||||||
const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable)))
|
const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable)))
|
||||||
|
|
||||||
|
@ -240,6 +239,7 @@ const JTable = defineComponent<JTableProps>({
|
||||||
)
|
)
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
windowChange() // 初始化
|
||||||
window.onresize = () => {
|
window.onresize = () => {
|
||||||
windowChange()
|
windowChange()
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,14 @@ import store from './store'
|
||||||
import components from './components'
|
import components from './components'
|
||||||
import router from './router'
|
import router from './router'
|
||||||
import './style.less'
|
import './style.less'
|
||||||
import 'ant-design-vue/es/notification/style/css';
|
import jComponents from 'jetlinks-ui-components'
|
||||||
// import jConmonents from 'jetlinks-ui-components'
|
import 'jetlinks-ui-components/es/style.js'
|
||||||
// import 'jetlinks-ui-components/lib/style'
|
import 'jetlinks-ui-components/es/style/variable.less'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
app.use(store)
|
app.use(store)
|
||||||
.use(router)
|
.use(router)
|
||||||
.use(components)
|
.use(components)
|
||||||
// .use(jConmonents)
|
.use(jComponents)
|
||||||
.mount('#app')
|
.mount('#app')
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
@import 'ant-design-vue/es/style/themes/default.less';
|
//@import 'ant-design-vue/es/style/themes/default.less';
|
||||||
|
@import 'jetlinks-ui-components/es/style/default.less';
|
||||||
|
|
||||||
.ellipsisFn(@num: 1, @width: 100%) {
|
.ellipsisFn(@num: 1, @width: 100%) {
|
||||||
display: -webkit-box;
|
display: -webkit-box;
|
||||||
|
|
|
@ -184,7 +184,12 @@
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message, Form } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import FileUpload from './FileUpload.vue';
|
import FileUpload from './FileUpload.vue';
|
||||||
import { save, update, queryProduct } from '@/api/device/firmware';
|
import {
|
||||||
|
save,
|
||||||
|
update,
|
||||||
|
queryProduct,
|
||||||
|
validateVersion,
|
||||||
|
} from '@/api/device/firmware';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import type { Properties } from '../type';
|
import type { Properties } from '../type';
|
||||||
|
|
||||||
|
@ -246,6 +251,21 @@ const validatorSign = async (_: Record<string, any>, value: string) => {
|
||||||
return Promise.resolve();
|
return Promise.resolve();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
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 ? ['版本序号已存在'] : '');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const { resetFields, validate, validateInfos } = useForm(
|
const { resetFields, validate, validateInfos } = useForm(
|
||||||
formData,
|
formData,
|
||||||
|
@ -258,8 +278,12 @@ const { resetFields, validate, validateInfos } = useForm(
|
||||||
version: [
|
version: [
|
||||||
{ required: true, message: '请输入版本号' },
|
{ required: true, message: '请输入版本号' },
|
||||||
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
{ max: 64, message: '最多可输入64个字符', trigger: 'change' },
|
||||||
|
{ validator: validatorVersionOrder, trigger: 'blur' },
|
||||||
|
],
|
||||||
|
versionOrder: [
|
||||||
|
{ required: true, message: '请输入版本序号' },
|
||||||
|
{ validator: validatorVersionOrder, trigger: 'blur' },
|
||||||
],
|
],
|
||||||
versionOrder: [{ required: true, message: '请输入版本序号' }],
|
|
||||||
signMethod: [{ required: true, message: '请选择签名方式' }],
|
signMethod: [{ required: true, message: '请选择签名方式' }],
|
||||||
sign: [
|
sign: [
|
||||||
{ required: true, message: '请输入签名' },
|
{ required: true, message: '请输入签名' },
|
||||||
|
@ -280,10 +304,10 @@ const onSubmit = async () => {
|
||||||
validate()
|
validate()
|
||||||
.then(async (res) => {
|
.then(async (res) => {
|
||||||
const product = productOptions.value.find(
|
const product = productOptions.value.find(
|
||||||
(item) => item.value === res.productId,
|
(item) => item?.value === res.productId,
|
||||||
);
|
);
|
||||||
const productName = product.label || props.data?.url;
|
const productName = product?.label || props.data?.url;
|
||||||
const size = extraValue.value.length || props.data?.size;
|
const size = extraValue.value?.length || props.data?.size;
|
||||||
|
|
||||||
const params = {
|
const params = {
|
||||||
...toRaw(formData.value),
|
...toRaw(formData.value),
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
message: 'ID只能由数字、字母、下划线、中划线组成',
|
message: 'ID只能由数字、字母、下划线、中划线组成',
|
||||||
},
|
},
|
||||||
]">
|
]">
|
||||||
<a-input v-model:value="value.id" size="small" @change="asyncOtherConfig"></a-input>
|
<a-input v-model:value="value.id" size="small" @change="asyncOtherConfig" :disabled="metadataStore.model.action === 'edit'"></a-input>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="名称" name="name" :rules="[
|
<a-form-item label="名称" name="name" :rules="[
|
||||||
{ required: true, message: '请输入名称' },
|
{ required: true, message: '请输入名称' },
|
||||||
|
@ -18,7 +18,6 @@
|
||||||
<template v-if="modelType === 'properties'">
|
<template v-if="modelType === 'properties'">
|
||||||
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"
|
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"
|
||||||
@change-type="changeValueType"></value-type-form>
|
@change-type="changeValueType"></value-type-form>
|
||||||
|
|
||||||
<expands-form :name="['expands']" v-model:value="value.expands" :type="type" :id="value.id" :config="config"
|
<expands-form :name="['expands']" v-model:value="value.expands" :type="type" :id="value.id" :config="config"
|
||||||
:valueType="value.valueType"></expands-form>
|
:valueType="value.valueType"></expands-form>
|
||||||
</template>
|
</template>
|
||||||
|
@ -46,20 +45,29 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="function" title="输出参数"></value-type-form>
|
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="function" title="输出参数"></value-type-form>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-if="modelType === 'tags'">
|
||||||
|
<value-type-form :name="['valueType']" v-model:value="value.valueType" key="property" title="数据类型"></value-type-form>
|
||||||
|
<a-form-item label="读写类型" :name="['expands', 'type']" :rules="[
|
||||||
|
{ required: true, message: '请选择读写类型' },
|
||||||
|
]">
|
||||||
|
<a-select v-model:value="value.expands.type" :options="ExpandsTypeList" mode="multiple" size="small"></a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</template>
|
||||||
<a-form-item label="说明" name="description" :rules="[
|
<a-form-item label="说明" name="description" :rules="[
|
||||||
{ max: 200, message: '最多可输入200个字符' },
|
{ max: 200, message: '最多可输入200个字符' },
|
||||||
]">
|
]">
|
||||||
<a-textarea v-model:value="value.description" size="small"></a-textarea>
|
<a-textarea v-model:value="value.description" size="small"></a-textarea>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="PropertyForm">
|
<script setup lang="ts" name="BaseForm">
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import ExpandsForm from './ExpandsForm.vue';
|
import ExpandsForm from './ExpandsForm.vue';
|
||||||
import ValueTypeForm from './ValueTypeForm.vue'
|
import ValueTypeForm from './ValueTypeForm.vue'
|
||||||
import { useProductStore } from '@/store/product';
|
import { useProductStore } from '@/store/product';
|
||||||
import { getMetadataConfig } from '@/api/device/product'
|
import { getMetadataConfig } from '@/api/device/product'
|
||||||
import JsonParam from '@/components/Metadata/JsonParam/index.vue'
|
import JsonParam from '@/components/Metadata/JsonParam/index.vue'
|
||||||
import { EventLevel } from '@/views/device/data';
|
import { EventLevel, ExpandsTypeList } from '@/views/device/data';
|
||||||
|
import { useMetadataStore } from '@/store/metadata';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
type: {
|
type: {
|
||||||
|
@ -76,7 +84,10 @@ const props = defineProps({
|
||||||
default: ''
|
default: ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (props.modelType === 'events') {
|
|
||||||
|
const metadataStore = useMetadataStore()
|
||||||
|
|
||||||
|
if (props.modelType === 'events' || props.modelType === 'tags') {
|
||||||
if (!props.value.expands) {
|
if (!props.value.expands) {
|
||||||
props.value.expands = {}
|
props.value.expands = {}
|
||||||
}
|
}
|
|
@ -1,2 +0,0 @@
|
||||||
<template></template>
|
|
||||||
<script setup lang="ts" name="CommonForm"></script>
|
|
|
@ -10,7 +10,7 @@
|
||||||
<a-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
|
<a-form-item label="读写类型" :name="name.concat(['type'])" :rules="[
|
||||||
{ required: true, message: '请选择读写类型' },
|
{ required: true, message: '请选择读写类型' },
|
||||||
]">
|
]">
|
||||||
<a-select v-model:value="_value.type" :options="options" mode="multiple" size="small"></a-select>
|
<a-select v-model:value="_value.type" :options="ExpandsTypeList" mode="multiple" size="small"></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="其他配置" v-if="config.length > 0">
|
<a-form-item label="其他配置" v-if="config.length > 0">
|
||||||
<a-form-item v-for="(item, index) in config" :key="index">
|
<a-form-item v-for="(item, index) in config" :key="index">
|
||||||
|
@ -19,12 +19,12 @@
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item v-if="type === 'product' && ['int', 'float', 'double', 'long', 'date', 'string', 'boolean'].includes(valueType.type)"
|
<a-form-item v-if="type === 'product' && ['int', 'float', 'double', 'long', 'date', 'string', 'boolean'].includes(valueType.type)"
|
||||||
label="指标配置" :name="name.concat(['metrics'])">
|
label="指标配置" :name="name.concat(['metrics'])">
|
||||||
<metrics-param v-model:value="_value.metrics" :type="valueType.type" :enum="valueType"></metrics-param>
|
<metrics-param v-model:value="_value.metrics" :type="valueType.type" :enum="valueType" :name="name.concat(['metrics'])"></metrics-param>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts" name="ExpandsForm">
|
<script setup lang="ts" name="ExpandsForm">
|
||||||
import { useMetadataStore } from '@/store/metadata';
|
import { useMetadataStore } from '@/store/metadata';
|
||||||
import { PropertySource } from '@/views/device/data';
|
import { ExpandsTypeList, PropertySource } from '@/views/device/data';
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
import VirtualRuleParam from '@/components/Metadata/VirtualRuleParam/index.vue';
|
import VirtualRuleParam from '@/components/Metadata/VirtualRuleParam/index.vue';
|
||||||
import ConfigParam from '@/components/Metadata/ConfigParam/index.vue'
|
import ConfigParam from '@/components/Metadata/ConfigParam/index.vue'
|
||||||
|
@ -78,21 +78,6 @@ watch(_value,
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true })
|
{ deep: true, immediate: true })
|
||||||
|
|
||||||
const options = [
|
|
||||||
{
|
|
||||||
label: '读',
|
|
||||||
value: 'read',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '写',
|
|
||||||
value: 'write',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: '上报',
|
|
||||||
value: 'report',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const metadataStore = useMetadataStore()
|
const metadataStore = useMetadataStore()
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<a-form-item :label="title" :name="name.concat(['type'])" :rules="[
|
<a-form-item :label="title" :name="name.concat(['type'])" :rules="[
|
||||||
metadataStore.model.type !== 'functions' ? { required: true, message: '请选择数据类型' } : {},
|
metadataStore.model.type !== 'functions' ? { required: true, message: `请选择${title}` } : {},
|
||||||
]">
|
]">
|
||||||
<a-select v-model:value="_value.type" :options="metadataStore.model.type === 'events' ? eventDataTypeList : _dataTypeList" size="small" @change="changeType"></a-select>
|
<a-select v-model:value="_value.type" :options="metadataStore.model.type === 'events' ? eventDataTypeList : _dataTypeList" size="small" @change="changeType"></a-select>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -17,7 +17,7 @@
|
||||||
<a-form-item label="枚举项" :name="name.concat(['elements'])" v-if="['enum'].includes(_value.type)" :rules="[
|
<a-form-item label="枚举项" :name="name.concat(['elements'])" v-if="['enum'].includes(_value.type)" :rules="[
|
||||||
{ required: true, message: '请配置枚举项' }
|
{ required: true, message: '请配置枚举项' }
|
||||||
]">
|
]">
|
||||||
<EnumParam v-model:value="_value.elements"></EnumParam>
|
<EnumParam v-model:value="_value.elements" :name="name.concat(['elements'])"></EnumParam>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item :name="name.concat(['expands', 'maxLength'])" v-if="['string', 'password'].includes(_value.type)">
|
<a-form-item :name="name.concat(['expands', 'maxLength'])" v-if="['string', 'password'].includes(_value.type)">
|
||||||
<template #label>
|
<template #label>
|
||||||
|
@ -34,8 +34,8 @@
|
||||||
<a-form-item label="元素配置" :name="name.concat(['elementType'])" v-if="['array'].includes(_value.type)">
|
<a-form-item label="元素配置" :name="name.concat(['elementType'])" v-if="['array'].includes(_value.type)">
|
||||||
<ArrayParam v-model:value="_value.elementType" :name="name.concat(['elementType'])"></ArrayParam>
|
<ArrayParam v-model:value="_value.elementType" :name="name.concat(['elementType'])"></ArrayParam>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="JSON对象" :name="name.concat(['properties'])" v-if="['object'].includes(_value.type)">
|
<a-form-item label="JSON对象" :name="name.concat(['properties'])" v-if="['object'].includes(_value.type)" :rules="[]">
|
||||||
<JsonParam v-model:value="_value.jsonConfig" :name="name.concat(['properties'])"></JsonParam>
|
<JsonParam v-model:value="_value.properties" :name="name.concat(['properties'])"></JsonParam>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item label="文件类型" :name="name.concat(['fileType'])" v-if="['file'].includes(_value.type)" initialValue="url"
|
<a-form-item label="文件类型" :name="name.concat(['fileType'])" v-if="['file'].includes(_value.type)" initialValue="url"
|
||||||
:rules="[
|
:rules="[
|
||||||
|
@ -68,7 +68,7 @@ const props = defineProps({
|
||||||
default: false
|
default: false
|
||||||
},
|
},
|
||||||
name: {
|
name: {
|
||||||
type: Array as PropType<string[]>,
|
type: Array as PropType<(string | number)[]>,
|
||||||
default: () => ([]),
|
default: () => ([]),
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
<a-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</a-button>
|
<a-button :loading="save.loading" type="primary" @click="save.saveMetadata">保存</a-button>
|
||||||
</template>
|
</template>
|
||||||
<a-form ref="formRef" :model="form.model" layout="vertical">
|
<a-form ref="formRef" :model="form.model" layout="vertical">
|
||||||
<PropertyForm :model-type="metadataStore.model.type" :type="type" ref="propertyForm" v-model:value="form.model"></PropertyForm>
|
<BaseForm :model-type="metadataStore.model.type" :type="type" v-model:value="form.model"></BaseForm>
|
||||||
</a-form>
|
</a-form>
|
||||||
</a-drawer>
|
</a-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
@ -20,7 +20,7 @@ import { updateMetadata, asyncUpdateMetadata } from '../../metadata'
|
||||||
import { Store } from 'jetlinks-store';
|
import { Store } from 'jetlinks-store';
|
||||||
import { detail } from '@/api/device/instance';
|
import { detail } from '@/api/device/instance';
|
||||||
import { DeviceInstance } from '@/views/device/Instance/typings';
|
import { DeviceInstance } from '@/views/device/Instance/typings';
|
||||||
import PropertyForm from './PropertyForm.vue';
|
import BaseForm from './BaseForm.vue';
|
||||||
import { PropType } from 'vue';
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
@ -56,8 +56,6 @@ const close = () => {
|
||||||
|
|
||||||
const title = computed(() => metadataStore.model.action === 'add' ? '新增' : '修改')
|
const title = computed(() => metadataStore.model.action === 'add' ? '新增' : '修改')
|
||||||
|
|
||||||
const propertyForm = ref()
|
|
||||||
|
|
||||||
const form = reactive({
|
const form = reactive({
|
||||||
model: {} as any,
|
model: {} as any,
|
||||||
})
|
})
|
||||||
|
@ -128,6 +126,7 @@ const save = reactive({
|
||||||
}
|
}
|
||||||
save.loading = false
|
save.loading = false
|
||||||
})
|
})
|
||||||
|
save.loading = false
|
||||||
},
|
},
|
||||||
resetMetadata: async () => {
|
resetMetadata: async () => {
|
||||||
const { id } = route.params
|
const { id } = route.params
|
||||||
|
|
|
@ -122,7 +122,7 @@ watch(
|
||||||
)
|
)
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
[props.visible, props.type],
|
() => [props.visible, props.type],
|
||||||
() => {
|
() => {
|
||||||
if (props.visible) {
|
if (props.visible) {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
|
@ -136,7 +136,7 @@ watch(
|
||||||
} else {
|
} else {
|
||||||
productDetail(id as string).then((resp) => {
|
productDetail(id as string).then((resp) => {
|
||||||
loading.value = false
|
loading.value = false
|
||||||
// productStore.setCurrent(resp.result)
|
productStore.setCurrent(resp.result)
|
||||||
value.value = resp.result.metadata
|
value.value = resp.result.metadata
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,3 +133,18 @@ export const DateTypeList = [
|
||||||
// value: 'yyyy-MM-dd HH:mm:ss zzz',
|
// value: 'yyyy-MM-dd HH:mm:ss zzz',
|
||||||
// },
|
// },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const ExpandsTypeList = [
|
||||||
|
{
|
||||||
|
label: '读',
|
||||||
|
value: 'read',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '写',
|
||||||
|
value: 'write',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '上报',
|
||||||
|
value: 'report',
|
||||||
|
},
|
||||||
|
]
|
|
@ -9,8 +9,10 @@
|
||||||
></Provider>
|
></Provider>
|
||||||
</div>
|
</div>
|
||||||
<div v-else>
|
<div v-else>
|
||||||
<div v-if="!id"><a @click="goBack">返回</a></div>
|
<div class="go-back" v-if="id === ':id'">
|
||||||
<AccessNetwork
|
<a @click="goBack">返回</a>
|
||||||
|
</div>
|
||||||
|
<Network
|
||||||
v-if="showType === 'network'"
|
v-if="showType === 'network'"
|
||||||
:provider="provider"
|
:provider="provider"
|
||||||
:data="data"
|
:data="data"
|
||||||
|
@ -42,18 +44,15 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessConfigDetail">
|
<script lang="ts" setup name="AccessConfigDetail">
|
||||||
import { getImage } from '@/utils/comm';
|
import Network from '../components/Network/index.vue';
|
||||||
import AccessNetwork from '../components/Network.vue';
|
|
||||||
import Provider from '../components/Provider/index.vue';
|
import Provider from '../components/Provider/index.vue';
|
||||||
import { getProviders, detail } from '@/api/link/accessConfig';
|
|
||||||
import Media from '../components/Media/index.vue';
|
import Media from '../components/Media/index.vue';
|
||||||
import Channel from '../components/Channel/index.vue';
|
import Channel from '../components/Channel/index.vue';
|
||||||
import Edge from '../components/Edge/index.vue';
|
import Edge from '../components/Edge/index.vue';
|
||||||
import Cloud from '../components/Cloud/index.vue';
|
import Cloud from '../components/Cloud/index.vue';
|
||||||
|
import { getProviders, detail } from '@/api/link/accessConfig';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
|
|
||||||
const view = route.query.view as string;
|
|
||||||
const id = route.params.id as string;
|
const id = route.params.id as string;
|
||||||
|
|
||||||
const dataSource = ref([]);
|
const dataSource = ref([]);
|
||||||
|
@ -74,7 +73,7 @@ const goBack = () => {
|
||||||
type.value = true;
|
type.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
const getTypeList = (result: any[]) => {
|
const getTypeList = (result: Record<string, any>) => {
|
||||||
const list = [];
|
const list = [];
|
||||||
const media: any[] = [];
|
const media: any[] = [];
|
||||||
const network: any[] = [];
|
const network: any[] = [];
|
||||||
|
@ -184,76 +183,7 @@ onMounted(() => {
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.provider {
|
.go-back {
|
||||||
position: relative;
|
margin: 0 0 20px 0;
|
||||||
width: 100%;
|
|
||||||
padding: 20px;
|
|
||||||
background: url('/public/images/access/background.png') no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
border: 1px solid #e6e6e6;
|
|
||||||
|
|
||||||
&::before {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: 40px;
|
|
||||||
display: block;
|
|
||||||
width: 15%;
|
|
||||||
min-width: 64px;
|
|
||||||
height: 2px;
|
|
||||||
background-image: url('/public/images/access/rectangle.png');
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
background-size: 100% 100%;
|
|
||||||
// border: 1px #8da1f4 solid;
|
|
||||||
// border-bottom-left-radius: 10%;
|
|
||||||
// border-bottom-right-radius: 10%;
|
|
||||||
content: ' ';
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
box-shadow: 0 0 24px rgba(#000, 0.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.box {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.left {
|
|
||||||
display: flex;
|
|
||||||
width: calc(100% - 70px);
|
|
||||||
.images {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.context {
|
|
||||||
width: calc(100% - 84px);
|
|
||||||
margin: 10px;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.desc {
|
|
||||||
width: 100%;
|
|
||||||
margin-top: 10px;
|
|
||||||
overflow: hidden;
|
|
||||||
color: rgba(0, 0, 0, 0.55);
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 13px;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.right {
|
|
||||||
width: 70px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -39,12 +39,16 @@
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button
|
<PermissionButton
|
||||||
v-if="view === 'false'"
|
v-if="view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
>保存</a-button
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -86,10 +90,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessChannel">
|
<script lang="ts" setup name="AccessChannel">
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
|
||||||
import { update, save } from '@/api/link/accessConfig';
|
import { update, save } from '@/api/link/accessConfig';
|
||||||
import { ProtocolMapping } from '../../Detail/data';
|
import { ProtocolMapping } from '../../data';
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -129,16 +132,7 @@ const onFinish = async (values: any) => {
|
||||||
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();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -164,8 +158,6 @@ onMounted(() => {
|
||||||
}
|
}
|
||||||
.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;
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="steps-content">
|
<div class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 0">
|
<div class="steps-box" v-if="current === 0">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<info-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
通过CTWing平台的HTTP推送服务进行数据接入
|
通过CTWing平台的HTTP推送服务进行数据接入
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 15px">
|
<div style="margin-top: 15px">
|
||||||
|
@ -160,7 +160,7 @@
|
||||||
<div class="steps-content">
|
<div class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 1">
|
<div class="steps-box" v-if="current === 1">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<info-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
只能选择HTTP通信方式的协议
|
只能选择HTTP通信方式的协议
|
||||||
</div>
|
</div>
|
||||||
<div class="search">
|
<div class="search">
|
||||||
|
@ -170,9 +170,14 @@
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
@search="procotolSearch"
|
@search="procotolSearch"
|
||||||
/>
|
/>
|
||||||
<a-button type="primary" @click="addProcotol"
|
<PermissionButton
|
||||||
>新增</a-button
|
type="primary"
|
||||||
|
@click="addProcotol"
|
||||||
|
hasPermission="link/Protocol:add"
|
||||||
>
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-item">
|
<div class="card-item">
|
||||||
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
||||||
|
@ -282,96 +287,39 @@
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<PermissionButton
|
||||||
v-if="current === 2 && view === 'false'"
|
v-if="current === 2 && view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
style="margin-right: 8px"
|
style="margin-right: 8px"
|
||||||
@click="saveData"
|
@click="saveData"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessCloudCtwing">
|
<script lang="ts" setup name="AccessCloudCtwing">
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import { update, save, getNetworkList } from '@/api/link/accessConfig';
|
import { update, save, getProtocolList } from '@/api/link/accessConfig';
|
||||||
import { ProtocolMapping, NetworkTypeMapping } from '../../Detail/data';
|
import { ProtocolMapping } from '../../data';
|
||||||
import { InfoCircleOutlined } from '@ant-design/icons-vue';
|
|
||||||
import AccessCard from '../AccessCard/index.vue';
|
import AccessCard from '../AccessCard/index.vue';
|
||||||
import { randomString } from '@/utils/utils';
|
import { randomString } from '@/utils/utils';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
const img1 = getImage('/network/01.png');
|
const img1 = getImage('/network/01.png');
|
||||||
const img2 = getImage('/network/02.jpg');
|
const img2 = getImage('/network/02.jpg');
|
||||||
const img3 = getImage('/network/03.png');
|
const img3 = getImage('/network/03.png');
|
||||||
const img4 = getImage('/network/04.jpg');
|
const img4 = getImage('/network/04.jpg');
|
||||||
|
|
||||||
//测试数据1{
|
|
||||||
const resultList1 = [
|
|
||||||
{
|
|
||||||
id: '1612354213444087808',
|
|
||||||
name: '大华烟感协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610475299002855424',
|
|
||||||
name: '宇视摄像头协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610466717670780928',
|
|
||||||
name: '官方协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610205217785524224',
|
|
||||||
name: 'demo协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610204985806958592',
|
|
||||||
name: '水压协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1605459961693745152',
|
|
||||||
name: '测试设备诊断日志显示',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1582302200020783104',
|
|
||||||
name: 'demo',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1581839391887794176',
|
|
||||||
name: '海康闸机协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1567062365030637568',
|
|
||||||
name: '协议20220906160914',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1561650927208628224',
|
|
||||||
name: 'local',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1552881998413754368',
|
|
||||||
name: '官方协议V3-支持固件升级3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2b283b28a16d61e5fc2bdf39ceff34f8',
|
|
||||||
name: 'JetLinks官方协议',
|
|
||||||
description: 'JetLinks官方协议包',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1551510679466844160',
|
|
||||||
name: '官方协议3.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1551509716811161600',
|
|
||||||
name: '官方协议3.0',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
apiAddress: string;
|
apiAddress: string;
|
||||||
appKey: string;
|
appKey: string;
|
||||||
|
@ -397,7 +345,6 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const channel = ref(props.provider.channel);
|
|
||||||
const formRef1 = ref<FormInstance>();
|
const formRef1 = ref<FormInstance>();
|
||||||
const formRef2 = ref<FormInstance>();
|
const formRef2 = ref<FormInstance>();
|
||||||
|
|
||||||
|
@ -459,37 +406,24 @@ const saveData = async () => {
|
||||||
});
|
});
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
// 回到列表页面
|
|
||||||
// if (window.onTabSaveSuccess) {
|
|
||||||
// window.onTabSaveSuccess(resp);
|
|
||||||
// setTimeout(() => window.close(), 300);
|
|
||||||
// } else {
|
|
||||||
// // this.$store.dispatch('jumpPathByKey', { key: MenuKeys['Link/AccessConfig'] })
|
|
||||||
// }
|
|
||||||
history.back();
|
history.back();
|
||||||
}
|
}
|
||||||
// onFinish(data);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryProcotolList = async (id: string, params = {}) => {
|
const queryProcotolList = async (id: string, params = {}) => {
|
||||||
// const resp = await getProtocolList(ProtocolMapping.get(id), {
|
const resp = await getProtocolList(ProtocolMapping.get(id), {
|
||||||
// ...params,
|
...params,
|
||||||
// 'sorts[0].name': 'createTime',
|
'sorts[0].name': 'createTime',
|
||||||
// 'sorts[0].order': 'desc',
|
'sorts[0].order': 'desc',
|
||||||
// });
|
});
|
||||||
// if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
// procotolList.value = resp.result;
|
procotolList.value = resp.result;
|
||||||
// allProcotolList.value = resp.result;
|
allProcotolList.value = resp.result;
|
||||||
// }
|
}
|
||||||
|
|
||||||
//使用测试数据1
|
|
||||||
procotolList.value = resultList1;
|
|
||||||
allProcotolList.value = resultList1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addProcotol = () => {
|
const addProcotol = () => {
|
||||||
// const url = this.$store.state.permission.routes['Link/Protocol']
|
const url = menuStory.menus['link/Protocol']?.path;
|
||||||
const url = '/iot/link/protocol';
|
|
||||||
const tab = window.open(
|
const tab = window.open(
|
||||||
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
||||||
);
|
);
|
||||||
|
@ -503,7 +437,7 @@ const addProcotol = () => {
|
||||||
|
|
||||||
const next = async () => {
|
const next = async () => {
|
||||||
if (current.value === 0) {
|
if (current.value === 0) {
|
||||||
let data1: any = await formRef1.value?.validate();
|
await formRef1.value?.validate();
|
||||||
queryProcotolList(props.provider.id);
|
queryProcotolList(props.provider.id);
|
||||||
current.value = current.value + 1;
|
current.value = current.value + 1;
|
||||||
} else if (current.value === 1) {
|
} else if (current.value === 1) {
|
||||||
|
@ -514,9 +448,11 @@ const next = async () => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const prev = () => {
|
const prev = () => {
|
||||||
current.value = current.value - 1;
|
current.value = current.value - 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
if (id !== ':id') {
|
if (id !== ':id') {
|
||||||
formState.value = props.data.configuration;
|
formState.value = props.data.configuration;
|
||||||
|
@ -527,6 +463,7 @@ onMounted(() => {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
current,
|
current,
|
||||||
(v) => {
|
(v) => {
|
||||||
|
@ -590,9 +527,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;
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="steps-content">
|
<div class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 0">
|
<div class="steps-box" v-if="current === 0">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<info-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
通过OneNet平台的HTTP推送服务进行数据接入
|
通过OneNet平台的HTTP推送服务进行数据接入
|
||||||
</div>
|
</div>
|
||||||
<div style="margin-top: 15px">
|
<div style="margin-top: 15px">
|
||||||
|
@ -41,7 +41,9 @@
|
||||||
同步物联网平台设备数据到OneNet
|
同步物联网平台设备数据到OneNet
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -105,7 +107,9 @@
|
||||||
接收OneNet推送的Token地址
|
接收OneNet推送的Token地址
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -136,7 +140,9 @@
|
||||||
端生成的消息加密key
|
端生成的消息加密key
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -253,7 +259,7 @@
|
||||||
<div class="steps-content">
|
<div class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 1">
|
<div class="steps-box" v-if="current === 1">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<info-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
只能选择HTTP通信方式的协议
|
只能选择HTTP通信方式的协议
|
||||||
</div>
|
</div>
|
||||||
<div class="search">
|
<div class="search">
|
||||||
|
@ -263,9 +269,14 @@
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
@search="procotolSearch"
|
@search="procotolSearch"
|
||||||
/>
|
/>
|
||||||
<a-button type="primary" @click="addProcotol"
|
<PermissionButton
|
||||||
>新增</a-button
|
type="primary"
|
||||||
|
@click="addProcotol"
|
||||||
|
hasPermission="link/Protocol:add"
|
||||||
>
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-item">
|
<div class="card-item">
|
||||||
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
||||||
|
@ -375,98 +386,38 @@
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<PermissionButton
|
||||||
style="margin-right: 8px"
|
style="margin-right: 8px"
|
||||||
v-if="current === 2 && view === 'false'"
|
v-if="current === 2 && view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
@click="saveData"
|
@click="saveData"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessCloudOneNet">
|
<script lang="ts" setup name="AccessCloudOneNet">
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import { update, save, getNetworkList } from '@/api/link/accessConfig';
|
import { update, save, getProtocolList } from '@/api/link/accessConfig';
|
||||||
import { ProtocolMapping, NetworkTypeMapping } from '../../Detail/data';
|
|
||||||
import {
|
|
||||||
InfoCircleOutlined,
|
|
||||||
QuestionCircleOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import AccessCard from '../AccessCard/index.vue';
|
import AccessCard from '../AccessCard/index.vue';
|
||||||
import { randomString } from '@/utils/utils';
|
import { randomString } from '@/utils/utils';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { ProtocolMapping } from '../../data';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
const origin = window.location.origin;
|
const origin = window.location.origin;
|
||||||
const img5 = getImage('/network/05.jpg');
|
const img5 = getImage('/network/05.jpg');
|
||||||
const img6 = getImage('/network/06.jpg');
|
const img6 = getImage('/network/06.jpg');
|
||||||
const img = getImage('/network/OneNet.jpg');
|
const img = getImage('/network/OneNet.jpg');
|
||||||
|
|
||||||
//测试数据1{
|
|
||||||
const resultList1 = [
|
|
||||||
{
|
|
||||||
id: '1612354213444087808',
|
|
||||||
name: '大华烟感协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610475299002855424',
|
|
||||||
name: '宇视摄像头协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610466717670780928',
|
|
||||||
name: '官方协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610205217785524224',
|
|
||||||
name: 'demo协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1610204985806958592',
|
|
||||||
name: '水压协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1605459961693745152',
|
|
||||||
name: '测试设备诊断日志显示',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1582302200020783104',
|
|
||||||
name: 'demo',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1581839391887794176',
|
|
||||||
name: '海康闸机协议',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1567062365030637568',
|
|
||||||
name: '协议20220906160914',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1561650927208628224',
|
|
||||||
name: 'local',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1552881998413754368',
|
|
||||||
name: '官方协议V3-支持固件升级3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '2b283b28a16d61e5fc2bdf39ceff34f8',
|
|
||||||
name: 'JetLinks官方协议',
|
|
||||||
description: 'JetLinks官方协议包',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1551510679466844160',
|
|
||||||
name: '官方协议3.1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1551509716811161600',
|
|
||||||
name: '官方协议3.0',
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
apiAddress: string;
|
apiAddress: string;
|
||||||
apiKey: string;
|
apiKey: string;
|
||||||
|
@ -478,9 +429,6 @@ interface Form {
|
||||||
name: string;
|
name: string;
|
||||||
description: string;
|
description: string;
|
||||||
}
|
}
|
||||||
const route = useRoute();
|
|
||||||
const view = route.query.view as string;
|
|
||||||
const id = route.params.id as string;
|
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
provider: {
|
provider: {
|
||||||
|
@ -493,7 +441,10 @@ const props = defineProps({
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const channel = ref(props.provider.channel);
|
const route = useRoute();
|
||||||
|
const view = route.query.view as string;
|
||||||
|
const id = route.params.id as string;
|
||||||
|
|
||||||
const formRef1 = ref<FormInstance>();
|
const formRef1 = ref<FormInstance>();
|
||||||
const formRef2 = ref<FormInstance>();
|
const formRef2 = ref<FormInstance>();
|
||||||
|
|
||||||
|
@ -557,36 +508,24 @@ const saveData = async () => {
|
||||||
|
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
// 回到列表页面
|
|
||||||
// if (window.onTabSaveSuccess) {
|
|
||||||
// window.onTabSaveSuccess(resp);
|
|
||||||
// setTimeout(() => window.close(), 300);
|
|
||||||
// } else {
|
|
||||||
// // this.$store.dispatch('jumpPathByKey', { key: MenuKeys['Link/AccessConfig'] })
|
|
||||||
// }
|
|
||||||
history.back();
|
history.back();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryProcotolList = async (id: string, params = {}) => {
|
const queryProcotolList = async (id: string, params = {}) => {
|
||||||
// const resp = await getProtocolList(ProtocolMapping.get(id), {
|
const resp = await getProtocolList(ProtocolMapping.get(id), {
|
||||||
// ...params,
|
...params,
|
||||||
// 'sorts[0].name': 'createTime',
|
'sorts[0].name': 'createTime',
|
||||||
// 'sorts[0].order': 'desc',
|
'sorts[0].order': 'desc',
|
||||||
// });
|
});
|
||||||
// if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
// procotolList.value = resp.result;
|
procotolList.value = resp.result;
|
||||||
// allProcotolList.value = resp.result;
|
allProcotolList.value = resp.result;
|
||||||
// }
|
}
|
||||||
|
|
||||||
//使用测试数据1
|
|
||||||
procotolList.value = resultList1;
|
|
||||||
allProcotolList.value = resultList1;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const addProcotol = () => {
|
const addProcotol = () => {
|
||||||
// const url = this.$store.state.permission.routes['Link/Protocol']
|
const url = menuStory.menus['link/Protocol']?.path;
|
||||||
const url = '/iot/link/protocol';
|
|
||||||
const tab = window.open(
|
const tab = window.open(
|
||||||
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
||||||
);
|
);
|
||||||
|
@ -689,9 +628,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;
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
<div v-if="channel !== 'edge-child-device'" class="steps-content">
|
<div v-if="channel !== 'edge-child-device'" class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 0">
|
<div class="steps-box" v-if="current === 0">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<question-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
选择与设备通信的网络组件
|
选择与设备通信的网络组件
|
||||||
</div>
|
</div>
|
||||||
<div class="search">
|
<div class="search">
|
||||||
|
@ -20,7 +20,14 @@
|
||||||
style="width: 300px"
|
style="width: 300px"
|
||||||
@search="networkSearch"
|
@search="networkSearch"
|
||||||
/>
|
/>
|
||||||
<a-button type="primary" @click="addNetwork">新增</a-button>
|
<PermissionButton
|
||||||
|
type="primary"
|
||||||
|
@click="addNetwork"
|
||||||
|
hasPermission="link/Type:add"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
</div>
|
</div>
|
||||||
<div class="card-item">
|
<div class="card-item">
|
||||||
<a-row :gutter="[24, 24]" v-if="networkList.length > 0">
|
<a-row :gutter="[24, 24]" v-if="networkList.length > 0">
|
||||||
|
@ -103,51 +110,53 @@
|
||||||
<a-row :gutter="[24, 24]">
|
<a-row :gutter="[24, 24]">
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<title-component data="基本信息" />
|
<title-component data="基本信息" />
|
||||||
<div>
|
<a-form
|
||||||
<a-form
|
:model="formState"
|
||||||
:model="formState"
|
name="basic"
|
||||||
name="basic"
|
autocomplete="off"
|
||||||
autocomplete="off"
|
layout="vertical"
|
||||||
layout="vertical"
|
@finish="onFinish"
|
||||||
@finish="onFinish"
|
ref="formRef"
|
||||||
ref="formRef"
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
trigger: 'blur',
|
||||||
|
},
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
]"
|
||||||
>
|
>
|
||||||
<a-form-item
|
<a-input
|
||||||
label="名称"
|
placeholder="请输入名称"
|
||||||
name="name"
|
v-model:value="formState.name"
|
||||||
:rules="[
|
/>
|
||||||
{
|
</a-form-item>
|
||||||
required: true,
|
<a-form-item label="说明" name="description">
|
||||||
message: '请输入名称',
|
<a-textarea
|
||||||
trigger: 'blur',
|
placeholder="请输入说明"
|
||||||
},
|
:rows="4"
|
||||||
{ max: 64, message: '最多可输入64个字符' },
|
v-model:value="formState.description"
|
||||||
]"
|
show-count
|
||||||
|
:maxlength="200"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item>
|
||||||
|
<PermissionButton
|
||||||
|
v-if="current !== 1 && view === 'false'"
|
||||||
|
type="primary"
|
||||||
|
html-type="submit"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
<a-input
|
保存
|
||||||
placeholder="请输入名称"
|
</PermissionButton>
|
||||||
v-model:value="formState.name"
|
</a-form-item>
|
||||||
/>
|
</a-form>
|
||||||
</a-form-item>
|
|
||||||
<a-form-item label="说明" name="description">
|
|
||||||
<a-textarea
|
|
||||||
placeholder="请输入说明"
|
|
||||||
:rows="4"
|
|
||||||
v-model:value="formState.description"
|
|
||||||
show-count
|
|
||||||
:maxlength="200"
|
|
||||||
/>
|
|
||||||
</a-form-item>
|
|
||||||
<a-form-item>
|
|
||||||
<a-button
|
|
||||||
v-if="current !== 1 && view === 'false'"
|
|
||||||
type="primary"
|
|
||||||
html-type="submit"
|
|
||||||
>保存</a-button
|
|
||||||
>
|
|
||||||
</a-form-item>
|
|
||||||
</a-form>
|
|
||||||
</div>
|
|
||||||
</a-col>
|
</a-col>
|
||||||
<a-col :span="12">
|
<a-col :span="12">
|
||||||
<div class="config-right">
|
<div class="config-right">
|
||||||
|
@ -178,119 +187,35 @@
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<PermissionButton
|
||||||
v-if="current === 1 && view === 'false'"
|
v-if="current === 1 && view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
style="margin-right: 8px"
|
style="margin-right: 8px"
|
||||||
@click="saveData"
|
@click="saveData"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessEdge">
|
<script lang="ts" setup name="AccessEdge">
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import { update, save, getNetworkList } from '@/api/link/accessConfig';
|
import { update, save, getNetworkList } from '@/api/link/accessConfig';
|
||||||
import {
|
import {
|
||||||
descriptionList,
|
descriptionList,
|
||||||
ProtocolMapping,
|
ProtocolMapping,
|
||||||
NetworkTypeMapping,
|
NetworkTypeMapping,
|
||||||
} from '../../Detail/data';
|
} from '../../data';
|
||||||
import { QuestionCircleOutlined } from '@ant-design/icons-vue';
|
|
||||||
import AccessCard from '../AccessCard/index.vue';
|
import AccessCard from '../AccessCard/index.vue';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
|
||||||
//测试数据1
|
const menuStory = useMenuStore();
|
||||||
const networkListTest = {
|
|
||||||
message: 'success',
|
|
||||||
result: [
|
|
||||||
{
|
|
||||||
id: '1585192878304051200',
|
|
||||||
name: 'MQTT网络组件',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://120.77.179.54:8101',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1583268266806009856',
|
|
||||||
name: '我的第一个MQTT服务组件',
|
|
||||||
description: '',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://120.77.179.54:8100',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1570335308902912000',
|
|
||||||
name: '0915MQTT网络组件_勿动',
|
|
||||||
description: '测试,勿动!',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://120.77.179.54:8083',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1567062350140858368',
|
|
||||||
name: '网络组件20220906160907',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://120.77.179.54:8083',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1556563257890742272',
|
|
||||||
name: 'MQTT网络组件',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://0.0.0.0:8104',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: '1534774770408108032',
|
|
||||||
name: 'MQTT',
|
|
||||||
addresses: [
|
|
||||||
{
|
|
||||||
address: 'mqtt://120.77.179.54:8088',
|
|
||||||
health: 1,
|
|
||||||
ok: true,
|
|
||||||
bad: false,
|
|
||||||
disabled: false,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
status: 200,
|
|
||||||
timestamp: 1674960624150,
|
|
||||||
};
|
|
||||||
|
|
||||||
interface FormState {
|
interface FormState {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -327,6 +252,7 @@ const stepCurrent = ref(0);
|
||||||
const steps = ref(['网络组件', '完成']);
|
const steps = ref(['网络组件', '完成']);
|
||||||
const networkCurrent = ref('');
|
const networkCurrent = ref('');
|
||||||
const networkList = ref([]);
|
const networkList = ref([]);
|
||||||
|
const allNetworkList = ref([]);
|
||||||
|
|
||||||
const onFinish = async (values: any) => {
|
const onFinish = async (values: any) => {
|
||||||
const providerId = props.provider.id;
|
const providerId = props.provider.id;
|
||||||
|
@ -341,16 +267,7 @@ const onFinish = async (values: any) => {
|
||||||
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();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -359,28 +276,27 @@ const checkedChange = (id: string) => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const queryNetworkList = async (id: string, include: string, data = {}) => {
|
const queryNetworkList = async (id: string, include: string, data = {}) => {
|
||||||
// const resp = await getNetworkList(
|
const resp = await getNetworkList(
|
||||||
// NetworkTypeMapping.get(id),
|
NetworkTypeMapping.get(id),
|
||||||
// include,
|
include,
|
||||||
// data,
|
data,
|
||||||
// );
|
);
|
||||||
// if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
// networkList.value = resp.result;
|
networkList.value = resp.result;
|
||||||
// }
|
allNetworkList.value = resp.result;
|
||||||
|
}
|
||||||
//使用测试数据1
|
|
||||||
networkList.value = networkListTest.result;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const networkSearch = (value: string) => {
|
const networkSearch = (value: string) => {
|
||||||
queryNetworkList(props.provider.id, networkCurrent.value || '', {
|
if (value) {
|
||||||
terms: [
|
networkList.value = allNetworkList.value.filter(
|
||||||
{
|
(i: any) =>
|
||||||
column: 'name$LIKE',
|
i.name &&
|
||||||
value: `%${value}%`,
|
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase()),
|
||||||
},
|
);
|
||||||
],
|
} else {
|
||||||
});
|
networkList.value = allNetworkList.value;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const saveData = async () => {
|
const saveData = async () => {
|
||||||
|
@ -389,8 +305,7 @@ const saveData = async () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const addNetwork = () => {
|
const addNetwork = () => {
|
||||||
// const url = this.$store.state.permission.routes['Link/Type/Detail']
|
const url = menuStory.menus['link/Type/Detail']?.path;
|
||||||
const url = '/iot/link/type/detail/:id';
|
|
||||||
const tab = window.open(
|
const tab = 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) || ''
|
||||||
|
@ -426,17 +341,17 @@ onMounted(() => {
|
||||||
};
|
};
|
||||||
networkCurrent.value = props.data.channelId;
|
networkCurrent.value = props.data.channelId;
|
||||||
}
|
}
|
||||||
}),
|
});
|
||||||
watch(
|
watch(
|
||||||
current,
|
current,
|
||||||
(v) => {
|
(v) => {
|
||||||
stepCurrent.value = v;
|
stepCurrent.value = v;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
deep: true,
|
deep: true,
|
||||||
immediate: true,
|
immediate: true,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
<div class="steps-content">
|
<div class="steps-content">
|
||||||
<div class="steps-box" v-if="current === 0">
|
<div class="steps-box" v-if="current === 0">
|
||||||
<div class="alert">
|
<div class="alert">
|
||||||
<info-circle-outlined />
|
<AIcon type="InfoCircleOutlined" />
|
||||||
配置设备信令参数
|
配置设备信令参数
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
独立配置:集群下不同节点使用不同配置
|
独立配置:集群下不同节点使用不同配置
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon type="QuestionCircleOutlined" />
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -218,9 +218,7 @@
|
||||||
:header="`#${index + 1}.节点`"
|
:header="`#${index + 1}.节点`"
|
||||||
>
|
>
|
||||||
<template #extra>
|
<template #extra>
|
||||||
<delete-outlined
|
<AIcon type="DeleteOutlined" />
|
||||||
@click="removeCluster(cluster)"
|
|
||||||
/>
|
|
||||||
</template>
|
</template>
|
||||||
<a-row :gutter="[24, 24]">
|
<a-row :gutter="[24, 24]">
|
||||||
<a-col :span="8">
|
<a-col :span="8">
|
||||||
|
@ -274,7 +272,9 @@
|
||||||
绑定到服务器上的网卡地址,绑定到所有网卡:0.0.0.0
|
绑定到服务器上的网卡地址,绑定到所有网卡:0.0.0.0
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -363,7 +363,9 @@
|
||||||
监听指定端口的请求
|
监听指定端口的请求
|
||||||
</p>
|
</p>
|
||||||
</template>
|
</template>
|
||||||
<question-circle-outlined />
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
/>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
<a-input
|
<a-input
|
||||||
|
@ -417,7 +419,7 @@
|
||||||
block
|
block
|
||||||
@click="addCluster"
|
@click="addCluster"
|
||||||
>
|
>
|
||||||
<PlusOutlined />
|
<AIcon type="PlusOutlined" />
|
||||||
新增
|
新增
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
|
@ -502,14 +504,17 @@
|
||||||
>
|
>
|
||||||
下一步
|
下一步
|
||||||
</a-button>
|
</a-button>
|
||||||
<a-button
|
<PermissionButton
|
||||||
v-if="current === 1 && view === 'false'"
|
v-if="current === 1 && view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
style="margin-right: 8px"
|
style="margin-right: 8px"
|
||||||
@click="saveData"
|
@click="saveData"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
保存
|
保存
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
<a-button v-if="current > 0" @click="prev"> 上一步 </a-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -519,12 +524,6 @@
|
||||||
import { message, Form } from 'ant-design-vue';
|
import { message, Form } from 'ant-design-vue';
|
||||||
import type { FormInstance } from 'ant-design-vue';
|
import type { FormInstance } from 'ant-design-vue';
|
||||||
import { getResourcesCurrent, getClusters } from '@/api/link/accessConfig';
|
import { getResourcesCurrent, getClusters } from '@/api/link/accessConfig';
|
||||||
import {
|
|
||||||
DeleteOutlined,
|
|
||||||
PlusOutlined,
|
|
||||||
QuestionCircleOutlined,
|
|
||||||
InfoCircleOutlined,
|
|
||||||
} from '@ant-design/icons-vue';
|
|
||||||
import { update, save } from '@/api/link/accessConfig';
|
import { update, save } from '@/api/link/accessConfig';
|
||||||
|
|
||||||
interface Form2 {
|
interface Form2 {
|
||||||
|
|
|
@ -39,12 +39,16 @@
|
||||||
/>
|
/>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button
|
<PermissionButton
|
||||||
v-if="view === 'false'"
|
v-if="view === 'false'"
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
>保存</a-button
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
>
|
>
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
</div>
|
</div>
|
||||||
|
@ -122,16 +126,7 @@ const onFinish = async (values: any) => {
|
||||||
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();
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,701 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-steps :current="stepCurrent">
|
||||||
|
<a-step v-for="item in steps" :key="item" :title="item" />
|
||||||
|
</a-steps>
|
||||||
|
<div class="steps-content">
|
||||||
|
<div class="steps-box" v-if="current === 0">
|
||||||
|
<div class="alert">
|
||||||
|
<AIcon type="InfoCircleOutlined" />
|
||||||
|
选择与设备通信的网络组件
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<a-input-search
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入"
|
||||||
|
style="width: 300px"
|
||||||
|
@search="networkSearch"
|
||||||
|
/>
|
||||||
|
<PermissionButton
|
||||||
|
type="primary"
|
||||||
|
@click="addNetwork"
|
||||||
|
hasPermission="link/Type:add"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
</div>
|
||||||
|
<div class="card-item">
|
||||||
|
<a-row :gutter="[24, 24]" v-if="networkList.length > 0">
|
||||||
|
<a-col
|
||||||
|
:span="8"
|
||||||
|
v-for="item in networkList"
|
||||||
|
:key="item.id"
|
||||||
|
>
|
||||||
|
<access-card
|
||||||
|
@checkedChange="checkedChange"
|
||||||
|
:checked="networkCurrent"
|
||||||
|
:data="{
|
||||||
|
...item,
|
||||||
|
description: item.description
|
||||||
|
? item.description
|
||||||
|
: descriptionList[provider.id],
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #other>
|
||||||
|
<div class="other">
|
||||||
|
<a-tooltip placement="topLeft">
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
|
(item.addresses || [])
|
||||||
|
.length > 1
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="i in item.addresses ||
|
||||||
|
[]"
|
||||||
|
:key="i.address"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<a-badge
|
||||||
|
:color="getColor(i)"
|
||||||
|
/>{{ i.address }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
v-for="i in (
|
||||||
|
item.addresses || []
|
||||||
|
).slice(0, 1)"
|
||||||
|
:key="i.address"
|
||||||
|
class="item"
|
||||||
|
>
|
||||||
|
<a-badge
|
||||||
|
:color="getColor(i)"
|
||||||
|
:text="i.address"
|
||||||
|
/>
|
||||||
|
<span
|
||||||
|
v-if="
|
||||||
|
(item.addresses || [])
|
||||||
|
.length > 1
|
||||||
|
"
|
||||||
|
>...</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</a-tooltip>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</access-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-box" v-else-if="current === 1">
|
||||||
|
<div class="alert">
|
||||||
|
<AIcon type="InfoCircleOutlined" />
|
||||||
|
使用选择的消息协议,对网络组件通信数据进行编解码、认证等操作
|
||||||
|
</div>
|
||||||
|
<div class="search">
|
||||||
|
<a-input-search
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入"
|
||||||
|
style="width: 300px"
|
||||||
|
@search="procotolSearch"
|
||||||
|
/>
|
||||||
|
<PermissionButton
|
||||||
|
type="primary"
|
||||||
|
@click="addProcotol"
|
||||||
|
hasPermission="link/Protocol:add"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
</div>
|
||||||
|
<div class="card-item">
|
||||||
|
<a-row :gutter="[24, 24]" v-if="procotolList.length > 0">
|
||||||
|
<a-col
|
||||||
|
:span="8"
|
||||||
|
v-for="item in procotolList"
|
||||||
|
:key="item?.id"
|
||||||
|
>
|
||||||
|
<access-card
|
||||||
|
@checkedChange="procotolChange"
|
||||||
|
:checked="procotolCurrent"
|
||||||
|
:data="item"
|
||||||
|
>
|
||||||
|
</access-card>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
<a-empty v-else description="暂无数据" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-box" v-else>
|
||||||
|
<div
|
||||||
|
class="card-last"
|
||||||
|
:style="`max-height:${
|
||||||
|
clientHeight > 900 ? 750 : clientHeight * 0.7
|
||||||
|
}px`"
|
||||||
|
>
|
||||||
|
<a-row :gutter="[24, 24]">
|
||||||
|
<a-col :span="12">
|
||||||
|
<title-component data="基本信息" />
|
||||||
|
<div>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
v-bind="validateInfos.name"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
allowClear
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
<a-form-item
|
||||||
|
label="说明"
|
||||||
|
v-bind="validateInfos.description"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
placeholder="请输入说明"
|
||||||
|
:rows="4"
|
||||||
|
v-model:value="formData.description"
|
||||||
|
show-count
|
||||||
|
:maxlength="200"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<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">
|
||||||
|
<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"
|
||||||
|
>
|
||||||
|
<a-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>
|
||||||
|
<a-table
|
||||||
|
:pagination="false"
|
||||||
|
:rowKey="generateUUID()"
|
||||||
|
:data-source="config.routes || []"
|
||||||
|
bordered
|
||||||
|
:columns="
|
||||||
|
config.id === 'MQTT'
|
||||||
|
? columnsMQTT
|
||||||
|
: columnsHTTP
|
||||||
|
"
|
||||||
|
:scroll="{ y: 300 }"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
#bodyCell="{ column, text, record }"
|
||||||
|
>
|
||||||
|
<template
|
||||||
|
v-if="
|
||||||
|
column.dataIndex ===
|
||||||
|
'stream'
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>{{
|
||||||
|
getStream(record)
|
||||||
|
}}</span>
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="steps-action">
|
||||||
|
<a-button
|
||||||
|
v-if="[0, 1].includes(current)"
|
||||||
|
type="primary"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
@click="next"
|
||||||
|
>
|
||||||
|
下一步
|
||||||
|
</a-button>
|
||||||
|
<PermissionButton
|
||||||
|
v-if="current === 2 && view === 'false'"
|
||||||
|
type="primary"
|
||||||
|
style="margin-right: 8px"
|
||||||
|
@click="saveData"
|
||||||
|
:hasPermission="`link/AccessConfig:${
|
||||||
|
id === ':id' ? 'add' : 'update'
|
||||||
|
}`"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</PermissionButton>
|
||||||
|
<a-button
|
||||||
|
v-if="type === 'child-device' ? current > 1 : current > 0"
|
||||||
|
@click="prev"
|
||||||
|
>
|
||||||
|
上一步
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="AccessNetwork">
|
||||||
|
import {
|
||||||
|
getNetworkList,
|
||||||
|
getProtocolList,
|
||||||
|
getConfigView,
|
||||||
|
save,
|
||||||
|
update,
|
||||||
|
getChildConfigView,
|
||||||
|
} from '@/api/link/accessConfig';
|
||||||
|
import {
|
||||||
|
descriptionList,
|
||||||
|
NetworkTypeMapping,
|
||||||
|
ProtocolMapping,
|
||||||
|
ColumnsMQTT,
|
||||||
|
ColumnsHTTP,
|
||||||
|
} from '../../data';
|
||||||
|
import AccessCard from '../AccessCard/index.vue';
|
||||||
|
import { message, Form } from 'ant-design-vue';
|
||||||
|
import type { FormInstance, TableColumnType } from 'ant-design-vue';
|
||||||
|
import Markdown from 'vue3-markdown-it';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
function generateUUID() {
|
||||||
|
var d = new Date().getTime();
|
||||||
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
||||||
|
/[xy]/g,
|
||||||
|
function (c) {
|
||||||
|
var r = (d + Math.random() * 16) % 16 | 0;
|
||||||
|
d = Math.floor(d / 16);
|
||||||
|
return (c === 'x' ? r : (r & 0x3) | 0x8).toString(16);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
provider: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const clientHeight = document.body.clientHeight;
|
||||||
|
const type = props.provider.channel;
|
||||||
|
const route = useRoute();
|
||||||
|
const view = route.query.view as string;
|
||||||
|
const id = route.params.id as string;
|
||||||
|
|
||||||
|
const formRef = ref<FormInstance>();
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
|
||||||
|
const current = ref(0);
|
||||||
|
const stepCurrent = ref(0);
|
||||||
|
const steps = ref(['网络组件', '消息协议', '完成']);
|
||||||
|
const networkList: any = ref([]);
|
||||||
|
const allNetworkList: any = ref([]);
|
||||||
|
const procotolList = ref([]);
|
||||||
|
const allProcotolList = ref([]);
|
||||||
|
const networkCurrent: any = ref('');
|
||||||
|
const procotolCurrent: any = ref('');
|
||||||
|
const config: any = ref({});
|
||||||
|
const columnsMQTT = ref(<TableColumnType>[]);
|
||||||
|
const columnsHTTP = ref(<TableColumnType>[]);
|
||||||
|
const formData = ref({
|
||||||
|
name: '',
|
||||||
|
description: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { resetFields, validate, validateInfos } = useForm(
|
||||||
|
formData,
|
||||||
|
reactive({
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称', trigger: 'blur' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
],
|
||||||
|
description: [{ max: 200, message: '最多可输入200个字符' }],
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
const queryNetworkList = async (id: string, include: string, data = {}) => {
|
||||||
|
const resp = await getNetworkList(
|
||||||
|
NetworkTypeMapping.get(id),
|
||||||
|
include,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
networkList.value = resp.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryProcotolList = async (id: string, params = {}) => {
|
||||||
|
const resp = await getProtocolList(ProtocolMapping.get(id), {
|
||||||
|
...params,
|
||||||
|
'sorts[0].name': 'createTime',
|
||||||
|
'sorts[0].order': 'desc',
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
procotolList.value = resp.result;
|
||||||
|
allProcotolList.value = resp.result;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const addNetwork = () => {
|
||||||
|
const url = menuStory.menus['link/Type/Detail']?.path;
|
||||||
|
const tab = window.open(
|
||||||
|
`${window.location.origin + window.location.pathname}#${url}?type=${
|
||||||
|
NetworkTypeMapping.get(props.provider?.id) || ''
|
||||||
|
}`,
|
||||||
|
);
|
||||||
|
tab.onTabSaveSuccess = (value) => {
|
||||||
|
if (value.success) {
|
||||||
|
networkCurrent.value = value.result.id;
|
||||||
|
queryNetworkList(props.provider?.id, networkCurrent.value || '');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const addProcotol = () => {
|
||||||
|
const url = menuStory.menus['link/Protocol']?.path;
|
||||||
|
const tab = window.open(
|
||||||
|
`${window.location.origin + window.location.pathname}#${url}?save=true`,
|
||||||
|
);
|
||||||
|
tab.onTabSaveSuccess = (value) => {
|
||||||
|
if (value.success) {
|
||||||
|
procotolCurrent.value = value.result?.id;
|
||||||
|
queryProcotolList(props.provider?.id);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNetworkCurrent = () =>
|
||||||
|
networkList.value.find((i) => i.id === networkCurrent) &&
|
||||||
|
(networkList.value.find((i) => i.id === networkCurrent).addresses || [])
|
||||||
|
.length > 0;
|
||||||
|
const getNetworkCurrentData = () =>
|
||||||
|
getNetworkCurrent()
|
||||||
|
? networkList.value.find((i) => i.id === networkCurrent).addresses
|
||||||
|
: [];
|
||||||
|
|
||||||
|
const getColor = (i) => (i.health === -1 ? 'red' : 'green');
|
||||||
|
|
||||||
|
const getStream = (record: any) => {
|
||||||
|
let stream = '';
|
||||||
|
if (record.upstream && record.downstream) stream = '上行、下行';
|
||||||
|
else if (record.upstream) stream = '上行';
|
||||||
|
else if (record.downstream) stream = '下行';
|
||||||
|
return stream;
|
||||||
|
};
|
||||||
|
|
||||||
|
const checkedChange = (id: string) => {
|
||||||
|
networkCurrent.value = id;
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const procotolChange = (id: string) => {
|
||||||
|
if (!props.data.id) {
|
||||||
|
procotolCurrent.value = id;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const procotolSearch = (value: string) => {
|
||||||
|
if (value) {
|
||||||
|
const list = allProcotolList.value.filter((i: any) => {
|
||||||
|
return (
|
||||||
|
i.name &&
|
||||||
|
i.name.toLocaleLowerCase().includes(value.toLocaleLowerCase())
|
||||||
|
);
|
||||||
|
});
|
||||||
|
procotolList.value = list;
|
||||||
|
} else {
|
||||||
|
procotolList.value = allProcotolList.value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveData = () => {
|
||||||
|
validate()
|
||||||
|
.then(async (values) => {
|
||||||
|
const params = {
|
||||||
|
...props.data,
|
||||||
|
...values,
|
||||||
|
protocol: procotolCurrent.value,
|
||||||
|
channel: 'network', // 网络组件
|
||||||
|
channelId: networkCurrent.value,
|
||||||
|
};
|
||||||
|
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),
|
||||||
|
});
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
history.back();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {});
|
||||||
|
};
|
||||||
|
|
||||||
|
const next = async () => {
|
||||||
|
if (current.value === 0) {
|
||||||
|
if (!networkCurrent.value) {
|
||||||
|
message.error('请选择网络组件!');
|
||||||
|
} else {
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
current.value = current.value + 1;
|
||||||
|
}
|
||||||
|
} else if (current.value === 1) {
|
||||||
|
if (!procotolCurrent.value) {
|
||||||
|
message.error('请选择消息协议!');
|
||||||
|
} else {
|
||||||
|
const resp =
|
||||||
|
type !== 'child-device'
|
||||||
|
? await getConfigView(
|
||||||
|
procotolCurrent.value,
|
||||||
|
ProtocolMapping.get(props.provider.id),
|
||||||
|
)
|
||||||
|
: await getChildConfigView(procotolCurrent.value);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
config.value = resp.result;
|
||||||
|
console.log(222, config.value);
|
||||||
|
|
||||||
|
current.value = current.value + 1;
|
||||||
|
const Group = {
|
||||||
|
title: '分组',
|
||||||
|
dataIndex: 'group',
|
||||||
|
key: 'group',
|
||||||
|
ellipsis: true,
|
||||||
|
align: 'center',
|
||||||
|
width: 100,
|
||||||
|
customCell: (record: any, rowIndex: number) => {
|
||||||
|
const obj = {
|
||||||
|
children: record,
|
||||||
|
rowSpan: 0,
|
||||||
|
};
|
||||||
|
const list = config.value?.routes || [];
|
||||||
|
|
||||||
|
const arr = list.filter(
|
||||||
|
(res: any) => res.group === record.group,
|
||||||
|
);
|
||||||
|
|
||||||
|
const isRowIndex =
|
||||||
|
rowIndex === 0 ||
|
||||||
|
list[rowIndex - 1].group !== record.group;
|
||||||
|
isRowIndex && (obj.rowSpan = arr.length);
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
columnsMQTT.value = [Group, ...ColumnsMQTT];
|
||||||
|
columnsHTTP.value = [Group, ...ColumnsHTTP];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const prev = () => {
|
||||||
|
current.value = current.value - 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (props.data && props.data.id) {
|
||||||
|
if (props.data.provider !== 'child-device') {
|
||||||
|
procotolCurrent.value = props.data.protocol;
|
||||||
|
current.value = 0;
|
||||||
|
networkCurrent.value = props.data.channelId;
|
||||||
|
queryNetworkList(props.provider.id, networkCurrent.value);
|
||||||
|
procotolCurrent.value = props.data.protocol;
|
||||||
|
steps.value = ['网络组件', '消息协议', '完成'];
|
||||||
|
} else {
|
||||||
|
steps.value = ['消息协议', '完成'];
|
||||||
|
current.value = 1;
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (props.provider?.id) {
|
||||||
|
if (type !== 'child-device') {
|
||||||
|
queryNetworkList(props.provider.id, '');
|
||||||
|
steps.value = ['网络组件', '消息协议', '完成'];
|
||||||
|
current.value = 0;
|
||||||
|
} else {
|
||||||
|
steps.value = ['消息协议', '完成'];
|
||||||
|
current.value = 1;
|
||||||
|
queryProcotolList(props.provider.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (id !== ':id') {
|
||||||
|
procotolCurrent.value = props.data.protocol;
|
||||||
|
formData.value = {
|
||||||
|
name: props.data.name,
|
||||||
|
description: props.data.description,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
current,
|
||||||
|
(v) => {
|
||||||
|
stepCurrent.value = type === 'child-device' ? v - 1 : v;
|
||||||
|
},
|
||||||
|
{
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.steps-content {
|
||||||
|
margin: 20px;
|
||||||
|
}
|
||||||
|
.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;
|
||||||
|
overflow-x: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.steps-action {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 24px;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.alert {
|
||||||
|
height: 40px;
|
||||||
|
padding-left: 10px;
|
||||||
|
color: rgba(0, 0, 0, 0.55);
|
||||||
|
line-height: 40px;
|
||||||
|
background-color: #f6f6f6;
|
||||||
|
}
|
||||||
|
.search {
|
||||||
|
display: flex;
|
||||||
|
margin: 15px 0;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.other {
|
||||||
|
width: 100%;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
.item {
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-right {
|
||||||
|
padding: 20px;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
background: rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
|
.config-right-item {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
|
||||||
|
.config-right-item-title {
|
||||||
|
width: 100%;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.config-right-item-context {
|
||||||
|
margin: 5px 0;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,6 +1,6 @@
|
||||||
<template>
|
<template>
|
||||||
<div v-for="items in dataSource" :key="items.type">
|
<div v-for="items in dataSource" :key="items.type" class="card-items">
|
||||||
<a-card class="card-items">
|
<div class="card-items-container">
|
||||||
<TitleComponent :data="items.title"></TitleComponent>
|
<TitleComponent :data="items.title"></TitleComponent>
|
||||||
<a-row :gutter="[24, 24]">
|
<a-row :gutter="[24, 24]">
|
||||||
<a-col :span="12" v-for="item in items.list" :key="item.id">
|
<a-col :span="12" v-for="item in items.list" :key="item.id">
|
||||||
|
@ -8,7 +8,7 @@
|
||||||
<div class="box">
|
<div class="box">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="images">
|
<div class="images">
|
||||||
<img :src="backMap.get(item.id)" />
|
<img :src="BackMap.get(item.id)" />
|
||||||
</div>
|
</div>
|
||||||
<div class="context">
|
<div class="context">
|
||||||
<div class="title">
|
<div class="title">
|
||||||
|
@ -30,13 +30,13 @@
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
</a-card>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup name="AccessConfigProvider">
|
<script lang="ts" setup name="AccessConfigProvider">
|
||||||
import TitleComponent from '@/components/TitleComponent/index.vue';
|
import TitleComponent from '@/components/TitleComponent/index.vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { BackMap } from '../../data';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
dataSource: {
|
dataSource: {
|
||||||
|
@ -47,32 +47,16 @@ const props = defineProps({
|
||||||
|
|
||||||
const emit = defineEmits(['onClick']);
|
const emit = defineEmits(['onClick']);
|
||||||
|
|
||||||
const backMap = new Map();
|
|
||||||
backMap.set('mqtt-server-gateway', getImage('/access/mqtt.png'));
|
|
||||||
backMap.set('websocket-server', getImage('/access/websocket.png'));
|
|
||||||
backMap.set('modbus-tcp', getImage('/access/modbus.png'));
|
|
||||||
backMap.set('coap-server-gateway', getImage('/access/coap.png'));
|
|
||||||
backMap.set('tcp-server-gateway', getImage('/access/tcp.png'));
|
|
||||||
backMap.set('Ctwing', getImage('/access/ctwing.png'));
|
|
||||||
backMap.set('child-device', getImage('/access/child-device.png'));
|
|
||||||
backMap.set('opc-ua', getImage('/access/opc-ua.png'));
|
|
||||||
backMap.set('http-server-gateway', getImage('/access/http.png'));
|
|
||||||
backMap.set('fixed-media', getImage('/access/video-device.png'));
|
|
||||||
backMap.set('udp-device-gateway', getImage('/access/udp.png'));
|
|
||||||
backMap.set('OneNet', getImage('/access/onenet.png'));
|
|
||||||
backMap.set('gb28181-2016', getImage('/access/gb28181.png'));
|
|
||||||
backMap.set('mqtt-client-gateway', getImage('/access/mqtt-broke.png'));
|
|
||||||
backMap.set('edge-child-device', getImage('/access/child-device.png'));
|
|
||||||
backMap.set('official-edge-gateway', getImage('/access/edge.png'));
|
|
||||||
|
|
||||||
const click = (value: object) => {
|
const click = (value: object) => {
|
||||||
emit('onClick', value);
|
emit('onClick', value);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.card-items{
|
.card-items {
|
||||||
margin-bottom: 24px;
|
margin-bottom: 24px;
|
||||||
|
.card-items-container {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.provider {
|
.provider {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
@ -93,9 +77,6 @@ const click = (value: object) => {
|
||||||
background-image: url('/public/images/access/rectangle.png');
|
background-image: url('/public/images/access/rectangle.png');
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 100% 100%;
|
background-size: 100% 100%;
|
||||||
// border: 1px #8da1f4 solid;
|
|
||||||
// border-bottom-left-radius: 10%;
|
|
||||||
// border-bottom-right-radius: 10%;
|
|
||||||
content: ' ';
|
content: ' ';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +90,6 @@ const click = (value: object) => {
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
||||||
.left {
|
.left {
|
||||||
display: flex;
|
display: flex;
|
||||||
width: calc(100% - 70px);
|
width: calc(100% - 70px);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
|
||||||
const ProtocolMapping = new Map();
|
const ProtocolMapping = new Map();
|
||||||
ProtocolMapping.set('websocket-server', 'WebSocket');
|
ProtocolMapping.set('websocket-server', 'WebSocket');
|
||||||
|
@ -25,6 +26,23 @@ NetworkTypeMapping.set('mqtt-server-gateway', 'MQTT_SERVER');
|
||||||
NetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
|
NetworkTypeMapping.set('tcp-server-gateway', 'TCP_SERVER');
|
||||||
NetworkTypeMapping.set('official-edge-gateway', 'MQTT_SERVER');
|
NetworkTypeMapping.set('official-edge-gateway', 'MQTT_SERVER');
|
||||||
|
|
||||||
|
const BackMap = new Map();
|
||||||
|
BackMap.set('mqtt-server-gateway', getImage('/access/mqtt.png'));
|
||||||
|
BackMap.set('websocket-server', getImage('/access/websocket.png'));
|
||||||
|
BackMap.set('modbus-tcp', getImage('/access/modbus.png'));
|
||||||
|
BackMap.set('coap-server-gateway', getImage('/access/coap.png'));
|
||||||
|
BackMap.set('tcp-server-gateway', getImage('/access/tcp.png'));
|
||||||
|
BackMap.set('Ctwing', getImage('/access/ctwing.png'));
|
||||||
|
BackMap.set('child-device', getImage('/access/child-device.png'));
|
||||||
|
BackMap.set('opc-ua', getImage('/access/opc-ua.png'));
|
||||||
|
BackMap.set('http-server-gateway', getImage('/access/http.png'));
|
||||||
|
BackMap.set('fixed-media', getImage('/access/video-device.png'));
|
||||||
|
BackMap.set('udp-device-gateway', getImage('/access/udp.png'));
|
||||||
|
BackMap.set('OneNet', getImage('/access/onenet.png'));
|
||||||
|
BackMap.set('gb28181-2016', getImage('/access/gb28181.png'));
|
||||||
|
BackMap.set('mqtt-client-gateway', getImage('/access/mqtt-broke.png'));
|
||||||
|
BackMap.set('edge-child-device', getImage('/access/child-device.png'));
|
||||||
|
BackMap.set('official-edge-gateway', getImage('/access/edge.png'));
|
||||||
|
|
||||||
const descriptionList = {
|
const descriptionList = {
|
||||||
'udp-device-gateway':
|
'udp-device-gateway':
|
||||||
|
@ -43,21 +61,21 @@ const descriptionList = {
|
||||||
'CoAP是针对只有少量的内存空间和有限的计算能力提供的一种基于UDP的协议。便于低功耗或网络受限的设备与平台通信,仅支持设备和平台之间单对单的请求-响应模式。',
|
'CoAP是针对只有少量的内存空间和有限的计算能力提供的一种基于UDP的协议。便于低功耗或网络受限的设备与平台通信,仅支持设备和平台之间单对单的请求-响应模式。',
|
||||||
};
|
};
|
||||||
|
|
||||||
const columnsMQTT = [
|
const ColumnsMQTT = [
|
||||||
{
|
// {
|
||||||
title: '分组',
|
// title: '分组',
|
||||||
dataIndex: 'group',
|
// dataIndex: 'group',
|
||||||
key: 'group',
|
// key: 'group',
|
||||||
ellipsis: true,
|
// ellipsis: true,
|
||||||
align: 'center',
|
// align: 'center',
|
||||||
width: 100,
|
// width: 100,
|
||||||
scopedSlots: { customRender: 'group' },
|
// scopedSlots: { customRender: 'group' },
|
||||||
},
|
// },
|
||||||
{
|
{
|
||||||
title: 'topic',
|
title: 'topic',
|
||||||
dataIndex: 'topic',
|
dataIndex: 'topic',
|
||||||
key: 'topic',
|
key: 'topic',
|
||||||
scopedSlots: { customRender: 'topic' },
|
ellipsis: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '上下行',
|
title: '上下行',
|
||||||
|
@ -72,37 +90,58 @@ const columnsMQTT = [
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
scopedSlots: { customRender: 'description' },
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const columnsHTTP = [
|
|
||||||
{
|
|
||||||
title: '分组',
|
|
||||||
dataIndex: 'group',
|
|
||||||
key: 'group',
|
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 100,
|
},
|
||||||
scopedSlots: { customRender: 'group' },
|
];
|
||||||
},
|
|
||||||
{
|
const ColumnsHTTP = [
|
||||||
|
// {
|
||||||
|
// title: '分组',
|
||||||
|
// dataIndex: 'group',
|
||||||
|
// key: 'group',
|
||||||
|
// ellipsis: true,
|
||||||
|
// width: 100,
|
||||||
|
// scopedSlots: { customRender: 'group' },
|
||||||
|
// },
|
||||||
|
{
|
||||||
title: '地址',
|
title: '地址',
|
||||||
dataIndex: 'address',
|
dataIndex: 'address',
|
||||||
key: 'address',
|
key: 'address',
|
||||||
scopedSlots: { customRender: 'address' },
|
ellipsis: true,
|
||||||
},
|
// scopedSlots: { customRender: 'address' },
|
||||||
{
|
},
|
||||||
|
{
|
||||||
title: '示例',
|
title: '示例',
|
||||||
dataIndex: 'example',
|
dataIndex: 'example',
|
||||||
key: 'example',
|
key: 'example',
|
||||||
scopedSlots: { customRender: 'example' },
|
ellipsis: true,
|
||||||
},
|
// scopedSlots: { customRender: 'example' },
|
||||||
{
|
},
|
||||||
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
scopedSlots: { customRender: 'description' }
|
ellipsis: true,
|
||||||
},
|
// scopedSlots: { customRender: 'description' },
|
||||||
]
|
},
|
||||||
|
];
|
||||||
|
|
||||||
export { NetworkTypeMapping, ProtocolMapping, descriptionList, columnsMQTT, columnsHTTP };
|
const TiTlePermissionButtonStyle = {
|
||||||
|
padding: 0,
|
||||||
|
color: ' #1890ff !important',
|
||||||
|
'font-weight': 700,
|
||||||
|
'font-size': '16px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
'text-overflow': 'ellipsis',
|
||||||
|
'white-space': 'nowrap',
|
||||||
|
};
|
||||||
|
|
||||||
|
export {
|
||||||
|
NetworkTypeMapping,
|
||||||
|
ProtocolMapping,
|
||||||
|
BackMap,
|
||||||
|
descriptionList,
|
||||||
|
ColumnsMQTT,
|
||||||
|
ColumnsHTTP,
|
||||||
|
TiTlePermissionButtonStyle,
|
||||||
|
};
|
|
@ -11,12 +11,23 @@
|
||||||
:defaultParams="{
|
:defaultParams="{
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
}"
|
}"
|
||||||
|
gridColumn="2"
|
||||||
|
gridColumns="[2]"
|
||||||
:params="params"
|
:params="params"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button type="primary" @click="handlAdd"
|
<a-space>
|
||||||
><AIcon type="PlusOutlined" />新增</a-button
|
<PermissionButton
|
||||||
>
|
type="primary"
|
||||||
|
@click="handlAdd"
|
||||||
|
hasPermission="link/AccessConfig:add"
|
||||||
|
>
|
||||||
|
<template #icon
|
||||||
|
><AIcon type="PlusOutlined"
|
||||||
|
/></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
|
@ -43,12 +54,15 @@
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<div class="card-item-content">
|
<div class="card-item-content">
|
||||||
<h3
|
<PermissionButton
|
||||||
|
type="link"
|
||||||
@click="handlEye(slotProps.id)"
|
@click="handlEye(slotProps.id)"
|
||||||
class="card-item-content-title card-item-content-title-a"
|
hasPermission="link/AccessConfig:view"
|
||||||
|
:style="TiTlePermissionButtonStyle"
|
||||||
>
|
>
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</h3>
|
</PermissionButton>
|
||||||
|
|
||||||
<a-row class="card-item-content-box">
|
<a-row class="card-item-content-box">
|
||||||
<a-col
|
<a-col
|
||||||
:span="12"
|
:span="12"
|
||||||
|
@ -65,13 +79,7 @@
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<a-badge
|
<a-badge
|
||||||
:status="
|
:status="getStatus(slotProps)"
|
||||||
slotProps.channelInfo
|
|
||||||
.addresses[0].health ===
|
|
||||||
-1
|
|
||||||
? 'error'
|
|
||||||
: 'success'
|
|
||||||
"
|
|
||||||
/>
|
/>
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>{{
|
<template #title>{{
|
||||||
|
@ -112,24 +120,12 @@
|
||||||
<a-tooltip>
|
<a-tooltip>
|
||||||
<template #title>
|
<template #title>
|
||||||
{{
|
{{
|
||||||
slotProps.description
|
getDescription(
|
||||||
? slotProps.description
|
slotProps,
|
||||||
: providersList.find(
|
)
|
||||||
(item) =>
|
|
||||||
item.id ===
|
|
||||||
slotProps.provider,
|
|
||||||
)?.description
|
|
||||||
}}
|
}}
|
||||||
</template>
|
</template>
|
||||||
{{
|
{{ getDescription(slotProps) }}
|
||||||
slotProps.description
|
|
||||||
? slotProps.description
|
|
||||||
: providersList.find(
|
|
||||||
(item) =>
|
|
||||||
item.id ===
|
|
||||||
slotProps.provider,
|
|
||||||
)?.description
|
|
||||||
}}
|
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
@ -138,42 +134,24 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<a-tooltip
|
<PermissionButton
|
||||||
v-bind="item.tooltip"
|
:disabled="item.disabled"
|
||||||
:title="item.disabled && item.tooltip.title"
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
:hasPermission="'link/AccessConfig:' + item.key"
|
||||||
>
|
>
|
||||||
<a-popconfirm
|
<AIcon
|
||||||
v-if="item.popConfirm"
|
type="DeleteOutlined"
|
||||||
v-bind="item.popConfirm"
|
v-if="item.key === 'delete'"
|
||||||
:disabled="item.disabled"
|
/>
|
||||||
>
|
|
||||||
<a-button :disabled="item.disabled">
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-button
|
<AIcon :type="item.icon" />
|
||||||
:disabled="item.disabled"
|
<span>{{ item?.text }}</span>
|
||||||
@click="item.onClick"
|
|
||||||
>
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
</template>
|
||||||
</a-tooltip>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
@ -198,12 +176,14 @@ import {
|
||||||
deploy,
|
deploy,
|
||||||
} from '@/api/link/accessConfig';
|
} from '@/api/link/accessConfig';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
|
import { useMenuStore } from 'store/menu';
|
||||||
|
import { TiTlePermissionButtonStyle } from './data';
|
||||||
|
|
||||||
|
const menuStory = useMenuStore();
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
const router = useRouter();
|
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
|
|
||||||
let providersList = ref([]);
|
let providersList = ref<Record<string, any>>([]);
|
||||||
|
|
||||||
const statusMap = new Map();
|
const statusMap = new Map();
|
||||||
statusMap.set('enabled', 'success');
|
statusMap.set('enabled', 'success');
|
||||||
|
@ -225,8 +205,6 @@ const columns = [
|
||||||
key: 'provider',
|
key: 'provider',
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
// options: providersList,
|
|
||||||
// options: getProvidersList
|
|
||||||
options: async () => {
|
options: async () => {
|
||||||
const res = await getProviders();
|
const res = await getProviders();
|
||||||
return (res?.result || []).map((item) => ({
|
return (res?.result || []).map((item) => ({
|
||||||
|
@ -275,9 +253,10 @@ const columns = [
|
||||||
const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
|
const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
|
||||||
if (!data) return [];
|
if (!data) return [];
|
||||||
const state = data.state.value;
|
const state = data.state.value;
|
||||||
|
const stateText = state === 'enabled' ? '禁用' : '启用';
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: 'edit',
|
key: 'update',
|
||||||
text: '编辑',
|
text: '编辑',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: '编辑',
|
title: '编辑',
|
||||||
|
@ -289,13 +268,13 @@ const getActions = (data: Partial<Record<string, any>>): ActionsType[] => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'action',
|
key: 'action',
|
||||||
text: state === 'enabled' ? '禁用' : '启用',
|
text: stateText,
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: state === 'enabled' ? '禁用' : '启用',
|
title: stateText,
|
||||||
},
|
},
|
||||||
icon: state === 'enabled' ? 'StopOutlined' : 'CheckCircleOutlined',
|
icon: state === 'enabled' ? 'StopOutlined' : 'CheckCircleOutlined',
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: `确认${state === 'enabled' ? '禁用' : '启用'}?`,
|
title: `确认${stateText}?`,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
let res =
|
let res =
|
||||||
state === 'enabled'
|
state === 'enabled'
|
||||||
|
@ -342,27 +321,29 @@ const getProvidersList = async () => {
|
||||||
getProvidersList();
|
getProvidersList();
|
||||||
|
|
||||||
const handlAdd = () => {
|
const handlAdd = () => {
|
||||||
// router.push('/link/accessConfig/detail/add/new');
|
menuStory.jumpPage(
|
||||||
router.push({
|
`link/AccessConfig/Detail`,
|
||||||
path: `/iot/link/accessConfig/detail/:id`,
|
{ id: ':id' },
|
||||||
query: { view: false },
|
{ view: false },
|
||||||
});
|
);
|
||||||
};
|
};
|
||||||
const handlEdit = (id: string) => {
|
const handlEdit = (id: string) => {
|
||||||
// router.push(`/link/accessConfig/detail/edit/${id}`);
|
menuStory.jumpPage(`link/AccessConfig/Detail`, { id }, { view: false });
|
||||||
router.push({
|
|
||||||
path: `/iot/link/accessConfig/detail/${id}`,
|
|
||||||
query: { view: false },
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
const handlEye = (id: string) => {
|
const handlEye = (id: string) => {
|
||||||
// router.push(`/link/accessConfig/detail/view/${id}`);
|
menuStory.jumpPage(`link/AccessConfig/Detail`, { id }, { view: true });
|
||||||
router.push({
|
|
||||||
path: `/iot/link/accessConfig/detail/${id}`,
|
|
||||||
query: { view: true },
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const getDescription = (slotProps: Record<string, any>) =>
|
||||||
|
slotProps.description
|
||||||
|
? slotProps.description
|
||||||
|
: providersList?.find(
|
||||||
|
(item: Record<string, any>) => item.id === slotProps.provider,
|
||||||
|
)?.description;
|
||||||
|
|
||||||
|
const getStatus = (slotProps: Record<string, any>) =>
|
||||||
|
slotProps.channelInfo.addresses[0].health === -1 ? 'error' : 'success';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索
|
* 搜索
|
||||||
* @param params
|
* @param params
|
||||||
|
@ -370,18 +351,6 @@ const handlEye = (id: string) => {
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
params.value = e;
|
params.value = e;
|
||||||
};
|
};
|
||||||
|
|
||||||
// const handlAdd = () => {
|
|
||||||
// router.push({
|
|
||||||
// path: '/link/accessConfig/detail/add',
|
|
||||||
// query: {
|
|
||||||
// id: '1610475400026861568',
|
|
||||||
// },
|
|
||||||
// });
|
|
||||||
// };
|
|
||||||
// const handlAdd = () => {
|
|
||||||
// router.push('/link/accessConfig/detail/add');
|
|
||||||
// }
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.tableCardDisabled {
|
.tableCardDisabled {
|
||||||
|
|
|
@ -0,0 +1,189 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<h3>CPU使用率趋势</h3>
|
||||||
|
<a-range-picker
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon><a-icon type="calendar" /></template>
|
||||||
|
<template #renderExtraFooter>
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today">
|
||||||
|
今日
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="week">
|
||||||
|
近一周
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group></template
|
||||||
|
>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
m
|
||||||
|
<script lang="ts" setup name="Cpu">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import moment from 'moment';
|
||||||
|
import {
|
||||||
|
getTimeFormat,
|
||||||
|
getTimeByType,
|
||||||
|
arrayReverse,
|
||||||
|
defulteParamsData,
|
||||||
|
areaStyleCpu,
|
||||||
|
typeDataLine,
|
||||||
|
} from './tool.ts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const data = ref({
|
||||||
|
type: 'hour',
|
||||||
|
time: [null, null],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getCPUEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await dashboard(defulteParamsData('cpu', val));
|
||||||
|
if (res.success) {
|
||||||
|
const _cpuOptions = {};
|
||||||
|
const _cpuXAxis = new Set();
|
||||||
|
if (res.result?.length) {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
_cpuXAxis.add(
|
||||||
|
moment(value.timestamp).format(
|
||||||
|
getTimeFormat(data.value.type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
if (!_cpuOptions[nodeID]) {
|
||||||
|
_cpuOptions[nodeID] = [];
|
||||||
|
}
|
||||||
|
_cpuOptions[nodeID].push(
|
||||||
|
Number(value.cpuSystemUsage).toFixed(2),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleCpuOptions(_cpuOptions, [..._cpuXAxis.keys()]);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (optionsData, key) => ({
|
||||||
|
data: arrayReverse(optionsData[key]),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: areaStyleCpu,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleCpuOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: arrayReverse(xAxis),
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
valueFormatter: (value) => `${value}%`,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '50px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: ['#2CB6E0'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.type,
|
||||||
|
(val) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(val);
|
||||||
|
data.value.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(val) => {
|
||||||
|
const { time } = val;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getCPUEcharts(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,193 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<h3>JVM内存使用率趋势</h3>
|
||||||
|
<a-range-picker
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time"
|
||||||
|
>
|
||||||
|
<template #suffixIcon><a-icon type="calendar" /></template>
|
||||||
|
<template #renderExtraFooter>
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today">
|
||||||
|
今日
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="week">
|
||||||
|
近一周
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group></template
|
||||||
|
>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 300px"></div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="Jvm">
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import moment from 'moment';
|
||||||
|
import {
|
||||||
|
getTimeFormat,
|
||||||
|
getTimeByType,
|
||||||
|
arrayReverse,
|
||||||
|
typeDataLine,
|
||||||
|
areaStyleJvm,
|
||||||
|
defulteParamsData,
|
||||||
|
} from './tool.ts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const loading = ref(false);
|
||||||
|
const data = ref({
|
||||||
|
type: 'hour',
|
||||||
|
time: [null, null],
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getJVMEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const res = await dashboard(defulteParamsData('jvm', val));
|
||||||
|
if (res.success) {
|
||||||
|
const _jvmOptions = {};
|
||||||
|
const _jvmXAxis = new Set();
|
||||||
|
if (res.result?.length) {
|
||||||
|
res.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const memoryJvmHeapFree = value.memoryJvmHeapFree;
|
||||||
|
const memoryJvmHeapTotal = value.memoryJvmHeapTotal;
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
|
||||||
|
const _value = (
|
||||||
|
((memoryJvmHeapTotal - memoryJvmHeapFree) /
|
||||||
|
memoryJvmHeapTotal) *
|
||||||
|
100
|
||||||
|
).toFixed(2);
|
||||||
|
if (!_jvmOptions[nodeID]) {
|
||||||
|
_jvmOptions[nodeID] = [];
|
||||||
|
}
|
||||||
|
_jvmXAxis.add(
|
||||||
|
moment(value.timestamp).format(
|
||||||
|
getTimeFormat(data.value.type),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
_jvmOptions[nodeID].push(_value);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
handleJVMOptions(_jvmOptions, [..._jvmXAxis.keys()]);
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (optionsData, key) => ({
|
||||||
|
data: arrayReverse(optionsData[key]),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
symbol: 'none',
|
||||||
|
areaStyle: areaStyleJvm,
|
||||||
|
});
|
||||||
|
const handleJVMOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: arrayReverse(xAxis),
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
valueFormatter: (value: any) => `${value}%`,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '50px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
dataZoom: [
|
||||||
|
{
|
||||||
|
type: 'inside',
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
start: 0,
|
||||||
|
end: 100,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
color: ['#60DFC7'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.type,
|
||||||
|
(val) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(val);
|
||||||
|
data.value.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(val) => {
|
||||||
|
const { time } = val;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getJVMEcharts(val);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,219 @@
|
||||||
|
<template>
|
||||||
|
<a-spin :spinning="loading">
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="header">
|
||||||
|
<div class="left">
|
||||||
|
<h3 style="width: 80px">网络流量</h3>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="data.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="bytesRead">
|
||||||
|
上行
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="bytesSent">
|
||||||
|
下行
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<a-radio-group
|
||||||
|
default-value="a"
|
||||||
|
button-style="solid"
|
||||||
|
style="margin-right: 10px"
|
||||||
|
v-model:value="data.time.type"
|
||||||
|
>
|
||||||
|
<a-radio-button value="hour">
|
||||||
|
最近1小时
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="today"> 今日 </a-radio-button>
|
||||||
|
<a-radio-button value="week"> 近一周 </a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
<a-range-picker
|
||||||
|
:allowClear="false"
|
||||||
|
:show-time="{ format: 'HH:mm:ss' }"
|
||||||
|
format="YYYY-MM-DD HH:mm:ss"
|
||||||
|
v-model="data.time.time"
|
||||||
|
@change="pickerTimeChange"
|
||||||
|
>
|
||||||
|
<template #suffixIcon
|
||||||
|
><a-icon type="calendar"
|
||||||
|
/></template>
|
||||||
|
</a-range-picker>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
ref="chartRef"
|
||||||
|
v-if="flag"
|
||||||
|
style="width: 100%; height: 350px"
|
||||||
|
></div>
|
||||||
|
<a-empty v-else style="height: 300px; margin-top: 120px" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-spin>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="Network">
|
||||||
|
import { dashboard } from '@/api/link/dashboard';
|
||||||
|
import {
|
||||||
|
getTimeByType,
|
||||||
|
typeDataLine,
|
||||||
|
areaStyle,
|
||||||
|
networkParams,
|
||||||
|
arrayReverse,
|
||||||
|
} from './tool.ts';
|
||||||
|
import moment from 'moment';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
const chartRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const flag = ref(true);
|
||||||
|
const loading = ref(false);
|
||||||
|
const myChart = ref(null);
|
||||||
|
const data = ref({
|
||||||
|
type: 'bytesRead',
|
||||||
|
time: {
|
||||||
|
type: 'today',
|
||||||
|
time: [null, null],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickerTimeChange = () => {
|
||||||
|
data.value.time.type = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getNetworkEcharts = async (val) => {
|
||||||
|
loading.value = true;
|
||||||
|
const resp = await dashboard(networkParams(val));
|
||||||
|
if (resp.success) {
|
||||||
|
const _networkOptions = {};
|
||||||
|
const _networkXAxis = new Set();
|
||||||
|
if (resp.result.length) {
|
||||||
|
resp.result.forEach((item) => {
|
||||||
|
const value = item.data.value;
|
||||||
|
const _data = [];
|
||||||
|
const nodeID = item.data.clusterNodeId;
|
||||||
|
value.forEach((item) => {
|
||||||
|
_data.push(item.value);
|
||||||
|
_networkXAxis.add(item.timeString);
|
||||||
|
});
|
||||||
|
_networkOptions[nodeID] = {
|
||||||
|
_data: _networkOptions[nodeID]
|
||||||
|
? _networkOptions[nodeID]._data.concat(_data)
|
||||||
|
: _data,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
handleNetworkOptions(_networkOptions, [..._networkXAxis.keys()]);
|
||||||
|
} else {
|
||||||
|
handleNetworkOptions([], []);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false;
|
||||||
|
}, 300);
|
||||||
|
};
|
||||||
|
const networkValueRender = (obj) => {
|
||||||
|
const { value } = obj;
|
||||||
|
let _data = '';
|
||||||
|
if (value >= 1024 && value < 1024 * 1024) {
|
||||||
|
_data = `${Number((value / 1024).toFixed(2))}KB`;
|
||||||
|
} else if (value >= 1024 * 1024) {
|
||||||
|
_data = `${Number((value / 1024 / 1024).toFixed(2))}M`;
|
||||||
|
} else {
|
||||||
|
_data = `${value}B`;
|
||||||
|
}
|
||||||
|
return `${obj?.axisValueLabel}<br />${obj?.marker}${obj?.seriesName}: ${_data}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setOptions = (data, key) => ({
|
||||||
|
data: data[key]._data, // .map((item) => Number((item / 1024 / 1024).toFixed(2))),
|
||||||
|
name: key,
|
||||||
|
type: 'line',
|
||||||
|
smooth: true,
|
||||||
|
areaStyle,
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleNetworkOptions = (optionsData, xAxis) => {
|
||||||
|
const chart = chartRef.value;
|
||||||
|
|
||||||
|
if (chart) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
const dataKeys = Object.keys(optionsData);
|
||||||
|
const options = {
|
||||||
|
xAxis: {
|
||||||
|
type: 'category',
|
||||||
|
boundaryGap: false,
|
||||||
|
data: xAxis,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: 'value',
|
||||||
|
},
|
||||||
|
grid: {
|
||||||
|
left: '80px',
|
||||||
|
right: '50px',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'axis',
|
||||||
|
formatter: (_value) => networkValueRender(_value[0]),
|
||||||
|
},
|
||||||
|
color: ['#979AFF'],
|
||||||
|
series: dataKeys.length
|
||||||
|
? dataKeys.map((key) => setOptions(optionsData, key))
|
||||||
|
: typeDataLine,
|
||||||
|
};
|
||||||
|
myChart.setOption(options);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => data.value.time.type,
|
||||||
|
(value) => {
|
||||||
|
const endTime = moment(new Date());
|
||||||
|
const startTime = getTimeByType(value);
|
||||||
|
data.value.time.time = [startTime, endTime];
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
watch(
|
||||||
|
() => data.value,
|
||||||
|
(value) => {
|
||||||
|
const {
|
||||||
|
time: { time },
|
||||||
|
} = value;
|
||||||
|
if (time && Array.isArray(time) && time.length === 2 && time[0]) {
|
||||||
|
getNetworkEcharts(value);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ immediate: true, deep: true },
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
padding: 24px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.left h3 {
|
||||||
|
width: 200px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.left,
|
||||||
|
.right {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,141 @@
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<a-select
|
||||||
|
style="width: 300px; margin-bottom: 20px"
|
||||||
|
@change="serverIdChange"
|
||||||
|
:value="serverId"
|
||||||
|
:options="serverNodeOptions"
|
||||||
|
v-if="serverNodeOptions.length > 1"
|
||||||
|
></a-select>
|
||||||
|
<div class="dash-board">
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode title="CPU使用率" :value="topValues.cpu" />
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="JVM内存"
|
||||||
|
:max="topValues.jvmTotal"
|
||||||
|
:bottom="`总JVM内存 ${topValues.jvmTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.jvm"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="磁盘占用"
|
||||||
|
:max="topValues.usageTotal"
|
||||||
|
:bottom="`总磁盘大小 ${topValues.usageTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.usage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="dash-board-item">
|
||||||
|
<TopEchartsItemNode
|
||||||
|
title="系统内存"
|
||||||
|
:max="topValues.systemUsageTotal"
|
||||||
|
:bottom="`系统内存 ${topValues.systemUsageTotal}G`"
|
||||||
|
formatter="G"
|
||||||
|
:value="topValues.systemUsage"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="TopCard">
|
||||||
|
import { serverNode } from '@/api/link/dashboard';
|
||||||
|
import TopEchartsItemNode from './TopEchartsItemNode.vue';
|
||||||
|
import { getWebSocket } from '@/utils/websocket';
|
||||||
|
import { map } from 'rxjs/operators';
|
||||||
|
|
||||||
|
const serverId = ref();
|
||||||
|
const serverNodeOptions = ref([]);
|
||||||
|
const topValues = ref({
|
||||||
|
cpu: 0,
|
||||||
|
jvm: 0,
|
||||||
|
jvmTotal: 0,
|
||||||
|
usage: 0,
|
||||||
|
usageTotal: 0,
|
||||||
|
systemUsage: 0,
|
||||||
|
systemUsageTotal: 0,
|
||||||
|
});
|
||||||
|
|
||||||
|
const serverIdChange = (val: string) => {
|
||||||
|
serverId.value = val;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getData = () => {
|
||||||
|
const id = 'operations-statistics-system-info-realTime';
|
||||||
|
const topic = '/dashboard/systemMonitor/stats/info/realTime';
|
||||||
|
getWebSocket(id, topic, {
|
||||||
|
type: 'all',
|
||||||
|
serverNodeId: serverId.value,
|
||||||
|
interval: '1s',
|
||||||
|
agg: 'avg',
|
||||||
|
})
|
||||||
|
.pipe(map((res) => res.payload))
|
||||||
|
.subscribe((payload) => {
|
||||||
|
const {
|
||||||
|
value: { cpu, memory, disk },
|
||||||
|
} = payload;
|
||||||
|
topValues.value = {
|
||||||
|
cpu: cpu.systemUsage,
|
||||||
|
jvm: Number(
|
||||||
|
(
|
||||||
|
(memory.jvmHeapUsage / 100) *
|
||||||
|
(memory.jvmHeapTotal / 1024)
|
||||||
|
).toFixed(1),
|
||||||
|
),
|
||||||
|
jvmTotal: Math.ceil(memory.jvmHeapTotal / 1024),
|
||||||
|
usage: Number(
|
||||||
|
((disk.total / 1024) * (disk.usage / 100)).toFixed(1),
|
||||||
|
),
|
||||||
|
usageTotal: Math.ceil(disk.total / 1024),
|
||||||
|
systemUsage: Number(
|
||||||
|
(
|
||||||
|
(memory.systemTotal / 1024) *
|
||||||
|
(memory.systemUsage / 100)
|
||||||
|
).toFixed(1),
|
||||||
|
),
|
||||||
|
systemUsageTotal: Math.ceil(memory.systemTotal / 1024),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
serverNode().then((resp) => {
|
||||||
|
if (resp.success) {
|
||||||
|
serverNodeOptions.value = resp.result.map((item) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
if (serverNodeOptions.value.length) {
|
||||||
|
serverId.value = serverNodeOptions.value[0]?.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
watch(
|
||||||
|
() => serverId.value,
|
||||||
|
(val) => {
|
||||||
|
val && getData();
|
||||||
|
},
|
||||||
|
);
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.dash-board {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
height: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
border-radius: 2px;
|
||||||
|
justify-content: space-between;
|
||||||
|
.dash-board-item {
|
||||||
|
flex: 1;
|
||||||
|
margin: 24px 12px;
|
||||||
|
min-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,174 @@
|
||||||
|
<template>
|
||||||
|
<div class="echarts-item">
|
||||||
|
<div class="echarts-item-left">
|
||||||
|
<div class="echarts-item-title">{{ title }}</div>
|
||||||
|
<div class="echarts-item-value">
|
||||||
|
{{ value || 0 }} {{ formatter || '%' }}
|
||||||
|
</div>
|
||||||
|
<div v-if="!!bottom" class="echarts-item-bottom">{{ bottom }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="echarts-item-right">
|
||||||
|
<div ref="chartRef" style="width: 100%; height: 100px"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
import { topOptionsSeries } from './tool';
|
||||||
|
export default {
|
||||||
|
name: 'TopEchartsItemNode',
|
||||||
|
props: {
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
bottom: {
|
||||||
|
type: String,
|
||||||
|
default: '',
|
||||||
|
},
|
||||||
|
formatter: {
|
||||||
|
type: String,
|
||||||
|
default: '%',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
options: {},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
createChart(val) {
|
||||||
|
const chart = this.$refs.chartRef;
|
||||||
|
if (chart && Object.keys(val).length > 0) {
|
||||||
|
const myChart = echarts.init(chart);
|
||||||
|
myChart.setOption(val);
|
||||||
|
window.addEventListener('resize', function () {
|
||||||
|
myChart.resize();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getOptions(max, formatter, val) {
|
||||||
|
let formatterCount = 0;
|
||||||
|
this.options = {
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
...topOptionsSeries,
|
||||||
|
max: max || 100,
|
||||||
|
axisLabel: {
|
||||||
|
distance: -22,
|
||||||
|
color: 'auto',
|
||||||
|
fontSize: 12,
|
||||||
|
width: 30,
|
||||||
|
padding: [6, 10, 0, 10],
|
||||||
|
formatter: (value) => {
|
||||||
|
formatterCount += 1;
|
||||||
|
if ([1, 3, 6, 9, 11].includes(formatterCount)) {
|
||||||
|
return value + (formatter || '%');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: [{ value: val || 0 }],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
options: {
|
||||||
|
handler(val) {
|
||||||
|
this.createChart(val);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(val, this.formatter, this.value);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(this.max, this.formatter, val);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
formatter: {
|
||||||
|
handler(val) {
|
||||||
|
this.getOptions(this.max, val, this.value);
|
||||||
|
},
|
||||||
|
immediate: true,
|
||||||
|
deep: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.echarts-item {
|
||||||
|
display: flex;
|
||||||
|
height: 150px;
|
||||||
|
padding: 16px;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0px 2.73036px 5.46071px rgba(31, 89, 245, 0.2);
|
||||||
|
|
||||||
|
.echarts-item-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
width: 45%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-right {
|
||||||
|
width: 55%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-title {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: rgba(#000, 0.6);
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-value {
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 36px;
|
||||||
|
width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-align: left;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.echarts-item-bottom {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
justify-content: center;
|
||||||
|
height: 0;
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 0;
|
||||||
|
width: 4px;
|
||||||
|
height: 12px;
|
||||||
|
background-color: #ff595e;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
content: ' ';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,220 @@
|
||||||
|
import moment from 'moment';
|
||||||
|
import * as echarts from 'echarts';
|
||||||
|
|
||||||
|
export const getInterval = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'year':
|
||||||
|
return '30d';
|
||||||
|
case 'month':
|
||||||
|
case 'week':
|
||||||
|
return '1d';
|
||||||
|
case 'hour':
|
||||||
|
return '1m';
|
||||||
|
default:
|
||||||
|
return '1h';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export const getTimeFormat = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'year':
|
||||||
|
return 'YYYY-MM-DD';
|
||||||
|
case 'month':
|
||||||
|
case 'week':
|
||||||
|
return 'MM-DD';
|
||||||
|
case 'hour':
|
||||||
|
return 'HH:mm';
|
||||||
|
default:
|
||||||
|
return 'HH';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getTimeByType = (type) => {
|
||||||
|
switch (type) {
|
||||||
|
case 'hour':
|
||||||
|
return moment().subtract(1, 'hours');
|
||||||
|
case 'week':
|
||||||
|
return moment().subtract(6, 'days');
|
||||||
|
case 'month':
|
||||||
|
return moment().subtract(29, 'days');
|
||||||
|
case 'year':
|
||||||
|
return moment().subtract(365, 'days');
|
||||||
|
default:
|
||||||
|
return moment().startOf('day');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export const arrayReverse = (data) => {
|
||||||
|
const newArray = [];
|
||||||
|
for (let i = data.length - 1; i >= 0; i--) {
|
||||||
|
newArray.push(data[i]);
|
||||||
|
}
|
||||||
|
return newArray;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const networkParams = (val) => [
|
||||||
|
{
|
||||||
|
dashboard: 'systemMonitor',
|
||||||
|
object: 'network',
|
||||||
|
measurement: 'traffic',
|
||||||
|
dimension: 'agg',
|
||||||
|
group: 'network',
|
||||||
|
params: {
|
||||||
|
type: val.type,
|
||||||
|
interval: getInterval(val.time.type),
|
||||||
|
from: moment(val.time.time[0]).valueOf(),
|
||||||
|
to: moment(val.time.time[1]).valueOf(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
export const defulteParamsData = (group, val) => [
|
||||||
|
{
|
||||||
|
dashboard: 'systemMonitor',
|
||||||
|
object: 'stats',
|
||||||
|
measurement: 'info',
|
||||||
|
dimension: 'history',
|
||||||
|
group,
|
||||||
|
params: {
|
||||||
|
from: moment(val.time[0]).valueOf(),
|
||||||
|
to: moment(val.time[1]).valueOf(),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const areaStyle = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(151, 154, 255, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(151, 154, 255, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
export const areaStyleCpu = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(44, 182, 224, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(44, 182, 224, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
export const areaStyleJvm = {
|
||||||
|
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(96, 223, 199, 0)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(96, 223, 199, .24)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
|
||||||
|
export const typeDataLine = [
|
||||||
|
{
|
||||||
|
data: [],
|
||||||
|
type: 'line',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const topOptionsSeries = {
|
||||||
|
type: 'gauge',
|
||||||
|
min: 0,
|
||||||
|
startAngle: 200,
|
||||||
|
endAngle: -20,
|
||||||
|
center: ['50%', '67%'],
|
||||||
|
title: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
axisTick: {
|
||||||
|
distance: -20,
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
color: 'rgba(0,0,0,0.15)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
distance: -22,
|
||||||
|
length: 9,
|
||||||
|
lineStyle: {
|
||||||
|
width: 1,
|
||||||
|
color: '#000',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
pointer: {
|
||||||
|
length: '80%',
|
||||||
|
width: 4,
|
||||||
|
itemStyle: {
|
||||||
|
color: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
anchor: {
|
||||||
|
show: true,
|
||||||
|
showAbove: true,
|
||||||
|
size: 20,
|
||||||
|
itemStyle: {
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: '#fff',
|
||||||
|
shadowBlur: 20,
|
||||||
|
shadowColor: 'rgba(0, 0, 0, .25)',
|
||||||
|
color: 'auto',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
width: 10,
|
||||||
|
color: [
|
||||||
|
[0.25, 'rgba(36, 178, 118, 1)'],
|
||||||
|
[
|
||||||
|
0.4,
|
||||||
|
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(66, 147, 255, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(36, 178, 118, 1)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
0.5,
|
||||||
|
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(250, 178, 71, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(66, 147, 255, 1)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
[
|
||||||
|
1,
|
||||||
|
new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: 'rgba(250, 178, 71, 1)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: 'rgba(247, 111, 93, 1)',
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
show: false,
|
||||||
|
},
|
||||||
|
};
|
|
@ -0,0 +1,21 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<div>
|
||||||
|
<a-row :gutter="[24, 24]">
|
||||||
|
<a-col :span="24"><TopCard /> </a-col>
|
||||||
|
<a-col :span="24"><Network /></a-col>
|
||||||
|
<a-col :span="12"><Cpu /></a-col>
|
||||||
|
<a-col :span="12"><Jvm /></a-col>
|
||||||
|
</a-row>
|
||||||
|
</div>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup name="DashBoardPage">
|
||||||
|
import TopCard from './components/TopCard.vue';
|
||||||
|
import Network from './components/Network.vue';
|
||||||
|
import Cpu from './components/Cpu.vue';
|
||||||
|
import Jvm from './components/Jvm.vue';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped></style>
|
|
@ -159,7 +159,7 @@ import _ from 'lodash';
|
||||||
|
|
||||||
const tableRef = ref<Record<string, any>>({});
|
const tableRef = ref<Record<string, any>>({});
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
|
const route = useRoute();
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const current = ref({});
|
const current = ref({});
|
||||||
|
|
||||||
|
@ -276,6 +276,14 @@ const saveChange = (value: object) => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => route.query?.save,
|
||||||
|
(value) => {
|
||||||
|
value === 'true' && handlAdd();
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true },
|
||||||
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 搜索
|
* 搜索
|
||||||
* @param params
|
* @param params
|
||||||
|
|
|
@ -1032,11 +1032,10 @@ import { Store } from 'jetlinks-store';
|
||||||
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
import MonacoEditor from '@/components/MonacoEditor/index.vue';
|
||||||
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const view = route.query.view as string;
|
const NetworkType = route.query.type as string;
|
||||||
|
const view = NetworkType ? 'false' : (route.query.view as string);
|
||||||
const id = route.params.id as string;
|
const id = route.params.id as string;
|
||||||
|
|
||||||
const activeKey = ref(['1']);
|
const activeKey = ref(['1']);
|
||||||
|
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
const formRef1 = ref<FormInstance>();
|
const formRef1 = ref<FormInstance>();
|
||||||
const formRef2 = ref<FormInstance>();
|
const formRef2 = ref<FormInstance>();
|
||||||
|
@ -1250,7 +1249,6 @@ watch(
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
// { deep: true, immediate: true },
|
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
@ -1263,7 +1261,6 @@ watch(
|
||||||
updateClustersListIndex();
|
updateClustersListIndex();
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
// { deep: true, immediate: true },
|
|
||||||
);
|
);
|
||||||
watch(
|
watch(
|
||||||
() => dynamicValidateForm.cluster?.length,
|
() => dynamicValidateForm.cluster?.length,
|
||||||
|
@ -1272,6 +1269,17 @@ watch(
|
||||||
},
|
},
|
||||||
{ deep: true, immediate: true },
|
{ deep: true, immediate: true },
|
||||||
);
|
);
|
||||||
|
watch(
|
||||||
|
() => NetworkType,
|
||||||
|
(value) => {
|
||||||
|
if (value) {
|
||||||
|
const { cluster } = dynamicValidateForm;
|
||||||
|
formData.value.type = value;
|
||||||
|
cluster[0].configuration.host = '0.0.0.0';
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ deep: true, immediate: true },
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
.doc {
|
||||||
|
height: 1050px;
|
||||||
|
padding: 24px;
|
||||||
|
overflow-y: auto;
|
||||||
|
color: rgba(#000, 0.8);
|
||||||
|
font-size: 14px;
|
||||||
|
background-color: #fafafa;
|
||||||
|
|
||||||
|
.url {
|
||||||
|
padding: 8px 16px;
|
||||||
|
color: #2f54eb;
|
||||||
|
background-color: rgba(#a7bdf7, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
margin: 16px 0;
|
||||||
|
color: rgba(#000, 0.85);
|
||||||
|
font-weight: bold;
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
margin: 6px 0;
|
||||||
|
color: rgba(0, 0, 0, 0.8);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image {
|
||||||
|
margin: 16px 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,780 @@
|
||||||
|
<!-- 国标级联新增/编辑 -->
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<a-card>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form ref="formRef" layout="vertical" :model="formData">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<TitleComponent data="基本信息" />
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="cascadeName"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 84,
|
||||||
|
message: '最多可输入84个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.cascadeName"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="代理视频流"
|
||||||
|
name="proxyStream"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择代理视频流',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="formData.proxyStream"
|
||||||
|
>
|
||||||
|
<a-radio-button :value="true">
|
||||||
|
启用
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button :value="false">
|
||||||
|
禁用
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<TitleComponent data="信令服务配置" />
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
name="clusterNodeId"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择集群节点',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
集群节点
|
||||||
|
<a-tooltip
|
||||||
|
title="使用此集群节点级联到上级平台"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.clusterNodeId"
|
||||||
|
placeholder="请选择集群节点"
|
||||||
|
:options="clustersList"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="信令名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入信令名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入信令名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
label="上级SIP ID"
|
||||||
|
name="sipId"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级SIP ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.sipId"
|
||||||
|
placeholder="请输入上级SIP ID"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="上级SIP域"
|
||||||
|
name="domain"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级平台SIP域',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.domain"
|
||||||
|
placeholder="请输入上级平台SIP域"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="上级SIP 地址"
|
||||||
|
name="remoteAddress"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级SIP 地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: checkSIP,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
formData.remoteAddress
|
||||||
|
"
|
||||||
|
placeholder="请输入IP地址"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-input-number
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
v-model:value="
|
||||||
|
formData.remotePort
|
||||||
|
"
|
||||||
|
placeholder="请输入端口"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
label="本地SIP ID"
|
||||||
|
name="localSipId"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入网关侧的SIP ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.localSipId"
|
||||||
|
placeholder="网关侧的SIP ID"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
name="host"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择SIP本地地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: checkLocalSIP,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template #label>
|
||||||
|
<span>
|
||||||
|
SIP本地地址
|
||||||
|
<a-tooltip
|
||||||
|
title="使用指定的网卡和端口进行请求"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="QuestionCircleOutlined"
|
||||||
|
style="margin-left: 2px"
|
||||||
|
/>
|
||||||
|
</a-tooltip>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.host"
|
||||||
|
placeholder="请选择IP地址"
|
||||||
|
:options="allList"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.port"
|
||||||
|
placeholder="请选择端口"
|
||||||
|
:options="allListPorts"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="SIP远程地址"
|
||||||
|
name="publicHost"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入SIP远程地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
validator: checkPublicSIP,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-input
|
||||||
|
v-model:value="
|
||||||
|
formData.publicHost
|
||||||
|
"
|
||||||
|
placeholder="请输入IP地址"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-input-number
|
||||||
|
:min="1"
|
||||||
|
:max="65535"
|
||||||
|
v-model:value="
|
||||||
|
formData.publicPort
|
||||||
|
"
|
||||||
|
placeholder="请输入端口"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
label="传输协议"
|
||||||
|
name="transport"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择传输协议',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="formData.transport"
|
||||||
|
@change="setPorts"
|
||||||
|
>
|
||||||
|
<a-radio-button value="UDP">
|
||||||
|
UDP
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="TCP">
|
||||||
|
TCP
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="用户"
|
||||||
|
name="user"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入用户',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.user"
|
||||||
|
placeholder="请输入用户"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="接入密码"
|
||||||
|
name="password"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入接入密码',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-password
|
||||||
|
v-model:value="formData.password"
|
||||||
|
placeholder="请输入接入密码"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="厂商"
|
||||||
|
name="manufacturer"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入厂商',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.manufacturer"
|
||||||
|
placeholder="请输入厂商"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="型号"
|
||||||
|
name="model"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入型号',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.model"
|
||||||
|
placeholder="请输入型号"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="版本号"
|
||||||
|
name="firmware"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入版本号',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.firmware"
|
||||||
|
placeholder="请输入版本号"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="心跳周期(秒)"
|
||||||
|
name="keepaliveInterval"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入心跳周期',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:min="1"
|
||||||
|
:max="10000"
|
||||||
|
v-model:value="
|
||||||
|
formData.keepaliveInterval
|
||||||
|
"
|
||||||
|
placeholder="请输入心跳周期"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="注册间隔(秒)"
|
||||||
|
name="registerInterval"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入注册间隔',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
:min="1"
|
||||||
|
:max="10000"
|
||||||
|
v-model:value="
|
||||||
|
formData.registerInterval
|
||||||
|
"
|
||||||
|
placeholder="请输入注册间隔"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item>
|
||||||
|
<a-button
|
||||||
|
type="primary"
|
||||||
|
@click="handleSubmit"
|
||||||
|
:loading="btnLoading"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</a-button>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<div class="doc">
|
||||||
|
<h1>1.概述</h1>
|
||||||
|
<div>
|
||||||
|
配置国标级联,平台可以将已经接入到自身的摄像头共享给第三方调用播放。
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<a-alert
|
||||||
|
message="注:该配置只用于将本平台向上级联至第三方平台,如需第三方平台向上级联至本平台,请在“视频设备”页面新增设备时选择“GB/T28181”接入方式。"
|
||||||
|
type="info"
|
||||||
|
show-icon
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h1>2.配置说明</h1>
|
||||||
|
<div>
|
||||||
|
以下配置说明以将本平台数据级联到LiveGBS平台为例。
|
||||||
|
</div>
|
||||||
|
<h2>1、上级SIP ID</h2>
|
||||||
|
<div>请填写第三方平台中配置的<b>SIP ID</b>。</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image
|
||||||
|
width="100%"
|
||||||
|
:src="getImage('/northbound/doc2.png')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h2>2、上级SIP 域</h2>
|
||||||
|
<div>请填写第三方平台中配置的<b>SIP ID域</b>。</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image
|
||||||
|
width="100%"
|
||||||
|
:src="getImage('/northbound/doc1.png')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h2>3、上级SIP 地址</h2>
|
||||||
|
<div>请填写第三方平台中配置的<b>SIP ID地址</b>。</div>
|
||||||
|
<div class="image">
|
||||||
|
<a-image
|
||||||
|
width="100%"
|
||||||
|
:src="getImage('/northbound/doc3.png')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<h2>4、本地SIP ID</h2>
|
||||||
|
<div>
|
||||||
|
请填写本地的<b>SIP ID地址</b>。
|
||||||
|
地址由中心编码(8位)、行业编码(2位)、类型编码(3位)和序号(7位)四个码段共20位十
|
||||||
|
进制数字字符构成。详细规则请参见《GB/T28181-2016》中附录D部分。
|
||||||
|
</div>
|
||||||
|
<h2>5、SIP本地地址</h2>
|
||||||
|
<div>
|
||||||
|
请选择<b>指定的网卡和端口</b>,如有疑问请联系系统运维人员。
|
||||||
|
</div>
|
||||||
|
<h2>6、用户</h2>
|
||||||
|
<div>
|
||||||
|
部分平台有基于用户和接入密码的特殊认证。通常情况下,请填写<b
|
||||||
|
>本地SIP ID</b
|
||||||
|
>值。
|
||||||
|
</div>
|
||||||
|
<h2>7、接入密码</h2>
|
||||||
|
<div>
|
||||||
|
需与上级平台设置的接入密码一致,用于身份认证。
|
||||||
|
</div>
|
||||||
|
<h2>8、厂商/型号/版本号</h2>
|
||||||
|
<div>
|
||||||
|
本平台将以“设备”的身份级联到上级平台,请设置本平台在上级平台中显示的厂商、型号、版本号。
|
||||||
|
</div>
|
||||||
|
<h2>9、心跳周期</h2>
|
||||||
|
<div>
|
||||||
|
需与上级平台设置的心跳周期保持一致,通常默认60秒。
|
||||||
|
</div>
|
||||||
|
<h2>10、注册间隔</h2>
|
||||||
|
<div>
|
||||||
|
若SIP代理通过注册方式校时,其注册间隔时间宜设置为小于
|
||||||
|
SIP代理与 SIP服务器出现1s误 差所经过的运行时间。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-card>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import CascadeApi from '@/api/media/cascade';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
id: route.query.id || undefined,
|
||||||
|
// name: '',
|
||||||
|
cascadeName: '',
|
||||||
|
proxyStream: false,
|
||||||
|
// 以下字段, 提交时需提取到sipConfigs[{}]字段当中
|
||||||
|
clusterNodeId: '',
|
||||||
|
name: '',
|
||||||
|
sipId: '',
|
||||||
|
domain: '',
|
||||||
|
remoteAddress: '',
|
||||||
|
remotePort: undefined,
|
||||||
|
localSipId: '',
|
||||||
|
host: '',
|
||||||
|
port: undefined,
|
||||||
|
// remotePublic: {
|
||||||
|
// host: '',
|
||||||
|
// port: undefined,
|
||||||
|
// },
|
||||||
|
publicHost: '',
|
||||||
|
publicPort: undefined,
|
||||||
|
transport: 'UDP',
|
||||||
|
user: '',
|
||||||
|
password: '',
|
||||||
|
manufacturer: '',
|
||||||
|
model: '',
|
||||||
|
firmware: '',
|
||||||
|
keepaliveInterval: '60',
|
||||||
|
registerInterval: '3600',
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取集群节点
|
||||||
|
*/
|
||||||
|
const clustersList = ref([]);
|
||||||
|
const getClustersList = async () => {
|
||||||
|
const { result } = await CascadeApi.clusters();
|
||||||
|
clustersList.value = result.map((m: any) => ({
|
||||||
|
label: m.name,
|
||||||
|
value: m.id,
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
getClustersList();
|
||||||
|
/**
|
||||||
|
* SIP本地地址
|
||||||
|
*/
|
||||||
|
const allList = ref<any[]>([]);
|
||||||
|
const getAllList = async () => {
|
||||||
|
const { result } = await CascadeApi.all();
|
||||||
|
allList.value = result.map((m: any) => ({
|
||||||
|
label: m.host,
|
||||||
|
value: m.host,
|
||||||
|
}));
|
||||||
|
setPorts();
|
||||||
|
};
|
||||||
|
getAllList();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 传输协议改变, 获取对应的端口
|
||||||
|
*/
|
||||||
|
const allListPorts = ref([]);
|
||||||
|
const setPorts = () => {
|
||||||
|
allListPorts.value = allList.value.find(
|
||||||
|
(f: any) => f.host === formData.value.host,
|
||||||
|
)?.ports[formData.value.transport || ''];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详情
|
||||||
|
*/
|
||||||
|
const getDetail = async () => {
|
||||||
|
if (!route.query.id) return;
|
||||||
|
const res = await CascadeApi.detail(route.query.id as string);
|
||||||
|
const { id, name, proxyStream, sipConfigs } = res.result;
|
||||||
|
formData.value = {
|
||||||
|
id,
|
||||||
|
cascadeName: name,
|
||||||
|
proxyStream,
|
||||||
|
clusterNodeId: sipConfigs[0]?.clusterNodeId,
|
||||||
|
name: sipConfigs[0]?.name,
|
||||||
|
sipId: sipConfigs[0]?.sipId,
|
||||||
|
domain: sipConfigs[0]?.domain,
|
||||||
|
remoteAddress: sipConfigs[0]?.remoteAddress,
|
||||||
|
remotePort: sipConfigs[0]?.remotePort,
|
||||||
|
localSipId: sipConfigs[0]?.localSipId,
|
||||||
|
host: sipConfigs[0]?.host,
|
||||||
|
port: sipConfigs[0]?.port,
|
||||||
|
publicHost: sipConfigs[0]?.publicHost,
|
||||||
|
publicPort: sipConfigs[0]?.publicPort,
|
||||||
|
transport: sipConfigs[0]?.transport,
|
||||||
|
user: sipConfigs[0]?.user,
|
||||||
|
password: sipConfigs[0]?.password,
|
||||||
|
manufacturer: sipConfigs[0]?.manufacturer,
|
||||||
|
model: sipConfigs[0]?.model,
|
||||||
|
firmware: sipConfigs[0]?.firmware,
|
||||||
|
keepaliveInterval: sipConfigs[0]?.keepaliveInterval,
|
||||||
|
registerInterval: sipConfigs[0]?.registerInterval,
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('formData.value: ', formData.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDetail();
|
||||||
|
});
|
||||||
|
|
||||||
|
const regDomain =
|
||||||
|
/[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?/;
|
||||||
|
/**
|
||||||
|
* 上级SIP地址 字段验证
|
||||||
|
* @param _
|
||||||
|
* @param value 此处绑定的是 remoteAddress
|
||||||
|
*/
|
||||||
|
const checkSIP = (_: any, value: string) => {
|
||||||
|
return checkHost(value, formData.value.remotePort);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* SIP远程地址 字段验证
|
||||||
|
* @param _
|
||||||
|
* @param value 此处绑定的是 publicHost
|
||||||
|
*/
|
||||||
|
const checkPublicSIP = (_: any, value: string) => {
|
||||||
|
return checkHost(value, formData.value.publicPort);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 字段验证
|
||||||
|
* @param host ip
|
||||||
|
* @param port 端口
|
||||||
|
*/
|
||||||
|
const checkHost = (host: string, port: string | number | undefined) => {
|
||||||
|
if (!host) {
|
||||||
|
return Promise.resolve();
|
||||||
|
} else if (!host) {
|
||||||
|
return Promise.reject(new Error('请输入IP 地址'));
|
||||||
|
} else if (host && !regDomain.test(host)) {
|
||||||
|
return Promise.reject(new Error('请输入正确的IP地址'));
|
||||||
|
} else if (!port) {
|
||||||
|
return Promise.reject(new Error('请输入端口'));
|
||||||
|
} else if ((host && Number(host) < 1) || Number(host) > 65535) {
|
||||||
|
return Promise.reject(new Error('端口请输入1~65535之间的正整数'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SIP本地地址 字段验证
|
||||||
|
* @param _
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
const checkLocalSIP = (_: any, value: string) => {
|
||||||
|
if (!value) {
|
||||||
|
return Promise.resolve();
|
||||||
|
} else if (!value) {
|
||||||
|
return Promise.reject(new Error('请选择IP地址'));
|
||||||
|
} else if (!formData.value.port) {
|
||||||
|
return Promise.reject(new Error('请选择端口'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const formRef = ref();
|
||||||
|
const btnLoading = ref<boolean>(false);
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// console.log('formData.value: ', formData.value);
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async () => {
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
cascadeName,
|
||||||
|
proxyStream,
|
||||||
|
publicHost,
|
||||||
|
publicPort,
|
||||||
|
...extraFormData
|
||||||
|
} = formData.value;
|
||||||
|
const params = {
|
||||||
|
id,
|
||||||
|
name: cascadeName,
|
||||||
|
proxyStream,
|
||||||
|
sipConfigs: [
|
||||||
|
{
|
||||||
|
...extraFormData,
|
||||||
|
remotePublic: {
|
||||||
|
host: publicHost,
|
||||||
|
port: publicPort,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
btnLoading.value = true;
|
||||||
|
const res = formData.value.id
|
||||||
|
? await CascadeApi.update(params)
|
||||||
|
: await CascadeApi.save(params);
|
||||||
|
btnLoading.value = false;
|
||||||
|
if (res.success) {
|
||||||
|
message.success('操作成功');
|
||||||
|
router.back();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.log('err: ', err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import './index.less';
|
||||||
|
</style>
|
|
@ -2,17 +2,19 @@
|
||||||
<page-container>
|
<page-container>
|
||||||
<Search
|
<Search
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
target="notice-config"
|
target="media-cascade"
|
||||||
@search="handleSearch"
|
@search="handleSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<JTable
|
<JTable
|
||||||
ref="listRef"
|
ref="listRef"
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="DeviceApi.list"
|
:request="(e:any) => lastValueFrom(e)"
|
||||||
:defaultParams="{
|
:defaultParams="{
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
}"
|
}"
|
||||||
:params="params"
|
:params="params"
|
||||||
|
:gridColumn="2"
|
||||||
>
|
>
|
||||||
<template #headerTitle>
|
<template #headerTitle>
|
||||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||||
|
@ -23,45 +25,37 @@
|
||||||
:actions="getActions(slotProps, 'card')"
|
:actions="getActions(slotProps, 'card')"
|
||||||
v-bind="slotProps"
|
v-bind="slotProps"
|
||||||
:showStatus="true"
|
:showStatus="true"
|
||||||
:status="
|
:status="slotProps.status?.value"
|
||||||
slotProps.state.value === 'online' ? 'success' : 'error'
|
:statusText="slotProps.status?.text"
|
||||||
"
|
:statusNames="{
|
||||||
:statusText="slotProps.state.text"
|
enabled: 'success',
|
||||||
:statusNames="{ success: 'success', error: 'error' }"
|
disabled: 'error',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
<template #img>
|
<template #img>
|
||||||
<slot name="img">
|
<slot name="img">
|
||||||
<img :src="getImage('/device-media.png')" />
|
<img
|
||||||
|
:src="
|
||||||
|
getImage('/device/instance/device-card.png')
|
||||||
|
"
|
||||||
|
/>
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<h3 class="card-item-content-title">
|
<h3 class="card-item-content-title">
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</h3>
|
</h3>
|
||||||
<a-row>
|
<p>通道数量:{{ slotProps.count }}</p>
|
||||||
<a-col :span="12">
|
<Ellipsis>
|
||||||
<div class="card-item-content-text">厂商</div>
|
<a-badge
|
||||||
<div>{{ slotProps.manufacturer }}</div>
|
:text="`sip:${slotProps.sipConfigs[0]?.sipId}@${slotProps.sipConfigs[0]?.hostAndPort}`"
|
||||||
</a-col>
|
:status="
|
||||||
<a-col :span="12">
|
slotProps.status?.value === 'enabled'
|
||||||
<div class="card-item-content-text">
|
? 'success'
|
||||||
通道数量
|
: 'error'
|
||||||
</div>
|
"
|
||||||
<div>{{ slotProps.channelNumber }}</div>
|
/>
|
||||||
</a-col>
|
</Ellipsis>
|
||||||
<a-col :span="12">
|
|
||||||
<div class="card-item-content-text">型号</div>
|
|
||||||
<div>{{ slotProps.model }}</div>
|
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
|
||||||
<div class="card-item-content-text">
|
|
||||||
接入方式
|
|
||||||
</div>
|
|
||||||
<!-- <div>
|
|
||||||
{{ providerType[slotProps.provider] }}
|
|
||||||
</div> -->
|
|
||||||
</a-col>
|
|
||||||
</a-row>
|
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
|
@ -73,9 +67,20 @@
|
||||||
v-bind="item.popConfirm"
|
v-bind="item.popConfirm"
|
||||||
:disabled="item.disabled"
|
:disabled="item.disabled"
|
||||||
>
|
>
|
||||||
<a-button :disabled="item.disabled">
|
<a-button
|
||||||
|
:disabled="item.disabled"
|
||||||
|
v-if="item.key === 'delete'"
|
||||||
|
>
|
||||||
<AIcon type="DeleteOutlined" />
|
<AIcon type="DeleteOutlined" />
|
||||||
</a-button>
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
:disabled="item.disabled"
|
||||||
|
@click="item.onClick"
|
||||||
|
v-else
|
||||||
|
>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item.text }}</span>
|
||||||
|
</a-button>
|
||||||
</a-popconfirm>
|
</a-popconfirm>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-button
|
<a-button
|
||||||
|
@ -87,9 +92,53 @@
|
||||||
</a-button>
|
</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<!-- <PermissionButton
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
:hasPermission="`media/Cascade:${item.key}`"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
v-if="item.key === 'delete'"
|
||||||
|
/>
|
||||||
|
<template v-else>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item?.text }}</span>
|
||||||
|
</template>
|
||||||
|
</PermissionButton> -->
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
<template #sipId="slotProps">
|
||||||
|
{{ slotProps.sipConfigs[0]?.sipId }}
|
||||||
|
</template>
|
||||||
|
<template #publicHost="slotProps">
|
||||||
|
{{ slotProps.sipConfigs[0]?.publicHost }}
|
||||||
|
</template>
|
||||||
|
<template #status="slotProps">
|
||||||
|
<a-badge
|
||||||
|
:text="slotProps.status?.text"
|
||||||
|
:status="
|
||||||
|
slotProps.status?.value === 'enabled'
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #onlineStatus="slotProps">
|
||||||
|
<a-badge
|
||||||
|
:text="slotProps.onlineStatus?.text"
|
||||||
|
:status="
|
||||||
|
slotProps.onlineStatus?.value === 'online'
|
||||||
|
? 'success'
|
||||||
|
: 'error'
|
||||||
|
"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip
|
<a-tooltip
|
||||||
|
@ -123,6 +172,24 @@
|
||||||
/></a-button>
|
/></a-button>
|
||||||
</a-button>
|
</a-button>
|
||||||
</a-tooltip>
|
</a-tooltip>
|
||||||
|
<!-- <template
|
||||||
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
|
:key="i.key"
|
||||||
|
>
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="i.disabled"
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0px"
|
||||||
|
:hasPermission="`device/Instance:${i.key}`"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template> -->
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -131,6 +198,7 @@
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import DeviceApi from '@/api/media/device';
|
import DeviceApi from '@/api/media/device';
|
||||||
|
import CascadeApi from '@/api/media/cascade';
|
||||||
import type { ActionsType } from '@/components/Table/index.vue';
|
import type { ActionsType } from '@/components/Table/index.vue';
|
||||||
import { message } from 'ant-design-vue';
|
import { message } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
|
@ -156,14 +224,14 @@ const columns = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '上级SIP ID',
|
title: '上级SIP ID',
|
||||||
dataIndex: 'sipConfigs',
|
dataIndex: 'sipId',
|
||||||
key: 'sipConfigs',
|
key: 'sipId',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '上级SIP 地址',
|
title: '上级SIP 地址',
|
||||||
dataIndex: 'sipConfigs',
|
dataIndex: 'publicHost',
|
||||||
key: 'sipConfigs',
|
key: 'publicHost',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -217,17 +285,35 @@ const columns = [
|
||||||
* @param params
|
* @param params
|
||||||
*/
|
*/
|
||||||
const handleSearch = (e: any) => {
|
const handleSearch = (e: any) => {
|
||||||
// console.log('handleSearch:', e);
|
|
||||||
params.value = e;
|
params.value = e;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理表格数据
|
||||||
|
* @param params
|
||||||
|
*/
|
||||||
|
const lastValueFrom = async (params: any) => {
|
||||||
|
const res = await CascadeApi.list(params);
|
||||||
|
res.result.data.forEach(async (item: any) => {
|
||||||
|
const resp = await queryBindChannel(item.id);
|
||||||
|
item.count = resp.result.total;
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询通道数量
|
||||||
|
* @param id
|
||||||
|
*/
|
||||||
|
const queryBindChannel = async (id: string) => {
|
||||||
|
return await CascadeApi.queryCount(id);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 新增
|
* 新增
|
||||||
*/
|
*/
|
||||||
const handleAdd = () => {
|
const handleAdd = () => {
|
||||||
menuStory.jumpPage('media/Device/Save', {
|
menuStory.jumpPage('media/Cascade/Save');
|
||||||
id: ':id',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const getActions = (
|
const getActions = (
|
||||||
|
@ -245,7 +331,7 @@ const getActions = (
|
||||||
icon: 'EditOutlined',
|
icon: 'EditOutlined',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
menuStory.jumpPage(
|
menuStory.jumpPage(
|
||||||
'media/Device/Save',
|
'media/Cascade/Save',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
id: data.id,
|
id: data.id,
|
||||||
|
@ -255,58 +341,79 @@ const getActions = (
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'view',
|
key: 'view',
|
||||||
text: '查看通道',
|
text: '选择通道',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: '查看通道',
|
title: '选择通道',
|
||||||
},
|
},
|
||||||
icon: 'PartitionOutlined',
|
icon: 'LinkOutlined',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// router.push(
|
|
||||||
// `/media/device/Channel?id=${data.id}&type=${data.provider}`,
|
|
||||||
// );
|
|
||||||
menuStory.jumpPage(
|
menuStory.jumpPage(
|
||||||
'media/Device/Channel',
|
'media/Cascade/Channel',
|
||||||
{},
|
{},
|
||||||
{
|
{
|
||||||
id: data.id,
|
id: data.id,
|
||||||
type: data.provider,
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'debug',
|
key: 'debug',
|
||||||
text: '更新通道',
|
text: '推送',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title:
|
title:
|
||||||
data.provider === 'fixed-media'
|
data.status?.value === 'disabled'
|
||||||
? '固定地址无法更新通道'
|
? '禁用状态下不可推送'
|
||||||
: data.state.value === 'offline'
|
: '推送',
|
||||||
? '设备已离线'
|
|
||||||
: data.state.value === 'notActive'
|
|
||||||
? '设备已禁用'
|
|
||||||
: '',
|
|
||||||
},
|
},
|
||||||
disabled:
|
disabled: data.status?.value === 'disabled',
|
||||||
data.state.value === 'offline' ||
|
icon: 'ShareAltOutlined',
|
||||||
data.state.value === 'notActive' ||
|
|
||||||
data.provider === 'fixed-media',
|
|
||||||
icon: 'SyncOutlined',
|
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
// updateChannel()
|
// updateChannel()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'action',
|
||||||
|
text: data.status?.value === 'enabled' ? '禁用' : '启用',
|
||||||
|
tooltip: {
|
||||||
|
title: data.status?.value === 'enabled' ? '禁用' : '启用',
|
||||||
|
},
|
||||||
|
icon:
|
||||||
|
data.status?.value === 'enabled'
|
||||||
|
? 'StopOutlined'
|
||||||
|
: 'PlayCircleOutlined',
|
||||||
|
popConfirm: {
|
||||||
|
title: `确认${
|
||||||
|
data.status?.value === 'enabled' ? '禁用' : '启用'
|
||||||
|
}?`,
|
||||||
|
onConfirm: async () => {
|
||||||
|
let res =
|
||||||
|
data.status.value === 'enabled'
|
||||||
|
? await CascadeApi.disabled(data.id)
|
||||||
|
: await CascadeApi.enabled(data.id);
|
||||||
|
|
||||||
|
if (res.success) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
listRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: 'delete',
|
key: 'delete',
|
||||||
text: '删除',
|
text: '删除',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
title: '在线设备无法删除',
|
title:
|
||||||
|
data.status?.value === 'enabled'
|
||||||
|
? '请先禁用, 再删除'
|
||||||
|
: '删除',
|
||||||
},
|
},
|
||||||
disabled: data.state.value === 'online',
|
disabled: data.status?.value === 'enabled',
|
||||||
popConfirm: {
|
popConfirm: {
|
||||||
title: '确认删除?',
|
title: '确认删除?',
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
const resp = await DeviceApi.del(data.id);
|
const resp = await CascadeApi.del(data.id);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
message.success('操作成功!');
|
message.success('操作成功!');
|
||||||
listRef.value?.reload();
|
listRef.value?.reload();
|
||||||
|
|
|
@ -31,7 +31,7 @@ type SipConfig = {
|
||||||
transport: string;
|
transport: string;
|
||||||
user: string;
|
user: string;
|
||||||
};
|
};
|
||||||
type CascadeItem = {
|
export type CascadeItem = {
|
||||||
mediaServerId: string;
|
mediaServerId: string;
|
||||||
onlineStatus: State;
|
onlineStatus: State;
|
||||||
proxyStream: boolean;
|
proxyStream: boolean;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<!-- 通知模板详情 -->
|
<!-- 视频设备新增/编辑 -->
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<a-card>
|
<a-card>
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
<JTable
|
<JTable
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:request="queryList"
|
:request="queryList"
|
||||||
|
:gridColumn="3"
|
||||||
ref="tableRef"
|
ref="tableRef"
|
||||||
:defaultParams="{
|
:defaultParams="{
|
||||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||||
|
@ -42,7 +43,7 @@
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis>
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span style="font-weight: 600; font-size: 16px">
|
<span style="font-weight: 600; font-size: 16px">
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -70,48 +71,25 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<a-tooltip
|
<PermissionButton
|
||||||
v-bind="item.tooltip"
|
|
||||||
:title="item.disabled && item.tooltip.title"
|
|
||||||
v-if="
|
v-if="
|
||||||
item.key != 'trigger' ||
|
item.key != 'trigger' ||
|
||||||
slotProps.sceneTriggerType == 'manual'
|
slotProps.sceneTriggerType == 'manual'
|
||||||
"
|
"
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{ ...item.tootip }"
|
||||||
|
@click="item.onClick"
|
||||||
>
|
>
|
||||||
<a-popconfirm
|
<AIcon
|
||||||
v-if="item.popConfirm"
|
type="DeleteOutlined"
|
||||||
v-bind="item.popConfirm"
|
v-if="item.key === 'delete'"
|
||||||
:disabled="item.disabled"
|
/>
|
||||||
okText="确定"
|
|
||||||
cancelText="取消"
|
|
||||||
>
|
|
||||||
<a-button :disabled="item.disabled">
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item?.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-button
|
<AIcon :type="item.icon" />
|
||||||
:disabled="item.disabled"
|
<span>{{ item?.text }}</span>
|
||||||
@click="item.onClick"
|
|
||||||
>
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item?.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
</template>
|
||||||
</a-tooltip>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
@ -151,45 +129,29 @@
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip
|
<template
|
||||||
v-for="i in getActions(slotProps)"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
v-bind="i.tooltip"
|
|
||||||
>
|
>
|
||||||
<span
|
<PermissionButton
|
||||||
v-if="
|
v-if="
|
||||||
i.key != 'trigger' ||
|
i.key != 'trigger' ||
|
||||||
slotProps.sceneTriggerType == 'manual'
|
slotProps.sceneTriggerType == 'manual'
|
||||||
"
|
"
|
||||||
|
:disabled="i.disabled"
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0px"
|
||||||
>
|
>
|
||||||
<a-popconfirm
|
<template #icon
|
||||||
v-if="i.popConfirm"
|
><AIcon :type="i.icon"
|
||||||
v-bind="i.popConfirm"
|
/></template>
|
||||||
okText="确定"
|
</PermissionButton>
|
||||||
cancelText="取消"
|
</template>
|
||||||
>
|
|
||||||
<a-button
|
|
||||||
:disabled="i.disabled"
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
><AIcon :type="i.icon"
|
|
||||||
/></a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
v-else
|
|
||||||
@click="i.onClick && i.onClick(slotProps)"
|
|
||||||
>
|
|
||||||
<a-button
|
|
||||||
:disabled="i.disabled"
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
><AIcon :type="i.icon"
|
|
||||||
/></a-button>
|
|
||||||
</a-button>
|
|
||||||
</span>
|
|
||||||
</a-tooltip>
|
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
@ -214,7 +176,6 @@ import { message } from 'ant-design-vue';
|
||||||
import { getImage } from '@/utils/comm';
|
import { getImage } from '@/utils/comm';
|
||||||
import { useMenuStore } from '@/store/menu';
|
import { useMenuStore } from '@/store/menu';
|
||||||
import encodeQuery from '@/utils/encodeQuery';
|
import encodeQuery from '@/utils/encodeQuery';
|
||||||
import { useStorage } from '@vueuse/core';
|
|
||||||
const params = ref<Record<string, any>>({});
|
const params = ref<Record<string, any>>({});
|
||||||
let isAdd = ref<number>(0);
|
let isAdd = ref<number>(0);
|
||||||
let title = ref<string>('');
|
let title = ref<string>('');
|
||||||
|
@ -290,8 +251,11 @@ const columns = [
|
||||||
sorts: { createTime: 'desc' },
|
sorts: { createTime: 'desc' },
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
if(res.status === 200){
|
if (res.status === 200) {
|
||||||
return res.result.map((item:any) => ({label:item.name, value:item.id}))
|
return res.result.map((item: any) => ({
|
||||||
|
label: item.name,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
return [];
|
return [];
|
||||||
},
|
},
|
||||||
|
@ -303,26 +267,26 @@ const columns = [
|
||||||
key: 'state',
|
key: 'state',
|
||||||
scopedSlots: true,
|
scopedSlots: true,
|
||||||
search: {
|
search: {
|
||||||
type: 'select',
|
type: 'select',
|
||||||
options: [
|
options: [
|
||||||
{
|
{
|
||||||
label: '正常',
|
label: '正常',
|
||||||
value: 'enabled',
|
value: 'enabled',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: '禁用',
|
label: '禁用',
|
||||||
value: 'disabled',
|
value: 'disabled',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '说明',
|
title: '说明',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
key: 'description',
|
key: 'description',
|
||||||
search:{
|
search: {
|
||||||
type:'string',
|
type: 'string',
|
||||||
}
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
title: '操作',
|
||||||
|
@ -396,7 +360,11 @@ const getActions = (
|
||||||
|
|
||||||
icon: 'EditOutlined',
|
icon: 'EditOutlined',
|
||||||
onClick: () => {
|
onClick: () => {
|
||||||
menuStory.jumpPage('rule-engine/Alarm/Configuration/Save',{},{id:data.id});
|
menuStory.jumpPage(
|
||||||
|
'rule-engine/Alarm/Configuration/Save',
|
||||||
|
{},
|
||||||
|
{ id: data.id },
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -456,8 +424,6 @@ const getActions = (
|
||||||
icon: 'DeleteOutlined',
|
icon: 'DeleteOutlined',
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
if (type === 'card')
|
|
||||||
return actions.filter((i: ActionsType) => i.key !== 'view');
|
|
||||||
return actions;
|
return actions;
|
||||||
};
|
};
|
||||||
const add = () => {
|
const add = () => {
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<Search :columns="columns" target="alarm-log-detail"></Search>
|
||||||
|
<JTable :columns="columns" model="TABLE" :request="queryList"></JTable>
|
||||||
|
</page-container>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
const columns = [{
|
||||||
|
title:'告警时间',
|
||||||
|
dataIndex:'alarmTime',
|
||||||
|
key:'alarmTime',
|
||||||
|
search:{
|
||||||
|
type:'date'
|
||||||
|
}
|
||||||
|
},{
|
||||||
|
title:'告警名称',
|
||||||
|
dataIndex:'alarmConfigName',
|
||||||
|
key:'alarmConfigName',
|
||||||
|
},{
|
||||||
|
title:'说明',
|
||||||
|
dataIndex:'description',
|
||||||
|
key:'description'
|
||||||
|
},{
|
||||||
|
title:'操作',
|
||||||
|
dataIndex:'action',
|
||||||
|
key:'action'
|
||||||
|
}]
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取详情列表
|
||||||
|
*/
|
||||||
|
const queryList = () =>{
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -0,0 +1,79 @@
|
||||||
|
<template>
|
||||||
|
<a-modal
|
||||||
|
title="告警处理"
|
||||||
|
okText="确定"
|
||||||
|
cancelText="取消"
|
||||||
|
visible
|
||||||
|
@cancel="handleCancel"
|
||||||
|
@ok="handleSave"
|
||||||
|
destroyOnClose
|
||||||
|
:confirmLoading="loading"
|
||||||
|
>
|
||||||
|
<a-form :rules="rules" layout="vertical" ref="formRef" :model="form">
|
||||||
|
<a-form-item label="处理结果" name="describe">
|
||||||
|
<a-textarea
|
||||||
|
:rows="8"
|
||||||
|
:maxlength="200"
|
||||||
|
showCount
|
||||||
|
placeholder="请输入处理结果"
|
||||||
|
v-model:value="form.describe"
|
||||||
|
></a-textarea>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { handleLog } from '@/api/rule-engine/log';
|
||||||
|
import { onlyMessage } from '@/utils/comm';
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const loading = ref<boolean>(false);
|
||||||
|
const formRef = ref();
|
||||||
|
const rules = {
|
||||||
|
describe: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入处理结果',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
const form = reactive({
|
||||||
|
describe: '',
|
||||||
|
});
|
||||||
|
let visible = ref(true);
|
||||||
|
const emit = defineEmits(['closeSolve'])
|
||||||
|
const handleCancel = () => {
|
||||||
|
emit('closeSolve');
|
||||||
|
};
|
||||||
|
const handleSave = () => {
|
||||||
|
loading.value = true;
|
||||||
|
formRef.value
|
||||||
|
.validate()
|
||||||
|
.then(async () => {
|
||||||
|
const res = await handleLog({
|
||||||
|
describe: form.describe,
|
||||||
|
type: 'user',
|
||||||
|
state: 'normal',
|
||||||
|
alarmRecordId: props.data?.current?.id || '',
|
||||||
|
alarmConfigId: props.data?.current?.alarmConfigId || '',
|
||||||
|
alarmTime: props?.data?.current?.alarmTime || '',
|
||||||
|
});
|
||||||
|
if (res.status === 200) {
|
||||||
|
onlyMessage('操作成功!');
|
||||||
|
} else {
|
||||||
|
onlyMessage('操作失败!', 'error');
|
||||||
|
}
|
||||||
|
loading.value = false;
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.log(error);
|
||||||
|
loading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="less" scoped>
|
||||||
|
</style>
|
|
@ -24,11 +24,18 @@
|
||||||
v-if="props.type === 'org'"
|
v-if="props.type === 'org'"
|
||||||
@search="search"
|
@search="search"
|
||||||
></Search>
|
></Search>
|
||||||
<JTable :columns="columns" :request="handleSearch" :params="params">
|
<JTable
|
||||||
|
:columns="columns"
|
||||||
|
:request="handleSearch"
|
||||||
|
:params="params"
|
||||||
|
:gridColumn="2"
|
||||||
|
model="CARD"
|
||||||
|
>
|
||||||
<template #card="slotProps">
|
<template #card="slotProps">
|
||||||
<CardBox
|
<CardBox
|
||||||
:value="slotProps"
|
:value="slotProps"
|
||||||
v-bind="slotProps"
|
v-bind="slotProps"
|
||||||
|
:actions="getActions(slotProps, 'card')"
|
||||||
:statusText="
|
:statusText="
|
||||||
data.defaultLevel.find(
|
data.defaultLevel.find(
|
||||||
(i) => i.level === slotProps.level,
|
(i) => i.level === slotProps.level,
|
||||||
|
@ -39,7 +46,7 @@
|
||||||
<img :src="imgMap.get(slotProps.targetType)" alt="" />
|
<img :src="imgMap.get(slotProps.targetType)" alt="" />
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis>
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span style="font-weight: 500">
|
<span style="font-weight: 500">
|
||||||
{{ slotProps.alarmName }}
|
{{ slotProps.alarmName }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -90,10 +97,25 @@
|
||||||
</span>
|
</span>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
|
</template>
|
||||||
|
<template #actions="item">
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="item.key === 'solve' && slotProps.state.value ==='normal'"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item?.text }}</span>
|
||||||
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
<SolveLog :data="data" v-if="data.solveVisible" @closeSolve="closeSolve"/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
@ -111,6 +133,11 @@ import { useAlarmStore } from '@/store/alarm';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { Store } from 'jetlinks-store';
|
import { Store } from 'jetlinks-store';
|
||||||
import moment from 'moment';
|
import moment from 'moment';
|
||||||
|
import type { ActionsType } from '@/components/Table';
|
||||||
|
import SolveLog from '../SolveLog/index.vue'
|
||||||
|
import { useMenuStore } from '@/store/menu';
|
||||||
|
const menuStory = useMenuStore();
|
||||||
|
|
||||||
const alarmStore = useAlarmStore();
|
const alarmStore = useAlarmStore();
|
||||||
const { data } = storeToRefs(alarmStore);
|
const { data } = storeToRefs(alarmStore);
|
||||||
const getDefaulitLevel = () => {
|
const getDefaulitLevel = () => {
|
||||||
|
@ -156,11 +183,11 @@ const columns = [
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '最近告警事件',
|
title: '最近告警时间',
|
||||||
dataIndex: 'alarmTime',
|
dataIndex: 'alarmTime',
|
||||||
key: 'alarmTime',
|
key: 'alarmTime',
|
||||||
search: {
|
search: {
|
||||||
type: 'dateTime',
|
type: 'date',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -254,12 +281,7 @@ let param = reactive({
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
terms: [],
|
terms: [],
|
||||||
});
|
});
|
||||||
// let dataSource = reactive({
|
|
||||||
// data: [],
|
|
||||||
// pageSize: 10,
|
|
||||||
// pageIndex: 0,
|
|
||||||
// total: 0,
|
|
||||||
// });
|
|
||||||
const handleSearch = async (params: any) => {
|
const handleSearch = async (params: any) => {
|
||||||
const resp = await query(params);
|
const resp = await query(params);
|
||||||
if (resp.status === 200) {
|
if (resp.status === 200) {
|
||||||
|
@ -284,33 +306,97 @@ const handleSearch = async (params: any) => {
|
||||||
};
|
};
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
if (props.type !== 'all' && !props.id) {
|
if (props.type !== 'all' && !props.id) {
|
||||||
params.value.terms.push({
|
params.value.terms = [
|
||||||
termType: 'eq',
|
{
|
||||||
column: 'targetType',
|
termType: 'eq',
|
||||||
value: props.type,
|
column: 'targetType',
|
||||||
type: 'and',
|
value: props.type,
|
||||||
});
|
type: 'and',
|
||||||
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
if (props.id) {
|
if (props.id) {
|
||||||
params.value.terms.push({
|
params.value.terms = [
|
||||||
termType: 'eq',
|
{
|
||||||
column: 'alarmConfigId',
|
termType: 'eq',
|
||||||
value: props.id,
|
column: 'alarmConfigId',
|
||||||
type: 'and',
|
value: props.id,
|
||||||
});
|
type: 'and',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
if(props.type === 'all'){
|
||||||
|
params.value.terms = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const search = (data: any) => {
|
const search = (data: any) => {
|
||||||
const dt = {
|
params.value.terms = [...data?.terms];
|
||||||
pageSize: 10,
|
if (props.type !== 'all' && !props.id) {
|
||||||
terms: [...data?.terms],
|
params.value.terms.push(
|
||||||
};
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'targetType',
|
||||||
|
value: props.type,
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (props.id) {
|
||||||
|
params.value.terms.push (
|
||||||
|
{
|
||||||
|
termType: 'eq',
|
||||||
|
column: 'alarmConfigId',
|
||||||
|
value: props.id,
|
||||||
|
type: 'and',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
const log = () => {
|
|
||||||
console.log(data.value.defaultLevel);
|
const getActions = (
|
||||||
|
currentData: Partial<Record<string, any>>,
|
||||||
|
type: 'card',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!currentData) return [];
|
||||||
|
const actions = [
|
||||||
|
{
|
||||||
|
key: 'solve',
|
||||||
|
text: '告警处理',
|
||||||
|
tooltip: {
|
||||||
|
title: '告警处理',
|
||||||
|
},
|
||||||
|
icon: 'ToolOutlined',
|
||||||
|
onClick: () =>{
|
||||||
|
data.value.current = currentData;
|
||||||
|
data.value.solveVisible = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'log',
|
||||||
|
text: '告警日志',
|
||||||
|
tooltip: {
|
||||||
|
title: '告警日志',
|
||||||
|
},
|
||||||
|
icon: 'FileOutlined',
|
||||||
|
onClick: () =>{
|
||||||
|
menuStory.jumpPage(`rule-engine/Alarm/Log/Detail`,{id:currentData.id});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'detail',
|
||||||
|
text: '处理记录',
|
||||||
|
tooltip: {
|
||||||
|
title: '处理记录',
|
||||||
|
},
|
||||||
|
icon: 'FileTextOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return actions;
|
||||||
};
|
};
|
||||||
log();
|
const closeSolve = () =>{
|
||||||
|
data.value.solveVisible = false
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
</slot>
|
</slot>
|
||||||
</template>
|
</template>
|
||||||
<template #content>
|
<template #content>
|
||||||
<Ellipsis>
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
<span style="font-weight: 600; font-size: 16px">
|
<span style="font-weight: 600; font-size: 16px">
|
||||||
{{ slotProps.name }}
|
{{ slotProps.name }}
|
||||||
</span>
|
</span>
|
||||||
|
@ -56,44 +56,23 @@
|
||||||
</a-row>
|
</a-row>
|
||||||
</template>
|
</template>
|
||||||
<template #actions="item">
|
<template #actions="item">
|
||||||
<a-tooltip
|
<PermissionButton
|
||||||
v-bind="item.tooltip"
|
:disabled="item.disabled"
|
||||||
:title="item.disabled && item.tooltip.title"
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
>
|
>
|
||||||
<a-popconfirm
|
<AIcon
|
||||||
v-if="item.popConfirm"
|
type="DeleteOutlined"
|
||||||
v-bind="item.popConfirm"
|
v-if="item.key === 'delete'"
|
||||||
:disabled="item.disabled"
|
/>
|
||||||
okText="确定"
|
|
||||||
cancelText="取消"
|
|
||||||
>
|
|
||||||
<a-button :disabled="item.disabled">
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item?.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<a-button
|
<AIcon :type="item.icon" />
|
||||||
:disabled="item.disabled"
|
<span>{{ item?.text }}</span>
|
||||||
@click="item.onClick"
|
|
||||||
>
|
|
||||||
<AIcon
|
|
||||||
type="DeleteOutlined"
|
|
||||||
v-if="item.key === 'delete'"
|
|
||||||
/>
|
|
||||||
<template v-else>
|
|
||||||
<AIcon :type="item.icon" />
|
|
||||||
<span>{{ item?.text }}</span>
|
|
||||||
</template>
|
|
||||||
</a-button>
|
|
||||||
</template>
|
</template>
|
||||||
</a-tooltip>
|
</PermissionButton>
|
||||||
</template>
|
</template>
|
||||||
</CardBox>
|
</CardBox>
|
||||||
</template>
|
</template>
|
||||||
|
@ -113,38 +92,26 @@
|
||||||
</template>
|
</template>
|
||||||
<template #action="slotProps">
|
<template #action="slotProps">
|
||||||
<a-space :size="16">
|
<a-space :size="16">
|
||||||
<a-tooltip
|
<template
|
||||||
v-for="i in getActions(slotProps)"
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
:key="i.key"
|
:key="i.key"
|
||||||
v-bind="i.tooltip"
|
|
||||||
>
|
>
|
||||||
<a-popconfirm
|
<PermissionButton
|
||||||
v-if="i.popConfirm"
|
:disabled="i.disabled"
|
||||||
v-bind="i.popConfirm"
|
:popConfirm="i.popConfirm"
|
||||||
okText="确定"
|
:tooltip="{
|
||||||
cancelText="取消"
|
...i.tooltip,
|
||||||
>
|
}"
|
||||||
<a-button
|
@click="i.onClick"
|
||||||
:disabled="i.disabled"
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
><AIcon :type="i.icon"
|
|
||||||
/></a-button>
|
|
||||||
</a-popconfirm>
|
|
||||||
<a-button
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
type="link"
|
||||||
v-else
|
style="padding: 0px"
|
||||||
@click="i.onClick && i.onClick(slotProps)"
|
:hasPermission="'device/Instance:' + i.key"
|
||||||
>
|
>
|
||||||
<a-button
|
<template #icon
|
||||||
:disabled="i.disabled"
|
|
||||||
style="padding: 0"
|
|
||||||
type="link"
|
|
||||||
><AIcon :type="i.icon"
|
><AIcon :type="i.icon"
|
||||||
/></a-button>
|
/></template>
|
||||||
</a-button>
|
</PermissionButton>
|
||||||
</a-tooltip>
|
</template>
|
||||||
</a-space>
|
</a-space>
|
||||||
</template>
|
</template>
|
||||||
</JTable>
|
</JTable>
|
||||||
|
|
|
@ -28,7 +28,7 @@ type Emit = {
|
||||||
const options = [
|
const options = [
|
||||||
{ value: 'device', label: '设备触发', tip: '适用于设备数据或行为满足触发条件时,执行指定的动作', image: getImage('/device-trigger.png') },
|
{ value: 'device', label: '设备触发', tip: '适用于设备数据或行为满足触发条件时,执行指定的动作', image: getImage('/device-trigger.png') },
|
||||||
{ value: 'manual', label: '手动触发', tip: '适用于第三方平台向物联网平台下发指令控制设备', image: getImage('/manual-trigger.png') },
|
{ value: 'manual', label: '手动触发', tip: '适用于第三方平台向物联网平台下发指令控制设备', image: getImage('/manual-trigger.png') },
|
||||||
{ value: 'timing', label: '定时触发', tip: '适用于定期执行固定任务', image: getImage('/timing-trigger.png') },
|
{ value: 'timer', label: '定时触发', tip: '适用于定期执行固定任务', image: getImage('/timing-trigger.png') },
|
||||||
]
|
]
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
|
|
|
@ -66,6 +66,10 @@ const props = defineProps({
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
watchEffect(() => {
|
||||||
|
Object.assign(formModel, props.data)
|
||||||
|
})
|
||||||
|
|
||||||
const emit = defineEmits<Emit>()
|
const emit = defineEmits<Emit>()
|
||||||
|
|
||||||
const title = computed(() => {
|
const title = computed(() => {
|
||||||
|
|
|
@ -0,0 +1,370 @@
|
||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<div
|
||||||
|
class="card-warp"
|
||||||
|
:class="{ active: active ? 'active' : '' }"
|
||||||
|
@click="handleClick"
|
||||||
|
>
|
||||||
|
<div class="card-type">
|
||||||
|
<div class="card-type-text"><slot name="type"></slot></div>
|
||||||
|
</div>
|
||||||
|
<div class="card-content">
|
||||||
|
<div style="display: flex">
|
||||||
|
<!-- 图片 -->
|
||||||
|
<div class="card-item-avatar">
|
||||||
|
<slot name="img"> </slot>
|
||||||
|
</div>
|
||||||
|
<!-- 内容 -->
|
||||||
|
<div class="card-item-body">
|
||||||
|
<slot name="title"></slot>
|
||||||
|
<span class="subTitle">
|
||||||
|
<slot name="subTitle"></slot>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 勾选 -->
|
||||||
|
<div v-if="active" class="checked-icon">
|
||||||
|
<div>
|
||||||
|
<AIcon type="CheckOutlined" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 状态 -->
|
||||||
|
<div
|
||||||
|
v-if="showStatus"
|
||||||
|
class="card-state"
|
||||||
|
:class="statusNames ? statusNames[status] : ''"
|
||||||
|
>
|
||||||
|
<div class="card-state-content">
|
||||||
|
<BadgeStatus
|
||||||
|
:status="status"
|
||||||
|
:text="statusText"
|
||||||
|
:statusNames="statusNames"
|
||||||
|
></BadgeStatus>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 按钮 -->
|
||||||
|
<slot name="bottom-tool">
|
||||||
|
<div
|
||||||
|
v-if="showTool && actions && actions.length"
|
||||||
|
class="card-tools"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="item in actions"
|
||||||
|
:key="item.key"
|
||||||
|
class="card-button"
|
||||||
|
:class="{
|
||||||
|
delete: item.key === 'delete',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<slot name="actions" v-bind="item"></slot>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</slot>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import BadgeStatus from '@/components/BadgeStatus/index.vue';
|
||||||
|
import { StatusColorEnum } from '@/utils/consts.ts';
|
||||||
|
import type { ActionsType } from '@/components/Table/index.vue';
|
||||||
|
import { PropType } from 'vue';
|
||||||
|
|
||||||
|
type EmitProps = {
|
||||||
|
(e: 'click', data: Record<string, any>): void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type TableActionsType = Partial<ActionsType>;
|
||||||
|
|
||||||
|
const emit = defineEmits<EmitProps>();
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
value: {
|
||||||
|
type: Object as PropType<Record<string, any>>,
|
||||||
|
default: () => {},
|
||||||
|
},
|
||||||
|
showStatus: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
showTool: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true,
|
||||||
|
},
|
||||||
|
statusText: {
|
||||||
|
type: String,
|
||||||
|
default: '正常',
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 'default',
|
||||||
|
},
|
||||||
|
statusNames: {
|
||||||
|
type: Object,
|
||||||
|
},
|
||||||
|
actions: {
|
||||||
|
type: Array as PropType<TableActionsType[]>,
|
||||||
|
default: () => [],
|
||||||
|
},
|
||||||
|
active: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleClick = () => {
|
||||||
|
emit('click', props.value);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.card {
|
||||||
|
width: 100%;
|
||||||
|
background-color: #fff;
|
||||||
|
.checked-icon {
|
||||||
|
position: absolute;
|
||||||
|
right: -22px;
|
||||||
|
bottom: -22px;
|
||||||
|
z-index: 2;
|
||||||
|
width: 44px;
|
||||||
|
height: 44px;
|
||||||
|
color: #fff;
|
||||||
|
background-color: red;
|
||||||
|
background-color: #2f54eb;
|
||||||
|
transform: rotate(-45deg);
|
||||||
|
|
||||||
|
> div {
|
||||||
|
position: relative;
|
||||||
|
height: 100%;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
|
||||||
|
> span {
|
||||||
|
position: absolute;
|
||||||
|
top: 6px;
|
||||||
|
left: 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-warp {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 0 24px rgba(#000, 0.1);
|
||||||
|
|
||||||
|
.card-mask {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
position: relative;
|
||||||
|
border: 1px solid #2f54eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-type {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: -14px;
|
||||||
|
height: 32px;
|
||||||
|
padding: 0 30px;
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
line-height: 32px;
|
||||||
|
background-color: rgba(0, 0, 0, 0.06);
|
||||||
|
transform: skewX(-45deg);
|
||||||
|
.card-type-text {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transform: skewX(45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-content {
|
||||||
|
position: relative;
|
||||||
|
padding: 43px 12px 19px 30px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.card-item-avatar {
|
||||||
|
margin-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-item-body {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex-grow: 1;
|
||||||
|
width: 0;
|
||||||
|
|
||||||
|
.subTitle {
|
||||||
|
color: rgba(0, 0, 0, 0.65);
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-state {
|
||||||
|
position: absolute;
|
||||||
|
top: 40px;
|
||||||
|
right: -12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100px;
|
||||||
|
padding: 2px 0;
|
||||||
|
background-color: rgba(#5995f5, 0.15);
|
||||||
|
transform: skewX(45deg);
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background-color: @success-color-deprecated-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background-color: rgba(#ff9000, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.error {
|
||||||
|
background-color: rgba(#e50012, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-state-content {
|
||||||
|
transform: skewX(-45deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.card-item-content-title) {
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 700;
|
||||||
|
color: @primary-color;
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.card-item-heard-name) {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.card-item-content-text) {
|
||||||
|
color: rgba(0, 0, 0, 0.75);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.item-active {
|
||||||
|
position: relative;
|
||||||
|
color: #2f54eb;
|
||||||
|
|
||||||
|
.checked-icon {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-warp {
|
||||||
|
border: 1px solid #2f54eb;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-tools {
|
||||||
|
display: flex;
|
||||||
|
margin-top: 8px;
|
||||||
|
|
||||||
|
.card-button {
|
||||||
|
display: flex;
|
||||||
|
flex-grow: 1;
|
||||||
|
|
||||||
|
& > :deep(span, button) {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(button) {
|
||||||
|
width: 100%;
|
||||||
|
border-radius: 0;
|
||||||
|
background: #f6f6f6;
|
||||||
|
border: 1px solid #e6e6e6;
|
||||||
|
color: #2f54eb;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @primary-color-hover;
|
||||||
|
border-color: @primary-color-hover;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: @primary-color-active;
|
||||||
|
border-color: @primary-color-active;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:not(:last-child) {
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.delete {
|
||||||
|
flex-basis: 60px;
|
||||||
|
flex-grow: 0;
|
||||||
|
|
||||||
|
:deep(button) {
|
||||||
|
background: @error-color-deprecated-bg;
|
||||||
|
border: 1px solid @error-color-outline;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: @error-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @error-color-hover;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: @error-color-active;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(button[disabled]) {
|
||||||
|
background: @disabled-bg;
|
||||||
|
border-color: @disabled-color;
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: @disabled-color !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: @disabled-active-bg;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
background-color: @disabled-active-bg;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -1,68 +1,340 @@
|
||||||
<template>
|
<template>
|
||||||
<page-container>
|
<page-container>
|
||||||
<search
|
<Search :columns="columns" target="scene" @search="handleSearch" />
|
||||||
:columns='columns'
|
<JTable
|
||||||
/>
|
ref="sceneRef"
|
||||||
<j-table
|
:columns="columns"
|
||||||
:columns='columns'
|
:request="query"
|
||||||
>
|
:defaultParams="{ sorts: [{ name: 'createTime', order: 'desc' }] }"
|
||||||
<template #headerTitle>
|
:params="params"
|
||||||
<a-space>
|
>
|
||||||
<a-button type="primary" @click="visible = true">新增</a-button>
|
<template #headerTitle>
|
||||||
</a-space>
|
<a-space>
|
||||||
</template>
|
<PermissionButton
|
||||||
</j-table>
|
type="primary"
|
||||||
<SaveModal v-if='visible' @close='visible = false'/>
|
@click="handleAdd"
|
||||||
</page-container>
|
hasPermission="device/Instance:add"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon type="PlusOutlined" /></template>
|
||||||
|
新增
|
||||||
|
</PermissionButton>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
<template #card="slotProps">
|
||||||
|
<SceneCard
|
||||||
|
:value="slotProps"
|
||||||
|
@click="handleClick"
|
||||||
|
:actions="getActions(slotProps, 'card')"
|
||||||
|
:status="slotProps.state?.value"
|
||||||
|
:statusText="slotProps.state?.text"
|
||||||
|
:statusNames="{
|
||||||
|
started: 'success',
|
||||||
|
disable: 'error',
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<template #type>
|
||||||
|
<span
|
||||||
|
><img
|
||||||
|
:height="16"
|
||||||
|
:src="typeMap.get(slotProps.triggerType)?.icon"
|
||||||
|
style="margin-right: 5px"
|
||||||
|
/>{{
|
||||||
|
typeMap.get(slotProps.triggerType)?.text
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<template #img>
|
||||||
|
<img :src="typeMap.get(slotProps.triggerType)?.img" />
|
||||||
|
</template>
|
||||||
|
<template #title>
|
||||||
|
<Ellipsis style="width: calc(100% - 100px)">
|
||||||
|
<span
|
||||||
|
style="font-size: 16px; font-weight: 600"
|
||||||
|
@click.stop="handleView(slotProps.id)"
|
||||||
|
>
|
||||||
|
{{ slotProps.name }}
|
||||||
|
</span>
|
||||||
|
</Ellipsis>
|
||||||
|
</template>
|
||||||
|
<template #subTitle>
|
||||||
|
<Ellipsis :lineClamp="2">
|
||||||
|
说明:{{
|
||||||
|
slotProps?.description ||
|
||||||
|
typeMap.get(slotProps.triggerType)?.tip
|
||||||
|
}}
|
||||||
|
</Ellipsis>
|
||||||
|
</template>
|
||||||
|
<template #actions="item">
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="item.disabled"
|
||||||
|
:popConfirm="item.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...item.tooltip,
|
||||||
|
}"
|
||||||
|
@click="item.onClick"
|
||||||
|
:hasPermission="'rule-engine/Scene:' + item.key"
|
||||||
|
>
|
||||||
|
<AIcon
|
||||||
|
type="DeleteOutlined"
|
||||||
|
v-if="item.key === 'delete'"
|
||||||
|
/>
|
||||||
|
<template v-else>
|
||||||
|
<AIcon :type="item.icon" />
|
||||||
|
<span>{{ item?.text }}</span>
|
||||||
|
</template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</SceneCard>
|
||||||
|
</template>
|
||||||
|
<template #triggerType="slotProps">
|
||||||
|
{{ typeMap.get(slotProps.triggerType)?.text }}
|
||||||
|
</template>
|
||||||
|
<template #state="slotProps">
|
||||||
|
<a-badge
|
||||||
|
:text="slotProps.state?.text"
|
||||||
|
:status="statusMap.get(slotProps.state?.value)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #action="slotProps">
|
||||||
|
<a-space>
|
||||||
|
<template
|
||||||
|
v-for="i in getActions(slotProps, 'table')"
|
||||||
|
:key="i.key"
|
||||||
|
>
|
||||||
|
<PermissionButton
|
||||||
|
:disabled="i.disabled"
|
||||||
|
:popConfirm="i.popConfirm"
|
||||||
|
:tooltip="{
|
||||||
|
...i.tooltip,
|
||||||
|
}"
|
||||||
|
@click="i.onClick"
|
||||||
|
type="link"
|
||||||
|
style="padding: 0px"
|
||||||
|
:hasPermission="'rule-engine/Scene:' + i.key"
|
||||||
|
>
|
||||||
|
<template #icon><AIcon :type="i.icon" /></template>
|
||||||
|
</PermissionButton>
|
||||||
|
</template>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</JTable>
|
||||||
|
<SaveModal v-if="visible" @close="visible = false" :data="current" />
|
||||||
|
</page-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import SaveModal from './Save/save.vue'
|
import SaveModal from './Save/save.vue';
|
||||||
import type { SceneItem } from './typings'
|
import type { SceneItem } from './typings';
|
||||||
import { useMenuStore } from 'store/menu'
|
import { useMenuStore } from 'store/menu';
|
||||||
|
import { query, _delete, _action } from '@/api/rule-engine/scene';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
import type { ActionsType } from '@/components/Table';
|
||||||
|
import { getImage } from '@/utils/comm';
|
||||||
|
import SceneCard from './SceneCard.vue';
|
||||||
|
|
||||||
const menuStory = useMenuStore()
|
const menuStory = useMenuStore();
|
||||||
const visible = ref<boolean>(false)
|
const visible = ref<boolean>(false);
|
||||||
|
const current = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const statusMap = new Map();
|
||||||
|
statusMap.set('started', 'success');
|
||||||
|
statusMap.set('disable', 'error');
|
||||||
|
|
||||||
|
const params = ref<Record<string, any>>({});
|
||||||
|
const sceneRef = ref<Record<string, any>>({});
|
||||||
|
|
||||||
|
const typeMap = new Map();
|
||||||
|
typeMap.set('manual', {
|
||||||
|
text: '手动触发',
|
||||||
|
img: getImage('/scene/scene-hand.png'),
|
||||||
|
icon: getImage('/scene/trigger-type-icon/manual.png'),
|
||||||
|
tip: '适用于第三方平台向物联网平台下发指令控制设备',
|
||||||
|
});
|
||||||
|
typeMap.set('timer', {
|
||||||
|
text: '定时触发',
|
||||||
|
img: getImage('/scene/scene-timer.png'),
|
||||||
|
icon: getImage('/scene/trigger-type-icon/timing.png'),
|
||||||
|
tip: '适用于定期执行固定任务',
|
||||||
|
});
|
||||||
|
typeMap.set('device', {
|
||||||
|
text: '设备触发',
|
||||||
|
img: getImage('/scene/scene-device.png'),
|
||||||
|
icon: getImage('/scene/trigger-type-icon/device.png'),
|
||||||
|
tip: '适用于设备数据或行为满足触发条件时,执行指定的动作',
|
||||||
|
});
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
{
|
{
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
fixed: 'left',
|
fixed: 'left',
|
||||||
ellipsis: true,
|
ellipsis: true,
|
||||||
width: 300,
|
width: 300,
|
||||||
title: '名称',
|
title: '名称',
|
||||||
search: {
|
search: {
|
||||||
type: 'string'
|
type: 'string',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'triggerType',
|
||||||
|
title: '触发方式',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: Array.from(typeMap).map((item) => ({
|
||||||
|
label: item[1],
|
||||||
|
value: item[0],
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'state',
|
||||||
|
title: '状态',
|
||||||
|
scopedSlots: true,
|
||||||
|
search: {
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{ label: '正常', value: 'started' },
|
||||||
|
{ label: '禁用', value: 'disable' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dataIndex: 'description',
|
||||||
|
title: '说明',
|
||||||
|
search: {
|
||||||
|
type: 'string',
|
||||||
|
},
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
key: 'action',
|
||||||
|
fixed: 'right',
|
||||||
|
width: 250,
|
||||||
|
scopedSlots: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const getActions = (
|
||||||
|
data: Partial<Record<string, any>>,
|
||||||
|
type: 'card' | 'table',
|
||||||
|
): ActionsType[] => {
|
||||||
|
if (!data) return [];
|
||||||
|
const actions: ActionsType[] = [
|
||||||
|
{
|
||||||
|
key: 'update',
|
||||||
|
text: '编辑',
|
||||||
|
tooltip: {
|
||||||
|
title: '编辑',
|
||||||
|
},
|
||||||
|
icon: 'EditOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
visible.value = true;
|
||||||
|
current.value = data;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'action',
|
||||||
|
text: data.state?.value !== 'disable' ? '禁用' : '启用',
|
||||||
|
tooltip: {
|
||||||
|
title: !(!!data.triggerType && (data.branches || [])?.length)
|
||||||
|
? '未配置规则的不能启用'
|
||||||
|
: data.state?.value !== 'disable'
|
||||||
|
? '禁用'
|
||||||
|
: '启用',
|
||||||
|
},
|
||||||
|
disabled: !(!!data?.triggerType && (data?.branches || [])?.length),
|
||||||
|
icon:
|
||||||
|
data.state.value !== 'disable'
|
||||||
|
? 'StopOutlined'
|
||||||
|
: 'CheckCircleOutlined',
|
||||||
|
popConfirm: {
|
||||||
|
title: `确认${
|
||||||
|
data.state.value !== 'disable' ? '禁用' : '启用'
|
||||||
|
}?`,
|
||||||
|
onConfirm: async () => {
|
||||||
|
let response = undefined;
|
||||||
|
if (data.state.value !== 'disable') {
|
||||||
|
response = await _action(data.id, '_disable');
|
||||||
|
} else {
|
||||||
|
response = await _action(data.id, '_enable');
|
||||||
|
}
|
||||||
|
if (response && response.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
sceneRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'delete',
|
||||||
|
text: '删除',
|
||||||
|
disabled: data.state?.value !== 'disable',
|
||||||
|
tooltip: {
|
||||||
|
title:
|
||||||
|
data.state.value !== 'disable'
|
||||||
|
? '请先禁用该场景,再删除'
|
||||||
|
: '删除',
|
||||||
|
},
|
||||||
|
popConfirm: {
|
||||||
|
title: '确认删除?',
|
||||||
|
onConfirm: async () => {
|
||||||
|
const resp = await _delete(data.id);
|
||||||
|
if (resp.status === 200) {
|
||||||
|
message.success('操作成功!');
|
||||||
|
sceneRef.value?.reload();
|
||||||
|
} else {
|
||||||
|
message.error('操作失败!');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
icon: 'DeleteOutlined',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
if (data.triggerType === 'manual') {
|
||||||
|
const _item: ActionsType = {
|
||||||
|
key: 'trigger',
|
||||||
|
text: '手动触发',
|
||||||
|
disabled: data.state?.value === 'disable',
|
||||||
|
tooltip: {
|
||||||
|
title:
|
||||||
|
data.state.value !== 'disable'
|
||||||
|
? '手动触发'
|
||||||
|
: '未启用,不能手动触发',
|
||||||
|
},
|
||||||
|
icon: 'LikeOutlined',
|
||||||
|
onClick: () => {
|
||||||
|
// handleView(data.id, data.triggerType);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
actions.splice(1, 0, _item);
|
||||||
}
|
}
|
||||||
},
|
if (type === 'table') {
|
||||||
{
|
actions.splice(0, 0, {
|
||||||
dataIndex: 'triggerType',
|
key: 'view',
|
||||||
title: '触发方式',
|
text: '查看',
|
||||||
search: {
|
tooltip: {
|
||||||
type: 'select',
|
title: '查看',
|
||||||
options: [
|
},
|
||||||
{ label: '手动触发', value: 'manual'},
|
icon: 'EyeOutlined',
|
||||||
{ label: '定时触发', value: 'timer'},
|
onClick: () => {
|
||||||
{ label: '设备触发', value: 'device'}
|
handleView(data.id, data.triggerType);
|
||||||
]
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
return actions;
|
||||||
{
|
};
|
||||||
dataIndex: 'description',
|
|
||||||
title: '说明',
|
const handleSearch = (_params: any) => {
|
||||||
},
|
params.value = _params;
|
||||||
{
|
};
|
||||||
dataIndex: 'state',
|
|
||||||
title: '状态',
|
const handleAdd = () => {
|
||||||
search: {
|
visible.value = true;
|
||||||
type: 'select',
|
current.value = {};
|
||||||
options: [
|
};
|
||||||
{ label: '正常', value: 'started'},
|
|
||||||
{ label: '禁用', value: 'disable'},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 编辑
|
* 编辑
|
||||||
|
@ -70,8 +342,12 @@ const columns = [
|
||||||
* @param triggerType 触发类型
|
* @param triggerType 触发类型
|
||||||
*/
|
*/
|
||||||
const handleEdit = (id: string, triggerType: string) => {
|
const handleEdit = (id: string, triggerType: string) => {
|
||||||
menuStory.jumpPage('Scene/Save', { }, { triggerType: triggerType, id, type: 'edit' })
|
menuStory.jumpPage(
|
||||||
}
|
'rule-engine/Scene/Save',
|
||||||
|
{},
|
||||||
|
{ triggerType: triggerType, id, type: 'edit' },
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查看
|
* 查看
|
||||||
|
@ -79,10 +355,13 @@ const handleEdit = (id: string, triggerType: string) => {
|
||||||
* @param triggerType 触发类型
|
* @param triggerType 触发类型
|
||||||
*/
|
*/
|
||||||
const handleView = (id: string, triggerType: string) => {
|
const handleView = (id: string, triggerType: string) => {
|
||||||
menuStory.jumpPage('Scene/Save', { }, { triggerType: triggerType, id, type: 'view' })
|
menuStory.jumpPage(
|
||||||
|
'rule-engine/Scene/Save',
|
||||||
|
{},
|
||||||
|
{ triggerType: triggerType, id, type: 'view' },
|
||||||
|
);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
|
||||||
</style>
|
</style>
|
|
@ -1906,7 +1906,7 @@ export default [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tigger',
|
id: 'trigger',
|
||||||
name: '手动触发',
|
name: '手动触发',
|
||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
|
@ -2323,7 +2323,7 @@ export default [
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'tigger',
|
id: 'trigger',
|
||||||
name: '手动触发',
|
name: '手动触发',
|
||||||
permissions: [
|
permissions: [
|
||||||
{
|
{
|
||||||
|
|
|
@ -115,7 +115,7 @@
|
||||||
>
|
>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
<a-form-item>
|
<a-form-item>
|
||||||
<a-button
|
<j-button
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
type="primary"
|
type="primary"
|
||||||
html-type="submit"
|
html-type="submit"
|
||||||
|
@ -123,7 +123,7 @@
|
||||||
block
|
block
|
||||||
>
|
>
|
||||||
登录
|
登录
|
||||||
</a-button>
|
</j-button>
|
||||||
</a-form-item>
|
</a-form-item>
|
||||||
</a-form>
|
</a-form>
|
||||||
<div class="other">
|
<div class="other">
|
||||||
|
@ -133,14 +133,14 @@
|
||||||
</div>
|
</div>
|
||||||
</a-divider>
|
</a-divider>
|
||||||
<div class="other-button">
|
<div class="other-button">
|
||||||
<a-button
|
<j-button
|
||||||
v-for="(item, index) in bindings"
|
v-for="(item, index) in bindings"
|
||||||
:key="index"
|
:key="index"
|
||||||
type="link"
|
type="link"
|
||||||
@Click="handleClickOther(item)"
|
@Click="handleClickOther(item)"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
style="width: 32px, height: 33px"
|
style="width: 32px; height: 33px"
|
||||||
:alt="item.name"
|
:alt="item.name"
|
||||||
:src="
|
:src="
|
||||||
iconMap.get(
|
iconMap.get(
|
||||||
|
@ -148,7 +148,7 @@
|
||||||
) || defaultImg
|
) || defaultImg
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
</a-button>
|
</j-button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -443,6 +443,7 @@ screenRotation(screenWidth.value, screenHeight.value);
|
||||||
position: relative;
|
position: relative;
|
||||||
bottom: 10px;
|
bottom: 10px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -463,6 +464,10 @@ screenRotation(screenWidth.value, screenHeight.value);
|
||||||
// vertical-align: middle;
|
// vertical-align: middle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.login-form-button {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,6 @@ export default defineConfig(({ mode}) => {
|
||||||
less: {
|
less: {
|
||||||
modifyVars: {
|
modifyVars: {
|
||||||
hack: `true; @import (reference) "${path.resolve('src/style/variable.less')}";`,
|
hack: `true; @import (reference) "${path.resolve('src/style/variable.less')}";`,
|
||||||
...Config.theme,
|
|
||||||
},
|
},
|
||||||
javascriptEnabled: true,
|
javascriptEnabled: true,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue