Commit 8439fe09efe1017840b81c0280eafe3631b64abc

Authored by ww
1 parent d73d1d8c

feat: 新增流量计组件与视频组件

  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,12 +10,12 @@
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 } = componentInfo || {};
19 19 const { backgroundColor, waveFirst, waveSecond, waveThird } = flowmeterConfig || {};
20 20 const { flowmeterConfig: presetFlowmeterConfig, unit: persetUnit } = persetOption || {};
21 21 const {
... ... @@ -54,7 +54,7 @@
54 54 </script>
55 55
56 56 <template>
57   - <main class="w-full h-full flex flex-col justify-center items-center">
  57 + <main class="w-full h-full flex flex-col justify-center items-center relative">
58 58 <svg
59 59 class="waves-rect"
60 60 viewBox="0 0 100 100"
... ... @@ -90,21 +90,13 @@
90 90 height="100"
91 91 :fill="getDesign.waveThird"
92 92 />
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 93 </svg>
  94 + <div
  95 + class="absolute w-full h-full top-0 left-0 text-center text-lg flex items-center justify-center flex-col"
  96 + >
  97 + <div>{{ currentValue }}</div>
  98 + <div class="ml-1">{{ getDesign.unit }}</div>
  99 + </div>
108 100 </main>
109 101 </template>
110 102
... ...
  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 +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 = MonitorVideoConfig.key;
  19 +
  20 + public attr = { ...componentInitConfig };
  21 +
  22 + public componentConfig: ConfigType = cloneDeep(MonitorVideoConfig);
  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('MonitorVideo');
  5 +
  6 +export const MonitorVideoConfig: ConfigType = {
  7 + ...componentKeys,
  8 + title: '监控视频',
  9 + package: PackagesCategoryEnum.OTHER,
  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 { BasicVideoPlay } from '/@/components/Video';
  6 + import { computed } from 'vue';
  7 + import { VideoJsPlayerOptions } from 'video.js';
  8 +
  9 + const props = defineProps<{
  10 + config: ComponentPropsConfigType<typeof option>;
  11 + }>();
  12 +
  13 + const getOptions = computed<VideoJsPlayerOptions>(() => {
  14 + const { option } = props.config;
  15 + const { itemHeightRatio, itemWidthRatio, widthPx, heightPx } = option;
  16 + const currentW = widthPx * (itemWidthRatio / 100);
  17 + const currentH = heightPx * (itemHeightRatio / 100);
  18 + return {
  19 + width: currentW - 8,
  20 + height: currentH - 8,
  21 + sources: [
  22 + {
  23 + src: 'https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.webm',
  24 + },
  25 + ],
  26 + };
  27 + });
  28 +
  29 + const updateFn: DataFetchUpdateFn = (_message) => {};
  30 +
  31 + useDataFetch(props, updateFn);
  32 +</script>
  33 +
  34 +<template>
  35 + <main class="w-full h-full flex flex-col justify-center items-center p-2">
  36 + <BasicVideoPlay :options="getOptions" />
  37 + </main>
  38 +</template>
... ...
  1 +import { MonitorVideoConfig } from './MonitorVideo';
  2 +
  3 +export const OtherList = [MonitorVideoConfig];
... ...
... ... @@ -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 /**
... ...
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 };
... ...