Commit 37b07bcff0578b32cb23ca06286768cd61a1fb13

Authored by xp.Huang
2 parents 087e8308 2475deea

Merge branch 'perf_video_channel' into 'main_dev'

perf: 调整视频管理GBT28181,联调通道号接口,和修改为用户选择

See merge request yunteng/thingskit-front!1222
... ... @@ -22,6 +22,7 @@ enum CameraManagerApi {
22 22 VIDEO_CONTROL_STOP = '/video/control/stop/',
23 23 CAMERA_ADDGPT_API_URL = '/video/add/gbt28181',
24 24 VIDEO_CONTROL_SYNC = '/video/control/sync/',
  25 + VIDEO_CHANNEL_LIST = '/video/channel/list',
25 26 }
26 27
27 28 export const cameraPage = (params: CameraQueryParam) => {
... ... @@ -152,3 +153,10 @@ export const syncVideoApiGet = (deviceId: string) => {
152 153 url: `${CameraManagerApi.VIDEO_CONTROL_SYNC}${deviceId}`,
153 154 });
154 155 };
  156 +
  157 +//获取设备通道列表
  158 +export const getDeviceChannelList = (deviceId: string) => {
  159 + return defHttp.get({
  160 + url: `${CameraManagerApi.VIDEO_CHANNEL_LIST}?deviceId=${deviceId}`,
  161 + });
  162 +};
... ...
... ... @@ -26,9 +26,10 @@
26 26 import { useMessage } from '/@/hooks/web/useMessage';
27 27 import { getStreamingMediaList, createGPTPostApi } from '/@/api/camera/cameraManager';
28 28 import SelectDevice from './SelectDevice.vue';
29   - import { formGBTSchema, StreamInterface } from '../config.data';
  29 + import { formGBTSchema, GBT28181DeviceSelectMethod, StreamInterface } from '../config.data';
30 30 import { getMeetTheConditionsDevice } from '/@/api/dataBoard';
31 31 import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const';
  32 + import { isArray } from '/@/utils/is';
32 33
33 34 const emits = defineEmits(['success', 'register']);
34 35
... ... @@ -99,14 +100,20 @@
99 100 return record;
100 101 });
101 102
  103 + const errorMessage = (errorText) => {
  104 + createMessage.error(errorText);
  105 + throw Error(errorText);
  106 + };
  107 +
102 108 //验证视频通道号
103 109 const validateChannelNos = (gptDeviceDTOS) => {
  110 + if (isArray(gptDeviceDTOS) && gptDeviceDTOS.length == 0) {
  111 + errorMessage('请选择设备通道号');
  112 + }
104 113 gptDeviceDTOS.some((deviceDTO) => {
105 114 const channelNo = deviceDTO['channelNos'];
106   - const ChannelNoRegexp = /^\d{1,20}$/;
107   - if (!ChannelNoRegexp.test(channelNo)) {
108   - createMessage.error('输入内容只能是数字,且不能超过20位');
109   - throw Error('输入内容只能是数字,且不能超过20位');
  115 + if (!channelNo || (isArray(channelNo) && channelNo.length == 0)) {
  116 + errorMessage('请选择通道号');
110 117 }
111 118 });
112 119 };
... ... @@ -116,10 +123,11 @@
116 123 setDrawerProps({ confirmLoading: true });
117 124 const values = await validate();
118 125 if (!values) return;
119   - const gptDeviceDTOS = selectDeviceRef.value?.getDeviceChannels();
120   - if (Array.isArray(gptDeviceDTOS) && gptDeviceDTOS.length == 0)
121   - return createMessage.error('请填写设备通道号');
122   - validateChannelNos(gptDeviceDTOS);
  126 + let gptDeviceDTOS: Recordable[] | null = null;
  127 + if (values.allDevice === GBT28181DeviceSelectMethod.PARTIAL) {
  128 + gptDeviceDTOS = selectDeviceRef.value?.getDeviceChannels() as any as Recordable[];
  129 + validateChannelNos(gptDeviceDTOS);
  130 + }
123 131 const mergeValues = {
124 132 ...values,
125 133 accessMode: 2,
... ...
1 1 <template>
2 2 <div v-for="(param, index) in dynamicInput.params" :key="index" class="mt-4 flex gap-2">
3 3 <a-input disabled v-model:value="param.deviceName" class="w-1/2 flex-1" />
4   - <a-input v-model:value="param.channelNos" class="w-1/2 flex-1" placeholder="请输入通道号" />
  4 + <Select
  5 + placeholder="请选择通道号"
  6 + v-model:value="param.channelNos"
  7 + class="!w-1/2"
  8 + :options="selectOptions"
  9 + v-bind="createPickerSearch()"
  10 + :disabled="disabled"
  11 + @change="handleChange"
  12 + mode="multiple"
  13 + allowClear
  14 + />
5 15 </div>
6 16 </template>
7 17 <script lang="ts">
... ... @@ -10,9 +20,14 @@
10 20 };
11 21 </script>
12 22 <script lang="ts" setup name="SelectAttributes">
13   - import { reactive, UnwrapRef, watchEffect } from 'vue';
  23 + import { reactive, UnwrapRef, watchEffect, ref } from 'vue';
14 24 import { propTypes } from '/@/utils/propTypes';
15 25 import { DeviceChannelInterface } from '../config.data';
  26 + import { createPickerSearch } from '/@/utils/pickerSearch';
  27 + import { Select } from 'ant-design-vue';
  28 + import { getDeviceChannelList } from '/@/api/camera/cameraManager';
  29 + import { isArray } from '/@/utils/is';
  30 + import cloneDeep from 'lodash-es/cloneDeep';
16 31
17 32 const props = defineProps({
18 33 value: propTypes.object.def({}),
... ... @@ -22,10 +37,25 @@
22 37 },
23 38 });
24 39
  40 + const selectOptions = ref<LabelValueOptions>([]);
  41 +
25 42 //动态数据
26 43 const dynamicInput: UnwrapRef<{ params: DeviceChannelInterface[] }> = reactive({ params: [] });
27 44
28 45 const initVal = async () => {
  46 + if (props.value?.value) {
  47 + const res = await getDeviceChannelList(props.value?.value);
  48 + if (isArray(res) && res.length !== 0) {
  49 + selectOptions.value = res.map((channelItem) => ({
  50 + label: channelItem.name,
  51 + value: channelItem.channelId,
  52 + }));
  53 + selectOptions.value.unshift({
  54 + label: '全选',
  55 + value: 'all',
  56 + });
  57 + }
  58 + }
29 59 if (props.value) {
30 60 dynamicInput.params.push({
31 61 deviceName: props.value.label,
... ... @@ -42,9 +72,40 @@
42 72
43 73 valEffect();
44 74
  75 + const selectValues = ref<string[]>([]); // 保存选择的值
  76 +
  77 + const saveSelectValues = ref<Recordable>({});
  78 +
45 79 //chang改变
46 80 const emitChange = () => {
47   - return { ...dynamicInput.params[0], channelNos: [dynamicInput.params[0].channelNos] };
  81 + return saveSelectValues.value;
  82 + };
  83 +
  84 + const excludeKeyAllValue = (value: LabelValueOptions) => {
  85 + return value?.filter((selectItem) => selectItem.value !== 'all');
  86 + };
  87 +
  88 + const handleChange = (_, options) => {
  89 + // 全选
  90 + const findSelectAll = options?.find((optionItem) => optionItem.value === 'all')?.value;
  91 + if (findSelectAll) {
  92 + dynamicInput.params[0].channelNos = ['all']; //全选
  93 + excludeKeyAllValue(selectOptions.value)?.forEach(
  94 + (disabledItem) => (disabledItem.disabled = true)
  95 + );
  96 + selectValues.value = cloneDeep(selectOptions.value)
  97 + ?.filter((selectItem) => selectItem.value !== 'all')
  98 + ?.map((mapItem) => mapItem.value);
  99 + } else {
  100 + excludeKeyAllValue(selectOptions.value)?.forEach(
  101 + (disabledItem) => (disabledItem.disabled = false)
  102 + );
  103 + }
  104 + const data = cloneDeep(options)?.map((mapItem) => mapItem.value);
  105 + saveSelectValues.value = {
  106 + ...dynamicInput.params[0],
  107 + channelNos: findSelectAll ? selectValues.value : data,
  108 + };
48 109 };
49 110
50 111 defineExpose({
... ...
... ... @@ -8,11 +8,12 @@
8 8 @change="handleDeviceChange"
9 9 :disabled="disabled"
10 10 mode="multiple"
  11 + :max-tag-count="selectChannelMaxCount"
11 12 labelInValue
12 13 />
13   - <template v-for="(item, index) in deviceList" :key="index">
  14 + <template v-for="(item, index) in deviceList" :key="item.value">
14 15 <InputChannel
15   - :ref="bindDeviceRef.deviceAttrRef"
  16 + :ref="bindDeviceRef.deviceChannelRef"
16 17 :value="item"
17 18 :index="index"
18 19 :disabled="disabled"
... ... @@ -25,6 +26,7 @@
25 26 import { createPickerSearch } from '/@/utils/pickerSearch';
26 27 import { StreamInterface } from '../config.data';
27 28 import InputChannel from './InputChannel.vue';
  29 + import { useMessage } from '/@/hooks/web/useMessage';
28 30
29 31 defineProps({
30 32 selectOptions: {
... ... @@ -37,19 +39,25 @@
37 39 },
38 40 });
39 41
  42 + const { createMessage } = useMessage();
  43 +
40 44 const selectValue = ref([]);
41 45
42 46 const bindDeviceRef = {
43   - deviceAttrRef: ref([]),
  47 + deviceChannelRef: ref([]),
44 48 };
45 49
46 50 const deviceList: Ref<StreamInterface[]> = ref([]);
47 51
  52 + const selectChannelMaxCount = ref(50); // 限制通道数为50
  53 +
48 54 const getDeviceChannels = () => {
49   - return unref(bindDeviceRef.deviceAttrRef)?.map((item: any) => item.emitChange());
  55 + return unref(bindDeviceRef.deviceChannelRef)?.map((item: any) => item.emitChange());
50 56 };
51 57
52 58 const handleDeviceChange = (_, options) => {
  59 + if (options.length > selectChannelMaxCount.value)
  60 + return createMessage.warn('通道数需要小于50个');
53 61 deviceList.value = options;
54 62 };
55 63
... ...
1 1 import { BasicColumn, FormSchema } from '/@/components/Table';
2 2 import { FormSchema as QFormSchema, useComponentRegister } from '/@/components/Form/index';
3 3
4   -import { CameraVideoUrl, CameraMaxLength, CameraChannelNoRule } from '/@/utils/rules';
  4 +import { CameraVideoUrl, CameraMaxLength } from '/@/utils/rules';
5 5 import { h } from 'vue';
6 6 import SnHelpMessage from './SnHelpMessage.vue';
7 7 import SnHelpMessage1 from './SnHelpMessage1.vue';
... ... @@ -15,6 +15,7 @@ import { DataSourceField } from '../../visual/packages/config/common.config';
15 15 import { getMeetTheConditionsDevice } from '/@/api/dataBoard';
16 16 import { useMessage } from '/@/hooks/web/useMessage';
17 17 import { TransportTypeEnum } from '../../device/profiles/components/TransportDescript/const';
  18 +import { getDeviceChannelList } from '/@/api/camera/cameraManager';
18 19
19 20 useComponentRegister('OrgTreeSelect', OrgTreeSelect);
20 21
... ... @@ -36,6 +37,12 @@ export enum CameraPermission {
36 37 DELETE = 'api:yt:video:delete',
37 38 }
38 39
  40 +// GBT28181设备选择全选或者部分
  41 +export enum GBT28181DeviceSelectMethod {
  42 + ALL = 1, //全部
  43 + PARTIAL = 0, //部分
  44 +}
  45 +
39 46 export enum AccessMode {
40 47 ManuallyEnter = 0,
41 48 Streaming = 1,
... ... @@ -265,7 +272,7 @@ export const formSchema: QFormSchema[] = [
265 272 placeholder: '请选择设备',
266 273 onChange() {
267 274 setFieldsValue({
268   - channelNo: '',
  275 + channelNo: [],
269 276 });
270 277 },
271 278 onFocus() {
... ... @@ -282,15 +289,30 @@ export const formSchema: QFormSchema[] = [
282 289 field: 'channelNo',
283 290 label: '通道号',
284 291 required: true,
285   - component: 'Input',
  292 + component: 'ApiSelect',
286 293 ifShow({ values }) {
287 294 return values.accessMode === AccessMode.GBT28181;
288 295 },
289   - componentProps: {
290   - maxLength: 20,
291   - placeholder: '请输入通道号',
  296 + componentProps({ formModel }) {
  297 + const deviceId = formModel[DataSourceField.DEVICE_ID];
  298 + return {
  299 + api: async () => {
  300 + if (deviceId) {
  301 + try {
  302 + const data = await getDeviceChannelList(deviceId);
  303 + if (data)
  304 + return data.map((item) => ({
  305 + ...item,
  306 + label: item.name,
  307 + value: item.channelId,
  308 + }));
  309 + } catch (error) {}
  310 + }
  311 + return [];
  312 + },
  313 + placeholder: '请选择通道号',
  314 + };
292 315 },
293   - rules: [{ required: true, message: '通道号是必填项' }, ...CameraChannelNoRule],
294 316 },
295 317 {
296 318 field: 'brand',
... ... @@ -537,10 +559,30 @@ export const formGBTSchema: QFormSchema[] = [
537 559 ifShow: ({ values }) => values.accessMode === AccessMode.GBT28181,
538 560 },
539 561 {
  562 + label: '设备选择方式',
  563 + field: 'allDevice',
  564 + component: 'RadioGroup',
  565 + rules: [{ required: true, message: '设备选择方式为必选项', type: 'number' }],
  566 + defaultValue: GBT28181DeviceSelectMethod.ALL,
  567 + componentProps() {
  568 + return {
  569 + defaultValue: GBT28181DeviceSelectMethod.ALL,
  570 + placeholder: '请选择设备选择方式',
  571 + options: [
  572 + { label: '全部', value: GBT28181DeviceSelectMethod.ALL },
  573 + { label: '部分', value: GBT28181DeviceSelectMethod.PARTIAL },
  574 + ],
  575 + };
  576 + },
  577 + ifShow: ({ values }) => !!values.organizationId,
  578 + },
  579 + {
540 580 field: 'deviceChannels',
541 581 label: '设备和通道号',
542 582 component: 'Input',
543 583 slot: 'deviceChannelsSlot',
544   - ifShow: ({ values }) => values.accessMode === AccessMode.GBT28181,
  584 + ifShow: ({ values }) =>
  585 + values.accessMode === AccessMode.GBT28181 &&
  586 + values.allDevice === GBT28181DeviceSelectMethod.PARTIAL,
545 587 },
546 588 ];
... ...
... ... @@ -88,7 +88,7 @@ export const descSchema = (emit: EmitType): DescItem[] => {
88 88 label: '设备编号',
89 89 },
90 90 {
91   - field: 'deviceInfo.sip.localIp',
  91 + field: 'deviceInfo.sip.hostAddress',
92 92 label: '地址',
93 93 },
94 94 {
... ...