Commit 520ac95c37af5977eb1514747e7fc10a1862562a

Authored by 张 峰林
Committed by xp.Huang
1 parent 59683e06

feat: gbt28181(支持点播、云台控制)

@@ -19,6 +19,7 @@ enum CameraManagerApi { @@ -19,6 +19,7 @@ enum CameraManagerApi {
19 STREAMING_POST_URL = '/video/platform', 19 STREAMING_POST_URL = '/video/platform',
20 STREAMING_DELETE_URL = '/video/platform', 20 STREAMING_DELETE_URL = '/video/platform',
21 STREAMING_PLAY_GET_URL = '/video/url', 21 STREAMING_PLAY_GET_URL = '/video/url',
  22 + VIDEO_CONTROL_STOP = '/video/control/stop/',
22 } 23 }
23 24
24 export const cameraPage = (params: CameraQueryParam) => { 25 export const cameraPage = (params: CameraQueryParam) => {
@@ -127,3 +128,10 @@ export const controlling = (params: any) => { @@ -127,3 +128,10 @@ export const controlling = (params: any) => {
127 params, 128 params,
128 }); 129 });
129 }; 130 };
  131 +
  132 +//云台控制 停止点播
  133 +export const stopOnDemandVideoApiGet = (deviceId: string, channelId: string) => {
  134 + return defHttp.get({
  135 + url: `${CameraManagerApi.VIDEO_CONTROL_STOP}${deviceId}/${channelId}`,
  136 + });
  137 +};
  1 +export interface VideoChannelQueryParamsType {
  2 + page: number;
  3 + pageSize: number;
  4 + tbDeviceId?: string;
  5 + name?: string;
  6 + cameraCode?: string;
  7 +}
  8 +
  9 +export interface VideoChannelItemType {
  10 + id: string;
  11 + createTime: string;
  12 + updateTime: string;
  13 + name: string;
  14 + enabled: boolean;
  15 + tenantId: string;
  16 + channelId: string;
  17 + cameraCode: string;
  18 + deviceId: string;
  19 + manufacturer: string;
  20 + model: string;
  21 + owner: string;
  22 + civilCode: string;
  23 + address: string;
  24 + parental: number;
  25 + safetyWay: number;
  26 + registerWay: number;
  27 + certifiable: number;
  28 + errorCode: number;
  29 + secrecy: number;
  30 + status: string;
  31 + longitude: number;
  32 + latitude: number;
  33 + longitudeGcj02: number;
  34 + latitudeGcj02: number;
  35 + longitudeWgs84: number;
  36 + latitudeWgs84: number;
  37 + subCount: number;
  38 + hasAudio: boolean;
  39 + channelType: number;
  40 + ptztype: number;
  41 + ifShowGBT?: boolean;
  42 +}
  43 +
  44 +export interface VideoChannelPlayAddressType {
  45 + code: number;
  46 + message: string;
  47 + data: Data;
  48 +}
  49 +
  50 +export interface Data {
  51 + app: string;
  52 + stream: string;
  53 + ip: any;
  54 + flv: string;
  55 + https_flv: string;
  56 + ws_flv: string;
  57 + wss_flv: string;
  58 + fmp4: string;
  59 + https_fmp4: string;
  60 + ws_fmp4: string;
  61 + wss_fmp4: string;
  62 + hls: string;
  63 + https_hls: string;
  64 + ws_hls: string;
  65 + wss_hls: string;
  66 + ts: string;
  67 + https_ts: string;
  68 + ws_ts: string;
  69 + wss_ts: any;
  70 + rtmp: string;
  71 + rtmps: string;
  72 + rtsp: string;
  73 + rtsps: string;
  74 + rtc: string;
  75 + rtcs: string;
  76 + mediaServerId: string;
  77 + tracks: Track[];
  78 + startTime: any;
  79 + endTime: any;
  80 + progress: number;
  81 +}
  82 +
  83 +export interface Track {
  84 + channels: number;
  85 + codecId: number;
  86 + codecIdName: any;
  87 + codecType: number;
  88 + ready: boolean;
  89 + sampleBit: number;
  90 + sampleRate: number;
  91 + fps: number;
  92 + height: number;
  93 + width: number;
  94 +}
  95 +
  96 +export interface VideoChanneControlType {
  97 + command?: string | number | object;
  98 + horizonSpeed?: string | number | object;
  99 + verticalSpeed?: string | number | object;
  100 + zoomSpeed?: string | number | object;
  101 + tbDeviceId?: string | number | object;
  102 + channelId?: string | number | object;
  103 +}
  1 +import {
  2 + VideoChannelItemType,
  3 + VideoChannelPlayAddressType,
  4 + VideoChannelQueryParamsType,
  5 + VideoChanneControlType,
  6 +} from './model/videoChannelModel';
  7 +import { PaginationResult } from '/#/axios';
  8 +import { defHttp } from '/@/utils/http/axios';
  9 +
  10 +enum Api {
  11 + GET_VIDEO_CHANNEL_LIST = '/video/channel',
  12 + GET_VIDEO_CONTROL_START = '/video/control/start',
  13 + GET_VIDEO_CONTROL_STOP = '/video/control/stop',
  14 + SET_VIDEO_CONTROL_CONTROL = '/video/control/control',
  15 +}
  16 +
  17 +export const getVideoChannelList = (params: VideoChannelQueryParamsType) => {
  18 + return defHttp.get<PaginationResult<VideoChannelItemType>>({
  19 + url: Api.GET_VIDEO_CHANNEL_LIST,
  20 + params,
  21 + });
  22 +};
  23 +
  24 +export const getVideoControlStart = ({
  25 + deviceId,
  26 + channelId,
  27 +}: Record<'deviceId' | 'channelId', string>) => {
  28 + return defHttp.get<VideoChannelPlayAddressType>(
  29 + {
  30 + url: `${Api.GET_VIDEO_CONTROL_START}/${deviceId}/${channelId}`,
  31 + timeout: 30 * 1000,
  32 + },
  33 + {}
  34 + );
  35 +};
  36 +
  37 +export const setVideoControl = (tbDeviceId, channelId, params: VideoChanneControlType) => {
  38 + return defHttp.get<VideoChannelPlayAddressType>({
  39 + url: `${Api.SET_VIDEO_CONTROL_CONTROL}/${tbDeviceId}/${channelId}`,
  40 + // timeout: 30 * 1000,
  41 + params,
  42 + });
  43 +};
@@ -16,10 +16,10 @@ @@ -16,10 +16,10 @@
16 @open-gateway-device="handleOpenGatewayDevice" 16 @open-gateway-device="handleOpenGatewayDevice"
17 /> 17 />
18 </Tabs.TabPane> 18 </Tabs.TabPane>
19 - <Tabs.TabPane key="modelOfMatter" tab="物模型数据"> 19 + <Tabs.TabPane v-if="!isGBTTransportType" key="modelOfMatter" tab="物模型数据">
20 <ModelOfMatter :deviceDetail="deviceDetail" /> 20 <ModelOfMatter :deviceDetail="deviceDetail" />
21 </Tabs.TabPane> 21 </Tabs.TabPane>
22 - <Tabs.TabPane key="3"> 22 + <Tabs.TabPane v-if="!isGBTTransportType" key="3">
23 <template #tab> 23 <template #tab>
24 <Badge :offset="[2, -5]" style="color: inherit"> 24 <Badge :offset="[2, -5]" style="color: inherit">
25 <span>告警</span> 25 <span>告警</span>
@@ -35,29 +35,32 @@ @@ -35,29 +35,32 @@
35 </template> 35 </template>
36 <AlarmLog :device-id="deviceDetail.id" class="bg-gray-100" /> 36 <AlarmLog :device-id="deviceDetail.id" class="bg-gray-100" />
37 </Tabs.TabPane> 37 </Tabs.TabPane>
38 - <Tabs.TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === 'GATEWAY'"> 38 + <Tabs.TabPane key="4" tab="子设备" v-if="deviceDetail?.deviceType === DeviceTypeEnum.GATEWAY">
39 <ChildDevice 39 <ChildDevice
40 :fromId="deviceDetail?.tbDeviceId" 40 :fromId="deviceDetail?.tbDeviceId"
41 @openTbDeviceDetail="handleOpenTbDeviceDetail" 41 @openTbDeviceDetail="handleOpenTbDeviceDetail"
42 /> 42 />
43 </Tabs.TabPane> 43 </Tabs.TabPane>
44 - <Tabs.TabPane key="7" tab="命令下发记录"> 44 + <Tabs.TabPane v-if="!isGBTTransportType" key="7" tab="命令下发记录">
45 <CommandRecord :deviceDetail="deviceDetail" :fromId="deviceDetail?.tbDeviceId" /> 45 <CommandRecord :deviceDetail="deviceDetail" :fromId="deviceDetail?.tbDeviceId" />
46 </Tabs.TabPane> 46 </Tabs.TabPane>
47 <!-- 网关设备并且场家是TBox --> 47 <!-- 网关设备并且场家是TBox -->
48 <Tabs.TabPane 48 <Tabs.TabPane
49 key="6" 49 key="6"
50 tab="TBox" 50 tab="TBox"
51 - v-if="deviceDetail?.deviceType === 'GATEWAY' && deviceDetail?.brand == 'TBox'" 51 + v-if="deviceDetail?.deviceType === DeviceTypeEnum.GATEWAY && deviceDetail?.brand == 'TBox'"
52 > 52 >
53 <TBoxDetail :deviceDetail="deviceDetail" /> 53 <TBoxDetail :deviceDetail="deviceDetail" />
54 </Tabs.TabPane> 54 </Tabs.TabPane>
55 - <Tabs.TabPane key="eventManage" tab="事件管理"> 55 + <Tabs.TabPane v-if="!isGBTTransportType" key="eventManage" tab="事件管理">
56 <EventManage :tbDeviceId="deviceDetail.tbDeviceId" /> 56 <EventManage :tbDeviceId="deviceDetail.tbDeviceId" />
57 </Tabs.TabPane> 57 </Tabs.TabPane>
58 - <Tabs.TabPane key="task" tab="任务"> 58 + <Tabs.TabPane v-if="!isGBTTransportType" key="task" tab="任务">
59 <Task :tbDeviceId="deviceDetail.tbDeviceId" /> 59 <Task :tbDeviceId="deviceDetail.tbDeviceId" />
60 </Tabs.TabPane> 60 </Tabs.TabPane>
  61 + <Tabs.TabPane v-if="isGBTTransportType" key="videoChanel" tab="视频通道">
  62 + <VideoChannel :deviceDetail="deviceDetail" />
  63 + </Tabs.TabPane>
61 </Tabs> 64 </Tabs>
62 </BasicDrawer> 65 </BasicDrawer>
63 </template> 66 </template>
@@ -72,10 +75,12 @@ @@ -72,10 +75,12 @@
72 import { getDeviceDetail } from '/@/api/device/deviceManager'; 75 import { getDeviceDetail } from '/@/api/device/deviceManager';
73 import ModelOfMatter from '../tabs/ModelOfMatter.vue'; 76 import ModelOfMatter from '../tabs/ModelOfMatter.vue';
74 import EventManage from '../tabs/EventManage/index.vue'; 77 import EventManage from '../tabs/EventManage/index.vue';
75 - import { DeviceRecord } from '/@/api/device/model/deviceModel'; 78 + import { DeviceRecord, DeviceTypeEnum } from '/@/api/device/model/deviceModel';
76 import Task from '../tabs/Task.vue'; 79 import Task from '../tabs/Task.vue';
77 import AlarmLog from '/@/views/alarm/log/index.vue'; 80 import AlarmLog from '/@/views/alarm/log/index.vue';
78 import { Icon } from '/@/components/Icon'; 81 import { Icon } from '/@/components/Icon';
  82 + import VideoChannel from '../tabs/VideoChannel/index.vue';
  83 + import { TransportTypeEnum } from '../../../profiles/components/TransportDescript/const';
79 84
80 const emit = defineEmits(['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail']); 85 const emit = defineEmits(['reload', 'register', 'openTbDeviceDetail', 'openGatewayDeviceDetail']);
81 86
@@ -84,12 +89,14 @@ @@ -84,12 +89,14 @@
84 const deviceDetailRef = ref(); 89 const deviceDetailRef = ref();
85 const deviceDetail = ref<DeviceRecord>({} as unknown as DeviceRecord); 90 const deviceDetail = ref<DeviceRecord>({} as unknown as DeviceRecord);
86 91
87 - const isTransportType = ref<Boolean>(false); //获取产品是不是GB/T 28181 92 + const isGBTTransportType = ref<boolean>(false); //获取产品是不是GB/T 28181
88 // 详情回显 93 // 详情回显
89 const [register] = useDrawerInner(async (data) => { 94 const [register] = useDrawerInner(async (data) => {
90 const { id, transportType, deviceType } = data || {}; 95 const { id, transportType, deviceType } = data || {};
91 - isTransportType.value =  
92 - transportType == 'GB/T28181' && deviceType == 'DIRECT_CONNECTION' ? true : false; 96 + isGBTTransportType.value = !!(
  97 + transportType == TransportTypeEnum.GBT28181 && deviceType == DeviceTypeEnum.DIRECT_CONNECTION
  98 + );
  99 +
93 // 设备详情 100 // 设备详情
94 const res = await getDeviceDetail(id); 101 const res = await getDeviceDetail(id);
95 deviceDetail.value = res; 102 deviceDetail.value = res;
@@ -2,43 +2,46 @@ import { h } from 'vue'; @@ -2,43 +2,46 @@ import { h } from 'vue';
2 import { BasicColumn, FormSchema } from '/@/components/Table'; 2 import { BasicColumn, FormSchema } from '/@/components/Table';
3 import { Tag } from 'ant-design-vue'; 3 import { Tag } from 'ant-design-vue';
4 import { withInstall } from '/@/utils/index'; 4 import { withInstall } from '/@/utils/index';
5 -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';  
6 5
7 import VideoPlay from './video.vue'; 6 import VideoPlay from './video.vue';
8 export const Video = withInstall(VideoPlay); 7 export const Video = withInstall(VideoPlay);
9 8
  9 +enum ChannelStatusEnum {
  10 + ONLINE = 'ONLINE',
  11 +}
  12 +
10 export const configColumns: BasicColumn[] = [ 13 export const configColumns: BasicColumn[] = [
11 { 14 {
12 title: '通道编号', 15 title: '通道编号',
13 - dataIndex: 'channellNumber',  
14 - },  
15 - {  
16 - title: '设备名称',  
17 - dataIndex: 'deviceName', 16 + dataIndex: 'cameraCode',
18 }, 17 },
19 { 18 {
20 title: '通道名称', 19 title: '通道名称',
21 - dataIndex: 'channelName', 20 + dataIndex: 'name',
22 }, 21 },
23 { 22 {
24 - title: '厂家',  
25 - dataIndex: 'manufacturer', 23 + title: '型号',
  24 + dataIndex: 'model',
26 }, 25 },
27 { 26 {
28 - title: '开启音频',  
29 - dataIndex: 'turnOnAudio',  
30 - slots: { customRender: 'turnOnAudio' }, 27 + title: '厂商',
  28 + dataIndex: 'manufacturer',
31 }, 29 },
  30 + // {
  31 + // title: '开启音频',
  32 + // dataIndex: 'hasAudio',
  33 + // slots: { customRender: 'hasAudio' },
  34 + // },
32 { 35 {
33 title: '状态', 36 title: '状态',
34 - dataIndex: 'state',  
35 - format: (text) => { 37 + dataIndex: 'status',
  38 + customRender: ({ text }: { text: ChannelStatusEnum }) => {
36 return h( 39 return h(
37 Tag, 40 Tag,
38 { 41 {
39 - color: Number(text) === 1 ? 'green' : 'blue', 42 + color: text === ChannelStatusEnum.ONLINE ? 'green' : 'blue',
40 }, 43 },
41 - () => (Number(text) === 1 ? '在线' : '离线') 44 + () => (text === ChannelStatusEnum.ONLINE ? '在线' : '离线')
42 ); 45 );
43 }, 46 },
44 }, 47 },
@@ -52,46 +55,21 @@ export const configColumns: BasicColumn[] = [ @@ -52,46 +55,21 @@ export const configColumns: BasicColumn[] = [
52 export const searchFormSchema: FormSchema[] | any = [ 55 export const searchFormSchema: FormSchema[] | any = [
53 { 56 {
54 field: 'name', 57 field: 'name',
55 - label: '设备名称', 58 + label: '通道名称',
56 component: 'Input', 59 component: 'Input',
57 colProps: { span: 6 }, 60 colProps: { span: 6 },
58 componentProps: { 61 componentProps: {
59 maxLength: 255, 62 maxLength: 255,
60 - placeholder: '请输入设备名称', 63 + placeholder: '请输入通道名称',
61 }, 64 },
62 }, 65 },
63 { 66 {
64 - field: 'deviceType',  
65 - label: '设备类型',  
66 - component: 'Select',  
67 - colProps: { span: 6 },  
68 - componentProps: {  
69 - options: [  
70 - { label: '网关设备', value: DeviceTypeEnum.GATEWAY },  
71 - { label: '直连设备', value: DeviceTypeEnum.DIRECT_CONNECTION },  
72 - { label: '网关子设备', value: DeviceTypeEnum.SENSOR },  
73 - ],  
74 - placeholder: '请选择设备类型',  
75 - },  
76 - },  
77 - // {  
78 - // field: 'channelName',  
79 - // label: '通道名称',  
80 - // component: 'Input',  
81 - // colProps: { span: 6 },  
82 - // componentProps: {  
83 - // maxLength: 255,  
84 - // placeholder: '请输入通道名称',  
85 - // },  
86 - // },  
87 - {  
88 - field: 'manufacturer',  
89 - label: '厂家',  
90 - component: 'Select', 67 + field: 'cameraCode',
  68 + label: '国标编号',
  69 + component: 'Input',
91 colProps: { span: 6 }, 70 colProps: { span: 6 },
92 componentProps: { 71 componentProps: {
93 - options: [{}],  
94 - placeholder: '请选择厂家', 72 + placeholder: '请输入国标编号',
95 }, 73 },
96 }, 74 },
97 ]; 75 ];
@@ -3,13 +3,13 @@ @@ -3,13 +3,13 @@
3 class="bg-neutral-100 dark:text-gray-300 dark:bg-dark-700 p-4" 3 class="bg-neutral-100 dark:text-gray-300 dark:bg-dark-700 p-4"
4 @register="registerTable" 4 @register="registerTable"
5 > 5 >
6 - <template #turnOnAudio="{ record }"> 6 + <template #hasAudio="{ record }">
7 <Switch 7 <Switch
8 :checked="record.status === 1" 8 :checked="record.status === 1"
9 :loading="record.pendingStatus" 9 :loading="record.pendingStatus"
10 checkedChildren="开启" 10 checkedChildren="开启"
11 unCheckedChildren="关闭" 11 unCheckedChildren="关闭"
12 - @change="(checked:boolean)=>handleTurnVideo(checked,record)" 12 + @change="(checked: boolean) => handleTurnVideo(checked, record)"
13 /> 13 />
14 </template> 14 </template>
15 <template #action="{ record }"> 15 <template #action="{ record }">
@@ -22,7 +22,8 @@ @@ -22,7 +22,8 @@
22 onClick: handlePlay.bind(null, record), 22 onClick: handlePlay.bind(null, record),
23 }, 23 },
24 ]" 24 ]"
25 - /></template> 25 + />
  26 + </template>
26 </BasicTable> 27 </BasicTable>
27 <VideoModal @register="registerModal" /> 28 <VideoModal @register="registerModal" />
28 </template> 29 </template>
@@ -34,28 +35,22 @@ @@ -34,28 +35,22 @@
34 import { DeviceRecord } from '/@/api/device/model/deviceModel'; 35 import { DeviceRecord } from '/@/api/device/model/deviceModel';
35 import VideoModal from './videoModal.vue'; 36 import VideoModal from './videoModal.vue';
36 import { useModal } from '/@/components/Modal'; 37 import { useModal } from '/@/components/Modal';
37 - import { onMounted } from 'vue';  
38 import { useMessage } from '/@/hooks/web/useMessage'; 38 import { useMessage } from '/@/hooks/web/useMessage';
  39 + import { getVideoChannelList } from '/@/api/device/videoChannel';
39 40
40 - defineProps({  
41 - fromId: {  
42 - type: String,  
43 - default: '',  
44 - },  
45 - deviceDetail: {  
46 - type: Object as PropType<DeviceRecord>,  
47 - required: true,  
48 - },  
49 - }); 41 + const props = defineProps<{ deviceDetail: DeviceRecord }>();
50 42
51 const [registerModal, { openModal }] = useModal(); 43 const [registerModal, { openModal }] = useModal();
52 44
53 - const [registerTable, { setTableData, setProps, setSelectedRowKeys, reload }] = useTable({  
54 - // api: deviceCommandRecordGetQuery, 45 + const [registerTable, { setProps, setSelectedRowKeys, reload }] = useTable({
  46 + api: getVideoChannelList,
55 columns: configColumns, 47 columns: configColumns,
56 showTableSetting: true, 48 showTableSetting: true,
57 bordered: true, 49 bordered: true,
58 showIndexColumn: false, 50 showIndexColumn: false,
  51 + beforeFetch: (params: Recordable) => {
  52 + return { ...params, tbDeviceId: props.deviceDetail.tbDeviceId };
  53 + },
59 formConfig: { 54 formConfig: {
60 labelWidth: 120, 55 labelWidth: 120,
61 schemas: searchFormSchema, 56 schemas: searchFormSchema,
@@ -81,196 +76,10 @@ @@ -81,196 +76,10 @@
81 } 76 }
82 }; 77 };
83 78
84 - const tableList = [  
85 - {  
86 - id: '8b66f4fa-88e0-42b2-be33-60652cd2bda1',  
87 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
88 - createTime: '2023-03-10 17:16:54',  
89 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
90 - updateTime: '2023-04-11 10:25:49',  
91 - name: 'dasd',  
92 - enabled: false,  
93 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
94 - videoUrl: 'ws://192.168.10.134:28080/rtp/62020000492000000002_34020000001320000001.live.flv',  
95 - sn: 's',  
96 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
97 - organizationName: '车车组织',  
98 - status: false,  
99 - accessMode: 0,  
100 - playProtocol: 0,  
101 - channellNumber: 1,  
102 - },  
103 - {  
104 - id: '9ff408ab-980f-470c-a55c-e4e284ddd34d',  
105 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
106 - createTime: '2023-02-22 14:50:13',  
107 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
108 - updateTime: '2023-04-11 10:36:25',  
109 - name: '2',  
110 - enabled: false,  
111 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
112 - videoUrl:  
113 - 'https://vcsplay.scjtonline.cn:8099/live/HD_d80a740b-2672-4ad1-90e4-52eb71d8c2ef.m3u8?auth_key=1681180571-0-0-dfdb334e5f83838ade5f01f61f910107',  
114 - sn: '212',  
115 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
116 - organizationName: '车车组织',  
117 - status: false,  
118 - accessMode: 0,  
119 - playProtocol: 0,  
120 - channellNumber: 1,  
121 - },  
122 - {  
123 - id: 'a6edd8fb-a91a-4a1b-8124-65959206dac4',  
124 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
125 - createTime: '2023-02-21 16:39:51',  
126 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
127 - updateTime: '2023-04-11 10:33:27',  
128 - name: 'dasda',  
129 - enabled: false,  
130 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
131 - videoUrl:  
132 - 'https://vcsplay.scjtonline.cn:8200/live/HD_1b361aa9-5230-48ba-b8be-d1e2c4e9b178.m3u8?auth_key=1681180365-0-0-e77f40e88550091053139f5562d8afa2',  
133 - brand: 'dasdad',  
134 - sn: 'adsad',  
135 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
136 - organizationName: '车车组织',  
137 - status: false,  
138 - accessMode: 0,  
139 - playProtocol: 0,  
140 - channellNumber: 1,  
141 - },  
142 - {  
143 - id: '51b4c0bc-8050-4b37-bdb3-9fe08b14cf86',  
144 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
145 - createTime: '2023-02-21 16:37:44',  
146 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
147 - updateTime: '2023-04-11 10:56:59',  
148 - name: '视频',  
149 - enabled: false,  
150 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
151 - videoUrl:  
152 - 'https://vcsplay.scjtonline.cn:8093/live/HD_c54034ca-4a6d-11eb-8edc-3cd2e55e088c.m3u8?auth_key=1681181808-0-0-d756d9b0426b71482e45b9377958e4dc',  
153 - brand: '视频厂家',  
154 - sn: 'DART2143SAD12RE',  
155 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
156 - organizationName: '车车组织',  
157 - status: false,  
158 - accessMode: 0,  
159 - playProtocol: 0,  
160 - channellNumber: 1,  
161 - },  
162 - {  
163 - id: '8c0e6ed0-3176-4e76-bd8e-92f722f61e79',  
164 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
165 - createTime: '2022-12-01 15:55:54',  
166 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
167 - updateTime: '2023-04-11 10:59:22',  
168 - name: '视频',  
169 - enabled: false,  
170 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
171 - avatar: 'https://demo.thingskit.com:9000/yunteng/itbbqOBXIUzWvjq.jpeg',  
172 - videoUrl: 'http://113.204.115.250:83/openUrl/iFzoWME/live.m3u8',  
173 - brand: '厂家',  
174 - sn: 'JDKSA11321',  
175 - organizationId: '9196fd9a-624a-4891-a8b3-ce588baedf9f',  
176 - organizationName: '丰田',  
177 - status: false,  
178 - channellNumber: 1,  
179 - accessMode: 0,  
180 - playProtocol: 0,  
181 - },  
182 - {  
183 - id: 'c30422a7-9498-49a5-96d5-e77f3a2823e3',  
184 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
185 - createTime: '2022-12-01 14:42:52',  
186 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
187 - updateTime: '2023-03-02 17:12:53',  
188 - name: '湖南卫视',  
189 - enabled: false,  
190 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
191 - videoUrl:  
192 - 'http://219.151.31.38/liveplay-kk.rtxapp.com/live/program/live/hnwshd/4000000/mnf.m3u8',  
193 - brand: 'BUZHIDAO',  
194 - sn: 'QKDK123456',  
195 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
196 - organizationName: '车车组织',  
197 - status: false,  
198 - channellNumber: 1,  
199 - accessMode: 0,  
200 - playProtocol: 0,  
201 - },  
202 - {  
203 - id: 'e3253015-b8df-4e8f-ad0c-f634d3fd927e',  
204 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
205 - createTime: '2022-11-29 11:19:00',  
206 - name: '玩具视频播放',  
207 - enabled: false,  
208 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
209 - avatar: 'http://nc.tianzow.com:29000/yunteng/tgygMQmYaaPDPdG.jpg',  
210 - videoUrl: 'https://ask.dcloud.net.cn/topic.m3u8',  
211 - brand: '成都厂家',  
212 - sn: 'TCL1528GOP',  
213 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
214 - organizationName: '车车组织',  
215 - status: false,  
216 - accessMode: 0,  
217 - channellNumber: 1,  
218 - playProtocol: 0,  
219 - },  
220 - {  
221 - id: '9edc9f73-3a9a-4e4b-9b4c-b955329b99b3',  
222 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
223 - createTime: '2022-11-29 11:13:44',  
224 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
225 - updateTime: '2023-01-11 10:03:18',  
226 - name: '视频',  
227 - enabled: false,  
228 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
229 - sn: '7aeb4061f3cf4ba384ed8e1a3027d2a8',  
230 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
231 - organizationName: '车车组织',  
232 - status: false,  
233 - accessMode: 1,  
234 - videoPlatformId: '8cc7c20e-693b-48ea-9124-994e0908c83e',  
235 - streamType: 0,  
236 - playProtocol: 0,  
237 - channellNumber: 1,  
238 - videoPlatformDTO: {  
239 - enabled: false,  
240 - host: '113.204.115.250:7120',  
241 - appKey: '28238690',  
242 - appSecret: 'F3e3Ffvo9Wyg9jkl8BUS',  
243 - ssl: 1,  
244 - },  
245 - },  
246 - {  
247 - id: '9d6675ad-07ed-45ae-bb4a-457000e164f8',  
248 - creator: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
249 - createTime: '2022-11-21 11:49:59',  
250 - updater: '694a8f7f-f7ed-4a9c-af89-a3d18fc3663e',  
251 - updateTime: '2023-04-11 10:53:27',  
252 - name: '新视频',  
253 - enabled: false,  
254 - tenantId: '0277ca80-693d-11ed-9e12-e5edad4f7148',  
255 - videoUrl: 'http://www.w3school.com.cn/example/html5/mov_bbb.mp4',  
256 - brand: '1111',  
257 - sn: 'XYZ',  
258 - organizationId: '27ef2a83-6f1f-4e33-824b-80afac684699',  
259 - organizationName: '车车组织',  
260 - status: false,  
261 - accessMode: 0,  
262 - channellNumber: 1,  
263 - playProtocol: 0,  
264 - },  
265 - ];  
266 -  
267 - onMounted(() => {  
268 - setTableData(tableList);  
269 - });  
270 -  
271 const handlePlay = (record: Recordable) => { 79 const handlePlay = (record: Recordable) => {
272 openModal(true, { 80 openModal(true, {
273 record, 81 record,
  82 + ifShowGBT: true,
274 }); 83 });
275 }; 84 };
276 </script> 85 </script>
@@ -20,13 +20,20 @@ @@ -20,13 +20,20 @@
20 } from '@ant-design/icons-vue'; 20 } from '@ant-design/icons-vue';
21 import { Button } from 'ant-design-vue'; 21 import { Button } from 'ant-design-vue';
22 import { nextTick } from 'vue'; 22 import { nextTick } from 'vue';
23 - import { controlling } from '/@/api/camera/cameraManager'; 23 + import { controlling, stopOnDemandVideoApiGet } from '/@/api/camera/cameraManager';
  24 + import { setVideoControl } from '/@/api/device/videoChannel';
24 25
25 const { prefixCls } = useDesign('basic-video-play'); 26 const { prefixCls } = useDesign('basic-video-play');
  27 + interface IGbtOption {
  28 + tbDeviceId: string;
  29 + channelId: string;
  30 + }
26 31
27 const props = defineProps<{ 32 const props = defineProps<{
28 options?: VideoJsPlayerOptions; 33 options?: VideoJsPlayerOptions;
29 withToken?: boolean; 34 withToken?: boolean;
  35 + isGBT?: boolean;
  36 + GBTOption: IGbtOption;
30 }>(); 37 }>();
31 38
32 const emit = defineEmits<{ 39 const emit = defineEmits<{
@@ -38,6 +45,11 @@ @@ -38,6 +45,11 @@
38 45
39 const videoPlayInstance = ref<Nullable<VideoJsPlayer>>(); 46 const videoPlayInstance = ref<Nullable<VideoJsPlayer>>();
40 47
  48 + const getGBT = computed(() => {
  49 + const { isGBT } = props;
  50 + return isGBT;
  51 + });
  52 +
41 const getOptions = computed(() => { 53 const getOptions = computed(() => {
42 const { options, withToken } = props; 54 const { options, withToken } = props;
43 55
@@ -85,8 +97,9 @@ @@ -85,8 +97,9 @@
85 97
86 //播放/暂停 98 //播放/暂停
87 const handleClick = () => { 99 const handleClick = () => {
88 - unref(isPlay) && unref(videoPlayInstance)?.pause();  
89 - !unref(isPlay) && unref(videoPlayInstance)?.play(); 100 + // unref(isPlay) ((true,停止),(false,开启))
  101 + if (unref(isPlay)) unref(videoPlayInstance)?.pause();
  102 + else unref(videoPlayInstance)?.play();
90 }; 103 };
91 104
92 const getId = () => { 105 const getId = () => {
@@ -100,6 +113,17 @@ @@ -100,6 +113,17 @@
100 controlling({ cameraIndexCode: organizationId, action, command: direction }); 113 controlling({ cameraIndexCode: organizationId, action, command: direction });
101 }; 114 };
102 115
  116 + // 国标控制
  117 + const handleGBTControl = (command: string, action?: number | string) => {
  118 + const { tbDeviceId, channelId } = props.GBTOption;
  119 + setVideoControl(tbDeviceId, channelId, {
  120 + command,
  121 + horizonSpeed: action,
  122 + verticalSpeed: action,
  123 + zoomSpeed: action,
  124 + });
  125 + };
  126 +
103 const isPlay = ref<Boolean | null | undefined>(false); 127 const isPlay = ref<Boolean | null | undefined>(false);
104 128
105 onMounted(async () => { 129 onMounted(async () => {
@@ -126,10 +150,19 @@ @@ -126,10 +150,19 @@
126 150
127 //长按开始 151 //长按开始
128 const moveStart = (action) => { 152 const moveStart = (action) => {
  153 + if (unref(getGBT)) {
  154 + handleGBTControl(action, '30');
  155 + return;
  156 + }
129 handleControl(0, action); 157 handleControl(0, action);
130 }; 158 };
  159 +
131 // 长按结束 160 // 长按结束
132 const moveStop = (action) => { 161 const moveStop = (action) => {
  162 + if (unref(getGBT)) {
  163 + handleGBTControl('STOP', '30');
  164 + return;
  165 + }
133 handleControl(1, action); 166 handleControl(1, action);
134 }; 167 };
135 168
@@ -137,8 +170,15 @@ @@ -137,8 +170,15 @@
137 unref(videoPlayInstance)?.dispose(); 170 unref(videoPlayInstance)?.dispose();
138 videoPlayInstance.value = null; 171 videoPlayInstance.value = null;
139 emit('onUnmounted'); 172 emit('onUnmounted');
  173 + stopOnDemandVideo();
140 }); 174 });
141 175
  176 + // 停止点播视频
  177 + const stopOnDemandVideo = async () => {
  178 + const { tbDeviceId, channelId } = props.GBTOption;
  179 + await stopOnDemandVideoApiGet(tbDeviceId, channelId);
  180 + };
  181 +
142 defineExpose({ 182 defineExpose({
143 reloadPlayer: init, 183 reloadPlayer: init,
144 getInstance: () => unref(videoPlayInstance), 184 getInstance: () => unref(videoPlayInstance),
@@ -178,8 +218,8 @@ @@ -178,8 +218,8 @@
178 <div> 218 <div>
179 <Button 219 <Button
180 class="left-top in-block" 220 class="left-top in-block"
181 - @mousedown="moveStart('up')"  
182 - @mouseup="moveStop('up')" 221 + @mousedown="moveStart('UP')"
  222 + @mouseup="moveStop('UP')"
183 > 223 >
184 <CaretUpOutlined class="icon-rotate child-icon" /> 224 <CaretUpOutlined class="icon-rotate child-icon" />
185 </Button> 225 </Button>
@@ -10,90 +10,73 @@ @@ -10,90 +10,73 @@
10 :showOkBtn="false" 10 :showOkBtn="false"
11 @cancel="handleCancel" 11 @cancel="handleCancel"
12 > 12 >
13 - <div class="flex items-center justify-center w-full h-full min-h-96 video-container">  
14 - <Video 13 + <div
  14 + class="flex items-center justify-center w-full h-full min-h-96 video-container bg-dark-50"
  15 + >
  16 + <VideoPlayer
  17 + ref="videoInstance"
15 v-if="showVideo" 18 v-if="showVideo"
16 - :options="(options as any)" 19 + :options="(options as VideoJsPlayerOptions)"
17 :withToken="withToken" 20 :withToken="withToken"
18 - @on-unmounted="handleCloseFlvPlayUrl" 21 + :isGBT="isGBT"
  22 + :GBTOption="GBTOption"
19 /> 23 />
20 </div> 24 </div>
21 </BasicModal> 25 </BasicModal>
22 </div> 26 </div>
23 </template> 27 </template>
24 <script setup lang="ts"> 28 <script setup lang="ts">
25 - import { ref, reactive, unref } from 'vue'; 29 + import { ref, reactive } from 'vue';
26 import { BasicModal, useModalInner } from '/@/components/Modal'; 30 import { BasicModal, useModalInner } from '/@/components/Modal';
27 - import type { StreamingManageRecord, CameraModel } from '/@/api/camera/model/cameraModel';  
28 - import { getVideoTypeByUrl } from '/@/components/Video';  
29 - import { closeFlvPlay, getFlvPlayUrl, getStreamingPlayUrl } from '/@/api/camera/cameraManager';  
30 - import { isRtspProtocol } from '/@/components/Video/src/utils';  
31 import { VideoJsPlayerOptions } from 'video.js'; 31 import { VideoJsPlayerOptions } from 'video.js';
32 - import { useFingerprint } from '/@/utils/useFingerprint';  
33 - import { GetResult } from '@fingerprintjs/fingerprintjs';  
34 - import { AccessMode } from '/@/views/camera/manage/config.data';  
35 - import { Video } from './config'; 32 + import VideoPlayer from './video.vue';
  33 + import { getVideoControlStart } from '/@/api/device/videoChannel';
  34 + import { VideoChannelItemType } from '/@/api/device/model/videoChannelModel';
  35 + import { getVideoTypeByUrl } from '/@/components/Video';
36 36
37 const heightNum = ref(800); 37 const heightNum = ref(800);
38 const showVideo = ref(false); 38 const showVideo = ref(false);
39 39
40 - const playUrl = ref(''); 40 + const videoInstance = ref<InstanceType<typeof VideoPlayer>>();
41 41
42 const videoId = ref<string>(); 42 const videoId = ref<string>();
43 43
44 const withToken = ref(false); 44 const withToken = ref(false);
45 45
46 - const fingerprintResult = ref<Nullable<GetResult>>(null);  
47 -  
48 const options = reactive<VideoJsPlayerOptions>({ 46 const options = reactive<VideoJsPlayerOptions>({
49 width: '100%' as unknown as number, 47 width: '100%' as unknown as number,
50 height: 384 as unknown as number, 48 height: 384 as unknown as number,
51 autoplay: true, 49 autoplay: true,
52 }); 50 });
  51 + const GBTOption = ref({
  52 + tbDeviceId: '',
  53 + channelId: '',
  54 + });
  55 + const isGBT = ref<boolean>(false);
53 56
54 - const setSources = (url: string, fingerprintResult: GetResult, id) => {  
55 - const flag = isRtspProtocol(url);  
56 - options.sources = [  
57 - {  
58 - src: flag ? getFlvPlayUrl(url, fingerprintResult.visitorId) : url,  
59 - type: getVideoTypeByUrl(url),  
60 - id,  
61 - },  
62 - ];  
63 - };  
64 -  
65 - const { getResult } = useFingerprint();  
66 - const [register] = useModalInner(  
67 - async (data: { record: CameraModel | StreamingManageRecord }) => {  
68 - const { record } = data; 57 + const [register, { setModalProps }] = useModalInner(
  58 + async (data: { record: VideoChannelItemType }) => {
  59 + const { record, ifShowGBT = false } = data;
69 videoId.value = record.id || ''; 60 videoId.value = record.id || '';
70 - const result = await getResult();  
71 - fingerprintResult.value = result;  
72 - if (record.accessMode === AccessMode.ManuallyEnter) {  
73 - if ((record as CameraModel).videoUrl) {  
74 - if (isRtspProtocol((record as CameraModel).videoUrl)) {  
75 - playUrl.value = (record as CameraModel).videoUrl;  
76 - closeFlvPlay(unref(playUrl), result.visitorId);  
77 - withToken.value = true;  
78 - }  
79 - setSources((record as CameraModel).videoUrl, result, record.id);  
80 - }  
81 - } else {  
82 - try {  
83 - const { data: { url } = { url: '' } } = await getStreamingPlayUrl(record.id!);  
84 - setSources(url, result, record.id);  
85 - } catch (error) {} 61 + isGBT.value = ifShowGBT;
  62 + GBTOption.value.tbDeviceId = record.deviceId;
  63 + GBTOption.value.channelId = record.channelId;
  64 + try {
  65 + setModalProps({ loading: true, loadingTip: '视频加载中...' });
  66 +
  67 + const result = await getVideoControlStart({
  68 + deviceId: record.deviceId,
  69 + channelId: record.channelId,
  70 + });
  71 + options.sources = [{ src: result.data.flv, type: getVideoTypeByUrl(result.data.flv) }];
  72 + showVideo.value = true;
  73 + } catch (error) {
  74 + } finally {
  75 + setModalProps({ loading: false });
86 } 76 }
87 - showVideo.value = true;  
88 } 77 }
89 ); 78 );
90 79
91 - const handleCloseFlvPlayUrl = () => {  
92 - if (isRtspProtocol(unref(playUrl))) {  
93 - closeFlvPlay(unref(playUrl)!, unref(fingerprintResult)!.visitorId!);  
94 - }  
95 - };  
96 -  
97 const handleCancel = () => { 80 const handleCancel = () => {
98 showVideo.value = false; 81 showVideo.value = false;
99 withToken.value = false; 82 withToken.value = false;
@@ -148,12 +148,12 @@ @@ -148,12 +148,12 @@
148 } 148 }
149 }; 149 };
150 const handleStepNext = (e, data) => { 150 const handleStepNext = (e, data) => {
151 - // const { deviceType } = unref(DevConStRef)?.getFieldsValue() || {}; 151 + const { deviceType } = unref(DevConStRef)?.getFieldsValue() || {};
152 if (e) { 152 if (e) {
153 current.value++; 153 current.value++;
154 unref(isUpdate) 154 unref(isUpdate)
155 - ? unref(TransConStRef)?.editOrAddTransportTypeStatus(true)  
156 - : unref(TransConStRef)?.editOrAddTransportTypeStatus(false); 155 + ? unref(TransConStRef)?.editOrAddTransportTypeStatus(true, deviceType)
  156 + : unref(TransConStRef)?.editOrAddTransportTypeStatus(false, deviceType);
157 } else { 157 } else {
158 setTransConfEditFormData(data); 158 setTransConfEditFormData(data);
159 } 159 }
  1 +export enum TransportTypeEnum {
  2 + DEFAULT = 'DEFAULT',
  3 + MQTT = 'MQTT',
  4 + COAP = 'COAP',
  5 + LWM2M = 'LWM2M',
  6 + SNMP = 'SNMP',
  7 + TCP = 'TCP',
  8 + GBT28181 = 'GBT28181',
  9 +}
  10 +
1 export enum TransportPayloadTypeEnum { 11 export enum TransportPayloadTypeEnum {
2 PROTOBUF = 'PROTOBUF', 12 PROTOBUF = 'PROTOBUF',
3 JSON = 'JSON', 13 JSON = 'JSON',
@@ -2,7 +2,7 @@ @@ -2,7 +2,7 @@
2 <div 2 <div
3 class="step2-style" 3 class="step2-style"
4 :style="[ 4 :style="[
5 - isMqttType == 'DEFAULT' || isMqttType == 'GB/T28181' 5 + isMqttType == 'DEFAULT' || isMqttType == 'GBT28181'
6 ? { minHeight: 0 + 'px' } 6 ? { minHeight: 0 + 'px' }
7 : { minHeight: 800 + 'px' }, 7 : { minHeight: 800 + 'px' },
8 ]" 8 ]"
@@ -58,6 +58,7 @@ @@ -58,6 +58,7 @@
58 import Lwm2mCpns from './cpns/lwm2m/index.vue'; 58 import Lwm2mCpns from './cpns/lwm2m/index.vue';
59 import SnmpCpns from './cpns/snmp/index.vue'; 59 import SnmpCpns from './cpns/snmp/index.vue';
60 import TcpCpns from './cpns/tcp/index.vue'; 60 import TcpCpns from './cpns/tcp/index.vue';
  61 + import { DeviceTypeEnum } from '/@/api/device/model/deviceModel';
61 62
62 const emits = defineEmits(['prev']); 63 const emits = defineEmits(['prev']);
63 const props = defineProps({ 64 const props = defineProps({
@@ -150,10 +151,14 @@ @@ -150,10 +151,14 @@
150 ...getTcpVal, 151 ...getTcpVal,
151 ...val, 152 ...val,
152 }; 153 };
  154 +
  155 + if (val?.transportType === 'GBT28181') {
  156 + step2Data.transportConfiguration = { type: val?.transportType };
  157 + }
153 return step2Data; 158 return step2Data;
154 }; 159 };
155 160
156 - const editOrAddTransportTypeStatus = (status: boolean) => { 161 + const editOrAddTransportTypeStatus = (status: boolean, deviceType: DeviceTypeEnum) => {
157 const options = [ 162 const options = [
158 { label: '默认', value: 'DEFAULT' }, 163 { label: '默认', value: 'DEFAULT' },
159 { label: 'MQTT', value: 'MQTT' }, 164 { label: 'MQTT', value: 'MQTT' },
@@ -162,12 +167,12 @@ @@ -162,12 +167,12 @@
162 // { label: 'SNMP', value: 'SNMP' }, 167 // { label: 'SNMP', value: 'SNMP' },
163 { label: 'TCP/UDP', value: 'TCP' }, 168 { label: 'TCP/UDP', value: 'TCP' },
164 ]; 169 ];
165 - // if (deviceType == 'DIRECT_CONNECTION') {  
166 - // options.push({ label: 'GB/T 28181', value: 'GB/T28181' });//暂时隐藏 GBT 28181写完放出来  
167 - // }  
168 - // if (deviceType != 'DIRECT_CONNECTION' && isMqttType.value == 'GB/T28181') {  
169 - // setFieldsValue({ transportType: null });  
170 - // } 170 + if (deviceType == DeviceTypeEnum.DIRECT_CONNECTION) {
  171 + options.push({ label: 'GBT28181', value: 'GBT28181' }); //暂时隐藏 GBT 28181写完放出来
  172 + }
  173 + if (deviceType != 'DIRECT_CONNECTION' && isMqttType.value == 'GBT28181') {
  174 + setFieldsValue({ transportType: null });
  175 + }
171 updateSchema({ 176 updateSchema({
172 field: 'transportType', 177 field: 'transportType',
173 componentProps: { 178 componentProps: {