Commit 7f3e4715fe11fada6207f8d40ae4a7aa193d9a64

Authored by ww
1 parent fd5c0bb0

feat: implement camera manage streaming config

@@ -12,9 +12,10 @@ enum CameraManagerApi { @@ -12,9 +12,10 @@ enum CameraManagerApi {
12 CAMERA_GET_URL = '/video', 12 CAMERA_GET_URL = '/video',
13 CAMERA_DELETE_URL = '/video', 13 CAMERA_DELETE_URL = '/video',
14 CAMERA_GET_DETAIL_URL = '/video', 14 CAMERA_GET_DETAIL_URL = '/video',
15 - STERAMING_GET_URL = '/video/platform',  
16 - STERAMING_POST_URL = '/video/platform',  
17 - STERAMING_DELETE_URL = '/video/platform', 15 + STREAMING_GET_URL = '/video/platform',
  16 + STREAMING_POST_URL = '/video/platform',
  17 + STREAMING_DELETE_URL = '/video/platform',
  18 + STREAMING_PLAY_GET_URL = '/video/url',
18 } 19 }
19 20
20 export const cameraPage = (params: CameraQueryParam) => { 21 export const cameraPage = (params: CameraQueryParam) => {
@@ -59,7 +60,7 @@ export const getCameraManageDetail = (id: string) => { @@ -59,7 +60,7 @@ export const getCameraManageDetail = (id: string) => {
59 */ 60 */
60 export const getStreamingMediaList = (params: StreamingQueryParam) => { 61 export const getStreamingMediaList = (params: StreamingQueryParam) => {
61 return defHttp.get({ 62 return defHttp.get({
62 - url: CameraManagerApi.STERAMING_GET_URL, 63 + url: CameraManagerApi.STREAMING_GET_URL,
63 params, 64 params,
64 }); 65 });
65 }; 66 };
@@ -71,7 +72,7 @@ export const getStreamingMediaList = (params: StreamingQueryParam) => { @@ -71,7 +72,7 @@ export const getStreamingMediaList = (params: StreamingQueryParam) => {
71 */ 72 */
72 export const createOrUpdateStreamingMediaRecord = (params: StreamingSubmitParam) => { 73 export const createOrUpdateStreamingMediaRecord = (params: StreamingSubmitParam) => {
73 return defHttp.post({ 74 return defHttp.post({
74 - url: CameraManagerApi.STERAMING_POST_URL, 75 + url: CameraManagerApi.STREAMING_POST_URL,
75 params, 76 params,
76 }); 77 });
77 }; 78 };
@@ -83,7 +84,18 @@ export const createOrUpdateStreamingMediaRecord = (params: StreamingSubmitParam) @@ -83,7 +84,18 @@ export const createOrUpdateStreamingMediaRecord = (params: StreamingSubmitParam)
83 */ 84 */
84 export const deleteStreamingMediaRecord = (params: StreamingMediaDeleteParam) => { 85 export const deleteStreamingMediaRecord = (params: StreamingMediaDeleteParam) => {
85 return defHttp.delete({ 86 return defHttp.delete({
86 - url: CameraManagerApi.STERAMING_POST_URL, 87 + url: CameraManagerApi.STREAMING_POST_URL,
87 params, 88 params,
88 }); 89 });
89 }; 90 };
  91 +
  92 +/**
  93 + * @description 获取流媒体播放地址
  94 + * @param entityId
  95 + * @returns
  96 + */
  97 +export const getStreamingPlayUrl = (entityId: string) => {
  98 + return defHttp.get({
  99 + url: `${CameraManagerApi.STREAMING_PLAY_GET_URL}/${entityId}`,
  100 + });
  101 +};
@@ -10,6 +10,7 @@ export type CameraParam = { @@ -10,6 +10,7 @@ export type CameraParam = {
10 }; 10 };
11 11
12 export interface CameraModel { 12 export interface CameraModel {
  13 + accessMode: number;
13 avatar?: string; 14 avatar?: string;
14 brand: string; 15 brand: string;
15 createTime?: '2022-04-19T11:33:13.113Z'; 16 createTime?: '2022-04-19T11:33:13.113Z';
@@ -36,6 +37,21 @@ export interface CameraModel { @@ -36,6 +37,21 @@ export interface CameraModel {
36 videoUrl: string; 37 videoUrl: string;
37 } 38 }
38 39
  40 +export interface StreamingManageRecord {
  41 + id: string;
  42 + creator: string;
  43 + createTime: string;
  44 + name: string;
  45 + enabled: boolean;
  46 + tenantId: string;
  47 + sn: string;
  48 + organizationId: string;
  49 + organizationName: string;
  50 + status: boolean;
  51 + accessMode: number;
  52 + playProtocol: number;
  53 +}
  54 +
39 export interface StreamingMediaModel { 55 export interface StreamingMediaModel {
40 id: string; 56 id: string;
41 creator: string; 57 creator: string;
@@ -29,9 +29,9 @@ @@ -29,9 +29,9 @@
29 </BasicDrawer> 29 </BasicDrawer>
30 </template> 30 </template>
31 <script lang="ts"> 31 <script lang="ts">
32 - import { defineComponent, ref, computed, unref } from 'vue'; 32 + import { defineComponent, ref, computed, unref, nextTick } from 'vue';
33 import { BasicForm, useForm } from '/@/components/Form'; 33 import { BasicForm, useForm } from '/@/components/Form';
34 - import { formSchema } from './config.data'; 34 + import { formSchema, manuallyEnter, streamingMediaAcquire } from './config.data';
35 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 35 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
36 import { createOrEditCameraManage } from '/@/api/camera/cameraManager'; 36 import { createOrEditCameraManage } from '/@/api/camera/cameraManager';
37 import { message, Upload } from 'ant-design-vue'; 37 import { message, Upload } from 'ant-design-vue';
@@ -49,7 +49,10 @@ @@ -49,7 +49,10 @@
49 49
50 const isUpdate = ref(true); 50 const isUpdate = ref(true);
51 const editId = ref(''); 51 const editId = ref('');
52 - const [registerForm, { validate, setFieldsValue, resetFields }] = useForm({ 52 + const [
  53 + registerForm,
  54 + { validate, setFieldsValue, resetFields, removeSchemaByFiled, appendSchemaByField },
  55 + ] = useForm({
53 labelWidth: 120, 56 labelWidth: 120,
54 schemas: formSchema, 57 schemas: formSchema,
55 showActionButtonGroup: false, 58 showActionButtonGroup: false,
@@ -60,6 +63,20 @@ @@ -60,6 +63,20 @@
60 setDrawerProps({ confirmLoading: false }); 63 setDrawerProps({ confirmLoading: false });
61 isUpdate.value = !!data?.isUpdate; 64 isUpdate.value = !!data?.isUpdate;
62 if (unref(isUpdate)) { 65 if (unref(isUpdate)) {
  66 + await nextTick();
  67 + removeSchemaByFiled([
  68 + 'videoPlatformId',
  69 + 'streamType',
  70 + 'playProtocol',
  71 + 'sn',
  72 + 'brand',
  73 + 'videoUrl',
  74 + ]);
  75 + if (data.record.accessMode === 0) {
  76 + manuallyEnter.forEach((schema) => appendSchemaByField(schema, undefined));
  77 + } else {
  78 + streamingMediaAcquire.forEach((schema) => appendSchemaByField(schema, undefined));
  79 + }
63 editId.value = data.record.id; 80 editId.value = data.record.id;
64 tenantLogo.value = data.record?.avatar; 81 tenantLogo.value = data.record?.avatar;
65 await setFieldsValue(data.record); 82 await setFieldsValue(data.record);
@@ -104,12 +121,6 @@ @@ -104,12 +121,6 @@
104 try { 121 try {
105 const { createMessage } = useMessage(); 122 const { createMessage } = useMessage();
106 const values = await validate(); 123 const values = await validate();
107 - if (values.name == undefined || values.name == '')  
108 - return createMessage.error('请填写视频名字');  
109 - if (values.sn == undefined || values.sn == '')  
110 - return createMessage.error('请填写摄像头编号');  
111 - if (values.videoUrl == undefined || values.videoUrl == '')  
112 - return createMessage.error('请填写视频流');  
113 if (!values) return; 124 if (!values) return;
114 if (tenantLogo.value !== '') { 125 if (tenantLogo.value !== '') {
115 values.avatar = tenantLogo.value; 126 values.avatar = tenantLogo.value;
@@ -23,10 +23,13 @@ @@ -23,10 +23,13 @@
23 </div> 23 </div>
24 </template> 24 </template>
25 <script setup lang="ts"> 25 <script setup lang="ts">
26 - import { ref, nextTick, reactive } from 'vue'; 26 + import { ref, nextTick, reactive, unref } from 'vue';
27 import { BasicModal, useModalInner } from '/@/components/Modal'; 27 import { BasicModal, useModalInner } from '/@/components/Modal';
  28 + import type { StreamingManageRecord, CameraModel } from '/@/api/camera/model/cameraModel';
28 import { videoPlay } from 'vue3-video-play'; // 引入组件 29 import { videoPlay } from 'vue3-video-play'; // 引入组件
29 import 'vue3-video-play/dist/style.css'; // 引入css 30 import 'vue3-video-play/dist/style.css'; // 引入css
  31 + import { AccessMode } from './config.data';
  32 + import { getStreamingPlayUrl } from '/@/api/camera/cameraManager';
30 33
31 const heightNum = ref(800); 34 const heightNum = ref(800);
32 const showVideo = ref(false); 35 const showVideo = ref(false);
@@ -63,16 +66,26 @@ @@ -63,16 +66,26 @@
63 console.log(video.value); 66 console.log(video.value);
64 }); 67 });
65 68
66 - const [register] = useModalInner((data) => {  
67 - if (data) {  
68 - if (data.record.videoUrl) {  
69 - showVideo.value = true;  
70 - options.src = data.record.videoUrl;  
71 - options.autoPlay = true; 69 + const [register] = useModalInner(
  70 + async (data: { record: CameraModel | StreamingManageRecord }) => {
  71 + const { record } = data;
  72 + if (record.accessMode === AccessMode.ManuallyEnter) {
  73 + if ((record as CameraModel).videoUrl) {
  74 + showVideo.value = true;
  75 + options.src = (record as CameraModel).videoUrl;
  76 + options.autoPlay = true;
  77 + }
  78 + } else {
  79 + try {
  80 + const { data: { url } = { url: '' } } = await getStreamingPlayUrl(record.id!);
  81 + showVideo.value = true;
  82 + options.src = url;
  83 + } catch (error) {}
72 } 84 }
73 } 85 }
74 - }); 86 + );
75 const handleCancel = () => { 87 const handleCancel = () => {
  88 + console.log(unref(video));
76 //关闭暂停播放视频 89 //关闭暂停播放视频
77 video.value.pause(); 90 video.value.pause();
78 }; 91 };
@@ -4,6 +4,23 @@ import { copyTransFun } from '/@/utils/fnUtils'; @@ -4,6 +4,23 @@ import { copyTransFun } from '/@/utils/fnUtils';
4 import type { FormSchema as QFormSchema } from '/@/components/Form/index'; 4 import type { FormSchema as QFormSchema } from '/@/components/Form/index';
5 5
6 import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules'; 6 import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules';
  7 +import { getStreamingMediaList } from '/@/api/camera/cameraManager';
  8 +
  9 +export enum AccessMode {
  10 + ManuallyEnter = 0,
  11 + Streaming = 1,
  12 +}
  13 +
  14 +export enum PlayProtocol {
  15 + HTTP = 0,
  16 + HTTPS = 1,
  17 +}
  18 +
  19 +export enum StreamType {
  20 + MASTER = 0,
  21 + CHILD = 1,
  22 + THIRD = 2,
  23 +}
7 24
8 // 表格列数据 25 // 表格列数据
9 export const columns: BasicColumn[] = [ 26 export const columns: BasicColumn[] = [
@@ -59,6 +76,97 @@ export const searchFormSchema: FormSchema[] = [ @@ -59,6 +76,97 @@ export const searchFormSchema: FormSchema[] = [
59 }, 76 },
60 ]; 77 ];
61 78
  79 +/**
  80 + * @description 手动输入
  81 + */
  82 +export const manuallyEnter: FormSchema[] = [
  83 + {
  84 + field: 'brand',
  85 + label: '视频厂家',
  86 + component: 'Input',
  87 + componentProps: {
  88 + placeholder: '请输入视频厂家',
  89 + },
  90 + },
  91 + {
  92 + field: 'sn',
  93 + label: '摄像头编号',
  94 + required: true,
  95 + component: 'Input',
  96 + rules: [...CameraVideoUrl, { required: true, message: '摄像头编号是必填项' }],
  97 + componentProps: {
  98 + placeholder: '请输入摄像头编号',
  99 + },
  100 + },
  101 + {
  102 + field: 'videoUrl',
  103 + label: '视频流',
  104 + required: true,
  105 + component: 'Input',
  106 + componentProps: {
  107 + placeholder: '请输入视频流',
  108 + maxLength: 255,
  109 + },
  110 + rules: [...CameraVideoUrl, { required: true, message: '视频流是必填项' }],
  111 + },
  112 +];
  113 +
  114 +/**
  115 + * @description 流媒体获取
  116 + */
  117 +export const streamingMediaAcquire: FormSchema[] = [
  118 + {
  119 + field: 'videoPlatformId',
  120 + label: '流媒体配置',
  121 + component: 'ApiSelect',
  122 + componentProps: {
  123 + placeholder: '请选择流媒体配置',
  124 + api: getStreamingMediaList,
  125 + labelField: 'host',
  126 + valueField: 'id',
  127 + },
  128 + },
  129 + {
  130 + field: 'streamType',
  131 + label: '码流',
  132 + component: 'RadioGroup',
  133 + defaultValue: StreamType.MASTER,
  134 + componentProps: {
  135 + placeholder: '请选择码流',
  136 + defaultValue: StreamType.MASTER,
  137 + options: [
  138 + { label: '主码流', value: StreamType.MASTER },
  139 + { label: '子码流', value: StreamType.CHILD },
  140 + { label: '第三码流', value: StreamType.THIRD },
  141 + ],
  142 + },
  143 + },
  144 + {
  145 + field: 'playProtocol',
  146 + label: '播放协议',
  147 + component: 'RadioGroup',
  148 + defaultValue: PlayProtocol.HTTP,
  149 + componentProps: {
  150 + placeholder: '请选择播放协议',
  151 + defaultValue: PlayProtocol.HTTP,
  152 + options: [
  153 + { label: 'http', value: PlayProtocol.HTTP },
  154 + { label: 'https', value: PlayProtocol.HTTPS },
  155 + ],
  156 + },
  157 + },
  158 + {
  159 + field: 'sn',
  160 + label: '监控点编号',
  161 + component: 'Input',
  162 + rules: [...CameraVideoUrl, { required: true, message: '摄像头编号是必填项' }],
  163 +
  164 + componentProps: {
  165 + placeholder: '请输入监控点编号',
  166 + },
  167 + },
  168 +];
  169 +
62 // 弹框配置项 170 // 弹框配置项
63 export const formSchema: QFormSchema[] = [ 171 export const formSchema: QFormSchema[] = [
64 { 172 {
@@ -76,7 +184,7 @@ export const formSchema: QFormSchema[] = [ @@ -76,7 +184,7 @@ export const formSchema: QFormSchema[] = [
76 placeholder: '请输入视频名字', 184 placeholder: '请输入视频名字',
77 maxLength: 30, 185 maxLength: 30,
78 }, 186 },
79 - rules: CameraMaxLength, 187 + rules: [...CameraMaxLength, { required: true, message: '视频名是必填项' }],
80 }, 188 },
81 { 189 {
82 field: 'organizationId', 190 field: 'organizationId',
@@ -92,32 +200,48 @@ export const formSchema: QFormSchema[] = [ @@ -92,32 +200,48 @@ export const formSchema: QFormSchema[] = [
92 }, 200 },
93 }, 201 },
94 { 202 {
95 - field: 'brand',  
96 - label: '视频厂家',  
97 - component: 'Input',  
98 - componentProps: {  
99 - placeholder: '请输入视频厂家',  
100 - },  
101 - },  
102 - {  
103 - field: 'sn',  
104 - label: '摄像头编号',  
105 - required: true,  
106 - component: 'Input',  
107 - rules: CameraVideoUrl,  
108 - componentProps: {  
109 - placeholder: '请输入摄像头编号',  
110 - },  
111 - },  
112 - {  
113 - field: 'videoUrl',  
114 - label: '视频流',  
115 - required: true,  
116 - component: 'Input',  
117 - componentProps: {  
118 - placeholder: '请输入视频流',  
119 - maxLength: 255, 203 + label: '视频流获取方式',
  204 + field: 'accessMode',
  205 + component: 'RadioGroup',
  206 + rules: [{ required: true, message: '视频流获取方式为必选项', type: 'number' }],
  207 + defaultValue: AccessMode.ManuallyEnter,
  208 + componentProps({ formActionType }) {
  209 + return {
  210 + defaultValue: AccessMode.ManuallyEnter,
  211 + placeholder: '请选择视频流获取方式',
  212 + options: [
  213 + { label: '手动输入', value: AccessMode.ManuallyEnter },
  214 + { label: '流媒体获取', value: AccessMode.Streaming },
  215 + ],
  216 + onChange(event: { target: { value: number } }) {
  217 + formActionType.removeSchemaByFiled([
  218 + 'videoPlatformId',
  219 + 'streamType',
  220 + 'playProtocol',
  221 + 'sn',
  222 + 'brand',
  223 + 'videoUrl',
  224 + ]);
  225 + const {
  226 + target: { value },
  227 + } = event;
  228 + if (value === AccessMode.ManuallyEnter) {
  229 + manuallyEnter.forEach((schema) =>
  230 + formActionType.appendSchemaByField(schema, undefined)
  231 + );
  232 + } else {
  233 + streamingMediaAcquire.forEach((schema) =>
  234 + formActionType.appendSchemaByField(schema, undefined)
  235 + );
  236 +
  237 + formActionType.setFieldsValue({
  238 + streamType: StreamType.MASTER,
  239 + playProtocol: PlayProtocol.HTTP,
  240 + });
  241 + }
  242 + },
  243 + };
120 }, 244 },
121 - rules: CameraVideoUrl,  
122 }, 245 },
  246 + ...manuallyEnter,
123 ]; 247 ];
  1 +import { PlayProtocol } from '../manage/config.data';
1 import type { StreamingMediaModel } from '/@/api/camera/model/cameraModel'; 2 import type { StreamingMediaModel } from '/@/api/camera/model/cameraModel';
2 import { BasicColumn, FormSchema } from '/@/components/Table'; 3 import { BasicColumn, FormSchema } from '/@/components/Table';
3 4
@@ -72,8 +73,8 @@ export const formDetailSchema: FormSchema[] = [ @@ -72,8 +73,8 @@ export const formDetailSchema: FormSchema[] = [
72 rules: [{ required: true, message: '流媒体部署环境为必填项', type: 'number' }], 73 rules: [{ required: true, message: '流媒体部署环境为必填项', type: 'number' }],
73 componentProps: { 74 componentProps: {
74 options: [ 75 options: [
75 - { label: 'http', value: 0 },  
76 - { label: 'https', value: 1 }, 76 + { label: 'http', value: PlayProtocol.HTTP },
  77 + { label: 'https', value: PlayProtocol.HTTPS },
77 ], 78 ],
78 }, 79 },
79 }, 80 },