Commit fabfa424903a2f2dff9a7a4f2811d770bf603e8f

Authored by ww
1 parent aa0a85c1

feat: implement data component control component

... ... @@ -8,6 +8,7 @@ import {
8 8 DeviceAttributeRecord,
9 9 GetDataBoardParams,
10 10 MasterDeviceList,
  11 + SendCommandParams,
11 12 UpdateDataBoardLayoutParams,
12 13 UpdateDataBoardParams,
13 14 UpdateDataComponentParams,
... ... @@ -33,6 +34,10 @@ enum DataBoardShareUrl {
33 34 GET_DATA_COMPONENT = '/noauth/share/data_board',
34 35 }
35 36
  37 +enum SendCommand {
  38 + ONEWAY = '/plugins/rpc/oneway',
  39 +}
  40 +
36 41 enum DeviceUrl {
37 42 GET_DEVICE = '/device/list/master',
38 43 GET_SLAVE_DEVICE = '/device/list/slave',
... ... @@ -194,3 +199,13 @@ export const getDeviceAttributes = (params: DeviceAttributeParams) => {
194 199 },
195 200 });
196 201 };
  202 +
  203 +export const sendCommandOneway = (params: SendCommandParams) => {
  204 + return defHttp.post(
  205 + {
  206 + url: `${SendCommand.ONEWAY}/${params.deviceId}`,
  207 + params: params.value,
  208 + },
  209 + { joinPrefix: false }
  210 + );
  211 +};
... ...
... ... @@ -156,3 +156,8 @@ export interface DeviceAttributeRecord {
156 156 identifier: string;
157 157 detail: DataType;
158 158 }
  159 +
  160 +export interface SendCommandParams {
  161 + deviceId: string;
  162 + value: any;
  163 +}
... ...
... ... @@ -5,8 +5,10 @@
5 5 </script>
6 6 <script lang="ts" setup>
7 7 import { RadioRecord } from '../../detail/config/util';
  8 + import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config';
  9 + import { useSendCommand } from './useSendCommand';
8 10
9   - interface VisualComponentProps<Layout = Recordable, Value = Recordable> {
  11 + interface VisualComponentProps<Layout = Recordable, Value = ControlComponentValue> {
10 12 value?: Value;
11 13 layout?: Layout;
12 14 radio?: RadioRecord;
... ... @@ -15,29 +17,40 @@
15 17 update?: () => void;
16 18 remove?: (key: string) => void;
17 19 }
  20 +
18 21 const props = defineProps<VisualComponentProps>();
19 22
20 23 const emit = defineEmits(['update:value', 'change']);
21 24
  25 + const { sendCommand } = useSendCommand();
22 26 const handleChange = (event: Event) => {
23 27 const _value = (event.target as HTMLInputElement).checked;
24 28 emit('update:value', _value);
25 29 emit('change', _value);
  30 + sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value);
26 31 };
27 32 </script>
28 33
29 34 <template>
30   - <label class="sliding-switch">
31   - <input
32   - :value="props.value?.value"
33   - type="checkbox"
34   - :checked="props.value?.value"
35   - @change="handleChange"
36   - />
37   - <span class="slider"></span>
38   - <span class="on">ON</span>
39   - <span class="off">OFF</span>
40   - </label>
  35 + <div class="flex flex-col justify-center">
  36 + <label class="sliding-switch">
  37 + <input
  38 + :value="props.value?.value"
  39 + type="checkbox"
  40 + :checked="props.value?.value"
  41 + @change="handleChange"
  42 + />
  43 + <span class="slider"></span>
  44 + <span class="on">ON</span>
  45 + <span class="off">OFF</span>
  46 + </label>
  47 + <div
  48 + class="text-center mt-2 text-gray-700"
  49 + :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }"
  50 + >
  51 + {{ props.value?.attributeRename || props.value?.attribute }}</div
  52 + >
  53 + </div>
41 54 </template>
42 55
43 56 <style scoped lang="less">
... ...
... ... @@ -5,7 +5,7 @@
5 5 </script>
6 6 <script lang="ts" setup>
7 7 import { Switch } from 'ant-design-vue';
8   - import { computed } from 'vue';
  8 + import { computed, ref, watchEffect } from 'vue';
9 9 import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util';
10 10 import SvgIcon from '/@/components/Icon/src/SvgIcon.vue';
11 11 import {
... ... @@ -13,6 +13,7 @@
13 13 ControlComponentValue,
14 14 ControlComponentLayout,
15 15 } from './control.config';
  16 + import { useSendCommand } from './useSendCommand';
16 17 const props = withDefaults(
17 18 defineProps<{
18 19 layout?: ControlComponentLayout;
... ... @@ -26,6 +27,18 @@
26 27 const getRadio = computed(() => {
27 28 return props.radio || DEFAULT_RADIO_RECORD;
28 29 });
  30 +
  31 + const checked = ref(!!Number(props.value.value));
  32 +
  33 + const { sendCommand } = useSendCommand();
  34 + const handleChange = (value: boolean) => {
  35 + console.log(props.value);
  36 + sendCommand(props.value.slaveDeviceId! || props.value.deviceId!, value);
  37 + };
  38 +
  39 + watchEffect(() => {
  40 + checked.value = !!Number(props.value.value);
  41 + });
29 42 </script>
30 43
31 44 <template>
... ... @@ -40,8 +53,13 @@
40 53 height: fontSize({ radioRecord: getRadio, basic: 30, min: 16 }),
41 54 }"
42 55 />
43   - <span class="flex-auto mx-4 flex items-center truncate inline-block">属性名</span>
  56 + <span
  57 + class="flex-auto mx-4 flex items-center truncate inline-block text-gray-700"
  58 + :style="{ color: props.value.fontColor || ControlComponentDefaultConfig.fontColor }"
  59 + >
  60 + {{ props.value.attributeRename || props.value.attribute }}
  61 + </span>
44 62 </div>
45   - <Switch />
  63 + <Switch v-model:checked="checked" @change="handleChange" />
46 64 </div>
47 65 </template>
... ...
... ... @@ -6,7 +6,8 @@
6 6 <script lang="ts" setup>
7 7 import { computed } from '@vue/reactivity';
8 8 import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util';
9   - import { ControlComponentValue } from './control.config';
  9 + import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config';
  10 + import { useSendCommand } from './useSendCommand';
10 11
11 12 const props = defineProps<{
12 13 value?: ControlComponentValue;
... ... @@ -16,10 +17,12 @@
16 17
17 18 const emit = defineEmits(['update:value', 'change']);
18 19
  20 + const { sendCommand } = useSendCommand();
19 21 const handleChange = (event: Event) => {
20 22 const _value = (event.target as HTMLInputElement).checked;
21 23 emit('update:value', _value);
22 24 emit('change', _value);
  25 + sendCommand(props.value?.slaveDeviceId || props.value?.deviceId, _value);
23 26 };
24 27
25 28 const getRadio = computed(() => {
... ... @@ -28,28 +31,36 @@
28 31 </script>
29 32
30 33 <template>
31   - <div
32   - class="toggle-switch"
33   - :style="{
34   - width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }),
35   - height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }),
36   - }"
37   - >
38   - <label class="switch">
39   - <input
40   - :value="props.value?.value"
41   - type="checkbox"
42   - :checked="props.value?.value"
43   - @change="handleChange"
44   - />
45   - <div class="button">
46   - <div class="light"></div>
47   - <div class="dots"></div>
48   - <div class="characters"></div>
49   - <div class="shine"></div>
50   - <div class="shadow"></div>
51   - </div>
52   - </label>
  34 + <div class="flex flex-col">
  35 + <div
  36 + class="toggle-switch"
  37 + :style="{
  38 + width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }),
  39 + height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }),
  40 + }"
  41 + >
  42 + <label class="switch">
  43 + <input
  44 + :value="props.value?.value"
  45 + type="checkbox"
  46 + :checked="props.value?.value"
  47 + @change="handleChange"
  48 + />
  49 + <div class="button">
  50 + <div class="light"></div>
  51 + <div class="dots"></div>
  52 + <div class="characters"></div>
  53 + <div class="shine"></div>
  54 + <div class="shadow"></div>
  55 + </div>
  56 + </label>
  57 + </div>
  58 + <div
  59 + class="text-center mt-2 text-gray-700"
  60 + :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }"
  61 + >
  62 + {{ props.value?.attributeRename || props.value?.attribute }}</div
  63 + >
53 64 </div>
54 65 </template>
55 66
... ...
... ... @@ -6,14 +6,19 @@ export interface ControlComponentLayout {
6 6
7 7 export interface ControlComponentValue {
8 8 value?: boolean;
9   - name?: string;
  9 + attribute?: string;
  10 + attributeRename?: string;
10 11 icon?: string;
11 12 iconColor?: string;
  13 + deviceId?: string;
  14 + fontColor?: string;
  15 + slaveDeviceId?: string;
12 16 }
13 17
14 18 export const ControlComponentDefaultConfig: ControlComponentValue = {
15 19 icon: 'shuiwen',
16 20 iconColor: '#367BFF',
  21 + fontColor: '#000',
17 22 };
18 23
19 24 export const transformControlConfig = (
... ... @@ -23,8 +28,11 @@ export const transformControlConfig = (
23 28 ) => {
24 29 return {
25 30 value: {
26   - value: dataSourceRecord.componentInfo.value,
27   - icon: dataSourceRecord.componentInfo.icon,
  31 + ...dataSourceRecord.componentInfo,
  32 + attribute: dataSourceRecord.attribute,
  33 + attributeRename: dataSourceRecord.attributeRename,
  34 + deviceId: dataSourceRecord.deviceId,
  35 + slaveDeviceId: dataSourceRecord.slaveDeviceId,
28 36 } as ControlComponentValue,
29 37 };
30 38 };
... ...
1   -export function useSendCommand() {}
  1 +import { sendCommandOneway } from '/@/api/dataBoard';
  2 +import { useMessage } from '/@/hooks/web/useMessage';
  3 +
  4 +const { createMessage } = useMessage();
  5 +export function useSendCommand() {
  6 + const sendCommand = async (deviceId: string, value: any) => {
  7 + if (!deviceId) return;
  8 + try {
  9 + await sendCommandOneway({
  10 + deviceId,
  11 + value: {
  12 + params: Number(value),
  13 + persistent: true,
  14 + additionalInfo: {
  15 + cmdType: 'API',
  16 + },
  17 + method: 'methodThingskit',
  18 + },
  19 + });
  20 + createMessage.success('命令下发成功');
  21 + } catch (error) {}
  22 + };
  23 + return {
  24 + sendCommand,
  25 + };
  26 +}
... ...
... ... @@ -146,7 +146,7 @@ frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, {
146 146 ComponentCategory: FrontComponentCategory.CONTROL,
147 147 isMultipleDataSource: false,
148 148 hasHistoryTrend: true,
149   - hasSetting: false,
  149 + hasSetting: true,
150 150 transformConfig: transformControlConfig,
151 151 });
152 152
... ...
... ... @@ -274,7 +274,11 @@
274 274 选择设备
275 275 </div>
276 276 <div class="pl-2 flex-auto">
277   - <component :is="dataSourceComponent" :ref="(el) => setFormEl(el, item.id)" />
  277 + <component
  278 + :frontId="$props.frontId"
  279 + :is="dataSourceComponent"
  280 + :ref="(el) => setFormEl(el, item.id)"
  281 + />
278 282 </div>
279 283 <div class="flex justify-center gap-3 w-28">
280 284 <Tooltip title="复制">
... ...
1 1 <script lang="ts" setup>
2 2 import { ref } from 'vue';
  3 + import { FrontComponent } from '../../../const/const';
3 4 import { dataSourceSchema } from '../../config/basicConfiguration';
4 5 import { FormActionType } from '/@/components/Form';
5 6 import BasicForm from '/@/components/Form/src/BasicForm.vue';
6 7 const formEl = ref<Nullable<FormActionType>>(null);
7 8
  9 + defineProps<{
  10 + frontId?: FrontComponent;
  11 + }>();
  12 +
8 13 defineExpose({ formActionType: formEl });
9 14 </script>
10 15
11 16 <template>
12 17 <BasicForm
13 18 ref="formEl"
14   - :schemas="dataSourceSchema"
  19 + :schemas="dataSourceSchema($props.frontId)"
15 20 class="w-full flex-1 data-source-form"
16 21 :show-action-button-group="false"
17 22 :row-props="{
... ...
1 1 <script lang="ts" setup>
2 2 import { ref, unref } from 'vue';
3 3 import { BasicForm, FormActionType } from '/@/components/Form';
4   - import { controlFormSchema } from '../../config/basicConfiguration';
  4 + import { dataSourceSchema } from '../../config/basicConfiguration';
  5 + import { FrontComponent } from '../../../const/const';
  6 +
  7 + defineProps<{
  8 + frontId?: FrontComponent;
  9 + }>();
5 10
6 11 const formEl = ref<Nullable<FormActionType>>();
7 12
... ... @@ -33,7 +38,7 @@
33 38 <div class="w-full flex-1">
34 39 <BasicForm
35 40 :ref="(el) => setFormEl(el)"
36   - :schemas="controlFormSchema"
  41 + :schemas="dataSourceSchema($props.frontId)"
37 42 class="w-full flex-1 data-source-form"
38 43 :show-action-button-group="false"
39 44 :row-props="{
... ...
1   -<script lang="ts" setup>
2   - import { ref, unref } from 'vue';
3   - import { BasicForm, FormActionType } from '/@/components/Form';
4   - import { mapFormSchema } from '../../config/basicConfiguration';
5   -
6   - const formEl = ref<Nullable<FormActionType>>();
7   -
8   - const setFormEl = (el: any) => {
9   - formEl.value = el;
10   - };
11   -
12   - const getFieldsValue = () => {
13   - return unref(formEl)!.getFieldsValue();
14   - };
15   -
16   - const validate = async () => {
17   - await unref(formEl)!.validate();
18   - };
19   -
20   - const setFieldsValue = async (record: Recordable) => {
21   - await unref(formEl)!.setFieldsValue(record);
22   - };
23   -
24   - const clearValidate = async (name?: string | string[]) => {
25   - await unref(formEl)!.clearValidate(name);
26   - };
27   - defineExpose({
28   - formActionType: { getFieldsValue, validate, setFieldsValue, clearValidate },
29   - });
30   -</script>
31   -
32   -<template>
33   - <div class="w-full flex-1">
34   - <BasicForm
35   - :ref="(el) => setFormEl(el)"
36   - :schemas="mapFormSchema"
37   - class="w-full flex-1 data-source-form"
38   - :show-action-button-group="false"
39   - :row-props="{
40   - gutter: 10,
41   - }"
42   - layout="horizontal"
43   - :label-col="{ span: 0 }"
44   - />
45   - </div>
46   -</template>
1 1 import { Component } from 'vue';
2 2 import { FrontComponent } from '../../../const/const';
3 3 import BasicDataSourceForm from './BasicDataSourceForm.vue';
4   -import ControlDataSourceForm from './ControlDataSourceForm.vue';
  4 +// import ControlDataSourceForm from './ControlDataSourceForm.vue';
5 5
6 6 const dataSourceComponentMap = new Map<FrontComponent, Component>();
7 7
8   -dataSourceComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, ControlDataSourceForm);
9 8 export const getDataSourceComponent = (frontId: FrontComponent) => {
10 9 if (dataSourceComponentMap.has(frontId)) return dataSourceComponentMap.get(frontId)!;
11 10 return BasicDataSourceForm;
... ...
... ... @@ -2,18 +2,17 @@ import { getAllDeviceByOrg, getDeviceAttributes, getGatewaySlaveDevice } from '/
2 2 import { getOrganizationList } from '/@/api/system/system';
3 3 import { FormSchema } from '/@/components/Form';
4 4 import { copyTransFun } from '/@/utils/fnUtils';
5   -import { OnChangeHookParams } from '/@/components/Form/src/components/ApiSearchSelect.vue';
6   -import { unref } from 'vue';
7   -import { MasterDeviceList } from '/@/api/dataBoard/model';
  5 +import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model';
  6 +import { FrontComponent } from '../../const/const';
8 7
9 8 export enum BasicConfigField {
10 9 NAME = 'name',
11 10 REMARK = 'remark',
12 11 }
13 12
14   -const getDeviceAttribute = async (deviceProfileId: string) => {
  13 +const getDeviceAttribute = async (params: DeviceAttributeParams) => {
15 14 try {
16   - const data = await getDeviceAttributes({ deviceProfileId });
  15 + const data = await getDeviceAttributes(params);
17 16 if (data) return data.map((item) => ({ label: item.name, value: item.identifier }));
18 17 } catch (error) {}
19 18 return [];
... ... @@ -34,6 +33,18 @@ export enum DataSourceField {
34 33 LATITUDE_ATTRIBUTE = 'latitudeAttribute',
35 34 }
36 35
  36 +const isControlComponent = (frontId: FrontComponent) => {
  37 + const list = [
  38 + FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH,
  39 + FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON,
  40 + FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH,
  41 + ];
  42 + if (list.includes(frontId)) {
  43 + return true;
  44 + }
  45 + return false;
  46 +};
  47 +
37 48 export const basicSchema: FormSchema[] = [
38 49 {
39 50 field: BasicConfigField.NAME,
... ... @@ -55,420 +66,214 @@ export const basicSchema: FormSchema[] = [
55 66 },
56 67 ];
57 68
58   -export const dataSourceSchema: FormSchema[] = [
59   - {
60   - field: DataSourceField.IS_GATEWAY_DEVICE,
61   - component: 'Switch',
62   - label: '是否是网关设备',
63   - show: false,
64   - },
65   - {
66   - field: DataSourceField.DEVICE_NAME,
67   - component: 'Input',
68   - label: '设备名',
69   - show: false,
70   - },
71   - {
72   - field: DataSourceField.ORIGINATION_ID,
73   - component: 'ApiTreeSelect',
74   - label: '组织',
75   - colProps: { span: 8 },
76   - rules: [{ required: true, message: '组织为必填项' }],
77   - componentProps({ formActionType }) {
78   - const { setFieldsValue } = formActionType;
79   - return {
80   - placeholder: '请选择组织',
81   - api: async () => {
82   - const data = await getOrganizationList();
83   - copyTransFun(data as any as any[]);
84   - return data;
85   - },
86   - onChange() {
87   - setFieldsValue({
88   - [DataSourceField.DEVICE_ID]: null,
89   - [DataSourceField.ATTRIBUTE]: null,
90   - [DataSourceField.SLAVE_DEVICE_ID]: null,
91   - [DataSourceField.IS_GATEWAY_DEVICE]: false,
92   - [DataSourceField.DEVICE_PROFILE_ID]: null,
93   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
94   - });
95   - },
96   - getPopupContainer: () => document.body,
97   - };
  69 +export const dataSourceSchema = (frontId?: FrontComponent): FormSchema[] => {
  70 + return [
  71 + {
  72 + field: DataSourceField.IS_GATEWAY_DEVICE,
  73 + component: 'Switch',
  74 + label: '是否是网关设备',
  75 + show: false,
98 76 },
99   - },
100   - {
101   - field: DataSourceField.DEVICE_PROFILE_ID,
102   - component: 'Input',
103   - label: '',
104   - show: false,
105   - },
106   - {
107   - field: DataSourceField.DEVICE_ID,
108   - component: 'ApiSelect',
109   - label: '设备',
110   - colProps: { span: 8 },
111   - rules: [{ required: true, message: '设备名称为必填项' }],
112   - componentProps({ formModel, formActionType }) {
113   - const { setFieldsValue } = formActionType;
114   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
115   - const deviceId = formModel[DataSourceField.DEVICE_ID];
116   - return {
117   - api: async () => {
118   - if (organizationId) {
119   - try {
120   - const data = await getAllDeviceByOrg(organizationId);
121   - if (deviceId) {
122   - const record = data.find((item) => item.id === deviceId);
123   - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
124   - }
125   - if (data)
126   - return data.map((item) => ({
127   - ...item,
128   - label: item.name,
129   - value: item.id,
130   - deviceType: item.deviceType,
131   - }));
132   - } catch (error) {}
133   - }
134   - return [];
135   - },
136   -
137   - onChange(_value, record: MasterDeviceList) {
138   - setFieldsValue({
139   - [DataSourceField.ATTRIBUTE]: null,
140   - [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',
141   - [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId,
142   - [DataSourceField.SLAVE_DEVICE_ID]: null,
143   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
144   - [DataSourceField.DEVICE_NAME]: record?.label,
145   - });
146   - },
147   - placeholder: '请选择设备',
148   - getPopupContainer: () => document.body,
149   - };
  77 + {
  78 + field: DataSourceField.DEVICE_NAME,
  79 + component: 'Input',
  80 + label: '设备名',
  81 + show: false,
150 82 },
151   - },
152   - {
153   - field: DataSourceField.SLAVE_DEVICE_PROFILE_ID,
154   - component: 'Input',
155   - label: '',
156   - show: false,
157   - },
158   - {
159   - field: DataSourceField.SLAVE_DEVICE_ID,
160   - label: '网关子设备',
161   - component: 'ApiSelect',
162   - colProps: { span: 8 },
163   - rules: [{ required: true, message: '网关子设备为必填项' }],
164   - ifShow({ model }) {
165   - return model[DataSourceField.IS_GATEWAY_DEVICE];
  83 + {
  84 + field: DataSourceField.ORIGINATION_ID,
  85 + component: 'ApiTreeSelect',
  86 + label: '组织',
  87 + colProps: { span: 8 },
  88 + rules: [{ required: true, message: '组织为必填项' }],
  89 + componentProps({ formActionType }) {
  90 + const { setFieldsValue } = formActionType;
  91 + return {
  92 + placeholder: '请选择组织',
  93 + api: async () => {
  94 + const data = await getOrganizationList();
  95 + copyTransFun(data as any as any[]);
  96 + return data;
  97 + },
  98 + onChange() {
  99 + setFieldsValue({
  100 + [DataSourceField.DEVICE_ID]: null,
  101 + [DataSourceField.ATTRIBUTE]: null,
  102 + [DataSourceField.SLAVE_DEVICE_ID]: null,
  103 + [DataSourceField.IS_GATEWAY_DEVICE]: false,
  104 + [DataSourceField.DEVICE_PROFILE_ID]: null,
  105 + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
  106 + });
  107 + },
  108 + getPopupContainer: () => document.body,
  109 + };
  110 + },
166 111 },
167   - dynamicRules({ model }) {
168   - return [{ required: model[DataSourceField.IS_GATEWAY_DEVICE], message: '请选择网关子设备' }];
  112 + {
  113 + field: DataSourceField.DEVICE_PROFILE_ID,
  114 + component: 'Input',
  115 + label: '',
  116 + show: false,
169 117 },
170   - componentProps({ formModel, formActionType }) {
171   - const { setFieldsValue } = formActionType;
172   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
173   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
174   - const deviceId = formModel[DataSourceField.DEVICE_ID];
175   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
176   - return {
177   - api: async () => {
178   - if (organizationId && isGatewayDevice) {
179   - try {
180   - const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });
181   - if (slaveDeviceId) {
182   - const record = data.find((item) => item.id === slaveDeviceId);
183   - setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
184   - }
185   - if (data)
186   - return data.map((item) => ({
187   - ...item,
188   - label: item.name,
189   - value: item.id,
190   - deviceType: item.deviceType,
191   - }));
192   - } catch (error) {}
193   - }
194   - return [];
195   - },
196   - onChange(_value, record: MasterDeviceList) {
197   - setFieldsValue({
198   - [DataSourceField.ATTRIBUTE]: null,
199   - [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: record.deviceProfileId,
200   - [DataSourceField.DEVICE_NAME]: record?.label,
201   - });
202   - },
203   - placeholder: '请选择网关子设备',
204   - getPopupContainer: () => document.body,
205   - };
206   - },
207   - },
208   - {
209   - field: DataSourceField.ATTRIBUTE,
210   - component: 'ApiSelect',
211   - label: '属性',
212   - colProps: { span: 8 },
213   - rules: [{ required: true, message: '属性为必填项' }],
214   - componentProps({ formModel }) {
215   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
216   - const deviceId = formModel[DataSourceField.DEVICE_ID];
217   - const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
218   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
219   - const slaveDeviceProfileId = formModel[DataSourceField.SLAVE_DEVICE_PROFILE_ID];
  118 + {
  119 + field: DataSourceField.DEVICE_ID,
  120 + component: 'ApiSelect',
  121 + label: '设备',
  122 + colProps: { span: 8 },
  123 + rules: [{ required: true, message: '设备名称为必填项' }],
  124 + componentProps({ formModel, formActionType }) {
  125 + const { setFieldsValue } = formActionType;
  126 + const organizationId = formModel[DataSourceField.ORIGINATION_ID];
  127 + const deviceId = formModel[DataSourceField.DEVICE_ID];
  128 + return {
  129 + api: async () => {
  130 + if (organizationId) {
  131 + try {
  132 + const data = await getAllDeviceByOrg(organizationId);
  133 + if (deviceId) {
  134 + const record = data.find((item) => item.id === deviceId);
  135 + setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
  136 + }
  137 + if (data)
  138 + return data.map((item) => ({
  139 + ...item,
  140 + label: item.name,
  141 + value: item.id,
  142 + deviceType: item.deviceType,
  143 + }));
  144 + } catch (error) {}
  145 + }
  146 + return [];
  147 + },
220 148
221   - return {
222   - api: async () => {
223   - if (deviceId) {
224   - try {
225   - if (isGatewayDevice && slaveDeviceId && slaveDeviceProfileId) {
226   - return await getDeviceAttribute(slaveDeviceProfileId);
227   - }
228   - if (!isGatewayDevice && deviceProfileId) {
229   - return await getDeviceAttribute(deviceProfileId);
230   - }
231   - } catch (error) {}
232   - }
233   - return [];
234   - },
235   - placeholder: '请选择属性',
236   - getPopupContainer: () => document.body,
237   - };
  149 + onChange(_value, record: MasterDeviceList) {
  150 + setFieldsValue({
  151 + [DataSourceField.ATTRIBUTE]: null,
  152 + [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',
  153 + [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId,
  154 + [DataSourceField.SLAVE_DEVICE_ID]: null,
  155 + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: null,
  156 + [DataSourceField.DEVICE_NAME]: record?.label,
  157 + });
  158 + },
  159 + placeholder: '请选择设备',
  160 + getPopupContainer: () => document.body,
  161 + };
  162 + },
238 163 },
239   - },
240   - {
241   - field: DataSourceField.DEVICE_RENAME,
242   - component: 'Input',
243   - label: '设备',
244   - colProps: { span: 8 },
245   - componentProps: {
246   - placeholder: '设备重命名',
  164 + {
  165 + field: DataSourceField.SLAVE_DEVICE_PROFILE_ID,
  166 + component: 'Input',
  167 + label: '',
  168 + show: false,
247 169 },
248   - },
249   - {
250   - field: DataSourceField.ATTRIBUTE_RENAME,
251   - component: 'Input',
252   - label: '属性',
253   - colProps: { span: 8 },
254   - componentProps: {
255   - placeholder: '属性重命名',
  170 + {
  171 + field: DataSourceField.SLAVE_DEVICE_ID,
  172 + label: '网关子设备',
  173 + component: 'ApiSelect',
  174 + colProps: { span: 8 },
  175 + rules: [{ required: true, message: '网关子设备为必填项' }],
  176 + ifShow({ model }) {
  177 + return model[DataSourceField.IS_GATEWAY_DEVICE];
  178 + },
  179 + dynamicRules({ model }) {
  180 + return [
  181 + { required: model[DataSourceField.IS_GATEWAY_DEVICE], message: '请选择网关子设备' },
  182 + ];
  183 + },
  184 + componentProps({ formModel, formActionType }) {
  185 + const { setFieldsValue } = formActionType;
  186 + const organizationId = formModel[DataSourceField.ORIGINATION_ID];
  187 + const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
  188 + const deviceId = formModel[DataSourceField.DEVICE_ID];
  189 + const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
  190 + return {
  191 + api: async () => {
  192 + if (organizationId && isGatewayDevice) {
  193 + try {
  194 + const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });
  195 + if (slaveDeviceId) {
  196 + const record = data.find((item) => item.id === slaveDeviceId);
  197 + setFieldsValue({ [DataSourceField.DEVICE_PROFILE_ID]: record?.deviceProfileId });
  198 + }
  199 + if (data)
  200 + return data.map((item) => ({
  201 + ...item,
  202 + label: item.name,
  203 + value: item.id,
  204 + deviceType: item.deviceType,
  205 + }));
  206 + } catch (error) {}
  207 + }
  208 + return [];
  209 + },
  210 + onChange(_value, record: MasterDeviceList) {
  211 + setFieldsValue({
  212 + [DataSourceField.ATTRIBUTE]: null,
  213 + [DataSourceField.SLAVE_DEVICE_PROFILE_ID]: record.deviceProfileId,
  214 + [DataSourceField.DEVICE_NAME]: record?.label,
  215 + });
  216 + },
  217 + placeholder: '请选择网关子设备',
  218 + getPopupContainer: () => document.body,
  219 + };
  220 + },
256 221 },
257   - },
258   -];
  222 + {
  223 + field: DataSourceField.ATTRIBUTE,
  224 + component: 'ApiSelect',
  225 + label: '属性',
  226 + colProps: { span: 8 },
  227 + rules: [{ required: true, message: '属性为必填项' }],
  228 + componentProps({ formModel }) {
  229 + const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
  230 + const deviceId = formModel[DataSourceField.DEVICE_ID];
  231 + const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID];
  232 + const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
  233 + const slaveDeviceProfileId = formModel[DataSourceField.SLAVE_DEVICE_PROFILE_ID];
259 234
260   -export const controlFormSchema: FormSchema[] = [
261   - {
262   - field: DataSourceField.DEVICE_RENAME,
263   - component: 'Input',
264   - label: '设备',
265   - colProps: { span: 8 },
266   - componentProps: {
267   - placeholder: '设备重命名',
268   - },
269   - },
270   - {
271   - field: DataSourceField.ATTRIBUTE_RENAME,
272   - component: 'Input',
273   - label: '属性',
274   - colProps: { span: 8 },
275   - componentProps: {
276   - placeholder: '属性重命名',
277   - },
278   - },
279   -];
280   -
281   -export const mapFormSchema: FormSchema[] = [
282   - {
283   - field: DataSourceField.IS_GATEWAY_DEVICE,
284   - component: 'Switch',
285   - label: '是否是网关设备',
286   - show: false,
287   - },
288   - {
289   - field: DataSourceField.DEVICE_NAME,
290   - component: 'Input',
291   - label: '设备名',
292   - show: false,
293   - },
294   - {
295   - field: DataSourceField.ORIGINATION_ID,
296   - component: 'ApiTreeSelect',
297   - label: '组织',
298   - colProps: { span: 8 },
299   - rules: [{ required: true, message: '组织为必填项' }],
300   - componentProps({ formActionType }) {
301   - const { setFieldsValue } = formActionType;
302   - return {
303   - placeholder: '请选择组织',
304   - api: async () => {
305   - const data = await getOrganizationList();
306   - copyTransFun(data as any as any[]);
307   - return data;
308   - },
309   - onChange() {
310   - setFieldsValue({
311   - [DataSourceField.DEVICE_ID]: null,
312   - [DataSourceField.LATITUDE_ATTRIBUTE]: null,
313   - [DataSourceField.LONGITUDE_ATTRIBUTE]: null,
314   - [DataSourceField.SLAVE_DEVICE_ID]: null,
315   - [DataSourceField.IS_GATEWAY_DEVICE]: false,
316   - });
317   - },
318   - getPopupContainer: () => document.body,
319   - };
320   - },
321   - },
322   - {
323   - field: DataSourceField.DEVICE_ID,
324   - component: 'ApiSelect',
325   - label: '设备',
326   - colProps: { span: 8 },
327   - rules: [{ required: true, message: '设备名称为必填项' }],
328   - componentProps({ formModel, formActionType }) {
329   - const { setFieldsValue } = formActionType;
330   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
331   - return {
332   - api: async () => {
333   - if (organizationId) {
334   - try {
335   - const data = await getAllDeviceByOrg(organizationId);
336   - if (data)
337   - return data.map((item) => ({
338   - label: item.name,
339   - value: item.id,
340   - deviceType: item.deviceType,
341   - }));
342   - } catch (error) {}
343   - }
344   - return [];
345   - },
346   - onChange(_value, record: Record<'value' | 'label' | 'deviceType', string>) {
347   - setFieldsValue({
348   - [DataSourceField.LONGITUDE_ATTRIBUTE]: null,
349   - [DataSourceField.LATITUDE_ATTRIBUTE]: null,
350   - [DataSourceField.IS_GATEWAY_DEVICE]: record?.deviceType === 'GATEWAY',
351   - [DataSourceField.SLAVE_DEVICE_ID]: null,
352   - [DataSourceField.DEVICE_NAME]: record?.label,
353   - });
354   - },
355   - placeholder: '请选择设备',
356   - getPopupContainer: () => document.body,
357   - };
358   - },
359   - },
360   - {
361   - field: DataSourceField.SLAVE_DEVICE_ID,
362   - label: '网关子设备',
363   - component: 'ApiSelect',
364   - colProps: { span: 8 },
365   - rules: [{ required: true, message: '网关子设备为必填项' }],
366   - ifShow({ model }) {
367   - return model[DataSourceField.IS_GATEWAY_DEVICE];
368   - },
369   - dynamicRules({ model }) {
370   - return [{ required: model[DataSourceField.IS_GATEWAY_DEVICE], message: '请选择网关子设备' }];
  235 + return {
  236 + api: async () => {
  237 + if (deviceId) {
  238 + try {
  239 + if (isGatewayDevice && slaveDeviceId && slaveDeviceProfileId) {
  240 + return await getDeviceAttribute({
  241 + deviceProfileId: slaveDeviceProfileId,
  242 + dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
  243 + });
  244 + }
  245 + if (!isGatewayDevice && deviceProfileId) {
  246 + return await getDeviceAttribute({
  247 + deviceProfileId,
  248 + dataType: isControlComponent(frontId!) ? 'BOOL' : undefined,
  249 + });
  250 + }
  251 + } catch (error) {}
  252 + }
  253 + return [];
  254 + },
  255 + placeholder: '请选择属性',
  256 + getPopupContainer: () => document.body,
  257 + };
  258 + },
371 259 },
372   - componentProps({ formModel, formActionType }) {
373   - const { setFieldsValue } = formActionType;
374   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
375   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
376   - const deviceId = formModel[DataSourceField.DEVICE_ID];
377   - return {
378   - api: async () => {
379   - if (organizationId && isGatewayDevice) {
380   - try {
381   - const data = await getGatewaySlaveDevice({ organizationId, masterId: deviceId });
382   - if (data)
383   - return data.map((item) => ({
384   - label: item.name,
385   - value: item.id,
386   - deviceType: item.deviceType,
387   - }));
388   - } catch (error) {}
389   - }
390   - return [];
391   - },
392   - onChange(_value, record: Record<'value' | 'label' | 'deviceType', string>) {
393   - setFieldsValue({
394   - [DataSourceField.LATITUDE_ATTRIBUTE]: null,
395   - [DataSourceField.LONGITUDE_ATTRIBUTE]: null,
396   - [DataSourceField.DEVICE_NAME]: record?.label,
397   - });
398   - },
399   - placeholder: '请选择网关子设备',
400   - getPopupContainer: () => document.body,
401   - };
  260 + {
  261 + field: DataSourceField.DEVICE_RENAME,
  262 + component: 'Input',
  263 + label: '设备',
  264 + colProps: { span: 8 },
  265 + componentProps: {
  266 + placeholder: '设备重命名',
  267 + },
402 268 },
403   - },
404   - {
405   - field: DataSourceField.LONGITUDE_ATTRIBUTE,
406   - component: 'ApiSearchSelect',
407   - label: '经度属性',
408   - colProps: { span: 8 },
409   - rules: [{ required: true, message: '属性为必填项' }],
410   - componentProps({ formModel }) {
411   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
412   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
413   - const deviceId = formModel[DataSourceField.DEVICE_ID];
414   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
415   - return {
416   - api: async () => {
417   - if (organizationId && deviceId) {
418   - try {
419   - if (isGatewayDevice && slaveDeviceId) {
420   - return await getDeviceAttribute(slaveDeviceId);
421   - }
422   - if (!isGatewayDevice) {
423   - return await getDeviceAttribute(deviceId);
424   - }
425   - } catch (error) {}
426   - }
427   - return [];
428   - },
429   - placeholder: '请选择经度属性',
430   - dropdownVisibleChangeHook: ({ options }: OnChangeHookParams) => {
431   - options.value = unref(options).filter(
432   - (item) => item.value !== formModel[DataSourceField.LATITUDE_ATTRIBUTE]
433   - );
434   - },
435   - getPopupContainer: () => document.body,
436   - };
437   - },
438   - },
439   - {
440   - field: DataSourceField.LATITUDE_ATTRIBUTE,
441   - component: 'ApiSearchSelect',
442   - label: '纬度属性',
443   - colProps: { span: 8 },
444   - rules: [{ required: true, message: '属性为必填项' }],
445   - componentProps({ formModel }) {
446   - const organizationId = formModel[DataSourceField.ORIGINATION_ID];
447   - const isGatewayDevice = formModel[DataSourceField.IS_GATEWAY_DEVICE];
448   - const deviceId = formModel[DataSourceField.DEVICE_ID];
449   - const slaveDeviceId = formModel[DataSourceField.SLAVE_DEVICE_ID];
450   - return {
451   - api: async () => {
452   - if (organizationId && deviceId) {
453   - try {
454   - if (isGatewayDevice && slaveDeviceId) {
455   - return getDeviceAttribute(slaveDeviceId);
456   - }
457   - if (!isGatewayDevice) {
458   - return await getDeviceAttribute(deviceId);
459   - }
460   - } catch (error) {}
461   - }
462   - return [];
463   - },
464   - dropdownVisibleChangeHook: ({ options }: OnChangeHookParams) => {
465   - options.value = unref(options).filter(
466   - (item) => item.value !== formModel[DataSourceField.LONGITUDE_ATTRIBUTE]
467   - );
468   - },
469   - placeholder: '请选择纬度属性',
470   - getPopupContainer: () => document.body,
471   - };
  269 + {
  270 + field: DataSourceField.ATTRIBUTE_RENAME,
  271 + component: 'Input',
  272 + label: '属性',
  273 + colProps: { span: 8 },
  274 + componentProps: {
  275 + placeholder: '属性重命名',
  276 + },
472 277 },
473   - },
474   -];
  278 + ];
  279 +};
... ...
... ... @@ -178,6 +178,39 @@ export const modeFour: FormSchema[] = [
178 178 },
179 179 ];
180 180
  181 +export const modeFive: FormSchema[] = [
  182 + {
  183 + field: visualOptionField.FONT_COLOR,
  184 + label: '数值字体颜色',
  185 + component: 'ColorPicker',
  186 + changeEvent: 'update:value',
  187 + componentProps: {
  188 + defaultValue: '#000',
  189 + },
  190 + },
  191 + {
  192 + field: visualOptionField.ICON_COLOR,
  193 + label: '图标颜色',
  194 + component: 'ColorPicker',
  195 + changeEvent: 'update:value',
  196 + componentProps: {
  197 + defaultValue: '#367BFF',
  198 + },
  199 + },
  200 + {
  201 + field: visualOptionField.ICON,
  202 + label: '图标',
  203 + component: 'IconDrawer',
  204 + changeEvent: 'update:value',
  205 + componentProps({ formModel }) {
  206 + const color = formModel[visualOptionField.ICON_COLOR];
  207 + return {
  208 + color,
  209 + };
  210 + },
  211 + },
  212 +];
  213 +
181 214 export const schemasMap = new Map<FrontComponent, FormSchema[]>();
182 215
183 216 schemasMap.set(FrontComponent.TEXT_COMPONENT_1, modeOne);
... ... @@ -188,3 +221,6 @@ schemasMap.set(FrontComponent.TEXT_COMPONENT_5, modeTwo);
188 221 schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, modeOne);
189 222 schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, modeThree);
190 223 schemasMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, modeFour);
  224 +schemasMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, modeFive);
  225 +schemasMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, modeOne);
  226 +schemasMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, modeOne);
... ...