Merge branch 'dev' of github.com:jetlinks/jetlinks-ui-vue into dev
This commit is contained in:
commit
5a613e40e9
|
@ -1,10 +1,11 @@
|
||||||
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}`),
|
||||||
// 新增
|
// 新增
|
||||||
|
@ -13,4 +14,15 @@ export default {
|
||||||
update: (id: string, data: any) => server.put(`/media/gb28181-cascade/${id}`, data),
|
update: (id: string, data: any) => server.put(`/media/gb28181-cascade/${id}`, 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(`/network/resources/alive/clusters`),
|
||||||
|
// SIP本地地址
|
||||||
|
all: () => server.get(`/network/resources/alive/_all`),
|
||||||
|
|
||||||
}
|
}
|
|
@ -48,7 +48,7 @@ const iconKeys = [
|
||||||
'ClockCircleOutlined',
|
'ClockCircleOutlined',
|
||||||
'PartitionOutlined',
|
'PartitionOutlined',
|
||||||
'ShareAltOutlined',
|
'ShareAltOutlined',
|
||||||
'playCircleOutlined',
|
'PlayCircleOutlined',
|
||||||
'RightOutlined',
|
'RightOutlined',
|
||||||
'FileTextOutlined',
|
'FileTextOutlined',
|
||||||
'UploadOutlined',
|
'UploadOutlined',
|
||||||
|
|
|
@ -90,7 +90,7 @@ const insert = (val) => {
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(() => props.value,
|
watch(() => props.modelValue,
|
||||||
(val) => {
|
(val) => {
|
||||||
instance.setValue(val)
|
instance.setValue(val)
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,671 @@
|
||||||
|
<!-- 国标级联新增/编辑 -->
|
||||||
|
<template>
|
||||||
|
<page-container>
|
||||||
|
<a-card>
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form layout="vertical" :model="formData">
|
||||||
|
<a-row :gutter="24">
|
||||||
|
<TitleComponent data="基本信息" />
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="名称"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 84,
|
||||||
|
message: '最多可输入84个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入名称"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="代理视频流"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择代理视频流',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="formData.name"
|
||||||
|
>
|
||||||
|
<a-radio-button value="enabled">
|
||||||
|
启用
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="disabled">
|
||||||
|
禁用
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<TitleComponent data="信令服务配置" />
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="集群节点"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择集群节点',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请选择集群节点"
|
||||||
|
/>
|
||||||
|
</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="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级SIP ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入上级SIP ID"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="上级SIP域"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级平台SIP域',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入上级平台SIP域"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="上级SIP 地址"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入上级SIP 地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入IP地址"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入端口"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
label="本地SIP ID"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入网关侧的SIP ID',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="网关侧的SIP ID"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="SIP本地地址"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入SIP本地地址',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请选择IP地址"
|
||||||
|
>
|
||||||
|
<a-select-option value="1">
|
||||||
|
1
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-select
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请选择端口"
|
||||||
|
>
|
||||||
|
<a-select-option value="1">
|
||||||
|
1
|
||||||
|
</a-select-option>
|
||||||
|
</a-select>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item
|
||||||
|
label="SIP远程地址"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入SIP远程地址',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-row :gutter="10">
|
||||||
|
<a-col :span="14">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入IP地址"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="10">
|
||||||
|
<a-input
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入端口"
|
||||||
|
/>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="24">
|
||||||
|
<a-form-item
|
||||||
|
label="传输协议"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择传输协议',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-radio-group
|
||||||
|
button-style="solid"
|
||||||
|
v-model:value="formData.name"
|
||||||
|
>
|
||||||
|
<a-radio-button value="UDP">
|
||||||
|
UDP
|
||||||
|
</a-radio-button>
|
||||||
|
<a-radio-button value="TCP_PASSIVE">
|
||||||
|
TCP
|
||||||
|
</a-radio-button>
|
||||||
|
</a-radio-group>
|
||||||
|
</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="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="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="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="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="12">
|
||||||
|
<a-form-item
|
||||||
|
label="心跳周期(秒)"
|
||||||
|
name="name"
|
||||||
|
:rules="[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入心跳周期(秒)',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
max: 64,
|
||||||
|
message: '最多可输入64个字符',
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<a-input-number
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入心跳周期(秒)"
|
||||||
|
/>
|
||||||
|
</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-number
|
||||||
|
v-model:value="formData.name"
|
||||||
|
placeholder="请输入注册间隔(秒)"
|
||||||
|
/>
|
||||||
|
</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 { Form } from 'ant-design-vue';
|
||||||
|
import { message } from 'ant-design-vue';
|
||||||
|
|
||||||
|
import DeviceApi from '@/api/media/device';
|
||||||
|
|
||||||
|
import { PROVIDER_OPTIONS } from '@/views/media/Device/const';
|
||||||
|
import type { ProductType } from '@/views/media/Device/typings';
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const route = useRoute();
|
||||||
|
const useForm = Form.useForm;
|
||||||
|
|
||||||
|
// 表单数据
|
||||||
|
const formData = ref({
|
||||||
|
id: '',
|
||||||
|
name: '',
|
||||||
|
channel: 'gb28181-2016',
|
||||||
|
photoUrl: getImage('/device-media.png'),
|
||||||
|
productId: '',
|
||||||
|
others: {
|
||||||
|
access_pwd: '',
|
||||||
|
},
|
||||||
|
description: '',
|
||||||
|
// 编辑字段
|
||||||
|
streamMode: 'UDP',
|
||||||
|
manufacturer: '',
|
||||||
|
model: '',
|
||||||
|
firmware: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
// 验证规则
|
||||||
|
const formRules = ref({
|
||||||
|
id: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入ID',
|
||||||
|
},
|
||||||
|
{ max: 64, message: '最多输入64个字符' },
|
||||||
|
{
|
||||||
|
pattern: /^[a-zA-Z0-9_\-]+$/,
|
||||||
|
message: '请输入英文或者数字或者-或者_',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{ required: true, message: '请输入名称' },
|
||||||
|
{ max: 64, message: '最多可输入64个字符' },
|
||||||
|
],
|
||||||
|
productId: [{ required: true, message: '请选择所属产品' }],
|
||||||
|
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,
|
||||||
|
);
|
||||||
|
|
||||||
|
const clearValid = () => {
|
||||||
|
setTimeout(() => {
|
||||||
|
clearValidate();
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所属产品
|
||||||
|
*/
|
||||||
|
const productList = ref<ProductType[]>([]);
|
||||||
|
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 DeviceApi.detail(route.query.id as string);
|
||||||
|
// console.log('res: ', res);
|
||||||
|
// formData.value = res.result;
|
||||||
|
Object.assign(formData.value, res.result);
|
||||||
|
formData.value.channel = res.result.provider;
|
||||||
|
|
||||||
|
console.log('formData.value: ', formData.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getDetail();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 表单提交
|
||||||
|
*/
|
||||||
|
const btnLoading = ref<boolean>(false);
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// console.log('formData.value: ', formData.value);
|
||||||
|
validate()
|
||||||
|
.then(async () => {
|
||||||
|
btnLoading.value = true;
|
||||||
|
let res;
|
||||||
|
if (!route.query.id) {
|
||||||
|
res = await DeviceApi.save(formData.value);
|
||||||
|
} else {
|
||||||
|
res = await DeviceApi.update(formData.value);
|
||||||
|
}
|
||||||
|
if (res?.success) {
|
||||||
|
message.success('保存成功');
|
||||||
|
router.back();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
console.log('err: ', err);
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
btnLoading.value = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</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>
|
||||||
|
|
Loading…
Reference in New Issue