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 type { CascadeItem } from '@/views/media/Cascade/typings'
|
||||
|
||||
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
|
||||
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}`),
|
||||
// 新增
|
||||
|
@ -13,4 +14,15 @@ export default {
|
|||
update: (id: string, data: any) => server.put(`/media/gb28181-cascade/${id}`, data),
|
||||
// 删除
|
||||
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',
|
||||
'PartitionOutlined',
|
||||
'ShareAltOutlined',
|
||||
'playCircleOutlined',
|
||||
'PlayCircleOutlined',
|
||||
'RightOutlined',
|
||||
'FileTextOutlined',
|
||||
'UploadOutlined',
|
||||
|
|
|
@ -90,7 +90,7 @@ const insert = (val) => {
|
|||
]);
|
||||
}
|
||||
|
||||
watch(() => props.value,
|
||||
watch(() => props.modelValue,
|
||||
(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>
|
||||
<Search
|
||||
:columns="columns"
|
||||
target="notice-config"
|
||||
target="media-cascade"
|
||||
@search="handleSearch"
|
||||
/>
|
||||
|
||||
<JTable
|
||||
ref="listRef"
|
||||
:columns="columns"
|
||||
:request="DeviceApi.list"
|
||||
:request="(e:any) => lastValueFrom(e)"
|
||||
:defaultParams="{
|
||||
sorts: [{ name: 'createTime', order: 'desc' }],
|
||||
}"
|
||||
:params="params"
|
||||
:gridColumn="2"
|
||||
>
|
||||
<template #headerTitle>
|
||||
<a-button type="primary" @click="handleAdd"> 新增 </a-button>
|
||||
|
@ -23,45 +25,37 @@
|
|||
:actions="getActions(slotProps, 'card')"
|
||||
v-bind="slotProps"
|
||||
:showStatus="true"
|
||||
:status="
|
||||
slotProps.state.value === 'online' ? 'success' : 'error'
|
||||
"
|
||||
:statusText="slotProps.state.text"
|
||||
:statusNames="{ success: 'success', error: 'error' }"
|
||||
:status="slotProps.status?.value"
|
||||
:statusText="slotProps.status?.text"
|
||||
:statusNames="{
|
||||
enabled: 'success',
|
||||
disabled: 'error',
|
||||
}"
|
||||
>
|
||||
<template #img>
|
||||
<slot name="img">
|
||||
<img :src="getImage('/device-media.png')" />
|
||||
<img
|
||||
:src="
|
||||
getImage('/device/instance/device-card.png')
|
||||
"
|
||||
/>
|
||||
</slot>
|
||||
</template>
|
||||
<template #content>
|
||||
<h3 class="card-item-content-title">
|
||||
{{ slotProps.name }}
|
||||
</h3>
|
||||
<a-row>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">厂商</div>
|
||||
<div>{{ slotProps.manufacturer }}</div>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<div class="card-item-content-text">
|
||||
通道数量
|
||||
</div>
|
||||
<div>{{ slotProps.channelNumber }}</div>
|
||||
</a-col>
|
||||
<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>
|
||||
<p>通道数量:{{ slotProps.count }}</p>
|
||||
<Ellipsis>
|
||||
<a-badge
|
||||
:text="`sip:${slotProps.sipConfigs[0]?.sipId}@${slotProps.sipConfigs[0]?.hostAndPort}`"
|
||||
:status="
|
||||
slotProps.status?.value === 'enabled'
|
||||
? 'success'
|
||||
: 'error'
|
||||
"
|
||||
/>
|
||||
</Ellipsis>
|
||||
</template>
|
||||
<template #actions="item">
|
||||
<a-tooltip
|
||||
|
@ -73,9 +67,20 @@
|
|||
v-bind="item.popConfirm"
|
||||
:disabled="item.disabled"
|
||||
>
|
||||
<a-button :disabled="item.disabled">
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
v-if="item.key === 'delete'"
|
||||
>
|
||||
<AIcon type="DeleteOutlined" />
|
||||
</a-button>
|
||||
<a-button
|
||||
:disabled="item.disabled"
|
||||
@click="item.onClick"
|
||||
v-else
|
||||
>
|
||||
<AIcon :type="item.icon" />
|
||||
<span>{{ item.text }}</span>
|
||||
</a-button>
|
||||
</a-popconfirm>
|
||||
<template v-else>
|
||||
<a-button
|
||||
|
@ -87,9 +92,53 @@
|
|||
</a-button>
|
||||
</template>
|
||||
</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>
|
||||
</CardBox>
|
||||
</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">
|
||||
<a-space :size="16">
|
||||
<a-tooltip
|
||||
|
@ -123,6 +172,24 @@
|
|||
/></a-button>
|
||||
</a-button>
|
||||
</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>
|
||||
</template>
|
||||
</JTable>
|
||||
|
@ -131,6 +198,7 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import DeviceApi from '@/api/media/device';
|
||||
import CascadeApi from '@/api/media/cascade';
|
||||
import type { ActionsType } from '@/components/Table/index.vue';
|
||||
import { message } from 'ant-design-vue';
|
||||
import { getImage } from '@/utils/comm';
|
||||
|
@ -156,14 +224,14 @@ const columns = [
|
|||
},
|
||||
{
|
||||
title: '上级SIP ID',
|
||||
dataIndex: 'sipConfigs',
|
||||
key: 'sipConfigs',
|
||||
dataIndex: 'sipId',
|
||||
key: 'sipId',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
title: '上级SIP 地址',
|
||||
dataIndex: 'sipConfigs',
|
||||
key: 'sipConfigs',
|
||||
dataIndex: 'publicHost',
|
||||
key: 'publicHost',
|
||||
scopedSlots: true,
|
||||
},
|
||||
{
|
||||
|
@ -217,17 +285,35 @@ const columns = [
|
|||
* @param params
|
||||
*/
|
||||
const handleSearch = (e: any) => {
|
||||
// console.log('handleSearch:', 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 = () => {
|
||||
menuStory.jumpPage('media/Device/Save', {
|
||||
id: ':id',
|
||||
});
|
||||
menuStory.jumpPage('media/Cascade/Save');
|
||||
};
|
||||
|
||||
const getActions = (
|
||||
|
@ -245,7 +331,7 @@ const getActions = (
|
|||
icon: 'EditOutlined',
|
||||
onClick: () => {
|
||||
menuStory.jumpPage(
|
||||
'media/Device/Save',
|
||||
'media/Cascade/Save',
|
||||
{},
|
||||
{
|
||||
id: data.id,
|
||||
|
@ -255,58 +341,79 @@ const getActions = (
|
|||
},
|
||||
{
|
||||
key: 'view',
|
||||
text: '查看通道',
|
||||
text: '选择通道',
|
||||
tooltip: {
|
||||
title: '查看通道',
|
||||
title: '选择通道',
|
||||
},
|
||||
icon: 'PartitionOutlined',
|
||||
icon: 'LinkOutlined',
|
||||
onClick: () => {
|
||||
// router.push(
|
||||
// `/media/device/Channel?id=${data.id}&type=${data.provider}`,
|
||||
// );
|
||||
menuStory.jumpPage(
|
||||
'media/Device/Channel',
|
||||
'media/Cascade/Channel',
|
||||
{},
|
||||
{
|
||||
id: data.id,
|
||||
type: data.provider,
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
key: 'debug',
|
||||
text: '更新通道',
|
||||
text: '推送',
|
||||
tooltip: {
|
||||
title:
|
||||
data.provider === 'fixed-media'
|
||||
? '固定地址无法更新通道'
|
||||
: data.state.value === 'offline'
|
||||
? '设备已离线'
|
||||
: data.state.value === 'notActive'
|
||||
? '设备已禁用'
|
||||
: '',
|
||||
data.status?.value === 'disabled'
|
||||
? '禁用状态下不可推送'
|
||||
: '推送',
|
||||
},
|
||||
disabled:
|
||||
data.state.value === 'offline' ||
|
||||
data.state.value === 'notActive' ||
|
||||
data.provider === 'fixed-media',
|
||||
icon: 'SyncOutlined',
|
||||
disabled: data.status?.value === 'disabled',
|
||||
icon: 'ShareAltOutlined',
|
||||
onClick: () => {
|
||||
// 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',
|
||||
text: '删除',
|
||||
tooltip: {
|
||||
title: '在线设备无法删除',
|
||||
title:
|
||||
data.status?.value === 'enabled'
|
||||
? '请先禁用, 再删除'
|
||||
: '删除',
|
||||
},
|
||||
disabled: data.state.value === 'online',
|
||||
disabled: data.status?.value === 'enabled',
|
||||
popConfirm: {
|
||||
title: '确认删除?',
|
||||
onConfirm: async () => {
|
||||
const resp = await DeviceApi.del(data.id);
|
||||
const resp = await CascadeApi.del(data.id);
|
||||
if (resp.status === 200) {
|
||||
message.success('操作成功!');
|
||||
listRef.value?.reload();
|
||||
|
|
|
@ -31,7 +31,7 @@ type SipConfig = {
|
|||
transport: string;
|
||||
user: string;
|
||||
};
|
||||
type CascadeItem = {
|
||||
export type CascadeItem = {
|
||||
mediaServerId: string;
|
||||
onlineStatus: State;
|
||||
proxyStream: boolean;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<!-- 通知模板详情 -->
|
||||
<!-- 视频设备新增/编辑 -->
|
||||
<template>
|
||||
<page-container>
|
||||
<a-card>
|
||||
|
|
Loading…
Reference in New Issue