Commit cafef23fee221cbd88dd5919c6525d6a06fb62e1

Authored by fengwotao
2 parents 27356ebd b3cd785a

Merge branch 'main_dev' into refractor_home

Showing 29 changed files with 1165 additions and 77 deletions
... ... @@ -14,6 +14,7 @@ enum CameraManagerApi {
14 14 CAMERA_GET_URL = '/video',
15 15 CAMERA_DELETE_URL = '/video',
16 16 CAMERA_GET_DETAIL_URL = '/video',
  17 + CAMERA_LIST_URL = '/video/list',
17 18 STREAMING_GET_URL = '/video/platform',
18 19 STREAMING_POST_URL = '/video/platform',
19 20 STREAMING_DELETE_URL = '/video/platform',
... ... @@ -27,6 +28,13 @@ export const cameraPage = (params: CameraQueryParam) => {
27 28 });
28 29 };
29 30
  31 +export const getCameraList = (params: Record<'organizationId', string>) => {
  32 + return defHttp.get<{ data: CameraModel[] }>({
  33 + url: CameraManagerApi.CAMERA_LIST_URL,
  34 + params,
  35 + });
  36 +};
  37 +
30 38 /**
31 39 * 删除视频
32 40 * @param ids 删除的ids
... ...
1   -import { RadioRecord } from '../../../views/visual/board/detail/config/util';
2   -import { DeviceTypeEnum } from '../../device/model/deviceModel';
3 1 import { DataType } from '../../device/model/modelOfMatterModel';
4 2 import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config';
  3 +import { DataSource } from '/@/views/visual/palette/types';
5 4
6 5 export interface AddDataBoardParams {
7 6 name: string;
... ... @@ -74,34 +73,6 @@ export interface ComponentInfo {
74 73 showDeviceName: boolean;
75 74 }
76 75
77   -export interface DataSource {
78   - attribute: string;
79   - deviceId: string;
80   - organizationId: string;
81   - attributeRename: string;
82   - deviceRename: string;
83   - slaveDeviceId: string;
84   - gatewayDevice: boolean;
85   - componentInfo: ComponentInfo;
86   - deviceName: string;
87   - deviceProfileId: string;
88   - tbDeviceId: string;
89   - customCommand: {
90   - transportType?: string;
91   - commandType?: string;
92   - command?: string;
93   - service?: string;
94   - };
95   -
96   - // front usage
97   - uuid?: string;
98   - width?: number;
99   - height?: number;
100   - radio?: RadioRecord;
101   - deviceType?: DeviceTypeEnum;
102   - [key: string]: any;
103   -}
104   -
105 76 export interface DataComponentRecord {
106 77 dataBoardId: string;
107 78 dataSource: DataSource[];
... ...
1   -export enum VideoPlayerType {
2   - m3u8 = 'application/x-mpegURL',
3   - mp4 = 'video/mp4',
4   - webm = 'video/webm',
5   -}
6   -
7   -export const getVideoTypeByUrl = (url: string) => {
8   - const splitExtReg = /(?:.*)(?<=\.)/;
9   - const type = url.replace(splitExtReg, '');
10   - /**
11   - * https://vcsplay.scjtonline.cn:8200/live/HD_1569b634-4789-11eb-ab67-3cd2e55e0b20.m3u8?auth_key=1681179278-0-0-5c54a376f2ca32d05c4a152ee96336e9
12   - * 如果是这种格式的m3u8,则截取的是这一部分.m3u8?auth_key=1681179278-0-0-5c54a376f2ca32d05c4a152ee96336e9
13   - */
14   - if (type.startsWith('m3u8')) return VideoPlayerType.m3u8;
15   - if (type.startsWith('mp4')) return VideoPlayerType.mp4;
16   - if (type.startsWith('webm')) return VideoPlayerType.webm;
17   - return;
18   -};
  1 +export enum VideoPlayerType {
  2 + m3u8 = 'application/x-mpegURL',
  3 + mp4 = 'video/mp4',
  4 + webm = 'video/webm',
  5 +}
  6 +
  7 +export const getVideoTypeByUrl = (url: string) => {
  8 + const splitExtReg = /(?:.*)(?<=\.)/;
  9 + const type = url.replace(splitExtReg, '');
  10 + /**
  11 + * https://vcsplay.scjtonline.cn:8200/live/HD_1569b634-4789-11eb-ab67-3cd2e55e0b20.m3u8?auth_key=1681179278-0-0-5c54a376f2ca32d05c4a152ee96336e9
  12 + * 如果是这种格式的m3u8,则截取的是这一部分.m3u8?auth_key=1681179278-0-0-5c54a376f2ca32d05c4a152ee96336e9
  13 + */
  14 + if (type.startsWith('m3u8')) return VideoPlayerType.m3u8;
  15 + if (type.startsWith('mp4')) return VideoPlayerType.mp4;
  16 + if (type.startsWith('webm')) return VideoPlayerType.webm;
  17 + return VideoPlayerType.webm;
  18 +};
... ...
  1 +import cloneDeep from 'lodash-es/cloneDeep';
  2 +import { CircleFlowmeterConfig } from '.';
  3 +import {
  4 + ConfigType,
  5 + CreateComponentType,
  6 + PublicComponentOptions,
  7 + PublicPresetOptions,
  8 +} from '/@/views/visual/packages/index.type';
  9 +import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig';
  10 +import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  11 +
  12 +export const option: PublicPresetOptions = {
  13 + [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  14 + [ComponentConfigFieldEnum.UNIT]: 'm',
  15 + [ComponentConfigFieldEnum.FONT_COLOR]: '#fff',
  16 + [ComponentConfigFieldEnum.FLOWMETER_CONFIG]: {
  17 + [ComponentConfigFieldEnum.BACKGROUND_COLOR]: '#8badcb',
  18 + [ComponentConfigFieldEnum.WAVE_FIRST]: '#4579e2',
  19 + [ComponentConfigFieldEnum.WAVE_SECOND]: '#3461c1',
  20 + [ComponentConfigFieldEnum.WAVE_THIRD]: '#2d55aa',
  21 + },
  22 +};
  23 +
  24 +export default class Config extends PublicConfigClass implements CreateComponentType {
  25 + public key: string = CircleFlowmeterConfig.key;
  26 +
  27 + public attr = { ...componentInitConfig };
  28 +
  29 + public componentConfig: ConfigType = cloneDeep(CircleFlowmeterConfig);
  30 +
  31 + public persetOption = cloneDeep(option);
  32 +
  33 + public option: PublicComponentOptions;
  34 +
  35 + constructor(option: PublicComponentOptions) {
  36 + super();
  37 + this.option = { ...option };
  38 + }
  39 +}
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  3 + import { useForm, BasicForm } from '/@/components/Form';
  4 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  5 + import { option } from './config';
  6 + import { ComponentInfo } from '/@/views/visual/palette/types';
  7 + import { toRaw } from 'vue';
  8 +
  9 + const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
  10 + schemas: [
  11 + {
  12 + field: ComponentConfigFieldEnum.FONT_COLOR,
  13 + label: '字体颜色',
  14 + component: 'ColorPicker',
  15 + changeEvent: 'update:value',
  16 + defaultValue: option.fontColor,
  17 + },
  18 + {
  19 + field: ComponentConfigFieldEnum.BACKGROUND_COLOR,
  20 + label: '背景颜色',
  21 + component: 'ColorPicker',
  22 + changeEvent: 'update:value',
  23 + defaultValue: option.flowmeterConfig?.backgroundColor,
  24 + },
  25 + {
  26 + field: ComponentConfigFieldEnum.WAVE_FIRST,
  27 + label: '浪1',
  28 + component: 'ColorPicker',
  29 + changeEvent: 'update:value',
  30 + defaultValue: option.flowmeterConfig?.waveFirst,
  31 + },
  32 + {
  33 + field: ComponentConfigFieldEnum.WAVE_SECOND,
  34 + label: '浪2',
  35 + component: 'ColorPicker',
  36 + changeEvent: 'update:value',
  37 + defaultValue: option.flowmeterConfig?.waveSecond,
  38 + },
  39 + {
  40 + field: ComponentConfigFieldEnum.WAVE_THIRD,
  41 + label: '浪3',
  42 + component: 'ColorPicker',
  43 + changeEvent: 'update:value',
  44 + defaultValue: option.flowmeterConfig?.waveThird,
  45 + },
  46 + {
  47 + field: ComponentConfigFieldEnum.UNIT,
  48 + label: '单位',
  49 + component: 'Input',
  50 + defaultValue: option.unit,
  51 + },
  52 + ],
  53 + showActionButtonGroup: false,
  54 + labelWidth: 120,
  55 + baseColProps: {
  56 + span: 12,
  57 + },
  58 + });
  59 +
  60 + const getFormValues = () => {
  61 + const value = getFieldsValue();
  62 + return {
  63 + ...value,
  64 + flowmeterConfig: {
  65 + backgroundColor: value[ComponentConfigFieldEnum.BACKGROUND_COLOR],
  66 + waveFirst: value[ComponentConfigFieldEnum.WAVE_FIRST],
  67 + waveSecond: value[ComponentConfigFieldEnum.WAVE_SECOND],
  68 + waveThird: value[ComponentConfigFieldEnum.WAVE_THIRD],
  69 + },
  70 + } as ComponentInfo;
  71 + };
  72 +
  73 + const setFormValues = (data: ComponentInfo) => {
  74 + const { flowmeterConfig } = data || {};
  75 + return setFieldsValue({
  76 + ...toRaw(data),
  77 + [ComponentConfigFieldEnum.BACKGROUND_COLOR]: flowmeterConfig?.backgroundColor,
  78 + [ComponentConfigFieldEnum.WAVE_FIRST]: flowmeterConfig?.waveFirst,
  79 + [ComponentConfigFieldEnum.WAVE_SECOND]: flowmeterConfig?.waveSecond,
  80 + [ComponentConfigFieldEnum.WAVE_THIRD]: flowmeterConfig?.waveThird,
  81 + });
  82 + };
  83 +
  84 + defineExpose({
  85 + getFormValues,
  86 + setFormValues,
  87 + resetFormValues: resetFields,
  88 + } as PublicFormInstaceType);
  89 +</script>
  90 +
  91 +<template>
  92 + <BasicForm @register="register" />
  93 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { CreateComponentType } from '/@/views/visual/packages/index.type';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration';
  5 + import {
  6 + PublicComponentValueType,
  7 + PublicFormInstaceType,
  8 + } from '/@/views/visual/dataSourceBindPanel/index.type';
  9 +
  10 + const props = defineProps<{
  11 + values: PublicComponentValueType;
  12 + componentConfig: CreateComponentType;
  13 + }>();
  14 +
  15 + const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({
  16 + labelWidth: 0,
  17 + showActionButtonGroup: false,
  18 + layout: 'horizontal',
  19 + labelCol: { span: 0 },
  20 + schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key),
  21 + });
  22 +
  23 + const getFormValues = () => {
  24 + return getFieldsValue();
  25 + };
  26 +
  27 + const setFormValues = (record: Recordable) => {
  28 + return setFieldsValue(record);
  29 + };
  30 +
  31 + defineExpose({
  32 + getFormValues,
  33 + setFormValues,
  34 + validate,
  35 + resetFormValues: resetFields,
  36 + } as PublicFormInstaceType);
  37 +</script>
  38 +
  39 +<template>
  40 + <BasicForm @register="register" />
  41 +</template>
... ...
  1 +import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
  2 +import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
  3 +
  4 +const componentKeys = useComponentKeys('CircleFlowmeter');
  5 +
  6 +export const CircleFlowmeterConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '流量计2',
  9 + package: PackagesCategoryEnum.FLOWMETER,
  10 +};
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type';
  3 + import { option } from './config';
  4 + import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';
  5 + import { computed } from 'vue';
  6 + import { ref } from 'vue';
  7 + import { unref } from 'vue';
  8 +
  9 + const props = defineProps<{
  10 + config: ComponentPropsConfigType<typeof option>;
  11 + }>();
  12 +
  13 + const currentValue = ref(25);
  14 +
  15 + const getDesign = computed(() => {
  16 + const { option, persetOption } = props.config;
  17 + const { componentInfo } = option;
  18 + const { flowmeterConfig, unit, fontColor } = componentInfo || {};
  19 + const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
  20 + const {
  21 + flowmeterConfig: presetFlowmeterConfig,
  22 + unit: persetUnit,
  23 + fontColor: presetFontColor,
  24 + } = persetOption || {};
  25 + const {
  26 + backgroundColor: presetBackgroundColor,
  27 + waveFirst: presetWaveFirst,
  28 + waveSecond: presetWaveSecond,
  29 + waveThird: presetWaveThird,
  30 + } = presetFlowmeterConfig || {};
  31 + return {
  32 + backgroundColor: backgroundColor ?? presetBackgroundColor,
  33 + waveFirst: waveFirst ?? presetWaveFirst,
  34 + waveSecond: waveSecond ?? presetWaveSecond,
  35 + waveThird: waveThird ?? presetWaveThird,
  36 + unit: unit ?? persetUnit,
  37 + fontColor: fontColor ?? presetFontColor,
  38 + };
  39 + });
  40 +
  41 + const getSize = computed(() => {
  42 + const { option } = props.config;
  43 + const { itemHeightRatio, itemWidthRatio, widthPx, heightPx } = option;
  44 + const currentW = (widthPx * itemWidthRatio) / 100;
  45 + const currentH = (heightPx * itemHeightRatio) / 100;
  46 +
  47 + const size = Math.min(currentW, currentH);
  48 + return size;
  49 + });
  50 +
  51 + const getWaveHeight = computed(() => {
  52 + const value = unref(currentValue);
  53 + return value <= 0 ? 0 : -value - 15;
  54 + });
  55 +
  56 + const getHeight = computed(() => {
  57 + const value = unref(currentValue);
  58 + return value >= 100 ? 0 : 100 - value + 10;
  59 + });
  60 +
  61 + const updateFn: DataFetchUpdateFn = (message, attribute) => {
  62 + const { data = {} } = message;
  63 + const [latest] = data[attribute] || [];
  64 + const [_, value] = latest;
  65 + currentValue.value = Number(value);
  66 + };
  67 +
  68 + useDataFetch(props, updateFn);
  69 +</script>
  70 +
  71 +<template>
  72 + <main class="w-full h-full flex flex-col justify-center items-center relative">
  73 + <svg
  74 + class="waves-rect"
  75 + viewBox="0 0 100 100"
  76 + preserveAspectRatio="none"
  77 + xmlns="http://www.w3.org/2000/svg"
  78 + xmlns:xlink="http://www.w3.org/1999/xlink"
  79 + clip-path="circle()"
  80 + transform="scale(0.9)"
  81 + :style="{ width: `${getSize}px`, height: `${getSize}px` }"
  82 + >
  83 + <defs>
  84 + <path
  85 + id="wave"
  86 + d="M-160 118c30 0 58-18 88-18s 58 18 88 18 58-18 88-18 58 18 88 18 v100h-352z"
  87 + />
  88 + </defs>
  89 + <circle cx="50" cy="50" r="50" :fill="getDesign.backgroundColor" />
  90 +
  91 + <g class="height" :transform="`translate(0 ${getWaveHeight})`">
  92 + <use class="wave waveFirst" xlink:href="#wave" :fill="getDesign.waveFirst" x="0" y="0" />
  93 + <use class="wave waveSecond" xlink:href="#wave" :fill="getDesign.waveSecond" x="0" y="2" />
  94 + <use class="wave waveThird" xlink:href="#wave" :fill="getDesign.waveThird" x="0" y="4" />
  95 + </g>
  96 + <rect
  97 + :transform="`translate(0 ${getHeight})`"
  98 + x="0"
  99 + y="0"
  100 + width="100"
  101 + height="100"
  102 + :fill="getDesign.waveThird"
  103 + />
  104 + </svg>
  105 +
  106 + <div
  107 + class="absolute w-full h-full top-0 left-0 text-center text-lg flex items-center justify-center flex-col"
  108 + :style="{ color: getDesign.fontColor }"
  109 + >
  110 + <div>{{ currentValue }}</div>
  111 + <div class="ml-1">{{ getDesign.unit }}</div>
  112 + </div>
  113 + </main>
  114 +</template>
  115 +
  116 +<style lang="less" scoped>
  117 + @keyframes move {
  118 + from {
  119 + transform: translate(-90px, 0%);
  120 + }
  121 +
  122 + to {
  123 + transform: translate(85px, 0%);
  124 + }
  125 + }
  126 +
  127 + .wave {
  128 + animation: move 3s linear infinite;
  129 + animation-play-state: running;
  130 + }
  131 +
  132 + .wave:nth-child(1) {
  133 + animation-delay: -2s;
  134 + animation-duration: 9s;
  135 + }
  136 +
  137 + .wave:nth-child(2) {
  138 + animation-delay: -4s;
  139 + animation-duration: 6s;
  140 + }
  141 +
  142 + .wave:nth-child(3) {
  143 + animation-delay: -6s;
  144 + animation-duration: 3s;
  145 + }
  146 +
  147 + .waves-rect > g + rect {
  148 + transition: transform linear 1s;
  149 + }
  150 +
  151 + .height {
  152 + transition: transform linear 1s;
  153 + }
  154 +</style>
... ...
... ... @@ -3,10 +3,19 @@
3 3 import { useForm, BasicForm } from '/@/components/Form';
4 4 import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
5 5 import { option } from './config';
  6 + import { ComponentInfo } from '/@/views/visual/palette/types';
  7 + import { toRaw } from 'vue';
6 8
7 9 const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
8 10 schemas: [
9 11 {
  12 + field: ComponentConfigFieldEnum.FONT_COLOR,
  13 + label: '字体颜色',
  14 + component: 'ColorPicker',
  15 + changeEvent: 'update:value',
  16 + defaultValue: option.fontColor,
  17 + },
  18 + {
10 19 field: ComponentConfigFieldEnum.BACKGROUND_COLOR,
11 20 label: '背景颜色',
12 21 component: 'ColorPicker',
... ... @@ -49,11 +58,27 @@
49 58 });
50 59
51 60 const getFormValues = () => {
52   - return getFieldsValue();
  61 + const value = getFieldsValue();
  62 + return {
  63 + ...value,
  64 + flowmeterConfig: {
  65 + backgroundColor: value[ComponentConfigFieldEnum.BACKGROUND_COLOR],
  66 + waveFirst: value[ComponentConfigFieldEnum.WAVE_FIRST],
  67 + waveSecond: value[ComponentConfigFieldEnum.WAVE_SECOND],
  68 + waveThird: value[ComponentConfigFieldEnum.WAVE_THIRD],
  69 + },
  70 + } as ComponentInfo;
53 71 };
54 72
55   - const setFormValues = (data: Recordable) => {
56   - return setFieldsValue(data);
  73 + const setFormValues = (data: ComponentInfo) => {
  74 + const { flowmeterConfig } = data || {};
  75 + return setFieldsValue({
  76 + ...toRaw(data),
  77 + [ComponentConfigFieldEnum.BACKGROUND_COLOR]: flowmeterConfig?.backgroundColor,
  78 + [ComponentConfigFieldEnum.WAVE_FIRST]: flowmeterConfig?.waveFirst,
  79 + [ComponentConfigFieldEnum.WAVE_SECOND]: flowmeterConfig?.waveSecond,
  80 + [ComponentConfigFieldEnum.WAVE_THIRD]: flowmeterConfig?.waveThird,
  81 + });
57 82 };
58 83
59 84 defineExpose({
... ...
... ... @@ -10,14 +10,18 @@
10 10 config: ComponentPropsConfigType<typeof option>;
11 11 }>();
12 12
13   - const currentValue = ref(0);
  13 + const currentValue = ref(25);
14 14
15 15 const getDesign = computed(() => {
16 16 const { option, persetOption } = props.config;
17 17 const { componentInfo } = option;
18   - const { flowmeterConfig, unit } = componentInfo;
  18 + const { flowmeterConfig, unit, fontColor } = componentInfo || {};
19 19 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
20   - const { flowmeterConfig: presetFlowmeterConfig, unit: persetUnit } = persetOption || {};
  20 + const {
  21 + flowmeterConfig: presetFlowmeterConfig,
  22 + unit: persetUnit,
  23 + fontColor: presetFontColor,
  24 + } = persetOption || {};
21 25 const {
22 26 backgroundColor: presetBackgroundColor,
23 27 waveFirst: presetWaveFirst,
... ... @@ -30,6 +34,7 @@
30 34 waveSecond: waveSecond ?? presetWaveSecond,
31 35 waveThird: waveThird ?? presetWaveThird,
32 36 unit: unit ?? persetUnit,
  37 + fontColor: fontColor ?? presetFontColor,
33 38 };
34 39 });
35 40
... ... @@ -54,7 +59,7 @@
54 59 </script>
55 60
56 61 <template>
57   - <main class="w-full h-full flex flex-col justify-center items-center">
  62 + <main class="w-full h-full flex flex-col justify-center items-center relative">
58 63 <svg
59 64 class="waves-rect"
60 65 viewBox="0 0 100 100"
... ... @@ -90,21 +95,14 @@
90 95 height="100"
91 96 :fill="getDesign.waveThird"
92 97 />
93   -
94   - <foreignObject
95   - x="0"
96   - y="0"
97   - width="100"
98   - height="100"
99   - text-anchor="middle"
100   - dominant-baseline="middle"
101   - >
102   - <div xmlns="http://www.w3.org/1999/xhtml" class="text">
103   - <span>{{ currentValue }}</span>
104   - <span class="ml-1">{{ getDesign.unit }}</span>
105   - </div>
106   - </foreignObject>
107 98 </svg>
  99 + <div
  100 + class="absolute w-full h-full top-0 left-0 text-center text-lg flex items-center justify-center flex-col"
  101 + :style="{ color: getDesign.fontColor }"
  102 + >
  103 + <div>{{ currentValue }}</div>
  104 + <div class="ml-1">{{ getDesign.unit }}</div>
  105 + </div>
108 106 </main>
109 107 </template>
110 108
... ...
  1 +import cloneDeep from 'lodash-es/cloneDeep';
  2 +import { ThermometerConfig } from '.';
  3 +import {
  4 + ConfigType,
  5 + CreateComponentType,
  6 + PublicComponentOptions,
  7 + PublicPresetOptions,
  8 +} from '/@/views/visual/packages/index.type';
  9 +import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig';
  10 +import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  11 +
  12 +export const option: PublicPresetOptions = {
  13 + [ComponentConfigFieldEnum.FONT_COLOR]: '#',
  14 + [ComponentConfigFieldEnum.SHOW_DEVICE_NAME]: false,
  15 +};
  16 +
  17 +export default class Config extends PublicConfigClass implements CreateComponentType {
  18 + public key: string = ThermometerConfig.key;
  19 +
  20 + public attr = { ...componentInitConfig };
  21 +
  22 + public componentConfig: ConfigType = cloneDeep(ThermometerConfig);
  23 +
  24 + public persetOption = cloneDeep(option);
  25 +
  26 + public option: PublicComponentOptions;
  27 +
  28 + constructor(option: PublicComponentOptions) {
  29 + super();
  30 + this.option = { ...option };
  31 + }
  32 +}
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  3 + import { useForm, BasicForm } from '/@/components/Form';
  4 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  5 +
  6 + const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
  7 + schemas: [
  8 + {
  9 + field: ComponentConfigFieldEnum.FONT_COLOR,
  10 + label: '数值字体颜色',
  11 + component: 'ColorPicker',
  12 + changeEvent: 'update:value',
  13 + componentProps: {
  14 + defaultValue: '#FD7347',
  15 + },
  16 + },
  17 + {
  18 + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  19 + label: '显示设备名称',
  20 + component: 'Checkbox',
  21 + },
  22 + ],
  23 + showActionButtonGroup: false,
  24 + labelWidth: 120,
  25 + baseColProps: {
  26 + span: 12,
  27 + },
  28 + });
  29 +
  30 + const getFormValues = () => {
  31 + return getFieldsValue();
  32 + };
  33 +
  34 + const setFormValues = (data: Recordable) => {
  35 + return setFieldsValue(data);
  36 + };
  37 +
  38 + defineExpose({
  39 + getFormValues,
  40 + setFormValues,
  41 + resetFormValues: resetFields,
  42 + } as PublicFormInstaceType);
  43 +</script>
  44 +
  45 +<template>
  46 + <BasicForm @register="register" />
  47 +</template>
... ...
  1 +<script lang="ts" setup>
  2 + import { CreateComponentType } from '/@/views/visual/packages/index.type';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration';
  5 + import {
  6 + PublicComponentValueType,
  7 + PublicFormInstaceType,
  8 + } from '/@/views/visual/dataSourceBindPanel/index.type';
  9 +
  10 + const props = defineProps<{
  11 + values: PublicComponentValueType;
  12 + componentConfig: CreateComponentType;
  13 + }>();
  14 +
  15 + const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({
  16 + labelWidth: 0,
  17 + showActionButtonGroup: false,
  18 + layout: 'horizontal',
  19 + labelCol: { span: 0 },
  20 + schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key),
  21 + });
  22 +
  23 + const getFormValues = () => {
  24 + return getFieldsValue();
  25 + };
  26 +
  27 + const setFormValues = (record: Recordable) => {
  28 + return setFieldsValue(record);
  29 + };
  30 +
  31 + defineExpose({
  32 + getFormValues,
  33 + setFormValues,
  34 + validate,
  35 + resetFormValues: resetFields,
  36 + } as PublicFormInstaceType);
  37 +</script>
  38 +
  39 +<template>
  40 + <BasicForm @register="register" />
  41 +</template>
... ...
  1 +import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
  2 +import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
  3 +
  4 +const componentKeys = useComponentKeys('Thermometer');
  5 +
  6 +export const ThermometerConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '温度计',
  9 + package: PackagesCategoryEnum.FLOWMETER,
  10 +};
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentPropsConfigType, DataFetchUpdateFn } from '/@/views/visual/packages/index.type';
  3 + import { option } from './config';
  4 + import { useDataFetch } from '/@/views/visual/packages/hook/useSocket';
  5 + import { ref } from 'vue';
  6 + import { computed } from 'vue';
  7 + import { unref } from 'vue';
  8 +
  9 + const props = defineProps<{
  10 + config: ComponentPropsConfigType<typeof option>;
  11 + }>();
  12 +
  13 + const currentValue = ref(50);
  14 +
  15 + const getValue = computed(() => {
  16 + const maxHeight = 190;
  17 + const minHeight = 15;
  18 + const height = maxHeight - minHeight;
  19 + const rangeNumber = 7;
  20 + const itemRange = 20;
  21 + const itemHeight = height / (rangeNumber * itemRange);
  22 + const value = unref(currentValue);
  23 + const transformValue =
  24 + maxHeight - (value >= 0 ? value + 20 : itemRange - Math.abs(value)) * itemHeight;
  25 +
  26 + return transformValue >= maxHeight
  27 + ? maxHeight
  28 + : transformValue <= minHeight
  29 + ? minHeight
  30 + : transformValue;
  31 + });
  32 +
  33 + const updateFn: DataFetchUpdateFn = (message, attribute) => {
  34 + const { data = {} } = message;
  35 + const [latest] = data[attribute] || [];
  36 + const [_, value] = latest;
  37 +
  38 + currentValue.value = Number(value);
  39 + };
  40 +
  41 + useDataFetch(props, updateFn);
  42 +</script>
  43 +
  44 +<template>
  45 + <main class="w-full h-full flex flex-col justify-center items-center relative">
  46 + <svg class="flowmeter-thermometer" viewBox="0 0 200 250" xmlns="http://www.w3.org/2000/svg">
  47 + <defs>
  48 + <radialGradient id="thermometerdiv_meter_2" cx="50%" cy="50%" r="50%" fx="50%" fy="50%">
  49 + <stop offset="0%" style="stop-color: rgb(230, 200, 200)" />
  50 + <stop offset="90%" style="stop-color: rgb(230, 0, 0)" />
  51 + </radialGradient>
  52 + <clipPath id="over">
  53 + <rect width="100" height="190" x="100" y="10" />
  54 + </clipPath>
  55 + </defs>
  56 + <circle
  57 + r="9.25"
  58 + cx="109"
  59 + cy="14.25"
  60 + style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136); stroke-width: 1px"
  61 + />
  62 + <rect
  63 + x="99.75"
  64 + y="14.25"
  65 + height="192.75"
  66 + width="18.5"
  67 + style="
  68 + shape-rendering: crispEdges;
  69 + fill: rgb(255, 255, 255);
  70 + stroke: rgb(136, 136, 136);
  71 + stroke-width: 1px;
  72 + "
  73 + />
  74 + <circle r="8.75" cx="109" cy="14.25" style="fill: rgb(255, 255, 255); stroke: none" />
  75 + <circle
  76 + r="18"
  77 + cx="109"
  78 + cy="207"
  79 + style="fill: rgb(255, 255, 255); stroke: rgb(136, 136, 136)"
  80 + />
  81 + <rect
  82 + x="100.25"
  83 + y="14.25"
  84 + height="192.75"
  85 + width="17.5"
  86 + style="shape-rendering: crispEdges; fill: rgb(255, 255, 255); stroke: none"
  87 + />
  88 + <line
  89 + class="thermometer-min-line"
  90 + x1="99.75"
  91 + x2="140.25"
  92 + y1="165"
  93 + y2="165"
  94 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispEdges"
  95 + />
  96 + <text
  97 + class="thermometer-min-label"
  98 + x="120.25"
  99 + y="168.46428571428572"
  100 + dy="0.72em"
  101 + style="fill: rgb(0, 0, 230); font-size: 10px"
  102 + >
  103 + min
  104 + </text>
  105 + <line
  106 + class="thermometer-max-line"
  107 + x1="99.75"
  108 + x2="140.25"
  109 + y1="40"
  110 + y2="40"
  111 + style="stroke: rgb(136, 136, 136); stroke-width: 1px; shape-rendering: crispEdges"
  112 + />
  113 + <text
  114 + class="thermometer-max-label"
  115 + x="120.25"
  116 + y="35.285714285714306"
  117 + style="fill: rgb(230, 0, 0); font-size: 10px"
  118 + >
  119 + max
  120 + </text>
  121 + <rect
  122 + class="thermometer-mercury-column"
  123 + x="104"
  124 + :y="getValue"
  125 + width="10.5"
  126 + height="190"
  127 + style="shape-rendering: crispEdges; fill: rgb(230, 0, 0)"
  128 + clip-path="url(#over)"
  129 + />
  130 + <circle
  131 + r="13"
  132 + cx="109"
  133 + cy="207"
  134 + style="fill: url('#thermometerdiv_meter_2'); stroke: rgb(230, 0, 0); stroke-width: 2px"
  135 + />
  136 + <foreignObject>
  137 + <div></div>
  138 + </foreignObject>
  139 + <g
  140 + class="thermometer-temperature-axis"
  141 + transform="translate(99.75,0)"
  142 + fill="none"
  143 + font-size="10"
  144 + font-family="sans-serif"
  145 + text-anchor="end"
  146 + >
  147 + <g class="tick" opacity="1" transform="translate(0,190)">
  148 + <line
  149 + stroke="currentColor"
  150 + x2="-7"
  151 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  152 + />
  153 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  154 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">-20</div>
  155 + </foreignObject>
  156 + </g>
  157 + <g class="tick" opacity="1" transform="translate(0,165)">
  158 + <line
  159 + stroke="currentColor"
  160 + x2="-7"
  161 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  162 + />
  163 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  164 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">0</div>
  165 + </foreignObject>
  166 + </g>
  167 + <g class="tick" opacity="1" transform="translate(0,140)">
  168 + <line
  169 + stroke="currentColor"
  170 + x2="-7"
  171 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  172 + />
  173 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  174 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">20</div>
  175 + </foreignObject>
  176 + </g>
  177 + <g class="tick" opacity="1" transform="translate(0,115)">
  178 + <line
  179 + stroke="currentColor"
  180 + x2="-7"
  181 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  182 + />
  183 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  184 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">40</div>
  185 + </foreignObject>
  186 + </g>
  187 + <g class="tick" opacity="1" transform="translate(0,90)">
  188 + <line
  189 + stroke="currentColor"
  190 + x2="-7"
  191 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  192 + />
  193 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  194 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">60</div>
  195 + </foreignObject>
  196 + </g>
  197 + <g class="tick" opacity="1" transform="translate(0,65)">
  198 + <line
  199 + stroke="currentColor"
  200 + x2="-7"
  201 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  202 + />
  203 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  204 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">80</div>
  205 + </foreignObject>
  206 + </g>
  207 + <g class="tick" opacity="1" transform="translate(0,40)">
  208 + <line
  209 + stroke="currentColor"
  210 + x2="-7"
  211 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  212 + />
  213 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  214 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">100</div>
  215 + </foreignObject>
  216 + </g>
  217 + <g class="tick" opacity="1" transform="translate(0,15)">
  218 + <line
  219 + stroke="currentColor"
  220 + x2="-7"
  221 + style="stroke: rgb(136, 136, 136); shape-rendering: crispEdges; stroke-width: 1px"
  222 + />
  223 + <foreignObject xmlns="http://www.w3.org/2000/svg" x="-55" y="-10" width="45" height="20">
  224 + <div class="tick-label" xmlns="http://www.w3.org/1999/xhtml">120</div>
  225 + </foreignObject>
  226 + </g>
  227 + </g>
  228 + </svg>
  229 + <!-- <div class="absolute w-full h-full flex justify-center items-center bg-transparent">
  230 + <div class="transform translate-x-full text-lg text-gray-500">
  231 + <span>{{ currentValue }}</span>
  232 + <span>{{ '℃' }}</span>
  233 + </div>
  234 + </div> -->
  235 + </main>
  236 +</template>
  237 +
  238 +<style scoped lang="less">
  239 + .tick-label {
  240 + font-size: 12px;
  241 + text-align: right;
  242 + overflow: hidden;
  243 + text-overflow: ellipsis;
  244 + color: #5b6b73;
  245 + }
  246 +
  247 + .thermometer-mercury-column {
  248 + transition: y 0.5s cubic-bezier(0.19, 1, 0.22, 1);
  249 + }
  250 +</style>
... ...
  1 +import { CircleFlowmeterConfig } from './CircleFlowmeter';
1 2 import { RectFlowmeteConfig } from './RectFlowmeter';
  3 +import { ThermometerConfig } from './Thermometer';
2 4
3   -export const FlowmeterList = [RectFlowmeteConfig];
  5 +export const FlowmeterList = [RectFlowmeteConfig, CircleFlowmeterConfig, ThermometerConfig];
... ...
  1 +import cloneDeep from 'lodash-es/cloneDeep';
  2 +import { MonitorVideoConfig } from '.';
  3 +import {
  4 + ConfigType,
  5 + CreateComponentType,
  6 + PublicComponentOptions,
  7 + PublicPresetOptions,
  8 +} from '/@/views/visual/packages/index.type';
  9 +import { PublicConfigClass, componentInitConfig } from '/@/views/visual/packages/publicConfig';
  10 +
  11 +export const option: PublicPresetOptions = {
  12 + componetDesign: false,
  13 +};
  14 +
  15 +export default class Config extends PublicConfigClass implements CreateComponentType {
  16 + public key: string = MonitorVideoConfig.key;
  17 +
  18 + public attr = { ...componentInitConfig };
  19 +
  20 + public componentConfig: ConfigType = cloneDeep(MonitorVideoConfig);
  21 +
  22 + public persetOption = cloneDeep(option);
  23 +
  24 + public option: PublicComponentOptions;
  25 +
  26 + constructor(option: PublicComponentOptions) {
  27 + super();
  28 + this.option = { ...option };
  29 + }
  30 +}
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentConfigFieldEnum } from '/@/views/visual/packages/enum';
  3 + import { useForm, BasicForm } from '/@/components/Form';
  4 + import { PublicFormInstaceType } from '/@/views/visual/dataSourceBindPanel/index.type';
  5 +
  6 + const [register, { getFieldsValue, setFieldsValue, resetFields }] = useForm({
  7 + schemas: [
  8 + {
  9 + field: ComponentConfigFieldEnum.FONT_COLOR,
  10 + label: '数值字体颜色',
  11 + component: 'ColorPicker',
  12 + changeEvent: 'update:value',
  13 + componentProps: {
  14 + defaultValue: '#FD7347',
  15 + },
  16 + },
  17 + {
  18 + field: ComponentConfigFieldEnum.SHOW_DEVICE_NAME,
  19 + label: '显示设备名称',
  20 + component: 'Checkbox',
  21 + },
  22 + ],
  23 + showActionButtonGroup: false,
  24 + labelWidth: 120,
  25 + baseColProps: {
  26 + span: 12,
  27 + },
  28 + });
  29 +
  30 + const getFormValues = () => {
  31 + return getFieldsValue();
  32 + };
  33 +
  34 + const setFormValues = (data: Recordable) => {
  35 + return setFieldsValue(data);
  36 + };
  37 +
  38 + defineExpose({
  39 + getFormValues,
  40 + setFormValues,
  41 + resetFormValues: resetFields,
  42 + } as PublicFormInstaceType);
  43 +</script>
  44 +
  45 +<template>
  46 + <BasicForm @register="register" />
  47 +</template>
... ...
  1 +import { getCameraList } from '/@/api/camera/cameraManager';
  2 +import { CameraModel } from '/@/api/camera/model/cameraModel';
  3 +import { FormSchema } from '/@/components/Form';
  4 +
  5 +export enum FormFieldEnum {
  6 + VIDEO_TYPE = 'type',
  7 + VIDEO_PLAY_URL = 'url',
  8 + VIDEO_CONFIG = 'videoConfig',
  9 + ACCESS_MODE = 'accessMode',
  10 + VIDEO_ID = 'id',
  11 + ORGANIZATION_ID = 'organizationId',
  12 +}
  13 +
  14 +export interface DataSourceValueType {
  15 + [FormFieldEnum.VIDEO_TYPE]?: string;
  16 + [FormFieldEnum.VIDEO_PLAY_URL]?: string;
  17 + [FormFieldEnum.ACCESS_MODE]?: number;
  18 + [FormFieldEnum.VIDEO_ID]?: string;
  19 + [FormFieldEnum.ORGANIZATION_ID]?: string;
  20 +}
  21 +
  22 +export enum VideoOriginalEnum {
  23 + CUSTOM = 'CUSTOM',
  24 + VIDEO_MANAGE = 'VIDEO_MANAGE',
  25 +}
  26 +
  27 +export enum VideoOriginalNameEnum {
  28 + CUSTOM = '自定义',
  29 + VIDEO_MANAGE = '流媒体获取',
  30 +}
  31 +
  32 +export const formSchemas: FormSchema[] = [
  33 + {
  34 + field: FormFieldEnum.VIDEO_TYPE,
  35 + component: 'Select',
  36 + label: '视频来源',
  37 + colProps: { span: 8 },
  38 + defaultValue: VideoOriginalEnum.CUSTOM,
  39 + componentProps: {
  40 + placeholder: '请选择视频来源',
  41 + options: [
  42 + { label: VideoOriginalNameEnum[VideoOriginalEnum.CUSTOM], value: VideoOriginalEnum.CUSTOM },
  43 + {
  44 + label: VideoOriginalNameEnum[VideoOriginalEnum.VIDEO_MANAGE],
  45 + value: VideoOriginalEnum.VIDEO_MANAGE,
  46 + },
  47 + ],
  48 + },
  49 + },
  50 + {
  51 + field: FormFieldEnum.ACCESS_MODE,
  52 + component: 'InputNumber',
  53 + label: '视频流获取方式',
  54 + show: false,
  55 + },
  56 + {
  57 + field: FormFieldEnum.VIDEO_PLAY_URL,
  58 + component: 'Input',
  59 + label: '地址',
  60 + colProps: { span: 16 },
  61 + ifShow: ({ model }) => model[FormFieldEnum.VIDEO_TYPE] === VideoOriginalEnum.CUSTOM,
  62 + componentProps: {
  63 + placeholder: '请输入自定义地址',
  64 + },
  65 + },
  66 + {
  67 + field: FormFieldEnum.ORGANIZATION_ID,
  68 + component: 'OrgTreeSelect',
  69 + label: '组织',
  70 + colProps: { span: 8 },
  71 + ifShow: ({ model }) => model[FormFieldEnum.VIDEO_TYPE] === VideoOriginalEnum.VIDEO_MANAGE,
  72 + componentProps: {
  73 + showCreate: false,
  74 + allowClean: true,
  75 + },
  76 + },
  77 + {
  78 + field: FormFieldEnum.VIDEO_ID,
  79 + component: 'ApiSelect',
  80 + label: '地址',
  81 + colProps: { span: 8 },
  82 + ifShow: ({ model }) => model[FormFieldEnum.VIDEO_TYPE] === VideoOriginalEnum.VIDEO_MANAGE,
  83 + componentProps: ({ formActionType, formModel }) => {
  84 + const { setFieldsValue } = formActionType;
  85 + const organizationId = formModel[FormFieldEnum.ORGANIZATION_ID];
  86 + return {
  87 + placeholder: '请选择视频',
  88 + params: { organizationId },
  89 + api: getCameraList,
  90 + labelField: 'name',
  91 + valueField: 'id',
  92 + resultField: 'data',
  93 + onChange(value: string, options: CameraModel) {
  94 + console.log(options);
  95 + setFieldsValue({
  96 + [FormFieldEnum.ACCESS_MODE]: value ? (options || {}).accessMode : null,
  97 + [FormFieldEnum.VIDEO_PLAY_URL]: value ? (options || {}).videoUrl : null,
  98 + });
  99 + },
  100 + };
  101 + },
  102 + },
  103 +];
... ...
  1 +<script lang="ts" setup>
  2 + import { CreateComponentType } from '/@/views/visual/packages/index.type';
  3 + import { BasicForm, useForm } from '/@/components/Form';
  4 + import {
  5 + PublicComponentValueType,
  6 + PublicFormInstaceType,
  7 + } from '/@/views/visual/dataSourceBindPanel/index.type';
  8 + import { DataSourceValueType, FormFieldEnum, formSchemas } from './datasource.config';
  9 + import { DataSource, VideoConfigType } from '/@/views/visual/palette/types';
  10 +
  11 + defineProps<{
  12 + values: PublicComponentValueType;
  13 + componentConfig: CreateComponentType;
  14 + }>();
  15 +
  16 + const [register, { getFieldsValue, setFieldsValue, validate, resetFields }] = useForm({
  17 + labelWidth: 0,
  18 + showActionButtonGroup: false,
  19 + layout: 'horizontal',
  20 + labelCol: { span: 0 },
  21 + schemas: formSchemas,
  22 + });
  23 +
  24 + const getFormValues = () => {
  25 + const value = getFieldsValue() as DataSourceValueType;
  26 + return {
  27 + [FormFieldEnum.VIDEO_CONFIG]: value as VideoConfigType,
  28 + };
  29 + };
  30 +
  31 + const setFormValues = (record: DataSource) => {
  32 + const { videoConfig = {} } = record;
  33 + return setFieldsValue(videoConfig);
  34 + };
  35 +
  36 + defineExpose({
  37 + getFormValues,
  38 + setFormValues,
  39 + validate,
  40 + resetFormValues: resetFields,
  41 + } as PublicFormInstaceType);
  42 +</script>
  43 +
  44 +<template>
  45 + <BasicForm @register="register" />
  46 +</template>
... ...
  1 +import { useComponentKeys } from '/@/views/visual/packages/hook/useComponentKeys';
  2 +import { ConfigType, PackagesCategoryEnum } from '/@/views/visual/packages/index.type';
  3 +
  4 +const componentKeys = useComponentKeys('MonitorVideo');
  5 +
  6 +export const MonitorVideoConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '监控视频',
  9 + package: PackagesCategoryEnum.OTHER,
  10 +};
... ...
  1 +<script lang="ts" setup>
  2 + import { ComponentMode, ComponentPropsConfigType } from '/@/views/visual/packages/index.type';
  3 + import { option } from './config';
  4 + import { BasicVideoPlay, getVideoTypeByUrl } from '/@/components/Video';
  5 + import { computed } from 'vue';
  6 + import { VideoJsPlayerOptions } from 'video.js';
  7 + import { AccessMode } from '/@/views/camera/manage/config.data';
  8 + import { onMounted } from 'vue';
  9 + import { unref } from 'vue';
  10 + import { getStreamingPlayUrl } from '/@/api/camera/cameraManager';
  11 + import { ref } from 'vue';
  12 + import { Spin } from 'ant-design-vue';
  13 +
  14 + const props = defineProps<{
  15 + config: ComponentPropsConfigType<typeof option>;
  16 + }>();
  17 +
  18 + const loading = ref(true);
  19 +
  20 + const getIsSelectPreviewMode = computed(() => {
  21 + return props.config.option.mode === ComponentMode.SELECT_PREVIEW;
  22 + });
  23 +
  24 + const getIsStreamingMode = computed(() => {
  25 + const { option } = props.config;
  26 + const { videoConfig } = option;
  27 + return videoConfig?.accessMode === AccessMode.Streaming;
  28 + });
  29 +
  30 + const playSource = ref<Record<'src' | 'type', string>>(
  31 + {} as unknown as Record<'src' | 'type', string>
  32 + );
  33 +
  34 + const exampleVideoPlay =
  35 + 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm';
  36 +
  37 + const getVideoPlaySources = computed(() => {
  38 + const { option } = props.config;
  39 + const { videoConfig } = option;
  40 + const { url } = videoConfig || {};
  41 + return Object.assign(
  42 + {
  43 + src: unref(getIsSelectPreviewMode) ? exampleVideoPlay : url,
  44 + type: getVideoTypeByUrl(url || ''),
  45 + },
  46 + unref(playSource)
  47 + );
  48 + });
  49 +
  50 + const getOptions = computed<VideoJsPlayerOptions>(() => {
  51 + const { option } = props.config;
  52 + const { itemHeightRatio, itemWidthRatio, widthPx, heightPx } = option;
  53 + const currentW = widthPx * (itemWidthRatio / 100);
  54 + const currentH = heightPx * (itemHeightRatio / 100);
  55 + return {
  56 + width: currentW - 8,
  57 + height: currentH - 8,
  58 + sources: unref(getVideoPlaySources).src
  59 + ? ([unref(getVideoPlaySources)] as VideoJsPlayerOptions['sources'])
  60 + : [],
  61 + };
  62 + });
  63 +
  64 + const handleGetVideoPlayUrl = async () => {
  65 + try {
  66 + if (unref(getIsStreamingMode) && !unref(getVideoPlaySources).src) {
  67 + const { option } = props.config;
  68 + const { videoConfig } = option;
  69 + if (!videoConfig?.id) return;
  70 + const { data: { url } = { url: '' } } = await getStreamingPlayUrl(videoConfig?.id);
  71 + playSource.value = {
  72 + src: url,
  73 + type: getVideoTypeByUrl(url),
  74 + };
  75 + }
  76 + } finally {
  77 + loading.value = false;
  78 + }
  79 + };
  80 +
  81 + onMounted(() => {
  82 + handleGetVideoPlayUrl();
  83 + });
  84 +</script>
  85 +
  86 +<template>
  87 + <main class="w-full h-full flex flex-col justify-center items-center p-2">
  88 + <Spin :spinning="loading" wrapper-class-name="video-spin">
  89 + <BasicVideoPlay :options="getOptions" />
  90 + </Spin>
  91 + </main>
  92 +</template>
  93 +
  94 +<style lang="less" scoped>
  95 + .video-spin {
  96 + @apply w-full h-full;
  97 +
  98 + :deep(.ant-spin-container) {
  99 + @apply w-full h-full;
  100 + }
  101 + }
  102 +</style>
... ...
  1 +import { MonitorVideoConfig } from './MonitorVideo';
  2 +
  3 +export const OtherList = [MonitorVideoConfig];
... ...
1 1 import { createComponent } from '..';
2 2 import { transformComponentKey } from '../componentMap';
3   -import { ConfigType, CreateComponentType } from '../index.type';
  3 +import { ConfigType, CreateComponentType, PublicComponentOptions } from '../index.type';
4 4
5 5 export const useGetComponentConfig = (
6 6 key: string,
7   - options: Recordable = {}
  7 + options: Partial<PublicComponentOptions> = {}
8 8 ): CreateComponentType => {
9 9 try {
10 10 const config = createComponent({ key: transformComponentKey(key) } as ConfigType, options);
... ...
... ... @@ -33,7 +33,7 @@ export enum PackagesCategoryEnum {
33 33 CONTROL = 'CONTROL',
34 34 MAP = 'MAP',
35 35 FLOWMETER = 'FLOWMETER',
36   - OTHER = 'FLOWMETER',
  36 + OTHER = 'OTHER',
37 37 }
38 38
39 39 /**
... ... @@ -98,12 +98,25 @@ export interface PublicPresetOptions {
98 98 [key: string]: any;
99 99 }
100 100
  101 +export enum ComponentMode {
  102 + /**
  103 + * @description 选择预览
  104 + */
  105 + SELECT_PREVIEW = 'SELECT_PREVIEW',
  106 +
  107 + /**
  108 + * @description 绑定数据源后
  109 + */
  110 + BOUND_PREVIEW = 'BOUND_PREVIEW',
  111 +}
  112 +
101 113 export interface PublicComponentOptions {
102 114 uuid: string;
103 115 widthPx: number;
104 116 heightPx: number;
105 117 itemWidthRatio: number;
106 118 itemHeightRatio: number;
  119 + mode: ComponentMode;
107 120
108 121 dataSource?: DataSource[];
109 122 [key: string]: any;
... ...
1 1 import { ControlList } from './components/Control';
2   -// import { FlowmeterList } from './components/Flowmeter';
  2 +import { FlowmeterList } from './components/Flowmeter';
3 3 import { InstrumentList } from './components/Instrument';
4 4 import { MapList } from './components/Map';
  5 +import { OtherList } from './components/Other';
5 6 import { PictureList } from './components/Picture';
6 7 import { TextList } from './components/Text';
7 8 import { PackagesCategoryEnum, PackagesType } from './index.type';
... ... @@ -12,5 +13,6 @@ export const packageList: PackagesType = {
12 13 [PackagesCategoryEnum.PICTURE]: PictureList,
13 14 [PackagesCategoryEnum.CONTROL]: ControlList,
14 15 [PackagesCategoryEnum.MAP]: MapList,
15   - // [PackagesCategoryEnum.FLOWMETER]: FlowmeterList,
  16 + [PackagesCategoryEnum.FLOWMETER]: FlowmeterList,
  17 + [PackagesCategoryEnum.OTHER]: OtherList,
16 18 };
... ...
... ... @@ -2,7 +2,7 @@
2 2 import { computed } from 'vue';
3 3 import { WidgetDataType } from '../../hooks/useDataSource';
4 4 import { fetchViewComponent } from '../../../packages';
5   - import { ConfigType } from '/@/views/visual/packages/index.type';
  5 + import { ComponentMode, ConfigType } from '/@/views/visual/packages/index.type';
6 6 import { ExtraDataSource } from '../../types';
7 7 import { CSSProperties } from 'vue';
8 8 import { toRaw } from 'vue';
... ... @@ -36,6 +36,7 @@
36 36 heightPx,
37 37 itemWidthRatio,
38 38 itemHeightRatio,
  39 + mode: ComponentMode.BOUND_PREVIEW,
39 40 });
40 41 });
41 42
... ...
... ... @@ -26,6 +26,7 @@ export interface DataSource {
26 26 attributeRename: string;
27 27 componentInfo: ComponentInfo;
28 28 customCommand: CustomCommand;
  29 + videoConfig?: VideoConfigType;
29 30 }
30 31
31 32 export interface ExtraDataSource extends DataSource, PublicComponentOptions {
... ... @@ -78,3 +79,11 @@ export interface ApiDataBoardDataType {
78 79 export interface ApiDataBoardInfoType {
79 80 data: ApiDataBoardDataType;
80 81 }
  82 +
  83 +export interface VideoConfigType {
  84 + organizationId?: string;
  85 + id?: string;
  86 + accessMode?: number;
  87 + type?: string;
  88 + url?: string;
  89 +}
... ...
... ... @@ -4,6 +4,7 @@
4 4 import { computed, ref } from 'vue';
5 5 import { Tabs } from 'ant-design-vue';
6 6 import {
  7 + ComponentMode,
7 8 ConfigType,
8 9 PackagesCategoryEnum,
9 10 PackagesCategoryNameEnum,
... ... @@ -41,7 +42,7 @@
41 42 });
42 43
43 44 const getBindConfig = (key: string) => {
44   - const config = createComponent({ key } as ConfigType);
  45 + const config = createComponent({ key } as ConfigType, { mode: ComponentMode.SELECT_PREVIEW });
45 46 return {
46 47 config,
47 48 };
... ...