diff --git a/src/api/device/instance.ts b/src/api/device/instance.ts index a8a7213c..0d73ffb4 100644 --- a/src/api/device/instance.ts +++ b/src/api/device/instance.ts @@ -2,6 +2,7 @@ import { LocalStore } from '@/utils/comm' import server from '@/utils/request' import { BASE_API_PATH, TOKEN_KEY } from '@/utils/variable' import { DeviceInstance } from '@/views/device/Instance/typings' +import { UnitType } from '@/views/device/Product/typings'; /** * 删除设备物模型 @@ -242,3 +243,74 @@ export const unbindBatchDevice = (deviceId: string, data: Record) = * @returns */ export const bindDevice = (deviceId: string, data: Record) => server.post(`/device/gateway/${deviceId}/bind`, data) + +/** + * 设备接入网关状态 + * @param id 设备接入网关id + * @returns + */ +export const queryGatewayState = (id: string) => server.get(`/gateway/device/${id}/detail`) + +/** + * 网络组件状态 + * @param id 网络组件id + * @returns + */ +export const queryNetworkState = (id: string) => server.get(`/network/config/${id}`) + +/** + * 产品状态 + * @param id 产品id + * @returns + */ +export const queryProductState = (id: string) => server.get(`/device/product/${id}`) + +/** + * 产品配置 + * @param id 产品id + * @returns + */ +export const queryProductConfig = (id: string) => server.get(`/device/product/${id}/config-metadata`) + +/** + * 设备配置 + * @param id 设备id + * @returns + */ +export const queryDeviceConfig = (id: string) => server.get(`/device-instance/${id}/config-metadata`) + +/** + * 查询协议 + * @param type + * @param transport + * @returns + */ +export const queryProtocolDetail = (type: string, transport: string) => server.get(`/protocol/${type}/transport/${transport}`) + +/** + * 网络组件启用 + * @param id 网络组件ID + * @returns + */ +export const startNetwork = (id: string) => server.post(`/network/config/${id}/_start`) + +/** + * 启用网关 + * @param id 网关id + * @returns + */ +export const startGateway = (id: string) => server.post(`/gateway/device/${id}/_startup`) + +/** + * 网关详情 + * @param id 网关id + * @returns + */ +export const getGatewayDetail = (id: string) => server.get(`/gateway/device/${id}`) + + +/* + * 获取单位列表 + * @returns 单位列表 + */ +export const getUnit = () => server.get(`/protocol/units`) diff --git a/src/api/media/device.ts b/src/api/media/device.ts index d16d117e..4634bfbd 100644 --- a/src/api/media/device.ts +++ b/src/api/media/device.ts @@ -1,4 +1,5 @@ import server from '@/utils/request' +import type { ProductType } from '@/views/media/Device/typings'; export default { // 列表 @@ -10,4 +11,16 @@ export default { // 修改 update: (data: any) => server.put(`/media/device/${data.channel}/${data.id}`, data), del: (id: string) => server.remove(`/media/device/${id}`), + // 更新通道 + updateChannels: (id: string) => server.post(`/media/device/${id}/channels/_sync`), + // 查询产品列表 + queryProductList: (data: any) => server.post(`/device/product/_query/no-paging`, data), + // 快速添加产品 + saveProduct: (data: any) => server.post(`/device/product`, data), + // 产品发布 + deployProductById: (id: string) => server.post(`/device/product/${id}/deploy`), + // 查询设备接入配置 + queryProvider: (data?: any) => server.post(`/gateway/device/detail/_query`, data), + // 查询网关配置 + getConfiguration: (id: string, transport: string) => server.get(`/protocol/${id}/${transport}/configuration`), } \ No newline at end of file diff --git a/src/components/InputSelect/index.vue b/src/components/InputSelect/index.vue new file mode 100644 index 00000000..61035f31 --- /dev/null +++ b/src/components/InputSelect/index.vue @@ -0,0 +1,40 @@ + + + \ No newline at end of file diff --git a/src/components/JUpload/index.vue b/src/components/JUpload/index.vue index 93fd6562..03ee6536 100644 --- a/src/components/JUpload/index.vue +++ b/src/components/JUpload/index.vue @@ -10,7 +10,7 @@ @change="handleChange" :action="FILE_UPLOAD" :headers="{ - 'X-Access-Token': LocalStore.get(TOKEN_KEY) + 'X-Access-Token': LocalStore.get(TOKEN_KEY), }" v-bind="props" > @@ -26,8 +26,23 @@
点击修改
@@ -41,8 +56,8 @@ + \ No newline at end of file diff --git a/src/components/Metadata/BooleanParam/index.vue b/src/components/Metadata/BooleanParam/index.vue new file mode 100644 index 00000000..4e14a89a --- /dev/null +++ b/src/components/Metadata/BooleanParam/index.vue @@ -0,0 +1,81 @@ + + + \ No newline at end of file diff --git a/src/components/Metadata/EnumParam/index.vue b/src/components/Metadata/EnumParam/index.vue new file mode 100644 index 00000000..bb9daf5d --- /dev/null +++ b/src/components/Metadata/EnumParam/index.vue @@ -0,0 +1,160 @@ + + + \ No newline at end of file diff --git a/src/components/Metadata/JsonParam/index.vue b/src/components/Metadata/JsonParam/index.vue new file mode 100644 index 00000000..2b77587c --- /dev/null +++ b/src/components/Metadata/JsonParam/index.vue @@ -0,0 +1,172 @@ + + + \ No newline at end of file diff --git a/src/components/Table/index.tsx b/src/components/Table/index.tsx index a9536fb0..3d3054c8 100644 --- a/src/components/Table/index.tsx +++ b/src/components/Table/index.tsx @@ -1,10 +1,11 @@ import { UnorderedListOutlined, AppstoreOutlined } from '@ant-design/icons-vue' import styles from './index.module.less' import { Pagination, Table, Empty, Spin, Alert } from 'ant-design-vue' -import type { TableProps, ColumnProps } from 'ant-design-vue/es/table' +import type { TableProps } from 'ant-design-vue/es/table' import type { TooltipProps } from 'ant-design-vue/es/tooltip' import type { PopconfirmProps } from 'ant-design-vue/es/popconfirm' import { CSSProperties, PropType } from 'vue'; +import type { JColumnsProps } from './types' enum ModelEnum { TABLE = 'TABLE', @@ -40,14 +41,10 @@ export interface ActionsType { children?: ActionsType[]; } -export interface JColumnProps extends ColumnProps { - scopedSlots?: boolean; // 是否为插槽 true: 是 false: 否 -} - export interface JTableProps extends TableProps { request?: (params?: Record) => Promise>; cardBodyClass?: string; - columns: JColumnProps[]; + columns: JColumnsProps[]; params?: Record; model?: keyof typeof ModelEnum | undefined; // 显示table还是card // actions?: ActionsType[]; @@ -156,9 +153,10 @@ const JTable = defineComponent({ const pageIndex = ref(0) const pageSize = ref(6) const total = ref(0) - const _columns = ref(props?.columns || []) const loading = ref(true) + const _columns = computed(() => props.columns.filter(i => !(i?.hideInTable))) + /** * 监听宽度,计算显示卡片个数 */ diff --git a/src/components/Table/types.d.ts b/src/components/Table/types.d.ts index 68cb3dd8..2ae95532 100644 --- a/src/components/Table/types.d.ts +++ b/src/components/Table/types.d.ts @@ -3,5 +3,6 @@ import { ColumnType } from 'ant-design-vue/es/table' export interface JColumnsProps extends ColumnType{ scopedSlots?: boolean; - search: SearchProps + search: SearchProps; + hideInTable?: boolean; } \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Message/Dialog/index.vue b/src/views/device/Instance/Detail/Diagnose/Message/Dialog/index.vue new file mode 100644 index 00000000..1e4d60f2 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/Dialog/index.vue @@ -0,0 +1,142 @@ + + + + + \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Message/Function/EditTable.vue b/src/views/device/Instance/Detail/Diagnose/Message/Function/EditTable.vue new file mode 100644 index 00000000..975d91fd --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/Function/EditTable.vue @@ -0,0 +1,93 @@ + + + \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Message/Function/index.vue b/src/views/device/Instance/Detail/Diagnose/Message/Function/index.vue new file mode 100644 index 00000000..32462129 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/Function/index.vue @@ -0,0 +1,127 @@ + + + + + \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Message/Log/index.vue b/src/views/device/Instance/Detail/Diagnose/Message/Log/index.vue new file mode 100644 index 00000000..81efb838 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/Log/index.vue @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Message/index.vue b/src/views/device/Instance/Detail/Diagnose/Message/index.vue new file mode 100644 index 00000000..2a185673 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/index.vue @@ -0,0 +1,92 @@ + + + + + diff --git a/src/views/device/Instance/Detail/Diagnose/Message/util.ts b/src/views/device/Instance/Detail/Diagnose/Message/util.ts new file mode 100644 index 00000000..30da1df0 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Message/util.ts @@ -0,0 +1,29 @@ +export type MessageType = { + up: { + text: string; + status: 'loading' | 'success' | 'error'; + }; + down: { + text: string; + status: 'loading' | 'success' | 'error'; + }; +} + +export const messageStyleMap = new Map(); +messageStyleMap.set('loading', { + background: 'linear-gradient(0deg, rgba(30, 165, 241, 0.03), rgba(30, 165, 241, 0.03)), #FFFFFF', + boxShadow: '-2px 0px 0px #1EA5F1', +}); +messageStyleMap.set('error', { + background: 'linear-gradient(0deg, rgba(255, 77, 79, 0.03), rgba(255, 77, 79, 0.03)), #FFFFFF', + boxShadow: '-2px 0px 0px #FF4D4F', +}); +messageStyleMap.set('success', { + background: 'linear-gradient(0deg, rgba(50, 212, 164, 0.03), rgba(50, 212, 164, 0.03)), #FFFFFF', + boxShadow: '-2px 0px 0px #32D4A4', +}); + +export const messageStatusMap = new Map(); +messageStatusMap.set('loading', 'processing'); +messageStatusMap.set('error', 'error'); +messageStatusMap.set('success', 'success'); \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx b/src/views/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx new file mode 100644 index 00000000..acb456ff --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Status/DiagnosticAdvice.tsx @@ -0,0 +1,101 @@ +import { Badge, Descriptions, Modal, Tooltip } from "ant-design-vue" +import TitleComponent from '@/components/TitleComponent/index.vue' +import styles from './index.module.less' +import AIcon from "@/components/AIcon"; +import _ from "lodash"; + +const DiagnosticAdvice = defineComponent({ + props: { + data: { + type: Object, + default: () => { } + } + }, + emits: ['close'], + setup(props, { emit }) { + const { data } = props + return () => { + emit('close') + }} + onCancel={() => { + emit('close') + }} + > +
+ +
+
+ + 所有诊断均无异常但设备仍未上线,请检查以下内容 +
+
+ { + (data?.list || []).map((item: any, index: number) => ( +
+ {item} +
+ )) + } +
+
+
+
+ + + + {data?.info?.id || ''} + + {data?.info?.address?.length > 0 && ( + + + {(data?.info?.address || []).map((i: any) => ( +
+ + {i.address} +
+ ))} +
+ } + > +
+ {(data?.info?.address || []).slice(0, 1).map((i: any) => ( +
+ + {i.address} +
+ ))} +
+ + + )} + + {(_.flatten(_.map(data?.info?.config, 'properties')) || []).map((item: any, index: number) => ( + + {item.name} + + + + + ) : ( + item.name + ) + } + > + {data?.info?.configValue[item?.property] || ''} + + ))} + + +
+ } +}) + +export default DiagnosticAdvice \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx b/src/views/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx new file mode 100644 index 00000000..cd066848 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Status/ManualInspection.tsx @@ -0,0 +1,217 @@ +import AIcon from "@/components/AIcon"; +import { Button, Descriptions, Modal } from "ant-design-vue" +import styles from './index.module.less' + +const ManualInspection = defineComponent({ + props: { + data: { + type: Object, + default: () => { } + } + }, + emits: ['close', 'save'], + setup(props, { emit }) { + + const { data } = props + + const dataRender = () => { + if (data.type === 'device' || data.type === 'product') { + return ( + <> +
+
+ + 请检查配置项是否填写正确,若您确定该项无需诊断可 + +
+
+ + {(data?.data?.properties || []).map((item: any) => ( + + {data?.configuration[item.property] || ''} + + ))} + +
+
+ {data?.data?.description ? ( +
+

诊断项说明

+

{data?.data?.description}

+
+ ) : ( + '' + )} + + ); + } else if (data.type === 'cloud') { + return ( + <> +
+
+ + 请检查配置项是否填写正确,若您确定该项无需诊断可 + +
+
+ + {data.configuration?.provider === 'OneNet' ? ( + <> + + {data?.configuration?.configuration?.apiAddress || ''} + + + {data?.configuration?.configuration?.apiKey || ''} + + + {data?.configuration?.configuration?.validateToken || ''} + + + {data?.configuration?.configuration?.aesKey || ''} + + + ) : ( + <> + + {data?.configuration?.configuration?.apiAddress || ''} + + + {data?.configuration?.configuration?.appKey || ''} + + + {data?.configuration?.configuration?.appSecret || ''} + + + )} + +
+
+ {data?.configuration?.configuration?.description ? ( +
+

诊断项说明

+

{data?.configuration?.configuration?.description}

+
+ ) : ( + '' + )} + + ); + } else if (data.type === 'media') { + return ( + <> +
+
+ + 请检查配置项是否填写正确,若您确定该项无需诊断可 + +
+
+ + {data?.configuration?.configuration?.shareCluster ? ( + <> + + {data?.configuration?.configuration?.domain || ''} + + + {data?.configuration?.configuration?.sipId || ''} + + + {data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'} + + + {`${data?.configuration?.configuration?.hostPort?.host}:${data?.configuration?.configuration?.hostPort?.port}`} + + + {`${data?.configuration?.configuration?.hostPort?.publicHost}:${data?.configuration?.configuration?.hostPort?.publicPort}`} + + + ) : ( + <> + + {data?.configuration?.configuration?.domain || ''} + + + {data?.configuration?.configuration?.sipId || ''} + + + {data?.configuration?.configuration?.shareCluster ? '共享配置' : '独立配置'} + + {data?.configuration?.configuration?.cluster.map((i: any, it: number) => ( +
+
节点{it + 1}
+ + {i?.clusterNodeId || ''} + + + {`${i.host}:${i?.port}`} + + + {`${i?.publicHost}:${i?.publicPort}`} + +
+ ))} + + )} +
+
+
+ {data?.configuration?.configuration.description ? ( +
+

诊断项说明

+

{data?.configuration?.description}

+
+ ) : ( + '' + )} + + ); + } else { + return null; + } + }; + + return () => { + emit('save', data) + }} + onCancel={() => { + // TODO 跳转设备和产品 + }}> +
{dataRender()}
+
+ } +}) + +export default ManualInspection \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Status/index.module.less b/src/views/device/Instance/Detail/Diagnose/Status/index.module.less new file mode 100644 index 00000000..ce805739 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Status/index.module.less @@ -0,0 +1,90 @@ +.statusBox { + width: 100%; + + .statusHeader { + display: flex; + } + + .statusContent { + width: 100%; + margin: 20px 0; + border: 1px solid #ececec; + border-bottom: none; + + .statusItem { + display: flex; + justify-content: space-between; + padding: 20px; + border-bottom: 1px solid #ececec; + + .statusLeft { + display: flex; + + .statusImg { + width: 32px; + height: 32px; + margin: 15px 20px 0 0; + } + + .statusContext { + .statusTitle { + color: rgba(0, 0, 0, 0.8); + font-weight: 700; + font-size: 18px; + } + + .statusDesc { + color: rgba(0, 0, 0, 0.65); + font-size: 14px; + } + + .info { + margin-top: 10px; + color: #646464; + font-size: 14px; + + .infoItem { + width: 100%; + } + } + } + } + + .statusRight { + margin-top: 10px; + font-weight: 700; + font-size: 18px; + } + } + } + } + + .loading { + animation: loading 2s linear infinite; + } + + @keyframes loading { + 0% { + transform: rotate(0deg); + } + 25% { + transform: rotate(90deg); + } + 50% { + transform: rotate(180deg); + } + 75% { + transform: rotate(270deg); + } + 100% { + transform: rotate(360deg); + } + } + + .alert { + height: 40px; + padding-left: 10px; + color: rgba(0, 0, 0, 0.55); + line-height: 40px; + background-color: #f6f6f6; + } \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Status/index.tsx b/src/views/device/Instance/Detail/Diagnose/Status/index.tsx new file mode 100644 index 00000000..db57848b --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Status/index.tsx @@ -0,0 +1,1585 @@ +import { Badge, Button, message, Popconfirm, Space } from "ant-design-vue" +import TitleComponent from '@/components/TitleComponent/index.vue' +import styles from './index.module.less' +import type { ListProps } from './util' +import { networkInitList, childInitList, cloudInitList, mediaInitList, TextColorMap, StatusMap, modifyArrayList, isExit, gatewayList, urlMap } from './util' +import { useInstanceStore } from "@/store/instance" +import { startNetwork, startGateway, getGatewayDetail, queryGatewayState, queryProtocolDetail, detail, queryProductState, queryProductConfig, queryDeviceConfig, _deploy } from '@/api/device/instance' +import { PropType, VNode } from "vue" +import { _deploy as _deployProduct } from "@/api/device/product" +import _ from "lodash" +import DiagnosticAdvice from './DiagnosticAdvice' +import ManualInspection from './ManualInspection' + +type TypeProps = 'network' | 'child-device' | 'media' | 'cloud' | 'channel' + +const Status = defineComponent({ + components: { TitleComponent }, + props: { + providerType: { + type: String as PropType, + default: undefined + } + }, + emits: ['percentChange', 'countChange', 'stateChange'], + setup(props, { emit }) { + const instanceStore = useInstanceStore() + const time = 500; + + const status = ref<'loading' | 'finish'>('loading') + + const device = ref(instanceStore.current) + const gateway = ref>>() // 网关信息 + const parent = ref>>() // 父设备 + const product = ref>>() // 产品 + + const artificialVisible = ref(false) + const artificialData = ref>>() + + const diagnoseVisible = ref(false) + const diagnoseData = ref>>() + + const bindParentVisible = ref(false) + + const configuration = reactive<{ + product: Record, + device: Record + }>({ + product: {}, + device: [] + }) + + const list = ref([]) + + const manualInspection = (params: any) => { + artificialVisible.value = true + artificialData.value = params + } + + // TODO + const jumpAccessConfig = () => { + // const purl = getMenuPathByCode(MENUS_CODE['device/Product/Detail']); + // if (purl) { + // history.push( + // `${getMenuPathByParams(MENUS_CODE['device/Product/Detail'], device.productId)}`, + // { + // tab: 'access', + // }, + // ); + // } else { + // message.error('规则可能有加密处理,请联系管理员'); + // } + }; + + const jumpDeviceConfig = () => { + instanceStore.tabActiveKey = 'Info' + } + + // 网络信息 + const diagnoseNetwork = () => new Promise(async (resolve) => { + if (unref(device).state?.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(unref(list), { + key: 'network', + name: '网络组件', + desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}) + }, time) + } else { + const _device = unref(device) + if (_device.accessId) { + const response: Record = await queryGatewayState(_device.accessId) + if (response.status === 200) { + gateway.value = response.request + const address = response.result?.channelInfo?.addresses || []; + const _label = address.some((i: any) => i.health === -1); + const __label = address.every((i: any) => i.health === 1); + const health = _label ? -1 : __label ? 1 : 0; + let _item: ListProps | undefined = undefined + if (health === 1) { + _item = { + key: 'network', + name: '网络组件', + desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }; + } else { + _item = { + key: 'network', + name: '网络组件', + desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + status: 'error', + text: '异常', + info: health === -1 ? ( +
+
+ 网络组件已禁用,请先 { + const res = await startNetwork( + unref(gateway)?.channelId, + ); + if (res.status === 200) { + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'network', + name: '网络组件', + desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + + } + /> +
+
+ ) : ( +
+
+ +
+
+ +
+
+ ), + }; + } + setTimeout(() => { + if (_item) { + list.value = modifyArrayList(list.value, _item); + } + resolve({}); + }, time); + } else { + message.error('请求发生错误') + } + } else { + message.error('设备不含accessId') + } + } + }) + + // 设备接入网关 + const diagnoseGateway = () => new Promise(async (resolve) => { + const desc = props.providerType && ['child-device', 'cloud'].includes(props.providerType) + ? '诊断设备接入网关状态是否正常,网关配置是否正确' + : '诊断设备接入网关状态是否正常,禁用状态将导致连接失败'; + if (unref(device).state.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else { + let _item: ListProps | undefined = undefined + if (!unref(gateway)) { + const accessId = unref(device)?.accessId + if (accessId) { + const response: Record = await queryGatewayState(accessId) + if (response.status === 200) { + gateway.value = response.result + if (response.result?.state?.value === 'enabled') { + if (props.providerType === 'cloud' || unref(device)?.accessProvider === 'gb28181-2016') { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ + 请网关配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }; + } else { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }; + } + } else { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'error', + text: '异常', + info: ( +
+
+ 设备接入网关已禁用,请先 + { + const resp = await startGateway(unref(device).accessId || ''); + if (resp.status === 200) { + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + + } + /> +
+
+ ), + }; + } + setTimeout(() => { + if (_item) { + list.value = modifyArrayList(list.value, _item); + } + resolve({}); + }, time); + } else { + message.error('请求发生错误') + } + } else { + message.error('设备不含accessId') + } + } else { + if (unref(gateway)?.state?.value === 'enabled') { + if (props.providerType === 'cloud' || unref(device)?.accessProvider === 'gb28181-2016') { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ + 请 + 网关配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }; + } else { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }; + } + } else { + _item = { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'error', + text: '异常', + info: ( +
+
+ + 设备接入网关已禁用,请先 { + const resp = await startGateway(unref(device).accessId || ''); + if (resp.status === 200) { + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'gateway', + name: '设备接入网关', + desc: desc, + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + + + } + /> +
+
+ ), + }; + } + setTimeout(() => { + if (_item) { + list.value = modifyArrayList(list.value, _item); + } + resolve({}); + }, time); + } + } + }) + + // 网关父设备 + const diagnoseParentDevice = new Promise(async (resolve) => { + if (unref(device).state.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(unref(list), { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else { + const _device = unref(device) + if (!_device?.parentId) { + setTimeout(() => { + list.value = modifyArrayList(unref(list), { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ + 未绑定父设备,请先 + + 父设备后重试 + + } + /> +
+
+ ), + }); + resolve({}); + }, time); + } else { + let _item: ListProps | undefined = undefined; + const response = await detail(_device?.parentId); + parent.value = response.result + if (response.status === 200) { + if (response?.result?.state?.value === 'notActive') { + _item = { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ + 网关父设备已禁用,请先 { + const resp = await _deploy(response?.result?.id || ''); + if (resp.status === 200) { + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + + + } + /> +
+
+ ), + }; + } else if (response?.result?.state?.value === 'online') { + _item = { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'success', + text: '正常', + info: null, + }; + } else { + _item = { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ 网关父设备已离线,请先排查网关设备故障} + /> +
+
+ ), + }; + } + setTimeout(() => { + if (_item) { + list.value = modifyArrayList(unref(list), _item); + } + resolve({}); + }, time); + } + } + } + }) + + // 产品状态 + const diagnoseProduct = () => new Promise(async (resolve) => { + if (unref(device).state?.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(unref(list), { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else { + const _device = unref(device) + if (_device.productId) { + const response: Record = await queryProductState(_device.productId) + if (response.status === 200) { + product.value = response.result + let _item: ListProps | undefined = undefined + const state = response.result?.state + _item = { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: state === 1 ? 'success' : 'error', + text: state === 1 ? '正常' : '异常', + info: + state === 1 ? null : ( +
+
+ + 产品已禁用,请 { + const resp = await _deployProduct(unref(device).productId || ''); + if (resp.status === 200) { + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + + 产品 + + } + /> +
+
+ ), + }; + setTimeout(() => { + if (_item) { + list.value = modifyArrayList(unref(list), _item); + } + resolve({}); + }, time); + } + } + } + }) + + // 设备状态 + const diagnoseDevice = () => new Promise(resolve => { + const _device = unref(device) + if (_device.state?.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(unref(list), { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else { + let item: ListProps | undefined = undefined; + if (_device.state?.value === 'notActive') { + item = { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ + 设备已禁用,请 { + const resp = await _deploy(unref(device)?.id || ''); + if (resp.status === 200) { + instanceStore.current.state = { value: 'offline', text: '离线' } + message.success('操作成功!'); + list.value = modifyArrayList( + list.value, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + } + }} + > + + 设备 + + } + /> +
+
+ ), + }; + } else { + item = { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'success', + text: '正常', + info: null, + }; + } + setTimeout(() => { + if (item) { + list.value = modifyArrayList(unref(list), item); + } + resolve({}); + }, time); + } + }) + + // 产品认证配置 + const diagnoseProductAuthConfig = () => new Promise(async (resolve) => { + const _device = unref(device) + if (_device.productId) { + const response: Record = await queryProductConfig(_device.productId) + if (response.status === 200 && response.result.length > 0) { + configuration.product = response.result; + const _configuration = unref(product)?.configuration || {}; + response.result.map((item: any, i: number) => { + const _list = [...list.value]; + if (!_.map(_list, 'key').includes(`product-auth${i}`)) { + list.value = modifyArrayList( + list.value, + { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + list.value.length, + ); + } + const properties = _.map(item?.properties, 'property'); + if (unref(device).state?.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else if ( + !isExit( + properties, + Object.keys(_configuration).filter((k: string) => !!_configuration[k]), + ) + ) { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ + 请根据设备接入配置需要 + + ,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }); + resolve({}); + }, time); + } else { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ + 请 + + 产品{item.name} + 配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: `product-auth${i}`, + name: `产品-${item?.name}`, + desc: '诊断产品MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }); + resolve({}); + }, time); + } + }); + } else { + resolve({}); + } + } + }) + + // 设备认证配置 + const diagnoseDeviceAuthConfig = () => new Promise(async (resolve) => { + const _device = unref(device) + if (_device.id) { + const response: Record = await queryDeviceConfig(_device.id) + if (response.status === 200 && response.result.length > 0) { + configuration.device = response.result; + const _configuration = _device?.configuration || {}; + response.result.map((item: any, i: number) => { + const _list = [...list.value]; + if (!_.map(_list, 'key').includes(`device-auth${i}`)) { + list.value = modifyArrayList( + list.value, + { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + list.value.length, + ); + } + const properties = _.map(item?.properties, 'property'); + if (_device.state?.value === 'online') { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + resolve({}); + }, time); + } else if ( + !isExit( + properties, + Object.keys(_configuration).filter((k: string) => !!_configuration[k]), + ) + ) { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ + 请根据设备接入配置需要 + + ,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }); + resolve({}); + }, time); + } else { + setTimeout(() => { + list.value = modifyArrayList(list.value, { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ + 请 + + 设备{item.name} + 配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList( + list.value, + { + key: `device-auth${i}`, + name: `设备-${item?.name}`, + desc: '诊断设备MQTT认证配置是否正确,错误的配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }, + ); + }} + > + + + + } + /> +
+
+ ), + }); + resolve({}); + }, time); + } + }); + } else { + resolve({}); + } + } + }) + + // onenet + const diagnoseOnenet = () => new Promise(async (resolve) => { + const _device = unref(device) + if (_device?.accessProvider === 'OneNet') { + const response: any = await queryDeviceConfig(_device?.id || ''); + configuration.device = response.result; + const _configuration = _device?.configuration || {}; + let item: ListProps | undefined = undefined; + if ( + _device.configuration?.onenet_imei || + _device.configuration?.onenet_imsi || + (unref(product)?.configuration && + unref(product)?.configuration['api-key']) + ) { + item = { + key: `onenet`, + name: `设备-OneNet配置`, + desc: '诊断设备OneNet是否已配置,未配置将导致连接失败', + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ 请 + + 设备-OneNet配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList(list.value, { + key: `onenet`, + name: `设备-OneNet配置`, + desc: '诊断设备OneNet是否已配置,未配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + }} + > + + +
+
+ ), + }; + } else { + item = { + key: `onenet`, + name: `设备-OneNet配置`, + desc: '诊断设备OneNet是否已配置,未配置将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ 请根据设备接入配置需要 + + ,若您确定该项无需诊断可 + { + list.value = modifyArrayList(list.value, { + key: `onenet`, + name: `设备-OneNet配置`, + desc: '诊断设备OneNet是否已配置,未配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + }} + > + + +
+
+ ), + }; + } + setTimeout(() => { + if (item) { + list.value = modifyArrayList( + list.value, + item, + list.value.length, + ); + } + resolve({}); + }, time); + } else { + resolve({}); + } + }) + + // ctwing + const diagnoseCTWing = () => new Promise(async (resolve) => { + const _device = unref(device) + if (_device?.accessProvider === 'Ctwing') { + const response: any = await queryDeviceConfig(_device?.id || ''); + configuration.device = response.result; + const _configuration = _device?.configuration || {}; + let item: ListProps | undefined = undefined; + const config = unref(product)?.configuration; + if ( + _device.configuration?.ctwing_imei || + _device.configuration?.ctwing_imsi || + (config && (config.ctwing_product_id || config.master_key)) + ) { + item = { + key: `ctwing`, + name: `设备-CTWing配置`, + desc: '诊断设备CTWing是否已配置,未配置将导致连接失败', + status: 'warning', + text: '可能存在异常', + info: ( +
+
+ 请 + + 设备-CTWing配置是否已填写正确,若您确定该项无需诊断可 + { + list.value = modifyArrayList(list.value, { + key: `ctwing`, + name: `设备-CTWing配置`, + desc: '诊断设备CTWing是否已配置,未配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + }} + > + + +
+
+ ), + }; + } else { + item = { + key: `ctwing`, + name: `设备-CTWing配置`, + desc: '诊断设备CTWing是否已配置,未配置将导致连接失败', + status: 'error', + text: '异常', + info: ( +
+
+ 请根据设备接入配置需要 + + ,若您确定该项无需诊断可 + { + list.value = modifyArrayList(list.value, { + key: `ctwing`, + name: `设备-CTWing配置`, + desc: '诊断设备CTWing是否已配置,未配置将导致连接失败', + status: 'success', + text: '正常', + info: null, + }); + }} + > + + +
+
+ ), + }; + } + setTimeout(() => { + if (item) { + list.value = modifyArrayList( + list.value, + item, + list.value.length, + ); + } + resolve({}); + }, time); + } else { + resolve({}); + } + }) + + const diagnoseNetworkOtherConfig = async () => { + if (unref(device).state?.value != 'online') { + const item: VNode[] = []; + let info: any = { + id: unref(device).id, + }; + item.push(); + if (props.providerType === 'network') { + item.push( + 1 ? ( + <> + 请检查设备网络是否畅通,并确保设备已连接到以下地址之一: +
+ {(unref(gateway)?.channelInfo?.addresses || []).map((i: any) => ( + + + {i.address} + + ))} +
+ + ) : ( + <> + 请检查设备网络是否畅通,并确保设备已连接到: + {(unref(gateway)?.channelInfo?.addresses || []).map((i: any) => ( + + + {i.address} + + ))} + + ) + } + />, + ); + if ( + unref(device)?.protocol && + unref(device)?.accessProvider && + gatewayList.includes(unref(device).accessProvider as string) + ) { + const response = await queryProtocolDetail(unref(device).protocol, 'MQTT'); + if (response.status === 200) { + if ((response.result?.routes || []).length > 0) { + item.push( + + 请根据 + + 中${urlMap.get(unref(device)?.accessProvider) || ''}信息,任意上报一条数据 + + } + />, + ); + } else { + item.push( + + 请联系管理员提供${urlMap.get(unref(device)?.accessProvider) || ''} + 信息,并根据URL信息任意上报一条数据 + + } + />, + ); + } + } + } + info = { + ...info, + address: unref(gateway)?.channelInfo?.addresses || [], + config: configuration.device || [], + }; + } else if (props.providerType === 'child-device') { + if (unref(device)?.accessProvider === 'gb28181-2016') { + const address = unref(gateway)?.channelInfo?.addresses[0]; + if (address) { + item.push( + + 请检查设备网络是否畅通,并确保设备已连接到:SIP{' '} + + + {address.address} + + + } + />, + ); + info = { + ...info, + address: [address] || [], + }; + } + } + } else if (props.providerType === 'media') { + if (unref(device)?.accessProvider === 'gb28181-2016') { + const address = unref(gateway)?.channelInfo?.addresses[0]; + if (address) { + item.push( + + 请检查设备网络是否畅通,并确保设备已连接到:SIP{' '} + + + {address.address} + + + } + />, + ); + info = { + ...info, + address: [address] || [], + }; + } + } + } else if (props.providerType === 'cloud') { + item.push( + , + ); + item.push(); + } else if (props.providerType === 'channel') { + } + info = { + ...info, + configValue: unref(device)?.configuration || {}, + }; + diagnoseData.value = { + list: [...item], + info, + } + diagnoseVisible.value = true + } else { + emit('stateChange', 'success') + } + }; + + const handleSearch = async () => { + emit('percentChange', 0) + emit('countChange', 0) + emit('stateChange', 'loading') + status.value = 'loading' + const { providerType } = props + let arr: any[] = []; + if (providerType === 'network') { + list.value = [...networkInitList]; + arr = [ + diagnoseNetwork, + diagnoseGateway, + diagnoseProduct, + diagnoseDevice, + diagnoseProductAuthConfig, + diagnoseDeviceAuthConfig, + ]; + } else if (providerType === 'child-device') { + list.value = [...childInitList]; + arr = [ + diagnoseGateway, + diagnoseParentDevice, + diagnoseProduct, + diagnoseDevice, + diagnoseProductAuthConfig, + diagnoseDeviceAuthConfig, + ]; + } else if (providerType === 'media') { + list.value = [...mediaInitList]; + arr = [diagnoseGateway, diagnoseProduct, diagnoseDevice]; + } else if (providerType === 'cloud') { + list.value = [...cloudInitList]; + arr = [diagnoseGateway, diagnoseProduct, diagnoseDevice, diagnoseCTWing, diagnoseOnenet]; + } else if (providerType === 'channel') { + message.error('未开发'); + return; + } + if (arr.length > 0) { + for (let i = 0; i < arr.length; i++) { + await arr[i](); + emit('percentChange') + } + emit('percentChange', 100) + status.value = 'finish' + } + } + + watch(() => props.providerType, (newVal) => { + if (newVal) { + device.value = instanceStore.current + handleSearch() + } + }, { deep: true, immediate: true }) + + watch(() => [list, status], () => { + if (status.value === 'finish') { + const _list = _.uniq(_.map(unref(list), 'status')); + if (unref(device).state?.value !== 'online') { + emit('stateChange', 'error') + if (_list[0] === 'success' && _list.length === 1) { + diagnoseNetworkOtherConfig(); + } + } else { + emit('stateChange', 'success') + } + } else if (status.value === 'loading') { + const arr = _.map(unref(list), 'status').filter((i) => i !== 'loading'); + emit('countChange', arr.length) + } + }, { deep: true, immediate: true }) + + return () =>
+
+ + + { + status.value === 'finish' && unref(device).state?.value !== 'online' && + } + + +
+
+ { + list.value.map(item => ( +
+
+
+ +
+
+
{item?.name}
+
{item.desc}
+
{item?.info}
+
+
+
+ {item?.text} +
+
+ )) + } +
+ { + diagnoseVisible.value && { + diagnoseVisible.value = false + }} + /> + } + {artificialVisible.value && ( + { + artificialVisible.value = false + }} + onSave={(params: any) => { + list.value = modifyArrayList(list.value, { + key: params.key, + name: params.name, + desc: params.desc, + status: 'success', + text: '正常', + info: null, + }); + artificialVisible.value = false + }} + /> + )} +
+ }, +}) + +export default Status \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/Status/util.ts b/src/views/device/Instance/Detail/Diagnose/Status/util.ts new file mode 100644 index 00000000..11811906 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/Status/util.ts @@ -0,0 +1,262 @@ +import { getImage } from '@/utils/comm'; +import { VNode } from 'vue'; + +export type ListProps = { + key: string; + name: string; + desc?: string; + status: 'loading' | 'error' | 'success' | 'warning'; + text?: string; + info?: VNode | null; +}; + +export const TextColorMap = new Map(); +TextColorMap.set('loading', 'black'); +TextColorMap.set('error', 'red'); +TextColorMap.set('success', 'green'); +TextColorMap.set('warning', '#FAB247'); + +export const StatusMap = new Map(); +StatusMap.set('error', getImage('/diagnose/status/error.png')); +StatusMap.set('success', getImage('/diagnose/status/success.png')); +StatusMap.set('warning', getImage('/diagnose/status/warning.png')); +StatusMap.set('loading', getImage('/diagnose/status/loading.png')); + +export const networkInitList: ListProps[] = [ + // { + // key: 'access', + // name: '设备接入配置', + // desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + { + key: 'network', + name: '网络组件', + desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'gateway', + name: '设备接入网关', + desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, +]; + +export const childInitList: ListProps[] = [ + // { + // key: 'access', + // name: '设备接入配置', + // desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + // { + // key: 'network', + // name: '网络组件', + // desc: '诊断网络组件配置是否正确,配置错误将导致设备连接失败', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + { + key: 'gateway', + name: '设备接入网关', + desc: '诊断设备接入网关状态是否正常,网关配置是否正确', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'parent-device', + name: '网关父设备', + desc: '诊断网关父设备状态是否正常,禁用或离线将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, +]; + +export const cloudInitList: ListProps[] = [ + // { + // key: 'access', + // name: '设备接入配置', + // desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + { + key: 'gateway', + name: '设备接入网关', + desc: '诊断设备接入网关状态是否正常,网关配置是否正确', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, +]; + +export const channelInitList: ListProps[] = [ + // { + // key: 'access', + // name: '设备接入配置', + // desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + { + key: 'gateway', + name: '设备接入网关', + desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, +]; + +export const mediaInitList: ListProps[] = [ + // { + // key: 'access', + // name: '设备接入配置', + // desc: '诊断该设备所属产品是否已配置“设备接入”方式,未配置将导致设备连接失败。', + // status: 'loading', + // text: '正在诊断中...', + // info: null, + // }, + { + key: 'gateway', + name: '设备接入网关', + desc: '诊断设备接入网关状态是否正常,禁用状态将导致连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'product', + name: '产品状态', + desc: '诊断产品状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, + { + key: 'device', + name: '设备状态', + desc: '诊断设备状态是否正常,禁用状态将导致设备连接失败', + status: 'loading', + text: '正在诊断中...', + info: null, + }, +]; + +export const modifyArrayList = (oldList: ListProps[], item: ListProps, index?: number) => { + let newList: ListProps[] = []; + if (index !== 0 && !index) { + // 添加 + for (let i = 0; i < oldList.length; i++) { + const dt = oldList[i]; + if (item.key === dt.key) { + newList.push(item); + } else { + newList.push(dt); + } + } + } else { + // 修改 + oldList.splice(index, 0, item); + newList = [...oldList]; + } + return newList; +}; + +export const isExit = (arr1: any[], arr2: any[]) => { + return arr1.find((item) => arr2.includes(item)); +}; + +export const gatewayList = [ + 'websocket-server', + 'http-server-gateway', + 'udp-device-gateway', + 'coap-server-gateway', + 'mqtt-client-gateway', + 'tcp-server-gateway', +]; + +export const urlMap = new Map(); +urlMap.set('mqtt-client-gateway', 'topic'); +urlMap.set('http-server-gateway', 'url'); +urlMap.set('websocket-server', 'url'); +urlMap.set('coap-server-gateway', 'url'); + diff --git a/src/views/device/Instance/Detail/Diagnose/index.vue b/src/views/device/Instance/Detail/Diagnose/index.vue new file mode 100644 index 00000000..c1180ffc --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/index.vue @@ -0,0 +1,201 @@ + + + + + \ No newline at end of file diff --git a/src/views/device/Instance/Detail/Diagnose/util.ts b/src/views/device/Instance/Detail/Diagnose/util.ts new file mode 100644 index 00000000..ad5ac915 --- /dev/null +++ b/src/views/device/Instance/Detail/Diagnose/util.ts @@ -0,0 +1,30 @@ +import { getImage } from '@/utils/comm'; + +export const headerImgMap = new Map(); +headerImgMap.set('loading', getImage('/diagnose/loading-2.png')); +headerImgMap.set('error', getImage('/diagnose/error.png')); +headerImgMap.set('success', getImage('/diagnose/success.png')); + +export const headerColorMap = new Map(); +headerColorMap.set('loading', 'linear-gradient(89.95deg, #E6F5FF 0.03%, #E9EAFF 99.95%)'); +headerColorMap.set( + 'error', + 'linear-gradient(89.95deg, rgba(231, 173, 86, 0.1) 0.03%, rgba(247, 111, 93, 0.1) 99.95%)', +); +headerColorMap.set('success', 'linear-gradient(89.95deg, #E8F8F7 0.03%, #EBEFFA 99.95%)'); + + +export const headerTitleMap = new Map(); +headerTitleMap.set('loading', '正在诊断中'); +headerTitleMap.set('error', '发现连接问题'); +headerTitleMap.set('success', '连接状态正常'); + +export const headerDescMap = new Map(); +headerDescMap.set('loading', '已诊断XX个'); +headerDescMap.set('error', '请处理连接异常'); +headerDescMap.set('success', '现在可调试消息通信'); + +export const progressMap = new Map(); +progressMap.set('loading', '#597EF7'); +progressMap.set('error', '#FAB247'); +progressMap.set('success', '#32D4A4'); diff --git a/src/views/device/Instance/Detail/index.vue b/src/views/device/Instance/Detail/index.vue index 2e4c2609..d7a90c8e 100644 --- a/src/views/device/Instance/Detail/index.vue +++ b/src/views/device/Instance/Detail/index.vue @@ -44,6 +44,7 @@ import Info from './Info/index.vue'; import Running from './Running/index.vue' import Metadata from '../../components/Metadata/index.vue'; import ChildDevice from './ChildDevice/index.vue'; +import Diagnose from './Diagnose/index.vue' import { _deploy, _disconnect } from '@/api/device/instance' import { message } from 'ant-design-vue'; import { getImage } from '@/utils/comm'; @@ -52,7 +53,7 @@ const route = useRoute(); const instanceStore = useInstanceStore() const statusMap = new Map(); -statusMap.set('online', 'processing'); +statusMap.set('online', 'success'); statusMap.set('offline', 'error'); statusMap.set('notActive', 'warning'); @@ -72,7 +73,11 @@ const list = [ { key: 'ChildDevice', tab: '子设备' - } + }, + { + key: 'Diagnose', + tab: '设备诊断' + }, ] const tabs = { @@ -80,6 +85,7 @@ const tabs = { Metadata, Running, ChildDevice, + Diagnose } watch( diff --git a/src/views/device/Instance/index.vue b/src/views/device/Instance/index.vue index 4ef603ac..3270902a 100644 --- a/src/views/device/Instance/index.vue +++ b/src/views/device/Instance/index.vue @@ -1,6 +1,10 @@ @@ -258,12 +249,11 @@ import { getImage } from '@/utils/comm'; import { Form } from 'ant-design-vue'; import { message } from 'ant-design-vue'; -import templateApi from '@/api/notice/template'; +import DeviceApi from '@/api/media/device'; -import { FILE_UPLOAD } from '@/api/comm'; -import { LocalStore } from '@/utils/comm'; -import { TOKEN_KEY } from '@/utils/variable'; import { PROVIDER_OPTIONS } from '@/views/media/Device/const'; +import type { ProductType } from '@/views/media/Device/typings'; +import SaveProduct from './SaveProduct.vue'; const router = useRouter(); const route = useRoute(); @@ -274,18 +264,26 @@ const formData = ref({ id: '', name: '', channel: 'gb28181-2016', - photoUrl: '', + photoUrl: getImage('/device-media.png'), productId: '', others: { access_pwd: '', }, description: '', + // 编辑字段 + streamMode: 'UDP', + manufacturer: '', + model: '', + firmware: '', }); // 验证规则 const formRules = ref({ id: [ - { required: true, message: '请输入ID' }, + { + required: true, + message: '请输入ID', + }, { max: 64, message: '最多输入64个字符' }, { pattern: /^[a-zA-Z0-9_\-]+$/, @@ -300,8 +298,20 @@ const formRules = ref({ channel: [{ required: true, message: '请选择接入方式' }], 'others.access_pwd': [{ required: true, message: '请输入接入密码' }], description: [{ max: 200, message: '最多可输入200个字符' }], + streamMode: [{ required: true, message: '请选择流传输模式' }], }); +watch( + () => formData.value.channel, + (val) => { + formRules.value['id'][0].required = val === 'gb28181-2016'; + formRules.value['others.access_pwd'][0].required = + val === 'gb28181-2016'; + validate(); + getProductList(); + }, +); + const { resetFields, validate, validateInfos, clearValidate } = useForm( formData.value, formRules.value, @@ -309,36 +319,64 @@ const { resetFields, validate, validateInfos, clearValidate } = useForm( const clearValid = () => { setTimeout(() => { - formData.value.variableDefinitions = []; clearValidate(); }, 200); }; +/** + * 获取所属产品 + */ +const productList = ref([]); +const getProductList = async () => { + // console.log('formData.productId: ', formData.value.productId); + const params = { + paging: false, + sorts: [{ name: 'createTime', order: 'desc' }], + terms: [ + { column: 'accessProvider', value: formData.value.channel }, + { column: 'state', value: 1 }, + ], + }; + const { result } = await DeviceApi.queryProductList(params); + productList.value = result; +}; +getProductList(); + +/** + * 新增产品 + */ +const saveProductVis = ref(false); + /** * 获取详情 */ const getDetail = async () => { - const res = await templateApi.detail(route.params.id as string); + const res = await DeviceApi.detail(route.query.id as string); // console.log('res: ', res); formData.value = res.result; - // console.log('formData.value: ', formData.value); + formData.value.channel = res.result.provider; + console.log('formData.value: ', formData.value); + // validate(); }; -// getDetail(); + +onMounted(() => { + getDetail(); +}); /** * 表单提交 */ const btnLoading = ref(false); const handleSubmit = () => { - // console.log('formData.value: ', formData.value); + console.log('formData.value: ', formData.value); validate() .then(async () => { btnLoading.value = true; let res; - if (!formData.value.id) { - res = await templateApi.save(formData.value); + if (!route.query.id) { + res = await DeviceApi.save(formData.value); } else { - res = await templateApi.update(formData.value); + res = await DeviceApi.update(formData.value); } // console.log('res: ', res); if (res?.success) { @@ -360,70 +398,5 @@ const handleSubmit = () => { .page-container { background: #f0f2f5; padding: 24px; - .upload-image-warp-logo { - display: flex; - justify-content: flex-start; - .upload-image-border-logo { - position: relative; - overflow: hidden; - border: 1px dashed #d9d9d9; - transition: all 0.3s; - width: 160px; - height: 150px; - &:hover { - border: 1px dashed #1890ff; - display: flex; - } - .upload-image-content-logo { - align-items: center; - justify-content: center; - position: relative; - display: flex; - flex-direction: column; - width: 160px; - height: 150px; - padding: 8px; - background-color: rgba(0, 0, 0, 0.06); - cursor: pointer; - .loading-logo { - position: absolute; - top: 50%; - } - .loading-icon { - position: absolute; - } - .upload-image { - width: 100%; - height: 100%; - background-repeat: no-repeat; - background-position: 50%; - background-size: cover; - } - .upload-image-icon { - width: 100%; - height: 100%; - background-repeat: no-repeat; - background-position: 50%; - background-size: inherit; - } - .upload-image-mask { - align-items: center; - justify-content: center; - position: absolute; - top: 0; - left: 0; - display: none; - width: 100%; - height: 100%; - color: #fff; - font-size: 16px; - background-color: rgba(0, 0, 0, 0.35); - } - &:hover .upload-image-mask { - display: flex; - } - } - } - } } diff --git a/src/views/media/Device/const.ts b/src/views/media/Device/const.ts index d1360534..1d2ccc69 100644 --- a/src/views/media/Device/const.ts +++ b/src/views/media/Device/const.ts @@ -1,4 +1,13 @@ export const PROVIDER_OPTIONS = [ { label: '固定地址', value: 'fixed-media' }, { label: 'GB/T28181', value: 'gb28181-2016' }, -] \ No newline at end of file +] +export const streamMode = [ + { label: 'UDP', value: 'UDP' }, + { label: 'TCP被动', value: 'TCP_PASSIVE' }, +] + +export const providerType = { + 'gb28181-2016': 'GB/T28181', + 'fixed-media': '固定地址', +}; \ No newline at end of file diff --git a/src/views/media/Device/index.vue b/src/views/media/Device/index.vue index d1eef81a..acbb3de5 100644 --- a/src/views/media/Device/index.vue +++ b/src/views/media/Device/index.vue @@ -135,11 +135,7 @@ import type { ActionsType } from '@/components/Table/index.vue'; import { message } from 'ant-design-vue'; import { getImage } from '@/utils/comm'; import { PROVIDER_OPTIONS } from '@/views/media/Device/const'; - -const providerType = { - 'gb28181-2016': 'GB/T28181', - 'fixed-media': '固定地址', -}; +import { providerType } from './const'; const router = useRouter(); diff --git a/src/views/media/Device/typings.d.ts b/src/views/media/Device/typings.d.ts index 012be458..436c9236 100644 --- a/src/views/media/Device/typings.d.ts +++ b/src/views/media/Device/typings.d.ts @@ -21,4 +21,65 @@ export type DeviceItem = { state: State; streamMode: string; transport: string; -} & BaseItem; \ No newline at end of file +} & BaseItem; + +export type ProductType = { + accessId: string; + accessName: string; + accessProvider: string; + createTime: number; + creatorId: string; + deviceType: { + text: string; + value: string; + }; + id: string; + messageProtocol: string; + metadata: string; + modifierId: string; + modifyTime: number; + name: string; + protocolName: string; + state: number; + transportProtocol: string; +} + + +type addressesType = { + address: string; + bad: boolean; + disabled: boolean; + health: number; + ok: boolean; +} +export type gatewayType = { + channel: string; + channelId: string; + channelInfo: { + id: string; + name: string; + addresses: addressesType[]; + }; + id: string; + name: string; + protocol: string; + protocolDetail: { + id: string; + name: string; + description?: string; + }; + provider: string; + state: { + text: string; + value: string; + }; + transport: string; + transportDetail: { + id: string; + name: string; + metadata: string; + features: string[]; + routes: string[]; + }; + description?: string; +} \ No newline at end of file diff --git a/src/views/northbound/AliCloud/Detail/index.vue b/src/views/northbound/AliCloud/Detail/index.vue index 10b99894..80c8e421 100644 --- a/src/views/northbound/AliCloud/Detail/index.vue +++ b/src/views/northbound/AliCloud/Detail/index.vue @@ -11,143 +11,312 @@ > - - + + - + - - {{item.name}} + + {{ item.name }} - + - + - + - + - + - + - + }" + > - - {{item.productName}} + + {{ + item.productName + }}

产品映射

- - - + + + - - - {{i.productName}} + + + {{ + i.productName + }} - - - {{i.name}} + + + {{ + i.name + }} @@ -156,17 +325,26 @@
- + 添加 + style="margin-left: 2px" + />添加 - +
- 保存 + 保存
@@ -190,8 +373,14 @@ \ No newline at end of file diff --git a/src/views/northbound/AliCloud/index.vue b/src/views/northbound/AliCloud/index.vue index 3d4e8ccc..52228bac 100644 --- a/src/views/northbound/AliCloud/index.vue +++ b/src/views/northbound/AliCloud/index.vue @@ -1,6 +1,6 @@ \ No newline at end of file diff --git a/src/views/northbound/DuerOS/index.vue b/src/views/northbound/DuerOS/index.vue index c35da774..b1079742 100644 --- a/src/views/northbound/DuerOS/index.vue +++ b/src/views/northbound/DuerOS/index.vue @@ -1,6 +1,10 @@