Showing
44 changed files
with
4 additions
and
4806 deletions
Too many changes to show.
To preserve performance only 44 of 59 files are displayed.
@@ -6,8 +6,6 @@ import { | @@ -6,8 +6,6 @@ import { | ||
6 | EXCEPTION_COMPONENT, | 6 | EXCEPTION_COMPONENT, |
7 | PAGE_NOT_FOUND_NAME, | 7 | PAGE_NOT_FOUND_NAME, |
8 | } from '/@/router/constant'; | 8 | } from '/@/router/constant'; |
9 | -import { DATA_BOARD_SHARE_URL } from '../../views/visual/board/config/config'; | ||
10 | - | ||
11 | // 404 on a page | 9 | // 404 on a page |
12 | export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { | 10 | export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { |
13 | path: '/:path(.*)*', | 11 | path: '/:path(.*)*', |
@@ -77,13 +75,3 @@ export const ERROR_LOG_ROUTE: AppRouteRecordRaw = { | @@ -77,13 +75,3 @@ export const ERROR_LOG_ROUTE: AppRouteRecordRaw = { | ||
77 | }, | 75 | }, |
78 | ], | 76 | ], |
79 | }; | 77 | }; |
80 | - | ||
81 | -export const DATA_BOARD_SHARE: AppRouteRecordRaw = { | ||
82 | - path: DATA_BOARD_SHARE_URL(), | ||
83 | - name: 'dataBoardSharePage', | ||
84 | - component: () => import('/@/views/visual/board/detail/index.vue'), | ||
85 | - meta: { | ||
86 | - ignoreAuth: true, | ||
87 | - title: '分享看板', | ||
88 | - }, | ||
89 | -}; |
1 | import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'; | 1 | import type { AppRouteRecordRaw, AppRouteModule } from '/@/router/types'; |
2 | -import { DATA_BOARD_SHARE, PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic'; | 2 | +import { PAGE_NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '/@/router/routes/basic'; |
3 | import { mainOutRoutes } from './mainOut'; | 3 | import { mainOutRoutes } from './mainOut'; |
4 | import { PageEnum } from '/@/enums/pageEnum'; | 4 | import { PageEnum } from '/@/enums/pageEnum'; |
5 | import { t } from '/@/hooks/web/useI18n'; | 5 | import { t } from '/@/hooks/web/useI18n'; |
@@ -86,6 +86,5 @@ export const basicRoutes = [ | @@ -86,6 +86,5 @@ export const basicRoutes = [ | ||
86 | ...mainOutRoutes, | 86 | ...mainOutRoutes, |
87 | REDIRECT_ROUTE, | 87 | REDIRECT_ROUTE, |
88 | PAGE_NOT_FOUND_ROUTE, | 88 | PAGE_NOT_FOUND_ROUTE, |
89 | - DATA_BOARD_SHARE, | ||
90 | PUBLIC_PAGE_ROUTER, | 89 | PUBLIC_PAGE_ROUTER, |
91 | ]; | 90 | ]; |
@@ -161,7 +161,7 @@ const transform: AxiosTransform = { | @@ -161,7 +161,7 @@ const transform: AxiosTransform = { | ||
161 | throw new Error(error); | 161 | throw new Error(error); |
162 | } | 162 | } |
163 | checkStatus(error?.response?.status, msg, errorMessageMode); | 163 | checkStatus(error?.response?.status, msg, errorMessageMode); |
164 | - return Promise.reject(response.data); | 164 | + return Promise.reject(response?.data); |
165 | }, | 165 | }, |
166 | }; | 166 | }; |
167 | 167 |
@@ -18,7 +18,7 @@ | @@ -18,7 +18,7 @@ | ||
18 | TABLE_CHART_MODE_LIST, | 18 | TABLE_CHART_MODE_LIST, |
19 | EnumTableChartMode, | 19 | EnumTableChartMode, |
20 | } from '/@/components/Widget'; | 20 | } from '/@/components/Widget'; |
21 | - import { SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config'; | 21 | + import { SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config'; |
22 | 22 | ||
23 | interface DeviceDetail { | 23 | interface DeviceDetail { |
24 | tbDeviceId: string; | 24 | tbDeviceId: string; |
@@ -7,8 +7,8 @@ import { getDeviceAttributes } from '/@/api/dataBoard'; | @@ -7,8 +7,8 @@ import { getDeviceAttributes } from '/@/api/dataBoard'; | ||
7 | import { DeviceAttributeRecord } from '/@/api/dataBoard/model'; | 7 | import { DeviceAttributeRecord } from '/@/api/dataBoard/model'; |
8 | import { dateUtil } from '/@/utils/dateUtil'; | 8 | import { dateUtil } from '/@/utils/dateUtil'; |
9 | import { isArray } from '/@/utils/is'; | 9 | import { isArray } from '/@/utils/is'; |
10 | -import { QueryWay, SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config'; | ||
11 | import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util'; | 10 | import { DEFAULT_DATE_FORMAT } from '/@/views/visual/board/detail/config/util'; |
11 | +import { QueryWay, SchemaFiled } from '/@/views/visual/palette/components/HistoryTrendModal/config'; | ||
12 | 12 | ||
13 | interface DeviceOption { | 13 | interface DeviceOption { |
14 | deviceProfileId: string; | 14 | deviceProfileId: string; |
src/views/visual/board/components/ControlComponent/SlidingSwitch.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - components: { Spin }, | ||
4 | - inheritAttrs: false, | ||
5 | - }; | ||
6 | -</script> | ||
7 | -<script lang="ts" setup> | ||
8 | - import { Spin } from 'ant-design-vue'; | ||
9 | - import { RadioRecord } from '../../detail/config/util'; | ||
10 | - import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; | ||
11 | - import { useSendCommand } from './useSendCommand'; | ||
12 | - import { ref } from 'vue'; | ||
13 | - | ||
14 | - interface VisualComponentProps<Layout = Recordable, Value = ControlComponentValue> { | ||
15 | - value?: Value; | ||
16 | - layout?: Layout; | ||
17 | - radio?: RadioRecord; | ||
18 | - random?: boolean; | ||
19 | - add?: (key: string, method: Fn) => void; | ||
20 | - update?: () => void; | ||
21 | - remove?: (key: string) => void; | ||
22 | - } | ||
23 | - | ||
24 | - const props = defineProps<VisualComponentProps>(); | ||
25 | - | ||
26 | - const emit = defineEmits(['update:value', 'change']); | ||
27 | - | ||
28 | - const { sendCommand } = useSendCommand(); | ||
29 | - | ||
30 | - const loading = ref(false); | ||
31 | - const handleChange = async (event: Event) => { | ||
32 | - const _value = (event.target as HTMLInputElement).checked; | ||
33 | - if (props.value) { | ||
34 | - loading.value = true; | ||
35 | - const flag = await sendCommand(props.value, _value); | ||
36 | - loading.value = false; | ||
37 | - if (!flag) { | ||
38 | - (event.target as HTMLInputElement).checked = !_value; | ||
39 | - return; | ||
40 | - } | ||
41 | - } | ||
42 | - emit('update:value', _value); | ||
43 | - emit('change', _value); | ||
44 | - }; | ||
45 | -</script> | ||
46 | - | ||
47 | -<template> | ||
48 | - <div class="flex flex-col justify-center"> | ||
49 | - <Spin :spinning="loading"> | ||
50 | - <label class="sliding-switch"> | ||
51 | - <input | ||
52 | - :value="!!Number(props.value?.value)" | ||
53 | - type="checkbox" | ||
54 | - :checked="!!Number(props.value?.value)" | ||
55 | - @change="handleChange" | ||
56 | - /> | ||
57 | - <span class="slider"></span> | ||
58 | - <span class="on">ON</span> | ||
59 | - <span class="off">OFF</span> | ||
60 | - </label> | ||
61 | - <div | ||
62 | - class="text-center mt-2 text-gray-700" | ||
63 | - :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
64 | - > | ||
65 | - {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
66 | - > | ||
67 | - </Spin> | ||
68 | - </div> | ||
69 | -</template> | ||
70 | - | ||
71 | -<style scoped lang="less"> | ||
72 | - .sliding-switch { | ||
73 | - position: relative; | ||
74 | - display: block; | ||
75 | - font-weight: 700; | ||
76 | - line-height: 40px; | ||
77 | - width: 80px; | ||
78 | - height: 40px; | ||
79 | - font-size: 14px; | ||
80 | - cursor: pointer; | ||
81 | - user-select: none; | ||
82 | - | ||
83 | - input[type='checkbox'] { | ||
84 | - display: none; | ||
85 | - } | ||
86 | - | ||
87 | - .slider { | ||
88 | - width: 80px; | ||
89 | - height: 40px; | ||
90 | - display: flex; | ||
91 | - align-items: center; | ||
92 | - box-sizing: border-box; | ||
93 | - border: 2px solid #ecf0f3; | ||
94 | - border-radius: 20px; | ||
95 | - box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%), | ||
96 | - inset -2px -2px 8px #fff, inset -2px -2px 12px hsl(0deg 0% 100% / 50%), | ||
97 | - inset 2px 2px 4px hsl(0deg 0% 100% / 10%), inset 2px 2px 8px rgb(0 0 0 / 30%), | ||
98 | - 2px 2px 8px rgb(0 0 0 / 30%); | ||
99 | - background-color: #ecf0f3; | ||
100 | - z-index: -1; | ||
101 | - } | ||
102 | - | ||
103 | - .slider::after { | ||
104 | - cursor: pointer; | ||
105 | - display: block; | ||
106 | - content: ''; | ||
107 | - width: 24px; | ||
108 | - height: 24px; | ||
109 | - border-radius: 50%; | ||
110 | - margin-left: 6px; | ||
111 | - margin-right: 6px; | ||
112 | - background-color: #ecf0f3; | ||
113 | - box-shadow: -2px -2px 8px #fff, -2px -2px 12px hsl(0deg 0% 100% / 50%), | ||
114 | - inset 2px 2px 4px hsl(0deg 0% 100% / 10%), 2px 2px 8px rgb(0 0 0 / 30%); | ||
115 | - z-index: 999; | ||
116 | - transition: 0.5s; | ||
117 | - } | ||
118 | - | ||
119 | - input:checked ~ .off { | ||
120 | - opacity: 0; | ||
121 | - } | ||
122 | - | ||
123 | - input:checked ~ .slider::after { | ||
124 | - transform: translateX(35px); | ||
125 | - } | ||
126 | - | ||
127 | - input:not(:checked) ~ .on { | ||
128 | - opacity: 0; | ||
129 | - transform: translateX(0); | ||
130 | - } | ||
131 | - | ||
132 | - .on, | ||
133 | - .off { | ||
134 | - position: absolute; | ||
135 | - top: 0; | ||
136 | - display: inline-block; | ||
137 | - margin-left: 3px; | ||
138 | - width: 34px; | ||
139 | - text-align: center; | ||
140 | - transition: 0.2s; | ||
141 | - } | ||
142 | - | ||
143 | - .on { | ||
144 | - color: #039be5; | ||
145 | - } | ||
146 | - | ||
147 | - .off { | ||
148 | - right: 6px; | ||
149 | - color: #999; | ||
150 | - } | ||
151 | - } | ||
152 | -</style> |
src/views/visual/board/components/ControlComponent/SwitchWithIcon.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import { Switch } from 'ant-design-vue'; | ||
8 | - import { computed, ref, watchEffect } from 'vue'; | ||
9 | - import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util'; | ||
10 | - import SvgIcon from '/@/components/Icon/src/SvgIcon.vue'; | ||
11 | - import { | ||
12 | - ControlComponentDefaultConfig, | ||
13 | - ControlComponentValue, | ||
14 | - ControlComponentLayout, | ||
15 | - } from './control.config'; | ||
16 | - import { useSendCommand } from './useSendCommand'; | ||
17 | - const props = withDefaults( | ||
18 | - defineProps<{ | ||
19 | - layout?: ControlComponentLayout; | ||
20 | - value?: ControlComponentValue; | ||
21 | - radio?: RadioRecord; | ||
22 | - }>(), | ||
23 | - { | ||
24 | - value: () => ControlComponentDefaultConfig, | ||
25 | - } | ||
26 | - ); | ||
27 | - const getRadio = computed(() => { | ||
28 | - return props.radio || DEFAULT_RADIO_RECORD; | ||
29 | - }); | ||
30 | - | ||
31 | - const checked = ref(!!Number(props.value.value)); | ||
32 | - | ||
33 | - const { sendCommand } = useSendCommand(); | ||
34 | - const loading = ref(false); | ||
35 | - const handleChange = async (value: boolean) => { | ||
36 | - loading.value = true; | ||
37 | - const flag = await sendCommand(props.value, value); | ||
38 | - loading.value = false; | ||
39 | - if (!flag) { | ||
40 | - checked.value = !value; | ||
41 | - } | ||
42 | - }; | ||
43 | - | ||
44 | - watchEffect(() => { | ||
45 | - checked.value = !!Number(props.value.value); | ||
46 | - }); | ||
47 | -</script> | ||
48 | - | ||
49 | -<template> | ||
50 | - <div class="flex items-center w-full h-full p-4"> | ||
51 | - <div class="flex-auto flex truncate"> | ||
52 | - <SvgIcon | ||
53 | - :name="props.value?.icon! || ControlComponentDefaultConfig.icon!" | ||
54 | - prefix="iconfont" | ||
55 | - :style="{ | ||
56 | - color: props.value?.iconColor || ControlComponentDefaultConfig.iconColor, | ||
57 | - width: fontSize({ radioRecord: getRadio, basic: 30, min: 16 }), | ||
58 | - height: fontSize({ radioRecord: getRadio, basic: 30, min: 16 }), | ||
59 | - }" | ||
60 | - /> | ||
61 | - <span | ||
62 | - class="flex-auto mx-4 flex items-center truncate inline-block text-gray-700" | ||
63 | - :style="{ color: props.value.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
64 | - > | ||
65 | - {{ props.value.attributeRename || props.value.attribute }} | ||
66 | - </span> | ||
67 | - </div> | ||
68 | - <Switch v-model:checked="checked" :loading="loading" @change="handleChange" /> | ||
69 | - </div> | ||
70 | -</template> |
src/views/visual/board/components/ControlComponent/ToggleSwitch.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - components: { Spin }, | ||
4 | - inheritAttrs: false, | ||
5 | - }; | ||
6 | -</script> | ||
7 | -<script lang="ts" setup> | ||
8 | - import { computed } from '@vue/reactivity'; | ||
9 | - import { DEFAULT_RADIO_RECORD, fontSize, RadioRecord } from '../../detail/config/util'; | ||
10 | - import { ControlComponentDefaultConfig, ControlComponentValue } from './control.config'; | ||
11 | - import { useSendCommand } from './useSendCommand'; | ||
12 | - import { ref } from 'vue'; | ||
13 | - import { Spin } from 'ant-design-vue'; | ||
14 | - | ||
15 | - const props = defineProps<{ | ||
16 | - value?: ControlComponentValue; | ||
17 | - layout?: Recordable; | ||
18 | - radio?: RadioRecord; | ||
19 | - }>(); | ||
20 | - | ||
21 | - const emit = defineEmits(['update:value', 'change']); | ||
22 | - | ||
23 | - const { sendCommand } = useSendCommand(); | ||
24 | - const loading = ref(false); | ||
25 | - const handleChange = async (event: Event) => { | ||
26 | - const _value = (event.target as HTMLInputElement).checked; | ||
27 | - if (props.value) { | ||
28 | - loading.value = true; | ||
29 | - const flag = await sendCommand(props.value, _value); | ||
30 | - loading.value = false; | ||
31 | - if (!flag) { | ||
32 | - (event.target as HTMLInputElement).checked = !_value; | ||
33 | - return; | ||
34 | - } | ||
35 | - } | ||
36 | - emit('update:value', _value); | ||
37 | - emit('change', _value); | ||
38 | - }; | ||
39 | - | ||
40 | - const getRadio = computed(() => { | ||
41 | - return props.radio! || DEFAULT_RADIO_RECORD; | ||
42 | - }); | ||
43 | -</script> | ||
44 | - | ||
45 | -<template> | ||
46 | - <div class="flex flex-col"> | ||
47 | - <Spin :spinning="loading"> | ||
48 | - <div | ||
49 | - class="toggle-switch" | ||
50 | - :style="{ | ||
51 | - width: fontSize({ radioRecord: getRadio, basic: 75, max: 75, min: 60 }), | ||
52 | - height: fontSize({ radioRecord: getRadio, basic: 97.5, max: 97.5, min: 80 }), | ||
53 | - }" | ||
54 | - > | ||
55 | - <label class="switch"> | ||
56 | - <input | ||
57 | - :value="!!Number(props.value?.value)" | ||
58 | - type="checkbox" | ||
59 | - :checked="!!Number(props.value?.value)" | ||
60 | - @change="handleChange" | ||
61 | - /> | ||
62 | - <div class="button"> | ||
63 | - <div class="light"></div> | ||
64 | - <div class="dots"></div> | ||
65 | - <div class="characters"></div> | ||
66 | - <div class="shine"></div> | ||
67 | - <div class="shadow"></div> | ||
68 | - </div> | ||
69 | - </label> | ||
70 | - </div> | ||
71 | - <div | ||
72 | - class="text-center mt-2 text-gray-700" | ||
73 | - :style="{ color: props?.value?.fontColor || ControlComponentDefaultConfig.fontColor }" | ||
74 | - > | ||
75 | - {{ props.value?.attributeRename || props.value?.attribute }}</div | ||
76 | - > | ||
77 | - </Spin> | ||
78 | - </div> | ||
79 | -</template> | ||
80 | - | ||
81 | -<style scoped> | ||
82 | - .toggle-switch { | ||
83 | - /* flex: 1 1 auto; */ | ||
84 | - max-width: 75px; | ||
85 | - | ||
86 | - /* height: 97.5px; */ | ||
87 | - display: flex; | ||
88 | - } | ||
89 | - | ||
90 | - .switch { | ||
91 | - background-color: black; | ||
92 | - box-sizing: border-box; | ||
93 | - width: 100%; | ||
94 | - height: 100%; | ||
95 | - box-shadow: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0 1px 2px black, inset 0 2px 2px -2px white, | ||
96 | - inset 0 0 2px 15px #47434c, inset 0 0 2px 22px black; | ||
97 | - border-radius: 5px; | ||
98 | - padding: 10px; | ||
99 | - perspective: 700px; | ||
100 | - } | ||
101 | - | ||
102 | - .switch input { | ||
103 | - display: none; | ||
104 | - } | ||
105 | - | ||
106 | - .switch input:checked + .button { | ||
107 | - transform: translateZ(20px) rotateX(25deg); | ||
108 | - box-shadow: 0 -5px 10px #ff1818; | ||
109 | - } | ||
110 | - | ||
111 | - .switch input:checked + .button .light { | ||
112 | - animation: flicker 0.2s infinite 0.3s; | ||
113 | - } | ||
114 | - | ||
115 | - .switch input:checked + .button .shine { | ||
116 | - opacity: 1; | ||
117 | - } | ||
118 | - | ||
119 | - .switch input:checked + .button .shadow { | ||
120 | - opacity: 0; | ||
121 | - } | ||
122 | - | ||
123 | - .switch .button { | ||
124 | - display: flex; | ||
125 | - justify-content: center; | ||
126 | - align-items: center; | ||
127 | - transition: all 0.3s cubic-bezier(1, 0, 1, 1); | ||
128 | - transform-origin: center center -20px; | ||
129 | - transform: translateZ(20px) rotateX(-25deg); | ||
130 | - transform-style: preserve-3d; | ||
131 | - width: 100%; | ||
132 | - height: 100%; | ||
133 | - position: relative; | ||
134 | - cursor: pointer; | ||
135 | - background: linear-gradient(#980000 0%, #6f0000 30%, #6f0000 70%, #980000 100%); | ||
136 | - background-color: #9b0621; | ||
137 | - background-repeat: no-repeat; | ||
138 | - } | ||
139 | - | ||
140 | - .switch .button::before { | ||
141 | - content: ''; | ||
142 | - background: linear-gradient( | ||
143 | - rgba(255, 255, 255, 0.8) 10%, | ||
144 | - rgba(255, 255, 255, 0.3) 30%, | ||
145 | - #650000 75%, | ||
146 | - #320000 | ||
147 | - ) | ||
148 | - 50% 50%/97% 97%, | ||
149 | - #b10000; | ||
150 | - background-repeat: no-repeat; | ||
151 | - width: 100%; | ||
152 | - height: 30px; | ||
153 | - transform-origin: top; | ||
154 | - transform: rotateX(-90deg); | ||
155 | - position: absolute; | ||
156 | - top: 0; | ||
157 | - } | ||
158 | - | ||
159 | - .switch .button::after { | ||
160 | - content: ''; | ||
161 | - background-image: linear-gradient(#650000, #320000); | ||
162 | - width: 100%; | ||
163 | - height: 30px; | ||
164 | - transform-origin: top; | ||
165 | - transform: translateY(30px) rotateX(-90deg); | ||
166 | - position: absolute; | ||
167 | - bottom: 0; | ||
168 | - box-shadow: 0 30px 8px 0 black, 0 60px 20px 0 rgb(0 0 0 / 50%); | ||
169 | - } | ||
170 | - | ||
171 | - .switch .light { | ||
172 | - opacity: 0; | ||
173 | - animation: light-off 1s; | ||
174 | - position: absolute; | ||
175 | - width: 80%; | ||
176 | - height: 80%; | ||
177 | - background-image: radial-gradient(#ffc97e, transparent 40%), | ||
178 | - radial-gradient(circle, #ff1818 50%, transparent 80%); | ||
179 | - } | ||
180 | - | ||
181 | - .switch .dots { | ||
182 | - position: absolute; | ||
183 | - width: 100%; | ||
184 | - height: 100%; | ||
185 | - background-image: radial-gradient(transparent 30%, rgba(101, 0, 0, 0.7) 70%); | ||
186 | - background-size: 10px 10px; | ||
187 | - } | ||
188 | - | ||
189 | - .switch .characters { | ||
190 | - position: absolute; | ||
191 | - width: 100%; | ||
192 | - height: 100%; | ||
193 | - background: linear-gradient(white, white) 50% 20%/5% 20%, | ||
194 | - radial-gradient(circle, transparent 50%, white 52%, white 70%, transparent 72%) 50% 80%/33% | ||
195 | - 25%; | ||
196 | - background-repeat: no-repeat; | ||
197 | - } | ||
198 | - | ||
199 | - .switch .shine { | ||
200 | - transition: all 0.3s cubic-bezier(1, 0, 1, 1); | ||
201 | - opacity: 0.3; | ||
202 | - position: absolute; | ||
203 | - width: 100%; | ||
204 | - height: 100%; | ||
205 | - background: linear-gradient(white, transparent 3%) 50% 50%/97% 97%, | ||
206 | - linear-gradient( | ||
207 | - rgba(255, 255, 255, 0.5), | ||
208 | - transparent 50%, | ||
209 | - transparent 80%, | ||
210 | - rgba(255, 255, 255, 0.5) | ||
211 | - ) | ||
212 | - 50% 50%/97% 97%; | ||
213 | - background-repeat: no-repeat; | ||
214 | - } | ||
215 | - | ||
216 | - .switch .shadow { | ||
217 | - transition: all 0.3s cubic-bezier(1, 0, 1, 1); | ||
218 | - opacity: 1; | ||
219 | - position: absolute; | ||
220 | - width: 100%; | ||
221 | - height: 100%; | ||
222 | - background: linear-gradient(transparent 70%, rgba(0, 0, 0, 0.8)); | ||
223 | - background-repeat: no-repeat; | ||
224 | - } | ||
225 | - | ||
226 | - @keyframes flicker { | ||
227 | - 0% { | ||
228 | - opacity: 1; | ||
229 | - } | ||
230 | - | ||
231 | - 80% { | ||
232 | - opacity: 0.8; | ||
233 | - } | ||
234 | - | ||
235 | - 100% { | ||
236 | - opacity: 1; | ||
237 | - } | ||
238 | - } | ||
239 | - | ||
240 | - @keyframes light-off { | ||
241 | - 0% { | ||
242 | - opacity: 1; | ||
243 | - } | ||
244 | - | ||
245 | - 80% { | ||
246 | - opacity: 0; | ||
247 | - } | ||
248 | - } | ||
249 | -</style> |
src/views/visual/board/components/ControlComponent/control.config.ts
deleted
100644 → 0
1 | -import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; | ||
2 | -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
3 | - | ||
4 | -export interface ControlComponentLayout { | ||
5 | - [key: string]: any; | ||
6 | -} | ||
7 | - | ||
8 | -export interface ControlComponentValue { | ||
9 | - value?: boolean; | ||
10 | - attribute?: string; | ||
11 | - attributeRename?: string; | ||
12 | - icon?: string; | ||
13 | - iconColor?: string; | ||
14 | - deviceId?: string; | ||
15 | - fontColor?: string; | ||
16 | - slaveDeviceId?: string; | ||
17 | - deviceProfileId?: string; | ||
18 | - deviceType?: DeviceTypeEnum; | ||
19 | - organizationId?: string; | ||
20 | - | ||
21 | - dataSource?: DataSource; | ||
22 | - [key: string]: any; | ||
23 | -} | ||
24 | - | ||
25 | -export const ControlComponentDefaultConfig: ControlComponentValue = { | ||
26 | - icon: 'shuiwen', | ||
27 | - iconColor: '#367BFF', | ||
28 | - fontColor: '#000', | ||
29 | -}; | ||
30 | - | ||
31 | -export const transformControlConfig = ( | ||
32 | - _ComponentConfig: Recordable, | ||
33 | - _record: DataComponentRecord, | ||
34 | - dataSourceRecord: DataSource | ||
35 | -) => { | ||
36 | - return { | ||
37 | - value: { | ||
38 | - ...dataSourceRecord.componentInfo, | ||
39 | - attribute: dataSourceRecord.attribute, | ||
40 | - attributeRename: dataSourceRecord.attributeRename, | ||
41 | - deviceProfileId: dataSourceRecord.deviceProfileId, | ||
42 | - deviceId: dataSourceRecord.deviceId, | ||
43 | - deviceType: dataSourceRecord.deviceType, | ||
44 | - slaveDeviceId: dataSourceRecord.slaveDeviceId, | ||
45 | - organizationId: dataSourceRecord.organizationId, | ||
46 | - dataSource: dataSourceRecord, | ||
47 | - } as ControlComponentValue, | ||
48 | - }; | ||
49 | -}; |
src/views/visual/board/components/ControlComponent/useSendCommand.ts
deleted
100644 → 0
1 | -import { ControlComponentValue } from './control.config'; | ||
2 | -import { sendCommandOneway } from '/@/api/dataBoard'; | ||
3 | -import { useMessage } from '/@/hooks/web/useMessage'; | ||
4 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
5 | - | ||
6 | -const { createMessage } = useMessage(); | ||
7 | -export function useSendCommand() { | ||
8 | - const error = () => { | ||
9 | - createMessage.error('下发指令失败'); | ||
10 | - return false; | ||
11 | - }; | ||
12 | - const sendCommand = async (record: ControlComponentValue, value: any) => { | ||
13 | - if (!record) return error(); | ||
14 | - const { attribute } = record; | ||
15 | - const { dataSource } = record; | ||
16 | - const { customCommand } = dataSource || {}; | ||
17 | - | ||
18 | - const { deviceId } = record; | ||
19 | - if (!deviceId) return error(); | ||
20 | - try { | ||
21 | - let params: string | Recordable = { | ||
22 | - [attribute!]: Number(value), | ||
23 | - }; | ||
24 | - | ||
25 | - // 如果是TCP设备从物模型中获取下发命令(TCP网关子设备无物模型服务与事件) | ||
26 | - if (customCommand?.transportType === TransportTypeEnum.TCP) { | ||
27 | - params = customCommand.command!; | ||
28 | - } | ||
29 | - | ||
30 | - // 控制按钮下发命令为0 或 1 | ||
31 | - await sendCommandOneway({ | ||
32 | - deviceId, | ||
33 | - value: { | ||
34 | - params: params, | ||
35 | - persistent: true, | ||
36 | - additionalInfo: { | ||
37 | - cmdType: 'API', | ||
38 | - }, | ||
39 | - method: 'methodThingskit', | ||
40 | - }, | ||
41 | - }); | ||
42 | - createMessage.success('命令下发成功'); | ||
43 | - return true; | ||
44 | - } catch (msg) { | ||
45 | - return error(); | ||
46 | - } | ||
47 | - }; | ||
48 | - return { | ||
49 | - sendCommand, | ||
50 | - }; | ||
51 | -} |
src/views/visual/board/components/InstrumentComponent/DashBoardComponent.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import type { ECharts, EChartsOption } from 'echarts'; | ||
8 | - import { watch } from 'vue'; | ||
9 | - import { nextTick, onMounted, onUnmounted, ref, unref, computed } from 'vue'; | ||
10 | - import { init } from 'echarts'; | ||
11 | - import { | ||
12 | - DashboardComponentLayout, | ||
13 | - DashBoardValue, | ||
14 | - instrumentComponent1, | ||
15 | - update_instrument_1_font, | ||
16 | - update_instrument_2_font, | ||
17 | - update_instrument_1_value, | ||
18 | - update_instrument_2_value, | ||
19 | - } from './dashBoardComponent.config'; | ||
20 | - import { | ||
21 | - DEFAULT_RADIO_RECORD, | ||
22 | - RadioRecord, | ||
23 | - fontSize, | ||
24 | - getUpdateTime, | ||
25 | - } from '../../detail/config/util'; | ||
26 | - import { Tooltip } from 'ant-design-vue'; | ||
27 | - import { useThrottleFn } from '@vueuse/shared'; | ||
28 | - import { buildUUID } from '/@/utils/uuid'; | ||
29 | - import { FrontComponent } from '../../const/const'; | ||
30 | - | ||
31 | - const props = withDefaults( | ||
32 | - defineProps<{ | ||
33 | - add?: Function; | ||
34 | - layout?: DashboardComponentLayout; | ||
35 | - value?: DashBoardValue; | ||
36 | - radio?: RadioRecord; | ||
37 | - random?: boolean; | ||
38 | - }>(), | ||
39 | - { | ||
40 | - layout: () => ({} as unknown as DashboardComponentLayout), | ||
41 | - value: () => ({ id: buildUUID() }), | ||
42 | - radio: () => DEFAULT_RADIO_RECORD, | ||
43 | - random: true, | ||
44 | - } | ||
45 | - ); | ||
46 | - | ||
47 | - const getControlsWidgetId = () => `widget-chart-${props.value.id}`; | ||
48 | - | ||
49 | - const chartRef = ref<Nullable<ECharts>>(null); | ||
50 | - | ||
51 | - function initChart() { | ||
52 | - const chartDom = document.getElementById(getControlsWidgetId())!; | ||
53 | - chartRef.value = init(chartDom); | ||
54 | - const option: EChartsOption = props.layout.chartOption || instrumentComponent1(); | ||
55 | - | ||
56 | - nextTick(() => { | ||
57 | - option && unref(chartRef)?.setOption(option); | ||
58 | - }); | ||
59 | - } | ||
60 | - | ||
61 | - const getRadio = computed(() => { | ||
62 | - return props.radio; | ||
63 | - }); | ||
64 | - | ||
65 | - const getChardRadio = computed(() => { | ||
66 | - const realWidth = unref(chartRef)?.getWidth(); | ||
67 | - const realHeight = unref(chartRef)?.getHeight(); | ||
68 | - const radioRecord = props.radio; | ||
69 | - return { | ||
70 | - ...radioRecord, | ||
71 | - height: realHeight || radioRecord.height, | ||
72 | - width: realWidth || radioRecord.height, | ||
73 | - }; | ||
74 | - }); | ||
75 | - | ||
76 | - const beforeUpdateFn = (componentType: FrontComponent) => { | ||
77 | - if (componentType === 'instrument-component-1') return update_instrument_1_font; | ||
78 | - if (componentType === 'instrument-component-2') return update_instrument_2_font; | ||
79 | - return (_radio: RadioRecord) => {}; | ||
80 | - }; | ||
81 | - | ||
82 | - function update() { | ||
83 | - const option = beforeUpdateFn(props.layout.componentType); | ||
84 | - unref(chartRef)?.setOption((option(unref(getChardRadio)) as unknown as EChartsOption) || {}); | ||
85 | - unref(chartRef)?.resize(); | ||
86 | - } | ||
87 | - | ||
88 | - const getUpdateValueFn = (componentType: FrontComponent) => { | ||
89 | - if (componentType === 'instrument-component-1') return update_instrument_1_value; | ||
90 | - if (componentType === 'instrument-component-2') return update_instrument_2_value; | ||
91 | - return (_radio: DashBoardValue) => {}; | ||
92 | - }; | ||
93 | - | ||
94 | - const updateChartValue = useThrottleFn(() => { | ||
95 | - const updateFn = getUpdateValueFn(props.layout.componentType); | ||
96 | - unref(chartRef)?.setOption((updateFn(props.value) as unknown as EChartsOption) || {}); | ||
97 | - }, 500); | ||
98 | - | ||
99 | - watch(() => props.value, updateChartValue); | ||
100 | - | ||
101 | - const updateChartFont = useThrottleFn(() => { | ||
102 | - const option = beforeUpdateFn(props.layout.componentType); | ||
103 | - setTimeout(() => { | ||
104 | - unref(chartRef)?.setOption((option(unref(getChardRadio)) as unknown as EChartsOption) || {}); | ||
105 | - }); | ||
106 | - }, 500); | ||
107 | - | ||
108 | - watch(() => props.radio, updateChartFont); | ||
109 | - | ||
110 | - const updateChartType = useThrottleFn(() => { | ||
111 | - unref(chartRef)?.clear(); | ||
112 | - unref(chartRef)?.setOption(props?.layout?.chartOption || {}); | ||
113 | - }, 500); | ||
114 | - | ||
115 | - watch(() => props.layout.componentType, updateChartType); | ||
116 | - | ||
117 | - let timeout: Nullable<number> = null; | ||
118 | - | ||
119 | - function handleRandomValue() { | ||
120 | - const newValue = Math.floor(Math.random() * 100); | ||
121 | - const updateFn = getUpdateValueFn(props.layout.componentType); | ||
122 | - unref(chartRef)?.setOption( | ||
123 | - (updateFn({ ...props.value, value: newValue }) as unknown as EChartsOption) || {} | ||
124 | - ); | ||
125 | - } | ||
126 | - | ||
127 | - onMounted(() => { | ||
128 | - initChart(); | ||
129 | - props.add && props.add(props.value.id, update); | ||
130 | - if (props.random) timeout = setInterval(handleRandomValue, 2000) as unknown as number; | ||
131 | - }); | ||
132 | - | ||
133 | - onUnmounted(() => { | ||
134 | - unref(chartRef)?.clear(); | ||
135 | - clearInterval(timeout as number); | ||
136 | - timeout = null; | ||
137 | - }); | ||
138 | - | ||
139 | - defineExpose({ update }); | ||
140 | -</script> | ||
141 | - | ||
142 | -<template> | ||
143 | - <div class="flex flex-col w-full h-full min-w-3 min-h-3"> | ||
144 | - <div :id="getControlsWidgetId()" class="widget-charts w-full h-full flex-auto"></div> | ||
145 | - <div> | ||
146 | - <div | ||
147 | - class="text-center" | ||
148 | - :style="{ | ||
149 | - fontSize: fontSize({ radioRecord: getRadio, basic: 16, max: 18 }), | ||
150 | - color: '#666', | ||
151 | - }" | ||
152 | - > | ||
153 | - {{ props.value.name }} | ||
154 | - </div> | ||
155 | - | ||
156 | - <div | ||
157 | - class="text-xs text-center p-5" | ||
158 | - :style="{ | ||
159 | - fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 12 }), | ||
160 | - color: '#999', | ||
161 | - }" | ||
162 | - > | ||
163 | - <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)"> | ||
164 | - <div class="truncate"> | ||
165 | - <span class="mr-2">更新时间:</span> | ||
166 | - <span> | ||
167 | - {{ getUpdateTime(props.value?.updateTime) }} | ||
168 | - </span> | ||
169 | - </div> | ||
170 | - </Tooltip> | ||
171 | - </div> | ||
172 | - </div> | ||
173 | - </div> | ||
174 | -</template> | ||
175 | - | ||
176 | -<style scoped> | ||
177 | - .widget-charts > div { | ||
178 | - width: 100%; | ||
179 | - height: 100%; | ||
180 | - } | ||
181 | -</style> |
src/views/visual/board/components/InstrumentComponent/DigitalDashBoard.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import { computed, onMounted, onUnmounted, ref, unref } from 'vue'; | ||
8 | - import { Space, Tooltip } from 'ant-design-vue'; | ||
9 | - import { | ||
10 | - DigitalComponentDefaultConfig, | ||
11 | - DigitalDashBoardLayout, | ||
12 | - DigitalDashBoardValue, | ||
13 | - } from './digitalDashBoard.config'; | ||
14 | - import { | ||
15 | - fontSize, | ||
16 | - RadioRecord, | ||
17 | - getUpdateTime, | ||
18 | - DEFAULT_RADIO_RECORD, | ||
19 | - DEFAULT_ANIMATION_INTERVAL, | ||
20 | - } from '../../detail/config/util'; | ||
21 | - import { isNaN } from 'lodash'; | ||
22 | - | ||
23 | - const props = withDefaults( | ||
24 | - defineProps<{ | ||
25 | - layout?: DigitalDashBoardLayout; | ||
26 | - value?: DigitalDashBoardValue; | ||
27 | - radio?: RadioRecord; | ||
28 | - random?: boolean; | ||
29 | - }>(), | ||
30 | - { | ||
31 | - value: () => ({}), | ||
32 | - layout: () => ({ max: 5, keepNumber: 2 }), | ||
33 | - } | ||
34 | - ); | ||
35 | - | ||
36 | - const changeValue = ref(0); | ||
37 | - | ||
38 | - const getPropsValue = computed(() => { | ||
39 | - return { value: unref(changeValue), ...DigitalComponentDefaultConfig, ...props.value }; | ||
40 | - }); | ||
41 | - | ||
42 | - const integerPart = computed(() => { | ||
43 | - let { value = 0 } = unref(getPropsValue); | ||
44 | - const { max = 5 } = props.layout; | ||
45 | - if (isNaN(value)) value = 0; | ||
46 | - let _value = Number(value).toFixed(2).split('.')[0]; | ||
47 | - if (_value.length < max) _value = _value.padStart(5, '0'); | ||
48 | - if (_value.length > max) _value = ''.padStart(5, '9'); | ||
49 | - | ||
50 | - return _value; | ||
51 | - }); | ||
52 | - | ||
53 | - const decimalPart = computed(() => { | ||
54 | - let { value = 0 } = unref(getPropsValue); | ||
55 | - const { keepNumber = 2 } = props.layout; | ||
56 | - if (isNaN(value)) value = 0; | ||
57 | - let _value = Number(value)?.toFixed(2).split('.')[1]; | ||
58 | - if (_value.length < keepNumber) _value = _value.padStart(5, '0'); | ||
59 | - | ||
60 | - if (_value.length > keepNumber) _value = ''.padStart(5, '0'); | ||
61 | - | ||
62 | - return _value; | ||
63 | - }); | ||
64 | - | ||
65 | - const getRadio = computed(() => { | ||
66 | - return props.radio || DEFAULT_RADIO_RECORD; | ||
67 | - }); | ||
68 | - | ||
69 | - let timeout: Nullable<number> = null; | ||
70 | - | ||
71 | - const handleRandom = () => { | ||
72 | - const newValue = Math.floor(Math.random() * 100); | ||
73 | - changeValue.value = newValue; | ||
74 | - }; | ||
75 | - | ||
76 | - const getScale = computed(() => { | ||
77 | - const { width } = props.radio || DEFAULT_RADIO_RECORD; | ||
78 | - return width / 360 > 1 ? 1 : width / 360; | ||
79 | - }); | ||
80 | - | ||
81 | - onMounted(() => { | ||
82 | - if (props.random) | ||
83 | - timeout = setInterval(handleRandom, DEFAULT_ANIMATION_INTERVAL) as unknown as number; | ||
84 | - }); | ||
85 | - | ||
86 | - onUnmounted(() => { | ||
87 | - clearInterval(timeout as number); | ||
88 | - timeout = null; | ||
89 | - }); | ||
90 | -</script> | ||
91 | - | ||
92 | -<template> | ||
93 | - <section class="w-full h-full"> | ||
94 | - <div class="flex flex-col w-full h-full"> | ||
95 | - <div class="flex-1 flex justify-center items-center"> | ||
96 | - <div class="flex px-4 items-center" :style="{ transform: `scale(${getScale})` }"> | ||
97 | - <Space | ||
98 | - justify="end" | ||
99 | - class="justify-end" | ||
100 | - :size="4" | ||
101 | - :style="{ | ||
102 | - backgroundColor: '#585357', | ||
103 | - padding: fontSize({ radioRecord: getRadio, basic: 10 }), | ||
104 | - }" | ||
105 | - > | ||
106 | - <div | ||
107 | - v-for="number in integerPart" | ||
108 | - :key="number" | ||
109 | - class="digital-wrapper__int" | ||
110 | - :style="{ | ||
111 | - color: getPropsValue.fontColor, | ||
112 | - fontSize: fontSize({ radioRecord: getRadio, basic: 20, max: 20 }), | ||
113 | - padding: fontSize({ radioRecord: getRadio, basic: 5 }), | ||
114 | - }" | ||
115 | - > | ||
116 | - <div class="digital-text__int p-1 text-light-50"> {{ number }}</div> | ||
117 | - </div> | ||
118 | - </Space> | ||
119 | - <div | ||
120 | - class="m-0.5 rounded-1/2" | ||
121 | - style="background-color: #333; width: 6px; height: 6px; align-self: flex-end" | ||
122 | - > | ||
123 | - </div> | ||
124 | - <Space | ||
125 | - justify="end" | ||
126 | - class="justify-end" | ||
127 | - :size="4" | ||
128 | - :style="{ | ||
129 | - backgroundColor: '#b74940', | ||
130 | - padding: fontSize({ radioRecord: getRadio, basic: 10 }), | ||
131 | - }" | ||
132 | - > | ||
133 | - <div | ||
134 | - v-for="number in decimalPart" | ||
135 | - :key="number" | ||
136 | - class="digital-wrapper__float" | ||
137 | - :style="{ | ||
138 | - color: getPropsValue.fontColor, | ||
139 | - fontSize: fontSize({ radioRecord: getRadio, basic: 20, max: 20 }), | ||
140 | - padding: fontSize({ radioRecord: getRadio, basic: 5 }), | ||
141 | - }" | ||
142 | - > | ||
143 | - <div class="digital-text__float p-1 text-light-50"> | ||
144 | - {{ number }} | ||
145 | - </div> | ||
146 | - </div> | ||
147 | - </Space> | ||
148 | - <div | ||
149 | - class="px-1 font-bold" | ||
150 | - :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 18, max: 18 }) }" | ||
151 | - > | ||
152 | - {{ getPropsValue.unit }} | ||
153 | - </div> | ||
154 | - </div> | ||
155 | - </div> | ||
156 | - | ||
157 | - <div | ||
158 | - class="text-center truncate" | ||
159 | - :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 18 }) }" | ||
160 | - > | ||
161 | - <span>{{ props.value.name || '电表' }}</span> | ||
162 | - </div> | ||
163 | - | ||
164 | - <div | ||
165 | - class="text-center text-xs p-5" | ||
166 | - :style="{ | ||
167 | - fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 16 }), | ||
168 | - color: '#999', | ||
169 | - }" | ||
170 | - > | ||
171 | - <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)"> | ||
172 | - <div class="truncate"> | ||
173 | - <span class="mr-1">更新时间:</span> | ||
174 | - <span> | ||
175 | - {{ getUpdateTime(props.value?.updateTime) }} | ||
176 | - </span> | ||
177 | - </div> | ||
178 | - </Tooltip> | ||
179 | - </div> | ||
180 | - </div> | ||
181 | - <div></div> | ||
182 | - </section> | ||
183 | -</template> | ||
184 | - | ||
185 | -<style scoped lang="less"> | ||
186 | - .digital-wrapper__int { | ||
187 | - border-radius: 1px; | ||
188 | - box-shadow: inset 0 1px 3px 0 rgba(0, 0, 0, 0.7); | ||
189 | - background: url('/@/assets/images/digital-wrapper-bg-int.png') 0 -1px no-repeat; | ||
190 | - padding: 5px; | ||
191 | - background-size: 100% 100%; | ||
192 | - } | ||
193 | - | ||
194 | - .digital-text_int { | ||
195 | - display: inline-block; | ||
196 | - overflow-wrap: break-word; | ||
197 | - color: rgba(255, 255, 255, 1); | ||
198 | - white-space: nowrap; | ||
199 | - text-align: center; | ||
200 | - } | ||
201 | - | ||
202 | - .digital-wrapper__float { | ||
203 | - border-radius: 1px; | ||
204 | - box-shadow: inset 0 1px 3px 0 rgba(112, 22, 15, 1); | ||
205 | - background: url('/@/assets/images/digital-wrapper-bg-float.png') 0 -1px no-repeat; | ||
206 | - padding: 5px; | ||
207 | - background-size: 100% 100%; | ||
208 | - } | ||
209 | - | ||
210 | - .digital-text_float { | ||
211 | - display: inline-block; | ||
212 | - overflow-wrap: break-word; | ||
213 | - color: rgba(255, 255, 255, 1); | ||
214 | - white-space: nowrap; | ||
215 | - text-align: center; | ||
216 | - } | ||
217 | -</style> |
src/views/visual/board/components/InstrumentComponent/dashBoardComponent.config.ts
deleted
100644 → 0
1 | -import { EChartsOption } from 'echarts'; | ||
2 | -import { FrontComponent, Gradient, GradientColor } from '../../const/const'; | ||
3 | -import { fontSize, RadioRecord } from '../../detail/config/util'; | ||
4 | -import { | ||
5 | - ComponentInfo, | ||
6 | - DataComponentRecord, | ||
7 | - DataSource, | ||
8 | - GradientInfo, | ||
9 | -} from '/@/api/dataBoard/model'; | ||
10 | -import { isArray } from '/@/utils/is'; | ||
11 | -import { buildUUID } from '/@/utils/uuid'; | ||
12 | - | ||
13 | -export interface GradientInfoRecord { | ||
14 | - key: Gradient; | ||
15 | - value: number; | ||
16 | - color: string; | ||
17 | -} | ||
18 | - | ||
19 | -export interface DashBoardValue { | ||
20 | - id: string; | ||
21 | - unit?: string; | ||
22 | - name?: string; | ||
23 | - updateTime?: string; | ||
24 | - value?: number; | ||
25 | - fontColor?: string; | ||
26 | - gradientInfo?: GradientInfoRecord[]; | ||
27 | -} | ||
28 | - | ||
29 | -export interface DashboardComponentLayout { | ||
30 | - chartOption: EChartsOption; | ||
31 | - componentType: FrontComponent; | ||
32 | -} | ||
33 | - | ||
34 | -export const instrumentComponent1 = (params?: Partial<ComponentInfo>): EChartsOption => { | ||
35 | - const { value = 10, unit = '°C' } = params || {}; | ||
36 | - return { | ||
37 | - series: [ | ||
38 | - { | ||
39 | - type: 'gauge', | ||
40 | - radius: '50%', | ||
41 | - center: ['50%', '60%'], | ||
42 | - startAngle: 200, | ||
43 | - endAngle: -20, | ||
44 | - min: 0, | ||
45 | - max: 100, | ||
46 | - splitNumber: 10, | ||
47 | - itemStyle: { | ||
48 | - color: '#FFAB91', | ||
49 | - }, | ||
50 | - progress: { | ||
51 | - show: true, | ||
52 | - width: 30, | ||
53 | - }, | ||
54 | - pointer: { | ||
55 | - show: false, | ||
56 | - }, | ||
57 | - axisLine: { | ||
58 | - lineStyle: { | ||
59 | - width: 30, | ||
60 | - }, | ||
61 | - }, | ||
62 | - axisTick: { | ||
63 | - distance: -35, | ||
64 | - splitNumber: 5, | ||
65 | - lineStyle: { | ||
66 | - width: 2, | ||
67 | - color: '#999', | ||
68 | - }, | ||
69 | - }, | ||
70 | - splitLine: { | ||
71 | - distance: -40, | ||
72 | - length: 10, | ||
73 | - lineStyle: { | ||
74 | - width: 3, | ||
75 | - color: '#999', | ||
76 | - }, | ||
77 | - }, | ||
78 | - axisLabel: { | ||
79 | - distance: 0, | ||
80 | - color: '#999', | ||
81 | - }, | ||
82 | - anchor: { | ||
83 | - show: false, | ||
84 | - }, | ||
85 | - title: { | ||
86 | - show: false, | ||
87 | - }, | ||
88 | - detail: { | ||
89 | - valueAnimation: true, | ||
90 | - width: '60%', | ||
91 | - lineHeight: 10, | ||
92 | - borderRadius: 8, | ||
93 | - offsetCenter: [0, '30%'], | ||
94 | - fontSize: 14, | ||
95 | - fontWeight: 'bolder', | ||
96 | - formatter: `{value} ${unit ?? ''}`, | ||
97 | - color: params?.fontColor || 'inherit', | ||
98 | - }, | ||
99 | - data: [ | ||
100 | - { | ||
101 | - value: value as number, | ||
102 | - }, | ||
103 | - ], | ||
104 | - }, | ||
105 | - { | ||
106 | - type: 'gauge', | ||
107 | - radius: '50%', | ||
108 | - center: ['50%', '60%'], | ||
109 | - startAngle: 200, | ||
110 | - endAngle: -20, | ||
111 | - min: 0, | ||
112 | - max: 100, | ||
113 | - itemStyle: { | ||
114 | - color: '#FD7347', | ||
115 | - }, | ||
116 | - progress: { | ||
117 | - show: true, | ||
118 | - width: 8, | ||
119 | - }, | ||
120 | - pointer: { | ||
121 | - show: false, | ||
122 | - }, | ||
123 | - axisLine: { | ||
124 | - show: false, | ||
125 | - }, | ||
126 | - axisTick: { | ||
127 | - show: false, | ||
128 | - }, | ||
129 | - splitLine: { | ||
130 | - show: false, | ||
131 | - }, | ||
132 | - axisLabel: { | ||
133 | - show: false, | ||
134 | - }, | ||
135 | - detail: { | ||
136 | - show: false, | ||
137 | - }, | ||
138 | - data: [ | ||
139 | - { | ||
140 | - value: value as number, | ||
141 | - }, | ||
142 | - ], | ||
143 | - }, | ||
144 | - ], | ||
145 | - }; | ||
146 | -}; | ||
147 | - | ||
148 | -export const instrumentComponent2 = (params?: Partial<ComponentInfo>): EChartsOption => { | ||
149 | - const { gradientInfo = [], value = 0, unit = 'km/h' } = params || {}; | ||
150 | - const firstRecord = getGradient(Gradient.FIRST, gradientInfo); | ||
151 | - const secondRecord = getGradient(Gradient.SECOND, gradientInfo); | ||
152 | - const thirdRecord = getGradient(Gradient.THIRD, gradientInfo); | ||
153 | - | ||
154 | - let max = thirdRecord?.value || secondRecord?.value || firstRecord?.value || 70; | ||
155 | - max = Number(1 + Array(String(max).length).fill(0).join('')); | ||
156 | - | ||
157 | - const firstGradient = firstRecord?.value ? firstRecord.value / max : 0.3; | ||
158 | - const secondGradient = secondRecord?.value ? secondRecord.value / max : 0.7; | ||
159 | - | ||
160 | - return { | ||
161 | - series: [ | ||
162 | - { | ||
163 | - type: 'gauge', | ||
164 | - min: 0, | ||
165 | - max, | ||
166 | - axisLine: { | ||
167 | - lineStyle: { | ||
168 | - width: 20, | ||
169 | - color: [ | ||
170 | - [firstGradient, firstRecord?.color || GradientColor.FIRST], | ||
171 | - [secondGradient, secondRecord?.color || GradientColor.SECOND], | ||
172 | - [1, thirdRecord?.color || GradientColor.THIRD], | ||
173 | - ], | ||
174 | - }, | ||
175 | - }, | ||
176 | - pointer: { | ||
177 | - itemStyle: { | ||
178 | - color: 'inherit', | ||
179 | - }, | ||
180 | - }, | ||
181 | - axisTick: { | ||
182 | - distance: -30, | ||
183 | - length: 8, | ||
184 | - splitNumber: max / 100, | ||
185 | - lineStyle: { | ||
186 | - color: '#fff', | ||
187 | - width: 2, | ||
188 | - }, | ||
189 | - }, | ||
190 | - splitLine: { | ||
191 | - distance: -10, | ||
192 | - length: 30, | ||
193 | - lineStyle: { | ||
194 | - color: '#fff', | ||
195 | - width: 4, | ||
196 | - }, | ||
197 | - }, | ||
198 | - axisLabel: { | ||
199 | - color: 'inherit', | ||
200 | - distance: 5, | ||
201 | - fontSize: 6, | ||
202 | - }, | ||
203 | - detail: { | ||
204 | - valueAnimation: true, | ||
205 | - formatter: `{value} ${unit ?? ''}`, | ||
206 | - color: params?.fontColor || 'inherit', | ||
207 | - offsetCenter: [0, '70%'], | ||
208 | - fontSize: 14, | ||
209 | - }, | ||
210 | - data: [ | ||
211 | - { | ||
212 | - value: value as number, | ||
213 | - }, | ||
214 | - ], | ||
215 | - }, | ||
216 | - ], | ||
217 | - }; | ||
218 | -}; | ||
219 | - | ||
220 | -export const getGradient = (key: Gradient, record: GradientInfo[] = []) => { | ||
221 | - if (!isArray(record)) return; | ||
222 | - return record.find((item) => item.key === key); | ||
223 | -}; | ||
224 | - | ||
225 | -export const update_instrument_1_font = (radioRecord: RadioRecord) => { | ||
226 | - const basicFontSize = fontSize({ radioRecord, basic: 16, max: 16, min: 12 }); | ||
227 | - return { | ||
228 | - series: [ | ||
229 | - { | ||
230 | - axisLabel: { | ||
231 | - fontSize: basicFontSize, | ||
232 | - }, | ||
233 | - detail: { | ||
234 | - fontSize: basicFontSize, | ||
235 | - }, | ||
236 | - }, | ||
237 | - ], | ||
238 | - } as EChartsOption; | ||
239 | -}; | ||
240 | - | ||
241 | -export const update_instrument_2_font = (radioRecord: RadioRecord) => { | ||
242 | - const axisLabelFontSize = fontSize({ radioRecord, basic: 10, max: 16 }); | ||
243 | - const detailFontSize = fontSize({ radioRecord, basic: 16, max: 16, min: 10 }); | ||
244 | - return { | ||
245 | - series: [ | ||
246 | - { | ||
247 | - axisLabel: { | ||
248 | - fontSize: axisLabelFontSize, | ||
249 | - }, | ||
250 | - detail: { | ||
251 | - fontSize: detailFontSize, | ||
252 | - }, | ||
253 | - }, | ||
254 | - ], | ||
255 | - } as EChartsOption; | ||
256 | -}; | ||
257 | - | ||
258 | -const handleValue = (value: any) => { | ||
259 | - return isNaN(value) ? 0 : Number(value).toFixed(2); | ||
260 | -}; | ||
261 | - | ||
262 | -export const update_instrument_1_value = (params: DashBoardValue) => { | ||
263 | - const { value = 0, unit = '°C', fontColor } = params; | ||
264 | - let max = | ||
265 | - value > 1 | ||
266 | - ? Number( | ||
267 | - 1 + | ||
268 | - Array(String(Math.floor(value)).length) | ||
269 | - .fill(0) | ||
270 | - .join('') | ||
271 | - ) / 2 | ||
272 | - : 100 / 2; | ||
273 | - max = value > max ? max * 2 : max; | ||
274 | - | ||
275 | - return { | ||
276 | - series: [ | ||
277 | - { | ||
278 | - max: max < 100 ? 100 : max, | ||
279 | - data: [{ value: handleValue(value) }], | ||
280 | - detail: { | ||
281 | - formatter: `{value} ${unit ?? ''}`, | ||
282 | - color: fontColor || 'inherit', | ||
283 | - }, | ||
284 | - }, | ||
285 | - { | ||
286 | - max: max < 100 ? 100 : max, | ||
287 | - data: [{ value: handleValue(value) }], | ||
288 | - }, | ||
289 | - ], | ||
290 | - } as EChartsOption; | ||
291 | -}; | ||
292 | - | ||
293 | -export const update_instrument_2_value = (params: DashBoardValue) => { | ||
294 | - const { value = 0, unit = 'km/h', fontColor, gradientInfo } = params; | ||
295 | - const firstRecord = getGradient(Gradient.FIRST, gradientInfo); | ||
296 | - const secondRecord = getGradient(Gradient.SECOND, gradientInfo); | ||
297 | - const thirdRecord = getGradient(Gradient.THIRD, gradientInfo); | ||
298 | - | ||
299 | - let max = thirdRecord?.value || secondRecord?.value || firstRecord?.value || 70; | ||
300 | - max = Number( | ||
301 | - 1 + | ||
302 | - Array(String(Math.floor(max)).length) | ||
303 | - .fill(0) | ||
304 | - .join('') | ||
305 | - ); | ||
306 | - | ||
307 | - max = | ||
308 | - value > 1 | ||
309 | - ? Number( | ||
310 | - 1 + | ||
311 | - Array(String(Math.floor(value)).length) | ||
312 | - .fill(0) | ||
313 | - .join('') | ||
314 | - ) / 2 | ||
315 | - : 100 / 2; | ||
316 | - max = value > max ? max * 2 : max; | ||
317 | - | ||
318 | - const firstGradient = firstRecord?.value ? firstRecord.value / max : 0.3; | ||
319 | - const secondGradient = secondRecord?.value ? secondRecord.value / max : 0.7; | ||
320 | - return { | ||
321 | - series: [ | ||
322 | - { | ||
323 | - max: max < 100 ? 100 : max, | ||
324 | - data: [{ value: handleValue(value) }], | ||
325 | - detail: { | ||
326 | - formatter: `{value} ${unit ?? ''}`, | ||
327 | - color: fontColor || 'inherit', | ||
328 | - }, | ||
329 | - axisLine: { | ||
330 | - lineStyle: { | ||
331 | - width: 20, | ||
332 | - color: [ | ||
333 | - [firstGradient, firstRecord?.color || GradientColor.FIRST], | ||
334 | - [secondGradient, secondRecord?.color || GradientColor.SECOND], | ||
335 | - [1, thirdRecord?.color || GradientColor.THIRD], | ||
336 | - ], | ||
337 | - }, | ||
338 | - }, | ||
339 | - }, | ||
340 | - ], | ||
341 | - } as EChartsOption; | ||
342 | -}; | ||
343 | - | ||
344 | -function setGradientInfo(dataSource: DataSource) { | ||
345 | - const componentInfo = dataSource.componentInfo; | ||
346 | - return instrumentComponent2(componentInfo); | ||
347 | -} | ||
348 | - | ||
349 | -export const Instrument1DefaultConfig: Partial<ComponentInfo> = { | ||
350 | - fontColor: '#FD7347', | ||
351 | -}; | ||
352 | - | ||
353 | -export const Instrument2DefaultConfig: Partial<ComponentInfo> = { | ||
354 | - fontColor: GradientColor.FIRST, | ||
355 | - unit: 'km/h', | ||
356 | - gradientInfo: [ | ||
357 | - { key: Gradient.FIRST, value: 30, color: GradientColor.FIRST }, | ||
358 | - { key: Gradient.SECOND, value: 70, color: GradientColor.SECOND }, | ||
359 | - { key: Gradient.THIRD, value: 80, color: GradientColor.THIRD }, | ||
360 | - ], | ||
361 | -}; | ||
362 | - | ||
363 | -export const transformDashboardComponentConfig = ( | ||
364 | - config: DashboardComponentLayout, | ||
365 | - _record: DataComponentRecord, | ||
366 | - dataSourceRecord: DataSource | ||
367 | -) => { | ||
368 | - let chartOption = config.chartOption; | ||
369 | - if (config.componentType === 'instrument-component-2') { | ||
370 | - chartOption = setGradientInfo(dataSourceRecord); | ||
371 | - } | ||
372 | - if (config.componentType === 'instrument-component-1') { | ||
373 | - const componentInfo = dataSourceRecord.componentInfo; | ||
374 | - chartOption = instrumentComponent1({ | ||
375 | - unit: componentInfo.unit, | ||
376 | - value: 0, | ||
377 | - fontColor: componentInfo.fontColor, | ||
378 | - }); | ||
379 | - } | ||
380 | - return { | ||
381 | - layout: { | ||
382 | - chartOption: chartOption, | ||
383 | - componentType: config.componentType, | ||
384 | - } as DashboardComponentLayout, | ||
385 | - value: { | ||
386 | - id: buildUUID(), | ||
387 | - name: dataSourceRecord.attributeRename || dataSourceRecord.attribute, | ||
388 | - value: dataSourceRecord.componentInfo.value, | ||
389 | - unit: dataSourceRecord.componentInfo.unit, | ||
390 | - updateTime: dataSourceRecord.componentInfo.updateTime, | ||
391 | - fontColor: dataSourceRecord.componentInfo.fontColor, | ||
392 | - gradientInfo: dataSourceRecord.componentInfo.gradientInfo, | ||
393 | - }, | ||
394 | - }; | ||
395 | -}; |
1 | -import { ComponentInfo } from '/@/api/dataBoard/model'; | ||
2 | - | ||
3 | -export interface DigitalDashBoardLayout { | ||
4 | - max: number; | ||
5 | - keepNumber: number; | ||
6 | -} | ||
7 | - | ||
8 | -export interface DigitalDashBoardValue { | ||
9 | - unit?: string; | ||
10 | - name?: string; | ||
11 | - updateTime?: string; | ||
12 | - value?: number; | ||
13 | - fontColor?: string; | ||
14 | -} | ||
15 | - | ||
16 | -export const DigitalComponentDefaultConfig: Partial<ComponentInfo> = { | ||
17 | - fontColor: '#000', | ||
18 | - unit: 'kw/h', | ||
19 | -}; |
src/views/visual/board/components/InstrumentComponent/index.ts
deleted
100644 → 0
src/views/visual/board/components/MapComponent/HistoryDataModel.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { BasicForm, useForm } from '/@/components/Form'; | ||
3 | - import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
4 | - import { | ||
5 | - formSchema, | ||
6 | - getHistorySearchParams, | ||
7 | - SchemaFiled, | ||
8 | - } from '../../../board/detail/config/historyTrend.config'; | ||
9 | - import { HistoryModalOkEmitParams, HistoryModalParams } from './type'; | ||
10 | - import { DataSource } from '/@/api/dataBoard/model'; | ||
11 | - import { ref } from 'vue'; | ||
12 | - import { getAllDeviceByOrg } from '/@/api/dataBoard'; | ||
13 | - import { getDeviceHistoryInfo } from '/@/api/alarm/position'; | ||
14 | - | ||
15 | - const emit = defineEmits(['register', 'ok']); | ||
16 | - | ||
17 | - const [registerForm, { updateSchema, setFieldsValue, validate, getFieldsValue }] = useForm({ | ||
18 | - schemas: formSchema(), | ||
19 | - showActionButtonGroup: false, | ||
20 | - fieldMapToTime: [ | ||
21 | - [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:mm:ss'], | ||
22 | - ], | ||
23 | - }); | ||
24 | - | ||
25 | - const [registerModal, { closeModal }] = useModalInner(async (params: HistoryModalParams) => { | ||
26 | - try { | ||
27 | - const { dataSource = [] } = params; | ||
28 | - const deviceRecord = dataSource?.at(0) || ({} as DataSource); | ||
29 | - if (!deviceRecord.organizationId) return; | ||
30 | - const deviceList = await getAllDeviceByOrg( | ||
31 | - deviceRecord.organizationId, | ||
32 | - deviceRecord.deviceProfileId | ||
33 | - ); | ||
34 | - const options = deviceList | ||
35 | - .filter((item) => item.tbDeviceId === deviceRecord.deviceId) | ||
36 | - .map((item) => ({ ...item, label: item.name, value: item.tbDeviceId })); | ||
37 | - const attKey = dataSource.map((item) => ({ | ||
38 | - ...item, | ||
39 | - label: item.attribute, | ||
40 | - value: item.attribute, | ||
41 | - })); | ||
42 | - updateSchema([ | ||
43 | - { | ||
44 | - field: SchemaFiled.DEVICE_ID, | ||
45 | - componentProps: { | ||
46 | - options, | ||
47 | - }, | ||
48 | - }, | ||
49 | - { | ||
50 | - field: SchemaFiled.KEYS, | ||
51 | - component: 'Select', | ||
52 | - defaultValue: attKey.map((item) => item.value), | ||
53 | - componentProps: { | ||
54 | - options: attKey, | ||
55 | - mode: 'multiple', | ||
56 | - disabled: true, | ||
57 | - }, | ||
58 | - }, | ||
59 | - ]); | ||
60 | - | ||
61 | - setFieldsValue({ | ||
62 | - [SchemaFiled.DEVICE_ID]: deviceRecord.deviceId, | ||
63 | - [SchemaFiled.KEYS]: attKey.map((item) => item.value), | ||
64 | - }); | ||
65 | - } catch (error) { | ||
66 | - throw error; | ||
67 | - } | ||
68 | - }); | ||
69 | - | ||
70 | - const loading = ref(false); | ||
71 | - const handleOk = async () => { | ||
72 | - try { | ||
73 | - await validate(); | ||
74 | - let value = getFieldsValue(); | ||
75 | - | ||
76 | - value = getHistorySearchParams(value); | ||
77 | - | ||
78 | - loading.value = true; | ||
79 | - | ||
80 | - const res = await getDeviceHistoryInfo({ | ||
81 | - ...value, | ||
82 | - [SchemaFiled.KEYS]: value[SchemaFiled.KEYS].join(','), | ||
83 | - }); | ||
84 | - | ||
85 | - let timespanList = Object.keys(res).reduce((prev, next) => { | ||
86 | - const ts = res[next].map((item) => item.ts); | ||
87 | - return [...prev, ...ts]; | ||
88 | - }, [] as number[]); | ||
89 | - timespanList = [...new Set(timespanList)]; | ||
90 | - | ||
91 | - const track: Record<'lng' | 'lat', number>[] = []; | ||
92 | - const keys = Object.keys(res); | ||
93 | - | ||
94 | - for (const ts of timespanList) { | ||
95 | - const list: { ts: number; value: number }[] = []; | ||
96 | - for (const key of keys) { | ||
97 | - const record = res[key].find((item) => ts === item.ts); | ||
98 | - list.push(record as any); | ||
99 | - } | ||
100 | - if (list.every(Boolean)) { | ||
101 | - const lng = list.at(0)?.value; | ||
102 | - const lat = list.at(1)?.value; | ||
103 | - if (lng && lat) track.push({ lng, lat }); | ||
104 | - } | ||
105 | - } | ||
106 | - emit('ok', { track, value } as HistoryModalOkEmitParams); | ||
107 | - closeModal(); | ||
108 | - } catch (error) { | ||
109 | - throw error; | ||
110 | - } finally { | ||
111 | - loading.value = false; | ||
112 | - } | ||
113 | - }; | ||
114 | -</script> | ||
115 | - | ||
116 | -<template> | ||
117 | - <BasicModal | ||
118 | - title="历史轨迹" | ||
119 | - @register="registerModal" | ||
120 | - @ok="handleOk" | ||
121 | - :ok-button-props="{ loading }" | ||
122 | - > | ||
123 | - <BasicForm @register="registerForm" /> | ||
124 | - </BasicModal> | ||
125 | -</template> |
src/views/visual/board/components/MapComponent/MapComponent.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - components: { Spin }, | ||
4 | - inheritAttrs: false, | ||
5 | - }; | ||
6 | -</script> | ||
7 | -<script lang="ts" setup> | ||
8 | - import { computed, onMounted, reactive, ref, unref, watchEffect } from 'vue'; | ||
9 | - import { RadioRecord } from '../../detail/config/util'; | ||
10 | - import { MapComponentLayout, MapComponentValue } from './map.config'; | ||
11 | - import { | ||
12 | - ClockCircleOutlined, | ||
13 | - PlayCircleOutlined, | ||
14 | - PauseCircleOutlined, | ||
15 | - } from '@ant-design/icons-vue'; | ||
16 | - import { Button, Spin, Tooltip } from 'ant-design-vue'; | ||
17 | - import { FrontComponent } from '../../const/const'; | ||
18 | - import { buildUUID } from '/@/utils/uuid'; | ||
19 | - import { useModal } from '/@/components/Modal'; | ||
20 | - import HistoryDataModel from './HistoryDataModel.vue'; | ||
21 | - import { HistoryModalOkEmitParams, HistoryModalParams } from './type'; | ||
22 | - import { formatToDateTime } from '/@/utils/dateUtil'; | ||
23 | - import { isEqual } from 'lodash-es'; | ||
24 | - import { useAsyncQueue } from '/@/views/device/localtion/useAsyncQueue'; | ||
25 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
26 | - | ||
27 | - // useVisualBoardContext(); | ||
28 | - type TrackRecord = Record<'lng' | 'lat' | 'ts', number>; | ||
29 | - | ||
30 | - const startMethodName = `trackPlayMethod_${buildUUID()}`; | ||
31 | - | ||
32 | - const wrapId = `bai-map-${buildUUID()}`; | ||
33 | - | ||
34 | - enum TrackAnimationStatus { | ||
35 | - PLAY = 1, | ||
36 | - DONE = 2, | ||
37 | - PAUSE = 3, | ||
38 | - } | ||
39 | - | ||
40 | - const props = withDefaults( | ||
41 | - defineProps<{ | ||
42 | - value?: MapComponentValue; | ||
43 | - layout?: MapComponentLayout; | ||
44 | - radio?: RadioRecord; | ||
45 | - random?: boolean; | ||
46 | - }>(), | ||
47 | - { | ||
48 | - random: true, | ||
49 | - } | ||
50 | - ); | ||
51 | - | ||
52 | - const wrapRef = ref<HTMLDivElement | null>(null); | ||
53 | - const trackAni = ref<Nullable<any>>(null); | ||
54 | - let mapInstance: Nullable<Recordable> = null; | ||
55 | - | ||
56 | - const trackList = ref<TrackRecord[]>([]); | ||
57 | - | ||
58 | - watchEffect(() => { | ||
59 | - if ( | ||
60 | - props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_REAL && | ||
61 | - props.value?.track?.length | ||
62 | - ) { | ||
63 | - const lng = props.value.track.at(0)!; | ||
64 | - const lat = props.value.track.at(1)!; | ||
65 | - const record = { | ||
66 | - lng: lng.value, | ||
67 | - lat: lat.value, | ||
68 | - ts: lng.ts, | ||
69 | - }; | ||
70 | - if (unref(trackList).length && isEqual(unref(trackList).at(-1), record)) return; | ||
71 | - marketPoint({ lat: lat.value, lng: lng.value }); | ||
72 | - trackList.value.push(record); | ||
73 | - | ||
74 | - randomAnimation(unref(trackList)); | ||
75 | - // marketPoint(record); | ||
76 | - } | ||
77 | - }); | ||
78 | - | ||
79 | - function marketPoint(params: Record<'lng' | 'lat', number>) { | ||
80 | - const { lng, lat } = params; | ||
81 | - const BMap = (window as any).BMapGL; | ||
82 | - const marker = new BMap.Marker(new BMap.Point(lng, lat)); | ||
83 | - unref(mapInstance)?.centerAndZoom(new BMap.Point(lng, lat)); | ||
84 | - unref(mapInstance)?.addOverlay(marker); | ||
85 | - } | ||
86 | - | ||
87 | - const prepare = ref(false); | ||
88 | - async function initMap() { | ||
89 | - const wrapEl = unref(wrapRef); | ||
90 | - if (!wrapEl) return; | ||
91 | - const BMapGL = (window as any).BMapGL; | ||
92 | - mapInstance = new BMapGL.Map(wrapId); | ||
93 | - const point = new BMapGL.Point(104.09457, 30.53189); | ||
94 | - mapInstance!.centerAndZoom(point, 15); | ||
95 | - mapInstance!.enableScrollWheelZoom(true); | ||
96 | - props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY && | ||
97 | - props.random && | ||
98 | - randomAnimation(); | ||
99 | - } | ||
100 | - | ||
101 | - const randomAnimation = (path?: Record<'lng' | 'lat', number>[], clearOverlays = true) => { | ||
102 | - path = path || [ | ||
103 | - { | ||
104 | - lng: 116.297611, | ||
105 | - lat: 40.047363, | ||
106 | - }, | ||
107 | - { | ||
108 | - lng: 116.302839, | ||
109 | - lat: 40.048219, | ||
110 | - }, | ||
111 | - { | ||
112 | - lng: 116.308301, | ||
113 | - lat: 40.050566, | ||
114 | - }, | ||
115 | - { | ||
116 | - lng: 116.305732, | ||
117 | - lat: 40.054957, | ||
118 | - }, | ||
119 | - { | ||
120 | - lng: 116.304754, | ||
121 | - lat: 40.057953, | ||
122 | - }, | ||
123 | - { | ||
124 | - lng: 116.306487, | ||
125 | - lat: 40.058312, | ||
126 | - }, | ||
127 | - { | ||
128 | - lng: 116.307223, | ||
129 | - lat: 40.056379, | ||
130 | - }, | ||
131 | - ]; | ||
132 | - | ||
133 | - const point: any[] = []; | ||
134 | - const BMapGL = (window as any).BMapGL; | ||
135 | - | ||
136 | - clearOverlays && unref(mapInstance)?.clearOverlays(); | ||
137 | - | ||
138 | - for (const { lng, lat } of path) { | ||
139 | - point.push(new BMapGL.Point(lng, lat)); | ||
140 | - } | ||
141 | - | ||
142 | - const pl = new BMapGL.Polyline(point); | ||
143 | - const BMapGLLib = (window as any).BMapGLLib; | ||
144 | - | ||
145 | - marketPoint({ lat: path.at(0)?.lat, lng: path.at(0)?.lng }); | ||
146 | - const dynamicPlayMethod = { | ||
147 | - [startMethodName]() { | ||
148 | - const duration = 5000; | ||
149 | - const delay = 300; | ||
150 | - trackAni.value = new BMapGLLib.TrackAnimation(unref(mapInstance), pl, { | ||
151 | - overallView: true, | ||
152 | - tilt: 30, | ||
153 | - duration, | ||
154 | - delay, | ||
155 | - }); | ||
156 | - trackAni.value!.start(); | ||
157 | - setTimeout(() => { | ||
158 | - marketPoint({ lat: path.at(-1)?.lat, lng: path.at(-1)?.lng }); | ||
159 | - }, duration + delay); | ||
160 | - }, | ||
161 | - }; | ||
162 | - | ||
163 | - (window as any)[startMethodName] = dynamicPlayMethod[startMethodName]; | ||
164 | - | ||
165 | - setTimeout(`${startMethodName}()`); | ||
166 | - }; | ||
167 | - | ||
168 | - const { setTask, executeFlag } = useAsyncQueue(); | ||
169 | - onMounted(() => { | ||
170 | - let interval: Nullable<NodeJS.Timer> = setInterval(() => { | ||
171 | - if ((window as any).BMapGL) { | ||
172 | - prepare.value = true; | ||
173 | - executeFlag.value = true; | ||
174 | - clearInterval(interval!); | ||
175 | - interval = null; | ||
176 | - } | ||
177 | - }, 1000); | ||
178 | - if (!(window as any).BMapGL) { | ||
179 | - setTask(initMap); | ||
180 | - return; | ||
181 | - } | ||
182 | - initMap(); | ||
183 | - }); | ||
184 | - | ||
185 | - const timeRange = reactive<Record<'start' | 'end', Nullable<number>>>({ | ||
186 | - start: null, | ||
187 | - end: null, | ||
188 | - }); | ||
189 | - const getTimeRange = computed(() => { | ||
190 | - const { start, end } = timeRange; | ||
191 | - if (!start || !end) return `- 请选择`; | ||
192 | - return ` - 从 ${formatToDateTime(start, 'YYYY-MM-DD HH:mm:ss')} 到 ${formatToDateTime( | ||
193 | - end, | ||
194 | - 'YYYY-MM-DD HH:mm:ss' | ||
195 | - )}`; | ||
196 | - }); | ||
197 | - | ||
198 | - const [register, { openModal }] = useModal(); | ||
199 | - | ||
200 | - const handleTrackSwitch = () => { | ||
201 | - openModal(true, { | ||
202 | - dataSource: props.value?.dataSource || [], | ||
203 | - } as HistoryModalParams); | ||
204 | - }; | ||
205 | - | ||
206 | - const getTrackPlayStatus = computed(() => { | ||
207 | - return (trackAni.value || {})._status; | ||
208 | - }); | ||
209 | - | ||
210 | - const { createMessage } = useMessage(); | ||
211 | - const handlePlay = () => { | ||
212 | - const { start, end } = timeRange; | ||
213 | - | ||
214 | - if (!props.random && (!start || !end)) { | ||
215 | - createMessage.warning('请先选择时间范围'); | ||
216 | - } | ||
217 | - if (unref(getTrackPlayStatus) === TrackAnimationStatus.DONE) unref(trackAni).start(); | ||
218 | - else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PLAY) unref(trackAni).pause(); | ||
219 | - else if (unref(getTrackPlayStatus) === TrackAnimationStatus.PAUSE) unref(trackAni).continue(); | ||
220 | - }; | ||
221 | - | ||
222 | - const handleRenderHistroyData = (params: HistoryModalOkEmitParams) => { | ||
223 | - const { track, value } = params; | ||
224 | - track.length && randomAnimation(track as unknown as Record<'lng' | 'lat', number>[]); | ||
225 | - timeRange.start = value.startTs as number; | ||
226 | - timeRange.end = value.endTs as number; | ||
227 | - }; | ||
228 | -</script> | ||
229 | - | ||
230 | -<template> | ||
231 | - <div class="w-full h-full flex justify-center items-center flex-col p-2"> | ||
232 | - <div | ||
233 | - class="w-full flex justify-end" | ||
234 | - v-if="props.layout?.componentType === FrontComponent.MAP_COMPONENT_TRACK_HISTORY" | ||
235 | - > | ||
236 | - <Button | ||
237 | - v-if="!random" | ||
238 | - type="text" | ||
239 | - class="!px-2 flex-auto !text-left truncate" | ||
240 | - @click="handleTrackSwitch" | ||
241 | - > | ||
242 | - <div class="w-full truncate text-gray-500 flex items-center"> | ||
243 | - <ClockCircleOutlined /> | ||
244 | - <span class="mx-1">历史</span> | ||
245 | - <Tooltip :title="getTimeRange.replace('-', '')"> | ||
246 | - <span class="truncate"> | ||
247 | - {{ getTimeRange }} | ||
248 | - </span> | ||
249 | - </Tooltip> | ||
250 | - </div> | ||
251 | - </Button> | ||
252 | - <Button type="text" class="!px-2 !text-gray-500" @click="handlePlay"> | ||
253 | - <PlayCircleOutlined v-show="getTrackPlayStatus !== TrackAnimationStatus.PLAY" /> | ||
254 | - <PauseCircleOutlined | ||
255 | - class="!ml-0" | ||
256 | - v-show="getTrackPlayStatus === TrackAnimationStatus.PLAY" | ||
257 | - /> | ||
258 | - <span> | ||
259 | - {{ getTrackPlayStatus !== TrackAnimationStatus.PLAY ? '播放轨迹' : '暂停播放' }} | ||
260 | - </span> | ||
261 | - </Button> | ||
262 | - </div> | ||
263 | - <Spin | ||
264 | - class="w-full h-full !flex flex-col justify-center items-center pointer-events-none" | ||
265 | - :spinning="!prepare" | ||
266 | - tip="地图加载中..." | ||
267 | - /> | ||
268 | - <div v-show="prepare" ref="wrapRef" :id="wrapId" class="w-full h-full no-drag"></div> | ||
269 | - <HistoryDataModel @register="register" @ok="handleRenderHistroyData" /> | ||
270 | - </div> | ||
271 | -</template> |
src/views/visual/board/components/MapComponent/map.config.ts
deleted
100644 → 0
1 | -import { FrontComponent } from '../../const/const'; | ||
2 | -import { ComponentConfig } from '../../types/type'; | ||
3 | -import { DataSource } from '/@/api/dataBoard/model'; | ||
4 | - | ||
5 | -export interface MapComponentLayout { | ||
6 | - componentType?: FrontComponent; | ||
7 | -} | ||
8 | - | ||
9 | -export interface MapComponentValue { | ||
10 | - icon?: string; | ||
11 | - track?: Record<'ts' | 'value', number>[]; | ||
12 | - dataSource?: DataSource[]; | ||
13 | -} | ||
14 | - | ||
15 | -interface Config { | ||
16 | - componentType?: FrontComponent; | ||
17 | -} | ||
18 | - | ||
19 | -export const MaphistoryTrackConfig: Config = { | ||
20 | - componentType: FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | ||
21 | -}; | ||
22 | - | ||
23 | -export const MapRealTrackConfig: Config = { | ||
24 | - componentType: FrontComponent.MAP_COMPONENT_TRACK_REAL, | ||
25 | -}; | ||
26 | - | ||
27 | -const getTrack = (dataSource: DataSource[], config: Config) => { | ||
28 | - if (dataSource.length >= 2) { | ||
29 | - const trackRecord = dataSource.slice(0, 2); | ||
30 | - if ( | ||
31 | - !trackRecord.every((item) => item.componentInfo.value) && | ||
32 | - config.componentType === FrontComponent.MAP_COMPONENT_TRACK_REAL | ||
33 | - ) | ||
34 | - return { track: [], dataSource: [] }; | ||
35 | - | ||
36 | - const track = trackRecord.map((item) => { | ||
37 | - return { | ||
38 | - ts: item.componentInfo.updateTime, | ||
39 | - value: item.componentInfo.value || 0, | ||
40 | - }; | ||
41 | - }); | ||
42 | - return { track, dataSource }; | ||
43 | - } | ||
44 | - return { track: [], dataSource: [] }; | ||
45 | -}; | ||
46 | - | ||
47 | -export const transfromMapComponentConfig: ComponentConfig['transformConfig'] = ( | ||
48 | - componentConfig: Config, | ||
49 | - _record, | ||
50 | - dataSourceRecord | ||
51 | -) => { | ||
52 | - const { track, dataSource } = getTrack(dataSourceRecord as DataSource[], componentConfig); | ||
53 | - return { | ||
54 | - layout: { | ||
55 | - ...componentConfig, | ||
56 | - } as MapComponentLayout, | ||
57 | - value: { | ||
58 | - track, | ||
59 | - dataSource, | ||
60 | - } as MapComponentValue, | ||
61 | - }; | ||
62 | -}; |
src/views/visual/board/components/MapComponent/type.ts
deleted
100644 → 0
1 | -import { SchemaFiled } from '../../detail/config/historyTrend.config'; | ||
2 | -import { DataSource } from '/@/api/dataBoard/model'; | ||
3 | - | ||
4 | -export interface HistoryModalParams { | ||
5 | - dataSource?: DataSource[]; | ||
6 | -} | ||
7 | - | ||
8 | -export interface HistoryModalOkEmitParams { | ||
9 | - track: { | ||
10 | - lng: number | string; | ||
11 | - lat: number | string; | ||
12 | - }[]; | ||
13 | - value: Record<SchemaFiled, string | number>; | ||
14 | -} |
src/views/visual/board/components/Other/CommandSendButton.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import icon from '/src/assets/icons/command.svg'; | ||
3 | - | ||
4 | - const props = defineProps<{ | ||
5 | - value?: boolean; | ||
6 | - }>(); | ||
7 | - | ||
8 | - const emit = defineEmits(['update:value', 'change']); | ||
9 | - | ||
10 | - const handleChange = (event: Event) => { | ||
11 | - const _value = (event.target as HTMLInputElement).checked; | ||
12 | - emit('update:value', _value); | ||
13 | - emit('change', _value); | ||
14 | - }; | ||
15 | -</script> | ||
16 | - | ||
17 | -<template> | ||
18 | - <div class="command-send-button"> | ||
19 | - 123 | ||
20 | - <a href="javascript:;"> | ||
21 | - <img :src="icon" alt="" /> | ||
22 | - </a> | ||
23 | - </div> | ||
24 | -</template> | ||
25 | - | ||
26 | -<style scoped lang="less"> | ||
27 | - .command-send-button { | ||
28 | - width: 80px; | ||
29 | - height: 60px; | ||
30 | - position: relative; | ||
31 | - | ||
32 | - a { | ||
33 | - position: absolute; | ||
34 | - box-sizing: border-box; | ||
35 | - cursor: pointer; | ||
36 | - text-decoration: none; | ||
37 | - color: #fff; | ||
38 | - background: #f2385a; | ||
39 | - top: 0; | ||
40 | - padding: 0 16px; | ||
41 | - height: 60px; | ||
42 | - width: 80px; | ||
43 | - overflow: hidden; | ||
44 | - font-size: 20px; | ||
45 | - line-height: 60px; | ||
46 | - border-radius: 10px; | ||
47 | - box-shadow: 0 15px 0 0 #f02046, 0 0 20px 0 #bbb; | ||
48 | - transition: all 0.2s; | ||
49 | - } | ||
50 | - } | ||
51 | -</style> |
src/views/visual/board/components/Other/IndicatorLight.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - const props = defineProps<{ | ||
3 | - value?: boolean; | ||
4 | - }>(); | ||
5 | - | ||
6 | - const emit = defineEmits(['update:value', 'change']); | ||
7 | - | ||
8 | - const handleChange = (event: Event) => { | ||
9 | - const _value = (event.target as HTMLInputElement).checked; | ||
10 | - emit('update:value', _value); | ||
11 | - emit('change', _value); | ||
12 | - }; | ||
13 | -</script> | ||
14 | - | ||
15 | -<template> | ||
16 | - <label class="indicator-light-switch"> | ||
17 | - <input :value="props.value" :checked="props.value" type="checkbox" @change="handleChange" /> | ||
18 | - <div class="panel"></div> | ||
19 | - </label> | ||
20 | -</template> | ||
21 | - | ||
22 | -<style scoped lang="less"> | ||
23 | - .indicator-light-switch { | ||
24 | - input[type='checkbox'] { | ||
25 | - display: none; | ||
26 | - } | ||
27 | - .panel { | ||
28 | - position: relative; | ||
29 | - width: 60px; | ||
30 | - height: 60px; | ||
31 | - cursor: pointer; | ||
32 | - border-radius: 50%; | ||
33 | - background: linear-gradient(#dedede, #fdfdfd); | ||
34 | - box-shadow: 0 3px 5px rgb(0 0 0 / 25%), inset 0 1px 0 hsl(0deg 0% 100% / 30%), | ||
35 | - inset 0 -5px 5px hsl(0deg 0% 39% / 10%), inset 0 5px 5px hsl(0deg 0% 100% / 30%); | ||
36 | - } | ||
37 | - | ||
38 | - .panel::after, | ||
39 | - .panel::before { | ||
40 | - content: ''; | ||
41 | - position: absolute; | ||
42 | - width: 20%; | ||
43 | - height: 20%; | ||
44 | - border-radius: 50%; | ||
45 | - left: 40%; | ||
46 | - top: 40%; | ||
47 | - } | ||
48 | - | ||
49 | - input:checked ~ .panel::after { | ||
50 | - display: none; | ||
51 | - } | ||
52 | - | ||
53 | - input:not(:checked) ~ .panel::before { | ||
54 | - display: none; | ||
55 | - } | ||
56 | - | ||
57 | - input:not(:checked) ~ .panel { | ||
58 | - background: linear-gradient(#eaeaea, #eaeaea); | ||
59 | - } | ||
60 | - | ||
61 | - .panel::after { | ||
62 | - background-color: #ddd; | ||
63 | - background: radial-gradient(40% 35%, #ccc, #969696 60%); | ||
64 | - box-shadow: inset 0 2px 1px rgb(0 0 0 / 15%), 0 2px 5px hsl(0deg 0% 78% / 10%); | ||
65 | - } | ||
66 | - | ||
67 | - .panel::before { | ||
68 | - background-color: #25d025; | ||
69 | - box-shadow: inset 0 3px 5px 1px rgb(0 0 0 / 10%), 0 1px 0 hsl(0deg 0% 100% / 40%), | ||
70 | - 0 0 10px 2px rgb(0 210 0 / 50%); | ||
71 | - } | ||
72 | - } | ||
73 | -</style> |
src/views/visual/board/components/Other/InformationPanel.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { SvgIcon } from '/@/components/Icon'; | ||
3 | - import { Statistic } from 'ant-design-vue'; | ||
4 | - | ||
5 | - const props = defineProps({ | ||
6 | - icon: { | ||
7 | - type: String, | ||
8 | - default: 'wind-speed', | ||
9 | - }, | ||
10 | - color: { | ||
11 | - type: String, | ||
12 | - default: 'blue', | ||
13 | - }, | ||
14 | - updateTime: { | ||
15 | - type: String, | ||
16 | - default: '2022-08-26 11:11:23', | ||
17 | - }, | ||
18 | - }); | ||
19 | -</script> | ||
20 | - | ||
21 | -<template> | ||
22 | - <section class="flex flex-col h-full justify-center w-full p-2"> | ||
23 | - <div class="flex items-center"> | ||
24 | - <div class="flex flex-col justify-center items-center"> | ||
25 | - <SvgIcon | ||
26 | - :style="{ color: props.color, width: '40%', height: '40%' }" | ||
27 | - :name="props.icon" | ||
28 | - prefix="iconfont" | ||
29 | - /> | ||
30 | - <div class="text-sm my-2">温度</div> | ||
31 | - </div> | ||
32 | - <div class="text-center flex-auto"> | ||
33 | - <Statistic | ||
34 | - value="123" | ||
35 | - :value-style="{ display: 'flex', fontSize: '20px', justifyContent: 'center' }" | ||
36 | - :suffix="'%'" | ||
37 | - /> | ||
38 | - </div> | ||
39 | - </div> | ||
40 | - <div class="text-xs text-gray-500 text-center truncate" :title="props.updateTime"> | ||
41 | - 更新时间: {{ props.updateTime }} | ||
42 | - </div> | ||
43 | - </section> | ||
44 | -</template> |
src/views/visual/board/components/Other/LightBulbSwitch.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import on from '/@/assets/icons/light-bulb-on.svg'; | ||
3 | - import off from '/@/assets/icons/light-bulb-off.svg'; | ||
4 | - | ||
5 | - const props = defineProps<{ | ||
6 | - value?: boolean; | ||
7 | - }>(); | ||
8 | - | ||
9 | - const emit = defineEmits(['update:value', 'change']); | ||
10 | - | ||
11 | - const handleChange = (event: Event) => { | ||
12 | - const _value = (event.target as HTMLInputElement).checked; | ||
13 | - emit('update:value', _value); | ||
14 | - emit('change', _value); | ||
15 | - }; | ||
16 | -</script> | ||
17 | - | ||
18 | -<template> | ||
19 | - <label class="light-bulb-switch"> | ||
20 | - <input :value="props.value" :checked="props.value" type="checkbox" @change="handleChange" /> | ||
21 | - <div class="light-bulb__on"> <img :src="on" alt="开" /> </div> | ||
22 | - <div class="light-bulb__off"> <img :src="off" alt="关" /> </div> | ||
23 | - </label> | ||
24 | -</template> | ||
25 | - | ||
26 | -<style scoped lang="less"> | ||
27 | - .light-bulb-switch { | ||
28 | - input[type='checkbox'] { | ||
29 | - display: none; | ||
30 | - } | ||
31 | - | ||
32 | - input:checked ~ .light-bulb__off { | ||
33 | - display: none; | ||
34 | - } | ||
35 | - | ||
36 | - input:not(:checked) ~ .light-bulb__on { | ||
37 | - display: none; | ||
38 | - } | ||
39 | - | ||
40 | - .light-bulb__on > img, | ||
41 | - .light-bulb__off > img { | ||
42 | - width: 48px; | ||
43 | - height: 48px; | ||
44 | - cursor: pointer; | ||
45 | - } | ||
46 | - } | ||
47 | -</style> |
src/views/visual/board/components/Other/RockerSwitch.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - const props = defineProps<{ | ||
3 | - value?: boolean; | ||
4 | - }>(); | ||
5 | - | ||
6 | - const emit = defineEmits(['update:value', 'change']); | ||
7 | - | ||
8 | - const handleChange = (event: Event) => { | ||
9 | - const _value = (event.target as HTMLInputElement).checked; | ||
10 | - emit('update:value', _value); | ||
11 | - emit('change', _value); | ||
12 | - }; | ||
13 | -</script> | ||
14 | - | ||
15 | -<template> | ||
16 | - <label class="rocker-switch"> | ||
17 | - <input :value="props.value" type="checkbox" :checked="props.value" @change="handleChange" /> | ||
18 | - <span class="switch-left">ON</span> | ||
19 | - <span class="switch-right">OFF</span> | ||
20 | - </label> | ||
21 | -</template> | ||
22 | - | ||
23 | -<style scoped lang="less"> | ||
24 | - .rocker-switch { | ||
25 | - display: flex; | ||
26 | - width: 72px; | ||
27 | - font-size: 14px; | ||
28 | - color: #fff; | ||
29 | - font-weight: 700; | ||
30 | - letter-spacing: 1px; | ||
31 | - border: 0.5em solid #eee; | ||
32 | - background-color: #999; | ||
33 | - user-select: none; | ||
34 | - | ||
35 | - input[type='checkbox'] { | ||
36 | - display: none; | ||
37 | - } | ||
38 | - | ||
39 | - input:checked + .switch-left + .switch-right { | ||
40 | - transform: rotate(-15deg) skew(-15deg) translate(-1px, -5px); | ||
41 | - } | ||
42 | - input:checked + .switch-left + .switch-right::before { | ||
43 | - background-color: #ccc; | ||
44 | - content: ''; | ||
45 | - position: absolute; | ||
46 | - width: 3px; | ||
47 | - height: 30px; | ||
48 | - transform: skewY(75deg) skewX(4deg) rotate(5deg) translateX(1px); | ||
49 | - top: 1.5px; | ||
50 | - right: -1.5px; | ||
51 | - } | ||
52 | - | ||
53 | - input + .switch-left { | ||
54 | - transform: rotate(15deg) skew(17deg) translate(1px, -5px); | ||
55 | - } | ||
56 | - | ||
57 | - input:checked + .switch-left { | ||
58 | - background-color: #0084d0; | ||
59 | - transform: none; | ||
60 | - } | ||
61 | - | ||
62 | - input:not(:checked) + .switch-left { | ||
63 | - background-color: #ccc; | ||
64 | - } | ||
65 | - | ||
66 | - input:not(:checked) + .switch-left + .switch-right { | ||
67 | - background-color: #bd5757; | ||
68 | - color: #fff; | ||
69 | - } | ||
70 | - | ||
71 | - input + .switch-left::before { | ||
72 | - background-color: #ccc; | ||
73 | - content: ''; | ||
74 | - position: absolute; | ||
75 | - width: 3px; | ||
76 | - height: 30px; | ||
77 | - background-color: #ccc; | ||
78 | - transform: skewY(-75deg) skewX(-4deg) rotate(-5deg) translateX(-1.5px); | ||
79 | - top: -1.5px; | ||
80 | - left: -1.5px; | ||
81 | - } | ||
82 | - | ||
83 | - .switch-left { | ||
84 | - width: 36px; | ||
85 | - height: 30px; | ||
86 | - text-align: center; | ||
87 | - line-height: 30px; | ||
88 | - cursor: pointer; | ||
89 | - } | ||
90 | - | ||
91 | - .switch-right { | ||
92 | - color: #888; | ||
93 | - width: 36px; | ||
94 | - height: 30px; | ||
95 | - text-align: center; | ||
96 | - line-height: 30px; | ||
97 | - background-color: #ddd; | ||
98 | - cursor: pointer; | ||
99 | - } | ||
100 | - } | ||
101 | -</style> |
src/views/visual/board/components/PictureComponent/PictureComponent.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import { computed, ref, watch } from 'vue'; | ||
8 | - import { Tooltip, Image as AntImage } from 'ant-design-vue'; | ||
9 | - import { | ||
10 | - getUpdateTime, | ||
11 | - DEFAULT_RADIO_RECORD, | ||
12 | - fontSize, | ||
13 | - RadioRecord, | ||
14 | - } from '../../detail/config/util'; | ||
15 | - import { PictureComponentValue } from './pictureComponent.config'; | ||
16 | - | ||
17 | - const props = defineProps<{ | ||
18 | - layout?: Recordable; | ||
19 | - value?: PictureComponentValue; | ||
20 | - radio?: RadioRecord; | ||
21 | - }>(); | ||
22 | - | ||
23 | - const fallback = | ||
24 | - 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMIAAADDCAYAAADQvc6UAAABRWlDQ1BJQ0MgUHJvZmlsZQAAKJFjYGASSSwoyGFhYGDIzSspCnJ3UoiIjFJgf8LAwSDCIMogwMCcmFxc4BgQ4ANUwgCjUcG3awyMIPqyLsis7PPOq3QdDFcvjV3jOD1boQVTPQrgSkktTgbSf4A4LbmgqISBgTEFyFYuLykAsTuAbJEioKOA7DkgdjqEvQHEToKwj4DVhAQ5A9k3gGyB5IxEoBmML4BsnSQk8XQkNtReEOBxcfXxUQg1Mjc0dyHgXNJBSWpFCYh2zi+oLMpMzyhRcASGUqqCZ16yno6CkYGRAQMDKMwhqj/fAIcloxgHQqxAjIHBEugw5sUIsSQpBobtQPdLciLEVJYzMPBHMDBsayhILEqEO4DxG0txmrERhM29nYGBddr//5/DGRjYNRkY/l7////39v///y4Dmn+LgeHANwDrkl1AuO+pmgAAADhlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAAqACAAQAAAABAAAAwqADAAQAAAABAAAAwwAAAAD9b/HnAAAHlklEQVR4Ae3dP3PTWBSGcbGzM6GCKqlIBRV0dHRJFarQ0eUT8LH4BnRU0NHR0UEFVdIlFRV7TzRksomPY8uykTk/zewQfKw/9znv4yvJynLv4uLiV2dBoDiBf4qP3/ARuCRABEFAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghggQAQZQKAnYEaQBAQaASKIAQJEkAEEegJmBElAoBEgghgg0Aj8i0JO4OzsrPv69Wv+hi2qPHr0qNvf39+iI97soRIh4f3z58/u7du3SXX7Xt7Z2enevHmzfQe+oSN2apSAPj09TSrb+XKI/f379+08+A0cNRE2ANkupk+ACNPvkSPcAAEibACyXUyfABGm3yNHuAECRNgAZLuYPgEirKlHu7u7XdyytGwHAd8jjNyng4OD7vnz51dbPT8/7z58+NB9+/bt6jU/TI+AGWHEnrx48eJ/EsSmHzx40L18+fLyzxF3ZVMjEyDCiEDjMYZZS5wiPXnyZFbJaxMhQIQRGzHvWR7XCyOCXsOmiDAi1HmPMMQjDpbpEiDCiL358eNHurW/5SnWdIBbXiDCiA38/Pnzrce2YyZ4//59F3ePLNMl4PbpiL2J0L979+7yDtHDhw8vtzzvdGnEXdvUigSIsCLAWavHp/+qM0BcXMd/q25n1vF57TYBp0a3mUzilePj4+7k5KSLb6gt6ydAhPUzXnoPR0dHl79WGTNCfBnn1uvSCJdegQhLI1vvCk+fPu2ePXt2tZOYEV6/fn31dz+shwAR1sP1cqvLntbEN9MxA9xcYjsxS1jWR4AIa2Ibzx0tc44fYX/16lV6NDFLXH+YL32jwiACRBiEbf5KcXoTIsQSpzXx4N28Ja4BQoK7rgXiydbHjx/P25TaQAJEGAguWy0+2Q8PD6/Ki4R8EVl+bzBOnZY95fq9rj9zAkTI2SxdidBHqG9+skdw43borCXO/ZcJdraPWdv22uIEiLA4q7nvvCug8WTqzQveOH26fodo7g6uFe/a17W3+nFBAkRYENRdb1vkkz1CH9cPsVy/jrhr27PqMYvENYNlHAIesRiBYwRy0V+8iXP8+/fvX11Mr7L7ECueb/r48eMqm7FuI2BGWDEG8cm+7G3NEOfmdcTQw4h9/55lhm7DekRYKQPZF2ArbXTAyu4kDYB2YxUzwg0gi/41ztHnfQG26HbGel/crVrm7tNY+/1btkOEAZ2M05r4FB7r9GbAIdxaZYrHdOsgJ/wCEQY0J74TmOKnbxxT9n3FgGGWWsVdowHtjt9Nnvf7yQM2aZU/TIAIAxrw6dOnAWtZZcoEnBpNuTuObWMEiLAx1HY0ZQJEmHJ3HNvGCBBhY6jtaMoEiJB0Z29vL6ls58vxPcO8/zfrdo5qvKO+d3Fx8Wu8zf1dW4p/cPzLly/dtv9Ts/EbcvGAHhHyfBIhZ6NSiIBTo0LNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiECRCjUbEPNCRAhZ6NSiAARCjXbUHMCRMjZqBQiQIRCzTbUnAARcjYqhQgQoVCzDTUnQIScjUohAkQo1GxDzQkQIWejUogAEQo121BzAkTI2agUIkCEQs021JwAEXI2KoUIEKFQsw01J0CEnI1KIQJEKNRsQ80JECFno1KIABEKNdtQcwJEyNmoFCJAhELNNtScABFyNiqFCBChULMNNSdAhJyNSiEC/wGgKKC4YMA4TAAAAABJRU5ErkJggg=='; | ||
25 | - | ||
26 | - const getImagBase64 = ref(fallback); | ||
27 | - | ||
28 | - const getRadio = computed(() => { | ||
29 | - return props.radio || DEFAULT_RADIO_RECORD; | ||
30 | - }); | ||
31 | - | ||
32 | - const getWidth = computed(() => { | ||
33 | - const marign = 5; | ||
34 | - const offsetHight = 62 + (props.radio ? 62 : 0); | ||
35 | - const { width = 240, height = 200 } = props.radio || {}; | ||
36 | - return width > height - offsetHight ? height - offsetHight - marign : width - marign; | ||
37 | - }); | ||
38 | - | ||
39 | - watch( | ||
40 | - () => props.value?.value, | ||
41 | - () => { | ||
42 | - if (props.value?.value) { | ||
43 | - getBase64Image(props.value.value); | ||
44 | - } | ||
45 | - } | ||
46 | - ); | ||
47 | - | ||
48 | - const getBase64Image = (url: string) => { | ||
49 | - let canvas: Nullable<HTMLCanvasElement> = document.createElement('canvas'); | ||
50 | - const ctx = canvas.getContext('2d'); | ||
51 | - let image: Nullable<HTMLImageElement> = new Image(); | ||
52 | - | ||
53 | - image.onload = function () { | ||
54 | - canvas!.height = image!.height; | ||
55 | - canvas!.width = image!.width; | ||
56 | - ctx?.drawImage(image!, 0, 0); | ||
57 | - const dataUrl = canvas!.toDataURL('image/png'); | ||
58 | - getImagBase64.value = dataUrl; | ||
59 | - image = null; | ||
60 | - canvas = null; | ||
61 | - }; | ||
62 | - image.setAttribute('crossOrigin', 'Anonymous'); | ||
63 | - image.src = url; | ||
64 | - }; | ||
65 | -</script> | ||
66 | - | ||
67 | -<template> | ||
68 | - <section | ||
69 | - class="w-full h-full flex flex-col justify-center items-center justify-between widget-picture" | ||
70 | - > | ||
71 | - <AntImage :width="getWidth" :src="getImagBase64" :fallback="fallback" /> | ||
72 | - <div | ||
73 | - class="w-full text-center truncate p-5" | ||
74 | - :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 12 }), color: '#999' }" | ||
75 | - > | ||
76 | - <Tooltip placement="top" :title="getUpdateTime(props.value?.updateTime)"> | ||
77 | - <span class="mr-1">更新时间:</span> | ||
78 | - <span class="truncate"> | ||
79 | - {{ getUpdateTime(props.value?.updateTime) }} | ||
80 | - </span> | ||
81 | - </Tooltip> | ||
82 | - </div> | ||
83 | - </section> | ||
84 | -</template> | ||
85 | - | ||
86 | -<style scoped lang="less"> | ||
87 | - .widget-picture:deep(.ant-image) { | ||
88 | - flex: auto; | ||
89 | - display: flex; | ||
90 | - justify-content: center; | ||
91 | - align-items: center; | ||
92 | - } | ||
93 | -</style> |
src/views/visual/board/components/PictureComponent/index.ts
deleted
100644 → 0
1 | -import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; | ||
2 | - | ||
3 | -export interface PictureComponentValue { | ||
4 | - value?: string; | ||
5 | - updateTime?: string; | ||
6 | -} | ||
7 | - | ||
8 | -export const transformPictureConfig = ( | ||
9 | - _config: Recordable, | ||
10 | - _record: DataComponentRecord, | ||
11 | - dataSourceRecord: DataSource | ||
12 | -) => { | ||
13 | - const componentInfo = dataSourceRecord.componentInfo; | ||
14 | - return { | ||
15 | - value: { | ||
16 | - value: componentInfo.value, | ||
17 | - updateTime: componentInfo.updateTime, | ||
18 | - } as PictureComponentValue, | ||
19 | - }; | ||
20 | -}; |
src/views/visual/board/components/TextComponent/TextComponent.vue
deleted
100644 → 0
1 | -<script lang="ts"> | ||
2 | - export default { | ||
3 | - inheritAttrs: false, | ||
4 | - }; | ||
5 | -</script> | ||
6 | -<script lang="ts" setup> | ||
7 | - import { computed } from 'vue'; | ||
8 | - import { Statistic, Tooltip } from 'ant-design-vue'; | ||
9 | - import { | ||
10 | - getUpdateTime, | ||
11 | - fontSize, | ||
12 | - RadioRecord, | ||
13 | - DEFAULT_RADIO_RECORD, | ||
14 | - } from '../../detail/config/util'; | ||
15 | - import { TextComponentDefaultConfig, TextComponentLayout, TextComponentValue } from './config'; | ||
16 | - import { SvgIcon } from '/@/components/Icon'; | ||
17 | - const props = defineProps({ | ||
18 | - layout: { | ||
19 | - type: Object as PropType<TextComponentLayout>, | ||
20 | - default: () => ({ base: true } as TextComponentLayout), | ||
21 | - }, | ||
22 | - value: { | ||
23 | - type: Object as PropType<TextComponentValue>, | ||
24 | - default: () => | ||
25 | - ({ ...TextComponentDefaultConfig, name: '温度', value: 123 } as TextComponentValue), | ||
26 | - }, | ||
27 | - radio: { | ||
28 | - type: Object as PropType<RadioRecord>, | ||
29 | - default: () => DEFAULT_RADIO_RECORD as RadioRecord, | ||
30 | - }, | ||
31 | - }); | ||
32 | - | ||
33 | - const getIsColumnLayout = computed(() => { | ||
34 | - const { base } = props.layout; | ||
35 | - return base; | ||
36 | - }); | ||
37 | - | ||
38 | - const getShowIcon = computed(() => { | ||
39 | - const { showIcon, base } = props.layout; | ||
40 | - return base ? false : showIcon; | ||
41 | - }); | ||
42 | - | ||
43 | - const getShowUpdate = computed(() => { | ||
44 | - const { showUpdate, base } = props.layout; | ||
45 | - return base ? false : showUpdate; | ||
46 | - }); | ||
47 | - | ||
48 | - const getShowUnit = computed(() => { | ||
49 | - const { showUnit, base } = props.layout; | ||
50 | - return base ? false : showUnit; | ||
51 | - }); | ||
52 | - | ||
53 | - const getRadio = computed(() => { | ||
54 | - return props.radio || DEFAULT_RADIO_RECORD; | ||
55 | - }); | ||
56 | -</script> | ||
57 | - | ||
58 | -<template> | ||
59 | - <div class="w-full h-full flex flex-col"> | ||
60 | - <div | ||
61 | - class="flex justify-center items-center w-full text-center flex-auto" | ||
62 | - :style="{ flexDirection: getIsColumnLayout ? 'column' : 'row' }" | ||
63 | - > | ||
64 | - <div class="w-1/2"> | ||
65 | - <div> | ||
66 | - <div v-if="getShowIcon"> | ||
67 | - <SvgIcon | ||
68 | - :name="props.value.icon || TextComponentDefaultConfig.icon!" | ||
69 | - prefix="iconfont" | ||
70 | - :style="{ | ||
71 | - color: props.value.iconColor, | ||
72 | - width: fontSize({ radioRecord: getRadio, basic: 50, min: 16 }), | ||
73 | - height: fontSize({ radioRecord: getRadio, basic: 50, min: 16 }), | ||
74 | - }" | ||
75 | - /> | ||
76 | - </div> | ||
77 | - <div class="flex justify-center"> | ||
78 | - <Statistic | ||
79 | - :value="props.value.value || 0" | ||
80 | - class="truncate" | ||
81 | - :suffix="getShowUnit ? props.value.unit : ''" | ||
82 | - :value-style="{ | ||
83 | - fontSize: fontSize({ radioRecord: getRadio, basic: 24, min: 16 }), | ||
84 | - color: props.value.fontColor, | ||
85 | - }" | ||
86 | - /> | ||
87 | - </div> | ||
88 | - <div :style="{ color: '#666', fontSize: fontSize({ radioRecord: getRadio, basic: 16 }) }"> | ||
89 | - {{ props.value.name }} | ||
90 | - </div> | ||
91 | - </div> | ||
92 | - </div> | ||
93 | - </div> | ||
94 | - | ||
95 | - <div class="text-center text-xs truncate p-5" style="color: #999"> | ||
96 | - <Tooltip v-if="getShowUpdate" placement="top" :title="getUpdateTime(props.value?.updateTime)"> | ||
97 | - <div | ||
98 | - :style="{ fontSize: fontSize({ radioRecord: getRadio, basic: 12, max: 12 }) }" | ||
99 | - class="truncate" | ||
100 | - > | ||
101 | - <span class="mr-1">更新时间:</span> | ||
102 | - <span class="truncate"> | ||
103 | - {{ getUpdateTime(props.value?.updateTime) }} | ||
104 | - </span> | ||
105 | - </div> | ||
106 | - </Tooltip> | ||
107 | - </div> | ||
108 | - </div> | ||
109 | -</template> |
src/views/visual/board/components/WidgetHeader/BaseWidgetHeader.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { MoreOutlined } from '@ant-design/icons-vue'; | ||
3 | - import { DropMenu } from '/@/components/Dropdown'; | ||
4 | - import Dropdown from '/@/components/Dropdown/src/Dropdown.vue'; | ||
5 | - import { Tooltip } from 'ant-design-vue'; | ||
6 | - import { | ||
7 | - isBataBoardSharePage, | ||
8 | - MoreActionEvent, | ||
9 | - VisualComponentPermission, | ||
10 | - } from '../../config/config'; | ||
11 | - import { computed } from '@vue/reactivity'; | ||
12 | - import { usePermission } from '/@/hooks/web/usePermission'; | ||
13 | - import { DataSource } from '/@/api/dataBoard/model'; | ||
14 | - import { useRoute } from 'vue-router'; | ||
15 | - import { useRole } from '/@/hooks/business/useRole'; | ||
16 | - | ||
17 | - const emit = defineEmits(['action']); | ||
18 | - const props = defineProps<{ | ||
19 | - id: string; | ||
20 | - record: DataSource[]; | ||
21 | - panelName?: string; | ||
22 | - }>(); | ||
23 | - const { isCustomerUser } = useRole(); | ||
24 | - const { hasPermission } = usePermission(); | ||
25 | - const dropMenuList = computed<DropMenu[]>(() => { | ||
26 | - const basicMenu: DropMenu[] = []; | ||
27 | - const hasUpdatePermission = hasPermission(VisualComponentPermission.UPDATE); | ||
28 | - const hasDeletePermission = hasPermission(VisualComponentPermission.DELETE); | ||
29 | - const hasCopyPermission = hasPermission(VisualComponentPermission.COPY); | ||
30 | - if (hasUpdatePermission) | ||
31 | - basicMenu.push({ | ||
32 | - text: '编辑组件', | ||
33 | - event: MoreActionEvent.EDIT, | ||
34 | - icon: 'ant-design:edit-outlined', | ||
35 | - }); | ||
36 | - if (hasDeletePermission) | ||
37 | - basicMenu.push({ | ||
38 | - text: '删除组件', | ||
39 | - event: MoreActionEvent.DELETE, | ||
40 | - icon: 'ant-design:delete-outlined', | ||
41 | - }); | ||
42 | - if (hasCopyPermission) | ||
43 | - basicMenu.push({ | ||
44 | - text: '复制组件', | ||
45 | - event: MoreActionEvent.COPY, | ||
46 | - icon: 'ant-design:copy-outlined', | ||
47 | - }); | ||
48 | - return basicMenu; | ||
49 | - }); | ||
50 | - | ||
51 | - const ROUTE = useRoute(); | ||
52 | - | ||
53 | - const getIsSharePage = computed(() => { | ||
54 | - return isBataBoardSharePage(ROUTE.path); | ||
55 | - }); | ||
56 | - | ||
57 | - const handleMenuEvent = (event: DropMenu) => { | ||
58 | - emit('action', event, props.id); | ||
59 | - }; | ||
60 | -</script> | ||
61 | - | ||
62 | -<template> | ||
63 | - <div> | ||
64 | - <div class="text-center pt-5 px-5 pb-3 font-bold text-lg truncate"> | ||
65 | - {{ props.panelName || '' }} | ||
66 | - </div> | ||
67 | - <div class="flex justify-between w-full px-5 pb-5"> | ||
68 | - <div class="flex" :style="{ width: `calc(100% - 60px)` }"> | ||
69 | - <div | ||
70 | - v-for="(item, index) in props.record" | ||
71 | - class="box-border truncate" | ||
72 | - :style="{ width: `${100 / props.record.length}%` }" | ||
73 | - :key="index" | ||
74 | - > | ||
75 | - <Tooltip :title="item.deviceName" placement="topLeft"> | ||
76 | - <div class="flex p-1"> | ||
77 | - <div v-if="item.componentInfo.showDeviceName" class="truncate font-bold"> | ||
78 | - {{ item.deviceRename || item.deviceName }} | ||
79 | - </div> | ||
80 | - </div> | ||
81 | - </Tooltip> | ||
82 | - </div> | ||
83 | - </div> | ||
84 | - <div class="flex items-center gap-5"> | ||
85 | - <slot name="moreAction"></slot> | ||
86 | - <Dropdown | ||
87 | - v-if="!isCustomerUser && dropMenuList.length" | ||
88 | - :drop-menu-list="dropMenuList" | ||
89 | - :trigger="['click']" | ||
90 | - @menu-event="handleMenuEvent" | ||
91 | - > | ||
92 | - <Tooltip title="更多"> | ||
93 | - <MoreOutlined | ||
94 | - v-if="!getIsSharePage" | ||
95 | - class="transform rotate-90 cursor-pointer w-4.5 h-4.5 text-lg" | ||
96 | - /> | ||
97 | - </Tooltip> | ||
98 | - </Dropdown> | ||
99 | - </div> | ||
100 | - </div> | ||
101 | - </div> | ||
102 | -</template> |
src/views/visual/board/components/WidgetWrapper/WidgetWrapper.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { useUpdateCenter } from '../../hook/useUpdateCenter'; | ||
3 | - import { FrontDataSourceRecord } from '../../types/type'; | ||
4 | - import { createVisualBoardContext } from '../../hook/useVisualBoardContext'; | ||
5 | - import { DataComponentRecord } from '/@/api/dataBoard/model'; | ||
6 | - import { computed } from 'vue'; | ||
7 | - import { frontComponentMap } from '../help'; | ||
8 | - import { FrontComponent } from '../../const/const'; | ||
9 | - | ||
10 | - const props = defineProps<{ | ||
11 | - record: DataComponentRecord; | ||
12 | - dataSource: FrontDataSourceRecord[]; | ||
13 | - }>(); | ||
14 | - | ||
15 | - const isMultipleDataSource = computed(() => { | ||
16 | - const { record } = props; | ||
17 | - const componentInfo = frontComponentMap.get(record.frontId as FrontComponent); | ||
18 | - return componentInfo?.isMultipleDataSource; | ||
19 | - }); | ||
20 | - | ||
21 | - const { update, add, remove } = useUpdateCenter(); | ||
22 | - | ||
23 | - createVisualBoardContext({ update, add, remove }); | ||
24 | - | ||
25 | - defineExpose({ update }); | ||
26 | -</script> | ||
27 | - | ||
28 | -<template> | ||
29 | - <section class="widget !dark:bg-dark-900"> | ||
30 | - <slot name="header"></slot> | ||
31 | - | ||
32 | - <div class="widget-content"> | ||
33 | - <template v-if="!isMultipleDataSource"> | ||
34 | - <div | ||
35 | - v-for="item in props.dataSource" | ||
36 | - :key="item.id" | ||
37 | - :style="{ width: `${item.width || 100}%`, height: `${item.height || 100}%` }" | ||
38 | - class="widget-item" | ||
39 | - > | ||
40 | - <div class="widget-box"> | ||
41 | - <div class="widget-controls-container"> | ||
42 | - <slot | ||
43 | - name="controls" | ||
44 | - :record="item" | ||
45 | - :add="add" | ||
46 | - :remove="remove" | ||
47 | - :update="update" | ||
48 | - ></slot> | ||
49 | - </div> | ||
50 | - </div> | ||
51 | - </div> | ||
52 | - </template> | ||
53 | - <template v-if="isMultipleDataSource"> | ||
54 | - <div :style="{ width: `${100}%`, height: `${100}%` }" class="widget-item"> | ||
55 | - <div class="widget-box"> | ||
56 | - <div class="widget-controls-container"> | ||
57 | - <slot | ||
58 | - name="controls" | ||
59 | - :record="props.dataSource" | ||
60 | - :add="add" | ||
61 | - :remove="remove" | ||
62 | - :update="update" | ||
63 | - ></slot> | ||
64 | - </div> | ||
65 | - </div> | ||
66 | - </div> | ||
67 | - </template> | ||
68 | - </div> | ||
69 | - <slot name="footer"></slot> | ||
70 | - </section> | ||
71 | -</template> | ||
72 | - | ||
73 | -<style scoped> | ||
74 | - .widget { | ||
75 | - display: flex; | ||
76 | - flex-direction: column; | ||
77 | - width: 100%; | ||
78 | - height: 100%; | ||
79 | - background-color: #fff; | ||
80 | - border-radius: 3px; | ||
81 | - box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1); | ||
82 | - } | ||
83 | - | ||
84 | - .widget-content { | ||
85 | - display: flex; | ||
86 | - flex-wrap: wrap; | ||
87 | - justify-content: center; | ||
88 | - align-items: center; | ||
89 | - width: 100%; | ||
90 | - height: 100%; | ||
91 | - } | ||
92 | - | ||
93 | - .widget-box .widget-charts { | ||
94 | - display: flex; | ||
95 | - } | ||
96 | - | ||
97 | - .widget-item { | ||
98 | - display: flex; | ||
99 | - flex-direction: column; | ||
100 | - overflow: hidden; | ||
101 | - justify-content: center; | ||
102 | - align-items: center; | ||
103 | - } | ||
104 | - | ||
105 | - .widget-box { | ||
106 | - position: relative; | ||
107 | - width: 100%; | ||
108 | - height: 100%; | ||
109 | - } | ||
110 | - | ||
111 | - .widget-controls-container { | ||
112 | - flex: 1 1 auto; | ||
113 | - width: 100%; | ||
114 | - height: 100%; | ||
115 | - display: flex; | ||
116 | - align-items: center; | ||
117 | - justify-content: center; | ||
118 | - } | ||
119 | - | ||
120 | - .widget-controls-container > div { | ||
121 | - width: 100%; | ||
122 | - height: 100%; | ||
123 | - background-color: red; | ||
124 | - } | ||
125 | - | ||
126 | - .widget-value { | ||
127 | - font-size: 14px; | ||
128 | - position: absolute; | ||
129 | - width: 100%; | ||
130 | - top: 0; | ||
131 | - text-align: center; | ||
132 | - overflow: hidden; | ||
133 | - text-overflow: ellipsis; | ||
134 | - } | ||
135 | - | ||
136 | - .widget-label { | ||
137 | - font-size: 14px; | ||
138 | - line-height: 1.2; | ||
139 | - text-align: center; | ||
140 | - overflow: hidden; | ||
141 | - text-overflow: ellipsis; | ||
142 | - } | ||
143 | -</style> |
src/views/visual/board/components/help.ts
deleted
100644 → 0
1 | -import { | ||
2 | - TextComponent1Config, | ||
3 | - TextComponent3Config, | ||
4 | - TextComponent4Config, | ||
5 | - TextComponent5Config, | ||
6 | - TextComponentDefaultConfig, | ||
7 | - transformTextComponentConfig, | ||
8 | -} from './TextComponent/config'; | ||
9 | -import { ComponentInfo } from '/@/api/dataBoard/model'; | ||
10 | -import { transformPictureConfig } from './PictureComponent/pictureComponent.config'; | ||
11 | -import { | ||
12 | - DashboardComponentLayout, | ||
13 | - Instrument1DefaultConfig, | ||
14 | - Instrument2DefaultConfig, | ||
15 | - instrumentComponent1, | ||
16 | - instrumentComponent2, | ||
17 | - transformDashboardComponentConfig, | ||
18 | -} from './InstrumentComponent/dashBoardComponent.config'; | ||
19 | -import { DigitalComponentDefaultConfig } from './InstrumentComponent/digitalDashBoard.config'; | ||
20 | -import { | ||
21 | - ControlComponentDefaultConfig, | ||
22 | - transformControlConfig, | ||
23 | -} from './ControlComponent/control.config'; | ||
24 | - | ||
25 | -import TextComponent from './TextComponent/TextComponent.vue'; | ||
26 | -import DashBoardComponent from './InstrumentComponent/DashBoardComponent.vue'; | ||
27 | -import PictureComponent from './PictureComponent/PictureComponent.vue'; | ||
28 | -import DigitalDashBoard from './InstrumentComponent/DigitalDashBoard.vue'; | ||
29 | -import ToggleSwitch from './ControlComponent/ToggleSwitch.vue'; | ||
30 | -import SlidingSwitch from './ControlComponent/SlidingSwitch.vue'; | ||
31 | -import SwitchWithIcon from './ControlComponent/SwitchWithIcon.vue'; | ||
32 | -import MapComponent from './MapComponent/MapComponent.vue'; | ||
33 | -import { | ||
34 | - MaphistoryTrackConfig, | ||
35 | - MapRealTrackConfig, | ||
36 | - transfromMapComponentConfig, | ||
37 | -} from './MapComponent/map.config'; | ||
38 | -import { ComponentConfig } from '../types/type'; | ||
39 | -import { FrontComponent, FrontComponentCategory } from '../const/const'; | ||
40 | - | ||
41 | -export const frontComponentDefaultConfigMap = new Map<FrontComponent, Partial<ComponentInfo>>(); | ||
42 | - | ||
43 | -export const frontComponentMap = new Map<FrontComponent, ComponentConfig>(); | ||
44 | - | ||
45 | -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_1, { | ||
46 | - Component: TextComponent, | ||
47 | - ComponentName: '文本组件1', | ||
48 | - ComponentKey: FrontComponent.TEXT_COMPONENT_1, | ||
49 | - ComponentConfig: TextComponent1Config, | ||
50 | - ComponentCategory: FrontComponentCategory.TEXT, | ||
51 | - isMultipleDataSource: false, | ||
52 | - hasHistoryTrend: true, | ||
53 | - hasSetting: true, | ||
54 | - transformConfig: transformTextComponentConfig, | ||
55 | -}); | ||
56 | - | ||
57 | -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_3, { | ||
58 | - Component: TextComponent, | ||
59 | - ComponentName: '文本组件2', | ||
60 | - ComponentKey: FrontComponent.TEXT_COMPONENT_3, | ||
61 | - ComponentConfig: TextComponent3Config, | ||
62 | - ComponentCategory: FrontComponentCategory.TEXT, | ||
63 | - isMultipleDataSource: false, | ||
64 | - hasHistoryTrend: true, | ||
65 | - hasSetting: true, | ||
66 | - transformConfig: transformTextComponentConfig, | ||
67 | -}); | ||
68 | - | ||
69 | -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_4, { | ||
70 | - Component: TextComponent, | ||
71 | - ComponentName: '文本组件3', | ||
72 | - ComponentKey: FrontComponent.TEXT_COMPONENT_4, | ||
73 | - ComponentConfig: TextComponent4Config, | ||
74 | - ComponentCategory: FrontComponentCategory.TEXT, | ||
75 | - isMultipleDataSource: false, | ||
76 | - hasHistoryTrend: true, | ||
77 | - hasSetting: true, | ||
78 | - transformConfig: transformTextComponentConfig, | ||
79 | -}); | ||
80 | - | ||
81 | -frontComponentMap.set(FrontComponent.TEXT_COMPONENT_5, { | ||
82 | - Component: TextComponent, | ||
83 | - ComponentName: '文本组件4', | ||
84 | - ComponentKey: FrontComponent.TEXT_COMPONENT_5, | ||
85 | - ComponentConfig: TextComponent5Config, | ||
86 | - ComponentCategory: FrontComponentCategory.TEXT, | ||
87 | - isMultipleDataSource: false, | ||
88 | - hasHistoryTrend: true, | ||
89 | - hasSetting: true, | ||
90 | - transformConfig: transformTextComponentConfig, | ||
91 | -}); | ||
92 | - | ||
93 | -frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, { | ||
94 | - Component: DashBoardComponent, | ||
95 | - ComponentName: '仪表盘', | ||
96 | - ComponentKey: FrontComponent.INSTRUMENT_COMPONENT_1, | ||
97 | - ComponentConfig: { | ||
98 | - chartOption: instrumentComponent1(Instrument1DefaultConfig), | ||
99 | - componentType: FrontComponent.INSTRUMENT_COMPONENT_1, | ||
100 | - } as DashboardComponentLayout, | ||
101 | - ComponentCategory: FrontComponentCategory.INSTRUMENT, | ||
102 | - isMultipleDataSource: false, | ||
103 | - hasHistoryTrend: true, | ||
104 | - hasSetting: true, | ||
105 | - transformConfig: transformDashboardComponentConfig, | ||
106 | -}); | ||
107 | - | ||
108 | -frontComponentMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, { | ||
109 | - Component: DashBoardComponent, | ||
110 | - ComponentName: '阶段仪表盘', | ||
111 | - ComponentKey: FrontComponent.INSTRUMENT_COMPONENT_2, | ||
112 | - ComponentConfig: { | ||
113 | - chartOption: instrumentComponent2(Instrument2DefaultConfig), | ||
114 | - componentType: FrontComponent.INSTRUMENT_COMPONENT_2, | ||
115 | - } as DashboardComponentLayout, | ||
116 | - ComponentCategory: FrontComponentCategory.INSTRUMENT, | ||
117 | - isMultipleDataSource: false, | ||
118 | - hasHistoryTrend: true, | ||
119 | - hasSetting: true, | ||
120 | - transformConfig: transformDashboardComponentConfig, | ||
121 | -}); | ||
122 | - | ||
123 | -frontComponentMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, { | ||
124 | - Component: DigitalDashBoard, | ||
125 | - ComponentName: '数字仪表盘', | ||
126 | - ComponentKey: FrontComponent.DIGITAL_DASHBOARD_COMPONENT, | ||
127 | - ComponentCategory: FrontComponentCategory.INSTRUMENT, | ||
128 | - isMultipleDataSource: false, | ||
129 | - hasHistoryTrend: true, | ||
130 | - hasSetting: true, | ||
131 | - transformConfig: transformDashboardComponentConfig, | ||
132 | -}); | ||
133 | - | ||
134 | -frontComponentMap.set(FrontComponent.PICTURE_COMPONENT_1, { | ||
135 | - Component: PictureComponent, | ||
136 | - ComponentName: '图片组件', | ||
137 | - ComponentKey: FrontComponent.PICTURE_COMPONENT_1, | ||
138 | - ComponentCategory: FrontComponentCategory.PICTURE, | ||
139 | - isMultipleDataSource: false, | ||
140 | - hasHistoryTrend: true, | ||
141 | - hasSetting: false, | ||
142 | - transformConfig: transformPictureConfig, | ||
143 | -}); | ||
144 | - | ||
145 | -frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, { | ||
146 | - Component: SwitchWithIcon, | ||
147 | - ComponentName: '控制按钮1', | ||
148 | - ComponentKey: FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, | ||
149 | - ComponentCategory: FrontComponentCategory.CONTROL, | ||
150 | - isMultipleDataSource: false, | ||
151 | - hasHistoryTrend: true, | ||
152 | - hasSetting: true, | ||
153 | - transformConfig: transformControlConfig, | ||
154 | -}); | ||
155 | - | ||
156 | -frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, { | ||
157 | - Component: SlidingSwitch, | ||
158 | - ComponentName: '控制按钮2', | ||
159 | - ComponentKey: FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, | ||
160 | - ComponentCategory: FrontComponentCategory.CONTROL, | ||
161 | - isMultipleDataSource: false, | ||
162 | - hasHistoryTrend: true, | ||
163 | - hasSetting: false, | ||
164 | - transformConfig: transformControlConfig, | ||
165 | -}); | ||
166 | - | ||
167 | -frontComponentMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, { | ||
168 | - Component: ToggleSwitch, | ||
169 | - ComponentName: '控制按钮3', | ||
170 | - ComponentKey: FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, | ||
171 | - ComponentCategory: FrontComponentCategory.CONTROL, | ||
172 | - isMultipleDataSource: false, | ||
173 | - hasHistoryTrend: true, | ||
174 | - hasSetting: false, | ||
175 | - transformConfig: transformControlConfig, | ||
176 | -}); | ||
177 | - | ||
178 | -frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_REAL, { | ||
179 | - Component: MapComponent, | ||
180 | - ComponentName: '实时轨迹', | ||
181 | - ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_REAL, | ||
182 | - ComponentConfig: MapRealTrackConfig, | ||
183 | - ComponentCategory: FrontComponentCategory.MAP, | ||
184 | - isMultipleDataSource: true, | ||
185 | - hasHistoryTrend: false, | ||
186 | - hasSetting: false, | ||
187 | - transformConfig: transfromMapComponentConfig, | ||
188 | -}); | ||
189 | - | ||
190 | -frontComponentMap.set(FrontComponent.MAP_COMPONENT_TRACK_HISTORY, { | ||
191 | - Component: MapComponent, | ||
192 | - ComponentName: '历史轨迹', | ||
193 | - ComponentKey: FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | ||
194 | - ComponentConfig: MaphistoryTrackConfig, | ||
195 | - ComponentCategory: FrontComponentCategory.MAP, | ||
196 | - isMultipleDataSource: true, | ||
197 | - hasHistoryTrend: false, | ||
198 | - hasSetting: false, | ||
199 | - transformConfig: transfromMapComponentConfig, | ||
200 | -}); | ||
201 | - | ||
202 | -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_1, TextComponentDefaultConfig); | ||
203 | -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_3, TextComponentDefaultConfig); | ||
204 | -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_4, TextComponentDefaultConfig); | ||
205 | -frontComponentDefaultConfigMap.set(FrontComponent.TEXT_COMPONENT_5, TextComponentDefaultConfig); | ||
206 | - | ||
207 | -frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, Instrument1DefaultConfig); | ||
208 | -frontComponentDefaultConfigMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, Instrument2DefaultConfig); | ||
209 | - | ||
210 | -frontComponentDefaultConfigMap.set( | ||
211 | - FrontComponent.DIGITAL_DASHBOARD_COMPONENT, | ||
212 | - DigitalComponentDefaultConfig | ||
213 | -); | ||
214 | - | ||
215 | -frontComponentDefaultConfigMap.set( | ||
216 | - FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, | ||
217 | - ControlComponentDefaultConfig | ||
218 | -); | ||
219 | - | ||
220 | -export const getComponentDefaultConfig = (key: FrontComponent) => { | ||
221 | - return frontComponentDefaultConfigMap.get(key) || {}; | ||
222 | -}; |
1 | -import { ViewTypeEnum } from '/@/views/sys/share/config/config'; | ||
2 | - | ||
3 | export enum MoreActionEvent { | 1 | export enum MoreActionEvent { |
4 | EDIT = 'edit', | 2 | EDIT = 'edit', |
5 | COPY = 'copy', | 3 | COPY = 'copy', |
@@ -27,9 +25,6 @@ export const DEFAULT_WIDGET_HEIGHT = 6; | @@ -27,9 +25,6 @@ export const DEFAULT_WIDGET_HEIGHT = 6; | ||
27 | export const DEFAULT_MIN_HEIGHT = 5; | 25 | export const DEFAULT_MIN_HEIGHT = 5; |
28 | export const DEFAULT_MIN_WIDTH = 3; | 26 | export const DEFAULT_MIN_WIDTH = 3; |
29 | 27 | ||
30 | -export const DATA_BOARD_SHARE_URL = (id: string, publicId: string) => | ||
31 | - `/share/${ViewTypeEnum.DATA_BOARD}/${id}/${publicId}`; | ||
32 | - | ||
33 | export const isBataBoardSharePage = (url: string) => { | 28 | export const isBataBoardSharePage = (url: string) => { |
34 | const reg = /^\/data\/board\/share/g; | 29 | const reg = /^\/data\/board\/share/g; |
35 | return reg.test(url); | 30 | return reg.test(url); |
src/views/visual/board/const/const.ts
deleted
100644 → 0
1 | -export enum FrontComponentCategory { | ||
2 | - TEXT = 'text', | ||
3 | - PICTURE = 'picture', | ||
4 | - INSTRUMENT = 'instrument', | ||
5 | - CONTROL = 'control', | ||
6 | - MAP = 'map', | ||
7 | -} | ||
8 | - | ||
9 | -export const FrontComponentCategoryName = { | ||
10 | - [FrontComponentCategory.TEXT]: '文本组件', | ||
11 | - [FrontComponentCategory.INSTRUMENT]: '仪表组件', | ||
12 | - [FrontComponentCategory.CONTROL]: '控制组件', | ||
13 | - [FrontComponentCategory.PICTURE]: '图片组件', | ||
14 | - [FrontComponentCategory.MAP]: '地图组件', | ||
15 | -}; | ||
16 | - | ||
17 | -export enum FrontComponent { | ||
18 | - TEXT_COMPONENT_1 = 'text-component-1', | ||
19 | - TEXT_COMPONENT_2 = 'text-component-2', | ||
20 | - TEXT_COMPONENT_3 = 'text-component-3', | ||
21 | - TEXT_COMPONENT_4 = 'text-component-4', | ||
22 | - TEXT_COMPONENT_5 = 'text-component-5', | ||
23 | - INSTRUMENT_COMPONENT_1 = 'instrument-component-1', | ||
24 | - INSTRUMENT_COMPONENT_2 = 'instrument-component-2', | ||
25 | - DIGITAL_DASHBOARD_COMPONENT = 'digital-dashboard-component', | ||
26 | - PICTURE_COMPONENT_1 = 'picture-component-1', | ||
27 | - CONTROL_COMPONENT_TOGGLE_SWITCH = 'control-component-toggle-switch', | ||
28 | - CONTROL_COMPONENT_SWITCH_WITH_ICON = 'control-component-switch-with-icon', | ||
29 | - CONTROL_COMPONENT_SLIDING_SWITCH = 'control-component-sliding-switch', | ||
30 | - MAP_COMPONENT_TRACK_REAL = 'map-component-track-real', | ||
31 | - MAP_COMPONENT_TRACK_HISTORY = 'map-component-track-history', | ||
32 | -} | ||
33 | - | ||
34 | -export enum Gradient { | ||
35 | - FIRST = 'first', | ||
36 | - SECOND = 'second', | ||
37 | - THIRD = 'third', | ||
38 | -} | ||
39 | -export enum GradientColor { | ||
40 | - FIRST = '#67e0e3', | ||
41 | - SECOND = '#37a2da', | ||
42 | - THIRD = '#fd666d', | ||
43 | -} | ||
44 | - | ||
45 | -export enum visualOptionField { | ||
46 | - FONT_COLOR = 'fontColor', | ||
47 | - UNIT = 'unit', | ||
48 | - ICON_COLOR = 'iconColor', | ||
49 | - ICON = 'icon', | ||
50 | - FIRST_PHASE_COLOR = 'firstPhaseColor', | ||
51 | - SECOND_PHASE_COLOR = 'secondPhaseColor', | ||
52 | - THIRD_PHASE_COLOR = 'thirdPhaseColor', | ||
53 | - FIRST_PHASE_VALUE = 'firstPhaseValue', | ||
54 | - SECOND_PHASE_VALUE = 'secondPhaseValue', | ||
55 | - THIRD_PHASE_VALUE = 'thirdPhaseValue', | ||
56 | -} |
src/views/visual/board/detail/components/BasicConfiguration.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { | ||
3 | - CopyOutlined, | ||
4 | - DeleteOutlined, | ||
5 | - SettingOutlined, | ||
6 | - SwapOutlined, | ||
7 | - } from '@ant-design/icons-vue'; | ||
8 | - import { Tooltip, Button, Alert } from 'ant-design-vue'; | ||
9 | - import { FormActionType, useForm } from '/@/components/Form'; | ||
10 | - import { basicSchema, DataSourceField } from '../config/basicConfiguration'; | ||
11 | - import BasicForm from '/@/components/Form/src/BasicForm.vue'; | ||
12 | - import { ref, shallowReactive, unref, nextTick, watch, computed, onMounted } from 'vue'; | ||
13 | - import VisualOptionsModal from './VisualOptionsModal.vue'; | ||
14 | - import { useModal } from '/@/components/Modal'; | ||
15 | - import { buildUUID } from '/@/utils/uuid'; | ||
16 | - import type { ComponentInfo, DataSource } from '/@/api/dataBoard/model'; | ||
17 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
18 | - import { DataBoardLayoutInfo } from '../../types/type'; | ||
19 | - import { getDataSourceComponent } from './DataSourceForm/help'; | ||
20 | - import { FrontComponent, FrontComponentCategory } from '../../const/const'; | ||
21 | - import { isNullAndUnDef } from '/@/utils/is'; | ||
22 | - import { useSortable } from '/@/hooks/web/useSortable'; | ||
23 | - import { cloneDeep } from 'lodash-es'; | ||
24 | - import { frontComponentMap } from '../../components/help'; | ||
25 | - import { isControlComponent } from '../config/basicConfiguration'; | ||
26 | - | ||
27 | - type DataSourceFormEL = { [key: string]: Nullable<FormActionType> }; | ||
28 | - | ||
29 | - type DataSourceEl = DataSource & { id: string }; | ||
30 | - | ||
31 | - const props = defineProps<{ | ||
32 | - record: DataBoardLayoutInfo; | ||
33 | - isEdit: boolean; | ||
34 | - frontId?: FrontComponent; | ||
35 | - defaultConfig?: Partial<ComponentInfo>; | ||
36 | - componentCategory?: FrontComponentCategory; | ||
37 | - }>(); | ||
38 | - | ||
39 | - const { createMessage } = useMessage(); | ||
40 | - | ||
41 | - const dataSource = ref<DataSourceEl[]>([ | ||
42 | - { id: buildUUID(), componentInfo: props.defaultConfig || {} } as unknown as DataSourceEl, | ||
43 | - ]); | ||
44 | - | ||
45 | - const [basicRegister, basicMethod] = useForm({ | ||
46 | - schemas: basicSchema, | ||
47 | - showActionButtonGroup: false, | ||
48 | - labelWidth: 96, | ||
49 | - }); | ||
50 | - | ||
51 | - const dataSourceEl = shallowReactive<DataSourceFormEL>({} as unknown as DataSourceFormEL); | ||
52 | - | ||
53 | - const setFormEl = (el: any, id: string) => { | ||
54 | - if (id && el) { | ||
55 | - const { formActionType } = el as unknown as { formActionType: FormActionType }; | ||
56 | - dataSourceEl[id] = formActionType; | ||
57 | - } | ||
58 | - }; | ||
59 | - | ||
60 | - const resetFormFields = async () => { | ||
61 | - const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | ||
62 | - for (const id of hasExistEl) { | ||
63 | - const oldValues = dataSourceEl[id]?.getFieldsValue(); | ||
64 | - dataSourceEl[id]?.setFieldsValue({ | ||
65 | - ...oldValues, | ||
66 | - [DataSourceField.ATTRIBUTE]: null, | ||
67 | - [DataSourceField.COMMAND_TYPE]: null, | ||
68 | - [DataSourceField.SERVICE]: null, | ||
69 | - }); | ||
70 | - dataSourceEl[id]?.clearValidate(); | ||
71 | - } | ||
72 | - }; | ||
73 | - | ||
74 | - const validate = async () => { | ||
75 | - await basicMethod.validate(); | ||
76 | - await validateDataSourceField(); | ||
77 | - }; | ||
78 | - | ||
79 | - const getAllDataSourceFieldValue = () => { | ||
80 | - const _dataSource = getDataSourceField(); | ||
81 | - const basicInfo = basicMethod.getFieldsValue(); | ||
82 | - return { | ||
83 | - ...basicInfo, | ||
84 | - dataSource: _dataSource, | ||
85 | - }; | ||
86 | - }; | ||
87 | - | ||
88 | - const validateDataSourceField = async () => { | ||
89 | - const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | ||
90 | - const _dataSource: Record<DataSourceField, string>[] = []; | ||
91 | - for (const id of hasExistEl) { | ||
92 | - const flag = (await (dataSourceEl[id] as FormActionType).validate()) as Record< | ||
93 | - DataSourceField, | ||
94 | - string | ||
95 | - >; | ||
96 | - flag && _dataSource.push(flag); | ||
97 | - } | ||
98 | - | ||
99 | - if ( | ||
100 | - [ | ||
101 | - FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | ||
102 | - FrontComponent.MAP_COMPONENT_TRACK_REAL, | ||
103 | - ].includes(props.frontId!) | ||
104 | - ) { | ||
105 | - await validateMapComponent(_dataSource); | ||
106 | - } | ||
107 | - return _dataSource; | ||
108 | - }; | ||
109 | - | ||
110 | - const validateMapComponent = async (dataSource: Record<DataSourceField, string>[]) => { | ||
111 | - if (dataSource.length) { | ||
112 | - const firstRecord = dataSource.at(0)!; | ||
113 | - const { deviceId } = firstRecord; | ||
114 | - const flag = dataSource.every((item) => item.deviceId === deviceId); | ||
115 | - if (!flag) { | ||
116 | - createMessage.warning('地图组件绑定的数据源应该一致'); | ||
117 | - return Promise.reject(false); | ||
118 | - } | ||
119 | - } | ||
120 | - }; | ||
121 | - | ||
122 | - const getDataSourceField = () => { | ||
123 | - const hasExistEl = Object.keys(dataSourceEl).filter((key) => dataSourceEl[key]); | ||
124 | - const _dataSource: DataSource[] = []; | ||
125 | - | ||
126 | - for (const id of hasExistEl) { | ||
127 | - const index = unref(dataSource).findIndex((item) => item.id === id); | ||
128 | - const value = (dataSourceEl[id] as FormActionType).getFieldsValue() as DataSource; | ||
129 | - if (!~index) continue; | ||
130 | - const componentInfo = unref(dataSource)[index].componentInfo || {}; | ||
131 | - _dataSource[index] = { | ||
132 | - ...value, | ||
133 | - componentInfo: { ...(props.defaultConfig || {}), ...componentInfo }, | ||
134 | - customCommand: { | ||
135 | - transportType: value.transportType, | ||
136 | - service: value.service, | ||
137 | - command: value.command, | ||
138 | - commandType: value.commandType, | ||
139 | - }, | ||
140 | - }; | ||
141 | - } | ||
142 | - | ||
143 | - return _dataSource; | ||
144 | - }; | ||
145 | - | ||
146 | - const handleCopy = async (data: DataSourceEl) => { | ||
147 | - const value = (dataSourceEl[data.id] as FormActionType).getFieldsValue() as DataSource; | ||
148 | - const index = unref(dataSource).findIndex((item) => item.id === data.id); | ||
149 | - | ||
150 | - const componentInfo = ~index | ||
151 | - ? unref(dataSource)[index].componentInfo | ||
152 | - : ({} as unknown as ComponentInfo); | ||
153 | - | ||
154 | - const copyRecordId = buildUUID(); | ||
155 | - unref(dataSource).push({ | ||
156 | - ...value, | ||
157 | - id: copyRecordId, | ||
158 | - componentInfo, | ||
159 | - }); | ||
160 | - await nextTick(); | ||
161 | - (dataSourceEl[copyRecordId] as FormActionType).setFieldsValue(value); | ||
162 | - (dataSourceEl[copyRecordId] as FormActionType).clearValidate(); | ||
163 | - }; | ||
164 | - | ||
165 | - const [registerVisualOptionModal, { openModal }] = useModal(); | ||
166 | - | ||
167 | - const handleSetting = (item: DataSourceEl) => { | ||
168 | - if (!props.frontId) { | ||
169 | - createMessage.warning('请先选择可视化组件'); | ||
170 | - return; | ||
171 | - } | ||
172 | - | ||
173 | - const componentInfo: ComponentInfo = { | ||
174 | - ...(props.defaultConfig || {}), | ||
175 | - ...(item.componentInfo || {}), | ||
176 | - }; | ||
177 | - | ||
178 | - openModal(true, { | ||
179 | - recordId: item.id, | ||
180 | - componentInfo, | ||
181 | - }); | ||
182 | - }; | ||
183 | - | ||
184 | - const handleDelete = (data: DataSourceEl) => { | ||
185 | - const index = unref(dataSource).findIndex((item) => item.id === data.id); | ||
186 | - ~index && unref(dataSource).splice(index, 1); | ||
187 | - dataSourceEl[data.id] = null; | ||
188 | - }; | ||
189 | - | ||
190 | - const isMapComponent = computed(() => { | ||
191 | - return props.componentCategory === FrontComponentCategory.MAP; | ||
192 | - }); | ||
193 | - | ||
194 | - const handleAdd = () => { | ||
195 | - if (unref(isMapComponent) && unref(dataSource).length === 2) { | ||
196 | - createMessage.warning('地图组件只能绑定两条数据源'); | ||
197 | - return; | ||
198 | - } | ||
199 | - unref(dataSource).push({ | ||
200 | - id: buildUUID(), | ||
201 | - componentInfo: props.defaultConfig || {}, | ||
202 | - } as unknown as DataSourceEl); | ||
203 | - }; | ||
204 | - | ||
205 | - const echoDataSource = () => { | ||
206 | - basicMethod.setFieldsValue(props.record.record); | ||
207 | - basicMethod.clearValidate(); | ||
208 | - dataSource.value = []; | ||
209 | - dataSource.value = props.record.record.dataSource.map((item) => { | ||
210 | - const id = buildUUID(); | ||
211 | - | ||
212 | - const customCommand = item.customCommand || {}; | ||
213 | - | ||
214 | - nextTick(() => { | ||
215 | - (dataSourceEl[id] as FormActionType).setFieldsValue({ | ||
216 | - ...item, | ||
217 | - transportType: customCommand.transportType, | ||
218 | - command: customCommand?.command, | ||
219 | - commandType: customCommand.commandType, | ||
220 | - service: customCommand.service, | ||
221 | - }); | ||
222 | - (dataSourceEl[id] as FormActionType).clearValidate(); | ||
223 | - }); | ||
224 | - | ||
225 | - return { | ||
226 | - id, | ||
227 | - ...item, | ||
228 | - transportType: customCommand.transportType, | ||
229 | - command: customCommand?.command, | ||
230 | - commandType: customCommand.commandType, | ||
231 | - service: customCommand.service, | ||
232 | - }; | ||
233 | - }); | ||
234 | - }; | ||
235 | - | ||
236 | - const showSettingButton = computed(() => { | ||
237 | - const flag = frontComponentMap.get(props.frontId!)?.hasSetting; | ||
238 | - return flag; | ||
239 | - }); | ||
240 | - | ||
241 | - watch( | ||
242 | - () => props.record, | ||
243 | - () => { | ||
244 | - if (Object.keys(props.record).length) echoDataSource(); | ||
245 | - } | ||
246 | - ); | ||
247 | - | ||
248 | - const handleRowComponentInfo = (recordId: string, value: ComponentInfo) => { | ||
249 | - const index = unref(dataSource).findIndex((item) => item.id === recordId); | ||
250 | - ~index && (unref(dataSource)[index].componentInfo = value); | ||
251 | - }; | ||
252 | - | ||
253 | - const dataSourceComponent = computed(() => { | ||
254 | - return getDataSourceComponent(props.frontId as FrontComponent); | ||
255 | - }); | ||
256 | - | ||
257 | - let inited = false; | ||
258 | - const formListEl = ref<HTMLElement>(); | ||
259 | - async function handleSort() { | ||
260 | - if (inited) return; | ||
261 | - await nextTick(); | ||
262 | - const formList = unref(formListEl); | ||
263 | - if (!formList) return; | ||
264 | - | ||
265 | - const { initSortable } = useSortable(unref(formList), { | ||
266 | - handle: '.sort-icon', | ||
267 | - onEnd: (evt) => { | ||
268 | - const { oldIndex, newIndex } = evt; | ||
269 | - if (isNullAndUnDef(oldIndex) || isNullAndUnDef(newIndex) || oldIndex === newIndex) { | ||
270 | - return; | ||
271 | - } | ||
272 | - | ||
273 | - const _dataSource = cloneDeep(unref(dataSource)); | ||
274 | - | ||
275 | - if (oldIndex > newIndex) { | ||
276 | - _dataSource.splice(newIndex, 0, _dataSource[oldIndex]); | ||
277 | - _dataSource.splice(oldIndex + 1, 1); | ||
278 | - } else { | ||
279 | - _dataSource.splice(newIndex + 1, 0, _dataSource[oldIndex]); | ||
280 | - _dataSource.splice(oldIndex, 1); | ||
281 | - } | ||
282 | - | ||
283 | - dataSource.value = _dataSource; | ||
284 | - }, | ||
285 | - }); | ||
286 | - initSortable(); | ||
287 | - inited = true; | ||
288 | - } | ||
289 | - | ||
290 | - const isControlCmp = computed(() => { | ||
291 | - return isControlComponent(props.frontId as FrontComponent); | ||
292 | - }); | ||
293 | - | ||
294 | - watch( | ||
295 | - () => props.frontId, | ||
296 | - async (target, oldTarget) => { | ||
297 | - if ([isControlComponent(oldTarget!), isControlComponent(target!)].some(Boolean)) { | ||
298 | - await resetFormFields(); | ||
299 | - } | ||
300 | - } | ||
301 | - ); | ||
302 | - | ||
303 | - onMounted(() => handleSort()); | ||
304 | - | ||
305 | - defineExpose({ | ||
306 | - getAllDataSourceFieldValue, | ||
307 | - validate, | ||
308 | - }); | ||
309 | -</script> | ||
310 | - | ||
311 | -<template> | ||
312 | - <section> | ||
313 | - <h3 class="w-24 text-right pr-2 my-4">基础信息</h3> | ||
314 | - <div class="w-3/4"> | ||
315 | - <BasicForm @register="basicRegister" class="w-full" /> | ||
316 | - </div> | ||
317 | - <Alert type="info" show-icon v-if="isControlCmp"> | ||
318 | - <template #description> | ||
319 | - <div> | ||
320 | - 控制组件数据源为TCP产品,则其控制命令下发为TCP产品 物模型=>服务,且不具备状态显示功能. | ||
321 | - </div> | ||
322 | - <div> | ||
323 | - 控制组件数据源为非TCP产品,则其控制命令下发为产品 物模型=>属性,且具备状态显示功能. | ||
324 | - </div> | ||
325 | - </template> | ||
326 | - </Alert> | ||
327 | - <Alert type="info" show-icon v-if="isMapComponent"> | ||
328 | - <template #description> | ||
329 | - <div> | ||
330 | - 地图组件,需绑定两个数据源,且数据源为同一设备。第一数据源为经度,第二数据源为纬度,否则地图组件不能正常显示。 | ||
331 | - </div> | ||
332 | - </template> | ||
333 | - </Alert> | ||
334 | - <h3 class="w-24 flex-shrink-0 text-right pr-2 my-4">选择数据源</h3> | ||
335 | - | ||
336 | - <section ref="formListEl"> | ||
337 | - <div | ||
338 | - v-for="item in dataSource" | ||
339 | - :data-id="item.id" | ||
340 | - :key="item.id" | ||
341 | - class="flex bg-light-50 dark:text-gray-300 dark:bg-dark-700" | ||
342 | - > | ||
343 | - <div class="w-24 text-right flex justify-end" style="flex: 0 0 96px"> 选择设备 </div> | ||
344 | - <div class="pl-2 flex-auto"> | ||
345 | - <component | ||
346 | - :frontId="$props.frontId" | ||
347 | - :isEdit="isEdit" | ||
348 | - :is="dataSourceComponent" | ||
349 | - :ref="(el) => setFormEl(el, item.id)" | ||
350 | - /> | ||
351 | - </div> | ||
352 | - <div class="flex justify-center gap-3 w-28"> | ||
353 | - <Tooltip title="复制"> | ||
354 | - <CopyOutlined @click="handleCopy(item)" class="cursor-pointer text-lg !leading-32px" /> | ||
355 | - </Tooltip> | ||
356 | - <Tooltip title="设置"> | ||
357 | - <SettingOutlined | ||
358 | - v-show="showSettingButton" | ||
359 | - @click="handleSetting(item)" | ||
360 | - class="cursor-pointer text-lg !leading-32px" | ||
361 | - /> | ||
362 | - </Tooltip> | ||
363 | - <Tooltip title="拖拽排序"> | ||
364 | - <SwapOutlined | ||
365 | - class="cursor-pointer text-lg !leading-32px svg:transform svg:rotate-90 sort-icon" | ||
366 | - /> | ||
367 | - </Tooltip> | ||
368 | - <Tooltip title="删除"> | ||
369 | - <DeleteOutlined | ||
370 | - @click="handleDelete(item)" | ||
371 | - class="cursor-pointer text-lg !leading-32px" | ||
372 | - /> | ||
373 | - </Tooltip> | ||
374 | - </div> | ||
375 | - </div> | ||
376 | - </section> | ||
377 | - | ||
378 | - <div class="text-center"> | ||
379 | - <Button type="primary" @click="handleAdd">添加数据源</Button> | ||
380 | - </div> | ||
381 | - <VisualOptionsModal | ||
382 | - :value="props.frontId" | ||
383 | - @close="handleRowComponentInfo" | ||
384 | - @register="registerVisualOptionModal" | ||
385 | - /> | ||
386 | - </section> | ||
387 | -</template> | ||
388 | - | ||
389 | -<style scoped> | ||
390 | - .data-source-form:deep(.ant-row) { | ||
391 | - width: 100%; | ||
392 | - } | ||
393 | - | ||
394 | - .data-source-form:deep(.ant-form-item-control-input-content > div > div) { | ||
395 | - width: 100%; | ||
396 | - } | ||
397 | -</style> |
src/views/visual/board/detail/components/DataBindModal.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { Tabs } from 'ant-design-vue'; | ||
3 | - import BasicModal from '/@/components/Modal/src/BasicModal.vue'; | ||
4 | - import BasicConfiguration from './BasicConfiguration.vue'; | ||
5 | - import VisualConfiguration from './VisualConfiguration.vue'; | ||
6 | - import { computed, ref, unref } from 'vue'; | ||
7 | - import { RouteParams, useRoute } from 'vue-router'; | ||
8 | - import { addDataComponent, updateDataComponent } from '/@/api/dataBoard'; | ||
9 | - import { useModalInner } from '/@/components/Modal'; | ||
10 | - import { DataBoardLayoutInfo } from '../../types/type'; | ||
11 | - import { useMessage } from '/@/hooks/web/useMessage'; | ||
12 | - import { decode } from '../../config/config'; | ||
13 | - import { ComponentInfo } from '/@/api/dataBoard/model'; | ||
14 | - import { useCalcGridLayout } from '../../hook/useCalcGridLayout'; | ||
15 | - import { FrontComponent } from '../../const/const'; | ||
16 | - import { frontComponentMap } from '../../components/help'; | ||
17 | - import { ValidateErrorEntity } from 'ant-design-vue/es/form/interface'; | ||
18 | - | ||
19 | - interface DataComponentRouteParams extends RouteParams { | ||
20 | - id: string; | ||
21 | - } | ||
22 | - | ||
23 | - const props = defineProps<{ | ||
24 | - layout: DataBoardLayoutInfo[]; | ||
25 | - }>(); | ||
26 | - | ||
27 | - const emit = defineEmits(['update', 'create', 'register']); | ||
28 | - const ROUTE = useRoute(); | ||
29 | - | ||
30 | - const loading = ref(false); | ||
31 | - const { createMessage } = useMessage(); | ||
32 | - | ||
33 | - const boardId = computed(() => { | ||
34 | - return decode((ROUTE.params as DataComponentRouteParams).boardId as string); | ||
35 | - }); | ||
36 | - | ||
37 | - const frontId = ref(); | ||
38 | - | ||
39 | - const isEdit = ref(false); | ||
40 | - | ||
41 | - const componentRecord = ref<DataBoardLayoutInfo>({} as unknown as DataBoardLayoutInfo); | ||
42 | - | ||
43 | - const componentDefaultConfig = ref<Partial<ComponentInfo>>({}); | ||
44 | - | ||
45 | - const getComponentCategory = computed(() => { | ||
46 | - return frontComponentMap.get(unref(frontId))?.ComponentCategory; | ||
47 | - }); | ||
48 | - | ||
49 | - const [register, { closeModal, changeOkLoading }] = useModalInner( | ||
50 | - (data: { isEdit: boolean; record?: DataBoardLayoutInfo }) => { | ||
51 | - componentRecord.value = data.record || ({} as unknown as DataBoardLayoutInfo); | ||
52 | - if (!unref(isEdit)) frontId.value = FrontComponent.TEXT_COMPONENT_1; | ||
53 | - frontId.value = | ||
54 | - (data.record?.record?.frontId as FrontComponent) || FrontComponent.TEXT_COMPONENT_1; | ||
55 | - isEdit.value = data.isEdit || false; | ||
56 | - } | ||
57 | - ); | ||
58 | - | ||
59 | - const basicConfigurationEl = ref<{ | ||
60 | - getAllDataSourceFieldValue: Fn<any, Recordable>; | ||
61 | - validate: Fn; | ||
62 | - }>(); | ||
63 | - | ||
64 | - const resetForm = () => { | ||
65 | - isEdit.value = false; | ||
66 | - frontId.value = undefined; | ||
67 | - componentRecord.value = {} as unknown as DataBoardLayoutInfo; | ||
68 | - componentDefaultConfig.value = {}; | ||
69 | - }; | ||
70 | - | ||
71 | - const handleSubmit = async () => { | ||
72 | - try { | ||
73 | - const { getAllDataSourceFieldValue, validate } = unref(basicConfigurationEl)!; | ||
74 | - await validate(); | ||
75 | - const value = getAllDataSourceFieldValue(); | ||
76 | - unref(isEdit) ? handleUpdateComponent(value) : handleAddComponent(value); | ||
77 | - resetForm(); | ||
78 | - } catch (error: unknown) { | ||
79 | - if ( | ||
80 | - ((error || {}) as ValidateErrorEntity).errorFields && | ||
81 | - ((error || {}) as ValidateErrorEntity).errorFields.length | ||
82 | - ) { | ||
83 | - const tooltip = ((error || {}) as ValidateErrorEntity).errorFields[0]; | ||
84 | - createMessage.warning(tooltip.errors[0]); | ||
85 | - } | ||
86 | - throw error; | ||
87 | - } | ||
88 | - }; | ||
89 | - | ||
90 | - const { calcLayoutInfo } = useCalcGridLayout(); | ||
91 | - const handleAddComponent = async (value: Recordable) => { | ||
92 | - try { | ||
93 | - if (!unref(frontId)) { | ||
94 | - createMessage.warning('请选择可视化组件'); | ||
95 | - return; | ||
96 | - } | ||
97 | - const layout = calcLayoutInfo(unref(props.layout)); | ||
98 | - changeOkLoading(true); | ||
99 | - loading.value = true; | ||
100 | - await addDataComponent({ | ||
101 | - boardId: unref(boardId), | ||
102 | - record: { dataBoardId: unref(boardId), frontId: unref(frontId), ...value, layout }, | ||
103 | - }); | ||
104 | - createMessage.success('创建成功'); | ||
105 | - closeModal(); | ||
106 | - emit('create'); | ||
107 | - } catch (error) { | ||
108 | - throw error; | ||
109 | - // createMessage.error('创建失败'); | ||
110 | - } finally { | ||
111 | - changeOkLoading(false); | ||
112 | - loading.value = false; | ||
113 | - } | ||
114 | - }; | ||
115 | - | ||
116 | - const handleUpdateComponent = async (value: Recordable) => { | ||
117 | - try { | ||
118 | - changeOkLoading(true); | ||
119 | - loading.value = true; | ||
120 | - const res = await updateDataComponent({ | ||
121 | - boardId: unref(boardId), | ||
122 | - record: { | ||
123 | - id: unref(componentRecord).i, | ||
124 | - dataBoardId: unref(boardId), | ||
125 | - frontId: unref(frontId), | ||
126 | - ...value, | ||
127 | - }, | ||
128 | - }); | ||
129 | - createMessage.success('修改成功'); | ||
130 | - closeModal(); | ||
131 | - // emit('submit'); | ||
132 | - emit('update', res.data.id); | ||
133 | - } catch (error) { | ||
134 | - // createMessage.error('修改失败'); | ||
135 | - } finally { | ||
136 | - changeOkLoading(false); | ||
137 | - loading.value = false; | ||
138 | - } | ||
139 | - }; | ||
140 | - | ||
141 | - const handleComponentCheckedChange = (record: ComponentInfo) => { | ||
142 | - componentDefaultConfig.value = record; | ||
143 | - }; | ||
144 | -</script> | ||
145 | - | ||
146 | -<template> | ||
147 | - <BasicModal | ||
148 | - v-bind="$attrs" | ||
149 | - @register="register" | ||
150 | - title="自定义组件" | ||
151 | - width="70%" | ||
152 | - :destroy-on-close="true" | ||
153 | - @ok="handleSubmit" | ||
154 | - @cancel="resetForm" | ||
155 | - :ok-button-props="{ loading }" | ||
156 | - > | ||
157 | - <section> | ||
158 | - <Tabs type="card"> | ||
159 | - <Tabs.TabPane key="basicConfig" tab="基础配置"> | ||
160 | - <BasicConfiguration | ||
161 | - ref="basicConfigurationEl" | ||
162 | - :front-id="frontId" | ||
163 | - :isEdit="isEdit" | ||
164 | - :record="componentRecord" | ||
165 | - :defaultConfig="componentDefaultConfig" | ||
166 | - :componentCategory="getComponentCategory" | ||
167 | - /> | ||
168 | - </Tabs.TabPane> | ||
169 | - <Tabs.TabPane key="visualConfig" tab="可视化配置"> | ||
170 | - <VisualConfiguration v-model:value="frontId" @change="handleComponentCheckedChange" /> | ||
171 | - </Tabs.TabPane> | ||
172 | - </Tabs> | ||
173 | - </section> | ||
174 | - </BasicModal> | ||
175 | -</template> |
1 | -<script lang="ts" setup> | ||
2 | - import { ref, computed } from 'vue'; | ||
3 | - import { FrontComponent } from '../../../const/const'; | ||
4 | - import { dataSourceSchema } from '../../config/basicConfiguration'; | ||
5 | - import { FormActionType } from '/@/components/Form'; | ||
6 | - import BasicForm from '/@/components/Form/src/BasicForm.vue'; | ||
7 | - const formEl = ref<Nullable<FormActionType>>(null); | ||
8 | - | ||
9 | - const props = defineProps<{ | ||
10 | - frontId?: FrontComponent; | ||
11 | - isEdit: boolean; | ||
12 | - }>(); | ||
13 | - | ||
14 | - defineExpose({ formActionType: formEl }); | ||
15 | - | ||
16 | - const getDataSchema = computed(() => { | ||
17 | - const { frontId, isEdit } = props; | ||
18 | - if (!frontId) return []; | ||
19 | - return dataSourceSchema(isEdit, frontId); | ||
20 | - }); | ||
21 | -</script> | ||
22 | - | ||
23 | -<template> | ||
24 | - <BasicForm | ||
25 | - ref="formEl" | ||
26 | - :schemas="getDataSchema" | ||
27 | - class="w-full flex-1 data-source-form" | ||
28 | - :show-action-button-group="false" | ||
29 | - :row-props="{ | ||
30 | - gutter: 10, | ||
31 | - }" | ||
32 | - layout="horizontal" | ||
33 | - :label-col="{ span: 0 }" | ||
34 | - /> | ||
35 | -</template> |
src/views/visual/board/detail/components/DataSourceForm/ControlDataSourceForm.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { ref, unref } from 'vue'; | ||
3 | - import { BasicForm, FormActionType } from '/@/components/Form'; | ||
4 | - import { dataSourceSchema } from '../../config/basicConfiguration'; | ||
5 | - import { FrontComponent } from '../../../const/const'; | ||
6 | - | ||
7 | - defineProps<{ | ||
8 | - isEdit: boolean; | ||
9 | - frontId?: FrontComponent; | ||
10 | - }>(); | ||
11 | - | ||
12 | - const formEl = ref<Nullable<FormActionType>>(); | ||
13 | - | ||
14 | - const setFormEl = (el: any) => { | ||
15 | - formEl.value = el; | ||
16 | - }; | ||
17 | - | ||
18 | - const getFieldsValue = () => { | ||
19 | - return unref(formEl)!.getFieldsValue(); | ||
20 | - }; | ||
21 | - | ||
22 | - const validate = async () => { | ||
23 | - await unref(formEl)!.validate(); | ||
24 | - }; | ||
25 | - | ||
26 | - const setFieldsValue = async (record: Recordable) => { | ||
27 | - await unref(formEl)!.setFieldsValue(record); | ||
28 | - }; | ||
29 | - | ||
30 | - const clearValidate = async (name?: string | string[]) => { | ||
31 | - await unref(formEl)!.clearValidate(name); | ||
32 | - }; | ||
33 | - defineExpose({ | ||
34 | - formActionType: { getFieldsValue, validate, setFieldsValue, clearValidate }, | ||
35 | - }); | ||
36 | -</script> | ||
37 | - | ||
38 | -<template> | ||
39 | - <div class="w-full flex-1"> | ||
40 | - <BasicForm | ||
41 | - :ref="(el) => setFormEl(el)" | ||
42 | - :schemas="dataSourceSchema($props.isEdit, $props.frontId)" | ||
43 | - class="w-full flex-1 data-source-form" | ||
44 | - :show-action-button-group="false" | ||
45 | - :row-props="{ | ||
46 | - gutter: 10, | ||
47 | - }" | ||
48 | - layout="horizontal" | ||
49 | - :label-col="{ span: 0 }" | ||
50 | - /> | ||
51 | - </div> | ||
52 | -</template> |
src/views/visual/board/detail/components/DataSourceForm/help.ts
deleted
100644 → 0
1 | -import { Component } from 'vue'; | ||
2 | -import { FrontComponent } from '../../../const/const'; | ||
3 | -import BasicDataSourceForm from './BasicDataSourceForm.vue'; | ||
4 | -// import ControlDataSourceForm from './ControlDataSourceForm.vue'; | ||
5 | - | ||
6 | -const dataSourceComponentMap = new Map<FrontComponent, Component>(); | ||
7 | - | ||
8 | -export const getDataSourceComponent = (frontId: FrontComponent) => { | ||
9 | - if (dataSourceComponentMap.has(frontId)) return dataSourceComponentMap.get(frontId)!; | ||
10 | - return BasicDataSourceForm; | ||
11 | -}; |
src/views/visual/board/detail/components/HistoryTrendModal.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { computed, nextTick, Ref, ref, unref } from 'vue'; | ||
3 | - import { getDeviceHistoryInfo } from '/@/api/alarm/position'; | ||
4 | - import { Empty, Spin } from 'ant-design-vue'; | ||
5 | - import { useECharts } from '/@/hooks/web/useECharts'; | ||
6 | - import { AggregateDataEnum } from '/@/views/device/localtion/config.data'; | ||
7 | - import { useGridLayout } from '/@/hooks/component/useGridLayout'; | ||
8 | - import { ColEx } from '/@/components/Form/src/types'; | ||
9 | - import { DataSource } from '/@/api/dataBoard/model'; | ||
10 | - import { useForm, BasicForm } from '/@/components/Form'; | ||
11 | - import { formSchema, SchemaFiled } from '../config/historyTrend.config'; | ||
12 | - import BasicModal from '/@/components/Modal/src/BasicModal.vue'; | ||
13 | - import { useModalInner } from '/@/components/Modal'; | ||
14 | - import { getAllDeviceByOrg } from '/@/api/dataBoard'; | ||
15 | - import { useHistoryData } from '/@/views/device/list/hook/useHistoryData'; | ||
16 | - import { BasicTable, useTable } from '/@/components/Table'; | ||
17 | - import { formatToDateTime } from '/@/utils/dateUtil'; | ||
18 | - import { | ||
19 | - ModeSwitchButton, | ||
20 | - TABLE_CHART_MODE_LIST, | ||
21 | - EnumTableChartMode, | ||
22 | - } from '/@/components/Widget'; | ||
23 | - | ||
24 | - type DeviceOption = Record<'label' | 'value' | 'organizationId', string>; | ||
25 | - | ||
26 | - defineEmits(['register']); | ||
27 | - | ||
28 | - const mode = ref<EnumTableChartMode>(EnumTableChartMode.CHART); | ||
29 | - | ||
30 | - const chartRef = ref(); | ||
31 | - | ||
32 | - const loading = ref(false); | ||
33 | - | ||
34 | - const isNull = ref(false); | ||
35 | - | ||
36 | - const historyData = ref<{ ts: number; value: string; name: string }[]>([]); | ||
37 | - | ||
38 | - const { deviceAttrs, getDeviceKeys, getSearchParams, setChartOptions, getDeviceAttribute } = | ||
39 | - useHistoryData(); | ||
40 | - | ||
41 | - const { setOptions, destory } = useECharts(chartRef as Ref<HTMLDivElement>); | ||
42 | - | ||
43 | - function hasDeviceAttr() { | ||
44 | - return !!unref(deviceAttrs).length; | ||
45 | - } | ||
46 | - | ||
47 | - const [register, method] = useForm({ | ||
48 | - schemas: formSchema(), | ||
49 | - baseColProps: useGridLayout(2, 3, 4) as unknown as ColEx, | ||
50 | - rowProps: { | ||
51 | - gutter: 10, | ||
52 | - }, | ||
53 | - labelWidth: 120, | ||
54 | - fieldMapToTime: [ | ||
55 | - [SchemaFiled.DATE_RANGE, [SchemaFiled.START_TS, SchemaFiled.END_TS], 'YYYY-MM-DD HH:ss'], | ||
56 | - ], | ||
57 | - submitButtonOptions: { | ||
58 | - loading: loading as unknown as boolean, | ||
59 | - }, | ||
60 | - async submitFunc() { | ||
61 | - search(); | ||
62 | - }, | ||
63 | - }); | ||
64 | - | ||
65 | - const search = async () => { | ||
66 | - // 表单验证 | ||
67 | - await method.validate(); | ||
68 | - const value = method.getFieldsValue(); | ||
69 | - const searchParams = getSearchParams(value); | ||
70 | - if (!hasDeviceAttr()) return; | ||
71 | - // 发送请求 | ||
72 | - loading.value = true; | ||
73 | - const res = await getDeviceHistoryInfo(searchParams); | ||
74 | - historyData.value = getTableHistoryData(res); | ||
75 | - loading.value = false; | ||
76 | - // 判断数据对象是否为空 | ||
77 | - if (!Object.keys(res).length) { | ||
78 | - isNull.value = false; | ||
79 | - return; | ||
80 | - } else { | ||
81 | - isNull.value = true; | ||
82 | - } | ||
83 | - | ||
84 | - const selectedKeys = unref(deviceAttrs).find( | ||
85 | - (item) => item.identifier === value[SchemaFiled.KEYS] | ||
86 | - ); | ||
87 | - setOptions(setChartOptions(res, selectedKeys)); | ||
88 | - }; | ||
89 | - | ||
90 | - const getTableHistoryData = (record: Recordable<{ ts: number; value: string }[]>) => { | ||
91 | - const keys = Object.keys(record); | ||
92 | - const list = keys.reduce((prev, next) => { | ||
93 | - const list = record[next].map((item) => { | ||
94 | - return { | ||
95 | - ...item, | ||
96 | - name: next, | ||
97 | - }; | ||
98 | - }); | ||
99 | - return [...prev, ...list]; | ||
100 | - }, []); | ||
101 | - return list; | ||
102 | - }; | ||
103 | - | ||
104 | - const getIdentifierNameMapping = computed(() => { | ||
105 | - const mapping = {}; | ||
106 | - unref(deviceAttrs).forEach((item) => { | ||
107 | - const { identifier, name } = item; | ||
108 | - mapping[identifier] = name; | ||
109 | - }); | ||
110 | - return mapping; | ||
111 | - }); | ||
112 | - | ||
113 | - const [registerTable] = useTable({ | ||
114 | - showIndexColumn: false, | ||
115 | - showTableSetting: false, | ||
116 | - dataSource: historyData, | ||
117 | - maxHeight: 300, | ||
118 | - size: 'small', | ||
119 | - columns: [ | ||
120 | - { | ||
121 | - title: '属性', | ||
122 | - dataIndex: 'name', | ||
123 | - format: (value) => { | ||
124 | - return unref(getIdentifierNameMapping)[value]; | ||
125 | - }, | ||
126 | - }, | ||
127 | - { | ||
128 | - title: '值', | ||
129 | - dataIndex: 'value', | ||
130 | - }, | ||
131 | - { | ||
132 | - title: '更新时间', | ||
133 | - dataIndex: 'ts', | ||
134 | - format: (val) => { | ||
135 | - return formatToDateTime(val, 'YYYY-MM-DD HH:mm:ss'); | ||
136 | - }, | ||
137 | - }, | ||
138 | - ], | ||
139 | - }); | ||
140 | - | ||
141 | - const getDeviceDataKey = async (record: DeviceOption) => { | ||
142 | - const { organizationId, value } = record; | ||
143 | - try { | ||
144 | - const options = await getAllDeviceByOrg(organizationId); | ||
145 | - const record = options.find((item) => item.tbDeviceId === value); | ||
146 | - await getDeviceAttribute(record!); | ||
147 | - await nextTick(); | ||
148 | - method.updateSchema({ | ||
149 | - field: SchemaFiled.KEYS, | ||
150 | - componentProps: { | ||
151 | - options: unref(deviceAttrs).map((item) => ({ label: item.name, value: item.identifier })), | ||
152 | - }, | ||
153 | - }); | ||
154 | - } catch (error) { | ||
155 | - throw error; | ||
156 | - } | ||
157 | - }; | ||
158 | - | ||
159 | - const handleModalOpen = async () => { | ||
160 | - await nextTick(); | ||
161 | - | ||
162 | - method.setFieldsValue({ | ||
163 | - [SchemaFiled.START_TS]: 1 * 24 * 60 * 60 * 1000, | ||
164 | - [SchemaFiled.LIMIT]: 7, | ||
165 | - [SchemaFiled.AGG]: AggregateDataEnum.NONE, | ||
166 | - }); | ||
167 | - | ||
168 | - if (!hasDeviceAttr()) return; | ||
169 | - | ||
170 | - const { deviceId } = method.getFieldsValue(); | ||
171 | - | ||
172 | - const res = await getDeviceHistoryInfo({ | ||
173 | - entityId: deviceId, | ||
174 | - keys: unref(getDeviceKeys).join(), | ||
175 | - startTs: Date.now() - 1 * 24 * 60 * 60 * 1000, | ||
176 | - endTs: Date.now(), | ||
177 | - agg: AggregateDataEnum.NONE, | ||
178 | - limit: 7, | ||
179 | - }); | ||
180 | - historyData.value = getTableHistoryData(res); | ||
181 | - // 判断对象是否为空 | ||
182 | - if (!Object.keys(res).length) { | ||
183 | - isNull.value = false; | ||
184 | - return; | ||
185 | - } else { | ||
186 | - isNull.value = true; | ||
187 | - } | ||
188 | - setOptions(setChartOptions(res)); | ||
189 | - }; | ||
190 | - | ||
191 | - const generateDeviceOptions = (dataSource: DataSource[]) => { | ||
192 | - const record: { [key: string]: boolean } = {}; | ||
193 | - | ||
194 | - const options: DeviceOption[] = []; | ||
195 | - for (const item of dataSource) { | ||
196 | - const { deviceName, gatewayDevice, slaveDeviceId, organizationId } = item; | ||
197 | - let { deviceId } = item; | ||
198 | - if (gatewayDevice && slaveDeviceId) { | ||
199 | - deviceId = slaveDeviceId; | ||
200 | - } | ||
201 | - if (record[deviceId]) continue; | ||
202 | - options.push({ | ||
203 | - label: deviceName, | ||
204 | - value: deviceId, | ||
205 | - organizationId, | ||
206 | - }); | ||
207 | - record[deviceId] = true; | ||
208 | - } | ||
209 | - | ||
210 | - return options; | ||
211 | - }; | ||
212 | - | ||
213 | - const [registerModal] = useModalInner(async (dataSource: DataSource[]) => { | ||
214 | - deviceAttrs.value = []; | ||
215 | - loading.value = false; | ||
216 | - const options = generateDeviceOptions(dataSource); | ||
217 | - await nextTick(); | ||
218 | - method.updateSchema({ | ||
219 | - field: SchemaFiled.DEVICE_ID, | ||
220 | - componentProps({ formActionType }) { | ||
221 | - const { setFieldsValue } = formActionType; | ||
222 | - return { | ||
223 | - options, | ||
224 | - onChange(_, record: DeviceOption) { | ||
225 | - getDeviceDataKey(record); | ||
226 | - setFieldsValue({ [SchemaFiled.KEYS]: null }); | ||
227 | - }, | ||
228 | - }; | ||
229 | - }, | ||
230 | - }); | ||
231 | - | ||
232 | - if (options.length && options.at(0)?.value) { | ||
233 | - const record = options.at(0)!; | ||
234 | - await getDeviceDataKey(record); | ||
235 | - try { | ||
236 | - await method.setFieldsValue({ [SchemaFiled.DEVICE_ID]: record.value }); | ||
237 | - } catch (error) {} | ||
238 | - } | ||
239 | - | ||
240 | - await handleModalOpen(); | ||
241 | - }); | ||
242 | - | ||
243 | - const handleCancel = () => { | ||
244 | - destory(); | ||
245 | - }; | ||
246 | - | ||
247 | - const switchMode = (flag: EnumTableChartMode) => { | ||
248 | - mode.value = flag; | ||
249 | - }; | ||
250 | -</script> | ||
251 | - | ||
252 | -<template> | ||
253 | - <BasicModal | ||
254 | - @register="registerModal" | ||
255 | - @cancel="handleCancel" | ||
256 | - :destroy-on-close="true" | ||
257 | - :show-ok-btn="false" | ||
258 | - cancel-text="关闭" | ||
259 | - width="70%" | ||
260 | - title="历史趋势" | ||
261 | - > | ||
262 | - <section | ||
263 | - class="flex flex-col p-4 h-full w-full min-w-7/10" | ||
264 | - style="color: #f0f2f5; background-color: #f0f2f5" | ||
265 | - > | ||
266 | - <section class="bg-white my-3 p-2"> | ||
267 | - <BasicForm @register="register" /> | ||
268 | - </section> | ||
269 | - <section class="bg-white p-3" style="min-height: 350px"> | ||
270 | - <Spin :spinning="loading" :absolute="true"> | ||
271 | - <div | ||
272 | - v-show="mode === EnumTableChartMode.CHART" | ||
273 | - class="flex h-70px items-center justify-end p-2" | ||
274 | - > | ||
275 | - <ModeSwitchButton | ||
276 | - v-model:value="mode" | ||
277 | - :mode="TABLE_CHART_MODE_LIST" | ||
278 | - @change="switchMode" | ||
279 | - /> | ||
280 | - </div> | ||
281 | - | ||
282 | - <div | ||
283 | - v-show="isNull && mode === EnumTableChartMode.CHART" | ||
284 | - ref="chartRef" | ||
285 | - :style="{ height: '350px', width: '100%' }" | ||
286 | - > | ||
287 | - </div> | ||
288 | - <Empty | ||
289 | - v-if="mode === EnumTableChartMode.CHART" | ||
290 | - class="h-350px flex flex-col justify-center items-center" | ||
291 | - description="暂无数据,请选择设备查询" | ||
292 | - v-show="!isNull" | ||
293 | - /> | ||
294 | - | ||
295 | - <BasicTable v-show="mode === EnumTableChartMode.TABLE" @register="registerTable"> | ||
296 | - <template #toolbar> | ||
297 | - <div class="flex h-70px items-center justify-end p-2"> | ||
298 | - <ModeSwitchButton | ||
299 | - v-model:value="mode" | ||
300 | - :mode="TABLE_CHART_MODE_LIST" | ||
301 | - @change="switchMode" | ||
302 | - /> | ||
303 | - </div> | ||
304 | - </template> | ||
305 | - </BasicTable> | ||
306 | - </Spin> | ||
307 | - </section> | ||
308 | - </section> | ||
309 | - </BasicModal> | ||
310 | -</template> | ||
311 | - | ||
312 | -<style scoped></style> |
src/views/visual/board/detail/components/VisualConfiguration.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { Tabs, List } from 'ant-design-vue'; | ||
3 | - import VisualWidgetSelect from './VisualWidgetSelect.vue'; | ||
4 | - import { getComponentDefaultConfig } from '../../components/help'; | ||
5 | - import { frontComponentMap } from '../../components/help'; | ||
6 | - import { computed } from 'vue'; | ||
7 | - import { | ||
8 | - FrontComponent, | ||
9 | - FrontComponentCategory, | ||
10 | - FrontComponentCategoryName, | ||
11 | - } from '../../const/const'; | ||
12 | - | ||
13 | - interface DataSource { | ||
14 | - category: string; | ||
15 | - categoryName: string; | ||
16 | - list: Recordable[]; | ||
17 | - } | ||
18 | - const props = defineProps<{ | ||
19 | - value?: string; | ||
20 | - }>(); | ||
21 | - const emit = defineEmits(['update:value', 'change']); | ||
22 | - | ||
23 | - const grid = { gutter: 10, column: 1, xs: 1, sm: 2, md: 2, lg: 3, xl: 3, xxl: 4 }; | ||
24 | - | ||
25 | - const getDataSource = computed(() => { | ||
26 | - const _dataSource = Array.from(frontComponentMap.values()); | ||
27 | - const category = new Map<FrontComponentCategory, DataSource>(); | ||
28 | - for (const item of _dataSource) { | ||
29 | - if (category.has(item.ComponentCategory)) { | ||
30 | - const value = category.get(item.ComponentCategory)!; | ||
31 | - value.list.push(item); | ||
32 | - continue; | ||
33 | - } | ||
34 | - category.set(item.ComponentCategory, { | ||
35 | - category: item.ComponentCategory, | ||
36 | - categoryName: FrontComponentCategoryName[item.ComponentCategory], | ||
37 | - list: [item], | ||
38 | - }); | ||
39 | - } | ||
40 | - return Array.from(category.values()); | ||
41 | - }); | ||
42 | - | ||
43 | - const handleCheck = (checked: FrontComponent) => { | ||
44 | - const defaultConfig = getComponentDefaultConfig(checked); | ||
45 | - emit('update:value', checked); | ||
46 | - emit('change', defaultConfig); | ||
47 | - }; | ||
48 | -</script> | ||
49 | - | ||
50 | -<template> | ||
51 | - <section> | ||
52 | - <Tabs> | ||
53 | - <Tabs.TabPane | ||
54 | - v-for="category in getDataSource" | ||
55 | - :key="category.category" | ||
56 | - :tab="category.categoryName" | ||
57 | - > | ||
58 | - <List :grid="grid" :data-source="category.list"> | ||
59 | - <template #renderItem="{ item }"> | ||
60 | - <List.Item class="!flex !justify-center"> | ||
61 | - <VisualWidgetSelect | ||
62 | - :checked-id="props.value" | ||
63 | - :control-id="item.ComponentKey" | ||
64 | - @change="handleCheck" | ||
65 | - > | ||
66 | - <template #default> | ||
67 | - <component :is="item.Component" :random="true" :layout="item.ComponentConfig" /> | ||
68 | - </template> | ||
69 | - <template #description> | ||
70 | - {{ item.ComponentName || '选择' }} | ||
71 | - </template> | ||
72 | - </VisualWidgetSelect> | ||
73 | - </List.Item> | ||
74 | - </template> | ||
75 | - </List> | ||
76 | - </Tabs.TabPane> | ||
77 | - </Tabs> | ||
78 | - </section> | ||
79 | -</template> |
src/views/visual/board/detail/components/VisualOptionsModal.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { ref, unref } from 'vue'; | ||
3 | - import { schemasMap, VisualOptionParams } from '../config/visualOptions'; | ||
4 | - import { useForm, BasicForm } from '/@/components/Form'; | ||
5 | - import { BasicModal, useModalInner } from '/@/components/Modal'; | ||
6 | - import { ComponentInfo } from '/@/api/dataBoard/model'; | ||
7 | - import { computed } from '@vue/reactivity'; | ||
8 | - import { FrontComponent, Gradient, visualOptionField } from '../../const/const'; | ||
9 | - | ||
10 | - const emit = defineEmits(['close', 'register']); | ||
11 | - | ||
12 | - const props = defineProps<{ | ||
13 | - value?: string; | ||
14 | - }>(); | ||
15 | - | ||
16 | - const recordId = ref(''); | ||
17 | - | ||
18 | - const getSchemas = computed(() => { | ||
19 | - return schemasMap.get((props.value as FrontComponent) || 'text-component-1'); | ||
20 | - }); | ||
21 | - | ||
22 | - const [registerForm, method] = useForm({ | ||
23 | - showActionButtonGroup: false, | ||
24 | - labelWidth: 120, | ||
25 | - baseColProps: { | ||
26 | - span: 12, | ||
27 | - }, | ||
28 | - }); | ||
29 | - | ||
30 | - const [register, { closeModal }] = useModalInner( | ||
31 | - (data: { recordId: string; componentInfo: ComponentInfo }) => { | ||
32 | - recordId.value = data.recordId; | ||
33 | - const gradientInfo = data.componentInfo?.gradientInfo || []; | ||
34 | - let gradientRecord = {}; | ||
35 | - if (gradientInfo && gradientInfo.length) { | ||
36 | - const first = gradientInfo.find((item) => item.key === Gradient.FIRST); | ||
37 | - const second = gradientInfo.find((item) => item.key === Gradient.SECOND); | ||
38 | - const third = gradientInfo.find((item) => item.key === Gradient.THIRD); | ||
39 | - gradientRecord = { | ||
40 | - [visualOptionField.FIRST_PHASE_COLOR]: first?.color, | ||
41 | - [visualOptionField.FIRST_PHASE_VALUE]: first?.value, | ||
42 | - [visualOptionField.SECOND_PHASE_COLOR]: second?.color, | ||
43 | - [visualOptionField.SECOND_PHASE_VALUE]: second?.value, | ||
44 | - [visualOptionField.THIRD_PHASE_COLOR]: third?.color, | ||
45 | - [visualOptionField.THIRD_PHASE_VALUE]: third?.value, | ||
46 | - }; | ||
47 | - } | ||
48 | - | ||
49 | - method.setFieldsValue({ ...(data.componentInfo || {}), ...gradientRecord }); | ||
50 | - } | ||
51 | - ); | ||
52 | - | ||
53 | - const handleGetValue = () => { | ||
54 | - const value = method.getFieldsValue(); | ||
55 | - emit('close', unref(recordId), transformValue(value)); | ||
56 | - }; | ||
57 | - | ||
58 | - const transformValue = (value: Partial<VisualOptionParams>) => { | ||
59 | - return { | ||
60 | - fontColor: value.fontColor || null, | ||
61 | - icon: value.icon || null, | ||
62 | - iconColor: value.iconColor || null, | ||
63 | - unit: value.unit || null, | ||
64 | - showDeviceName: value.showDeviceName, | ||
65 | - gradientInfo: [ | ||
66 | - { key: Gradient.FIRST, value: value.firstPhaseValue, color: value.firstPhaseColor }, | ||
67 | - { key: Gradient.SECOND, value: value.secondPhaseValue, color: value.secondPhaseColor }, | ||
68 | - { key: Gradient.THIRD, value: value.thirdPhaseValue, color: value.thirdPhaseColor }, | ||
69 | - ], | ||
70 | - }; | ||
71 | - }; | ||
72 | - | ||
73 | - const handleClose = () => { | ||
74 | - handleGetValue(); | ||
75 | - closeModal(); | ||
76 | - }; | ||
77 | -</script> | ||
78 | - | ||
79 | -<template> | ||
80 | - <BasicModal | ||
81 | - v-bind="$attrs" | ||
82 | - destroy-on-close | ||
83 | - @register="register" | ||
84 | - @ok="handleClose" | ||
85 | - title="选项" | ||
86 | - width="40%" | ||
87 | - > | ||
88 | - <BasicForm class="form" @register="registerForm" :schemas="getSchemas" /> | ||
89 | - </BasicModal> | ||
90 | -</template> | ||
91 | - | ||
92 | -<style scoped> | ||
93 | - .form:deep(.ant-input-number) { | ||
94 | - width: 100%; | ||
95 | - } | ||
96 | -</style> |
src/views/visual/board/detail/components/VisualWidgetSelect.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | ||
2 | - import { Card } from 'ant-design-vue'; | ||
3 | - | ||
4 | - const props = defineProps({ | ||
5 | - controlId: { | ||
6 | - type: String, | ||
7 | - // required: true, | ||
8 | - }, | ||
9 | - checkedId: { | ||
10 | - type: String, | ||
11 | - // required: true, | ||
12 | - }, | ||
13 | - }); | ||
14 | - const emit = defineEmits(['change']); | ||
15 | - | ||
16 | - const handleClick = () => { | ||
17 | - emit('change', props.controlId); | ||
18 | - }; | ||
19 | -</script> | ||
20 | - | ||
21 | -<template> | ||
22 | - <Card | ||
23 | - :style="{ borderColor: props.controlId === props.checkedId ? '#3079FF' : '#fff' }" | ||
24 | - hoverable | ||
25 | - bordered | ||
26 | - class="w-60 h-60 border-2 widget-select !bg-light-50 cursor-pointer" | ||
27 | - @click="handleClick" | ||
28 | - > | ||
29 | - <div class="widget-container"> | ||
30 | - <slot></slot> | ||
31 | - </div> | ||
32 | - <Card.Meta> | ||
33 | - <template #description> | ||
34 | - <slot name="description"></slot> | ||
35 | - </template> | ||
36 | - </Card.Meta> | ||
37 | - </Card> | ||
38 | -</template> | ||
39 | - | ||
40 | -<style scoped> | ||
41 | - .widget-select { | ||
42 | - box-shadow: 0 1px 10px 0 rgba(0, 0, 0, 0.1); | ||
43 | - border-width: 2px; | ||
44 | - } | ||
45 | - | ||
46 | - .widget-select:deep(.ant-card-body) { | ||
47 | - /* height: 240px; */ | ||
48 | - | ||
49 | - /* width: 236px; | ||
50 | - height: 196px; */ | ||
51 | - | ||
52 | - /* width: 100%; | ||
53 | - height: 100%; */ | ||
54 | - width: 236px; | ||
55 | - height: 236px; | ||
56 | - padding: 0; | ||
57 | - box-sizing: border-box; | ||
58 | - display: flex; | ||
59 | - flex-direction: column; | ||
60 | - align-items: center; | ||
61 | - justify-content: center; | ||
62 | - } | ||
63 | - | ||
64 | - .widget-select:deep(.ant-card-meta) { | ||
65 | - border-top: 1px solid #f0f0f0; | ||
66 | - width: 100%; | ||
67 | - height: 40px; | ||
68 | - text-align: center; | ||
69 | - line-height: 40px; | ||
70 | - margin: 0; | ||
71 | - } | ||
72 | - | ||
73 | - .widget-select .widget-container { | ||
74 | - width: 236px; | ||
75 | - height: 196px; | ||
76 | - display: flex; | ||
77 | - justify-content: center; | ||
78 | - align-items: center; | ||
79 | - } | ||
80 | - | ||
81 | - [data-theme='dark'] .widget-select:deep(.ant-card-body) { | ||
82 | - @apply bg-dark-900; | ||
83 | - } | ||
84 | - | ||
85 | - [data-theme='dark'] .widget-select:deep(.ant-card) { | ||
86 | - @apply border-dark-300; | ||
87 | - } | ||
88 | -</style> |
src/views/visual/board/detail/config/basicConfiguration.ts
deleted
100644 → 0
1 | -import { getDeviceAttributes, getMeetTheConditionsDevice } from '/@/api/dataBoard'; | ||
2 | -import { getOrganizationList } from '/@/api/system/system'; | ||
3 | -import { FormSchema, useComponentRegister } from '/@/components/Form'; | ||
4 | -import { copyTransFun } from '/@/utils/fnUtils'; | ||
5 | -import { DeviceAttributeParams, MasterDeviceList } from '/@/api/dataBoard/model'; | ||
6 | -import { FrontComponent } from '../../const/const'; | ||
7 | -import { getDeviceProfile } from '/@/api/alarm/position'; | ||
8 | -import { getModelServices } from '/@/api/device/modelOfMatter'; | ||
9 | -import { findDictItemByCode } from '/@/api/system/dict'; | ||
10 | -import { DeviceTypeEnum } from '/@/api/device/model/deviceModel'; | ||
11 | -import { DataTypeEnum } from '/@/components/Form/src/externalCompns/components/StructForm/config'; | ||
12 | -import { TransportTypeEnum } from '/@/views/device/profiles/components/TransportDescript/const'; | ||
13 | -import { JSONEditor } from '/@/components/CodeEditor'; | ||
14 | -import { CommandTypeEnum } from '/@/views/rule/linkedge/config/config.data'; | ||
15 | -import { ModelOfMatterParams } from '/@/api/device/model/modelOfMatterModel'; | ||
16 | - | ||
17 | -useComponentRegister('JSONEditor', JSONEditor); | ||
18 | - | ||
19 | -export enum BasicConfigField { | ||
20 | - NAME = 'name', | ||
21 | - REMARK = 'remark', | ||
22 | -} | ||
23 | - | ||
24 | -const getDeviceAttribute = async (params: DeviceAttributeParams) => { | ||
25 | - try { | ||
26 | - const data = await getDeviceAttributes(params); | ||
27 | - if (data) return data.map((item) => ({ label: item.name, value: item.identifier })); | ||
28 | - } catch (error) {} | ||
29 | - return []; | ||
30 | -}; | ||
31 | - | ||
32 | -const getDeviceService = async (deviceProfileId: string) => { | ||
33 | - try { | ||
34 | - const data = await getModelServices({ deviceProfileId }); | ||
35 | - if (data) | ||
36 | - return data.map((item) => ({ ...item, label: item.functionName, value: item.identifier })); | ||
37 | - } catch (error) {} | ||
38 | - return []; | ||
39 | -}; | ||
40 | - | ||
41 | -export enum DataSourceField { | ||
42 | - IS_GATEWAY_DEVICE = 'gatewayDevice', | ||
43 | - DEVICE_TYPE = 'deviceType', | ||
44 | - TRANSPORT_TYPE = 'transportType', | ||
45 | - ORIGINATION_ID = 'organizationId', | ||
46 | - DEVICE_ID = 'deviceId', | ||
47 | - // SLAVE_DEVICE_ID = 'slaveDeviceId', | ||
48 | - DEVICE_PROFILE_ID = 'deviceProfileId', | ||
49 | - ATTRIBUTE = 'attribute', | ||
50 | - ATTRIBUTE_RENAME = 'attributeRename', | ||
51 | - DEVICE_NAME = 'deviceName', | ||
52 | - DEVICE_RENAME = 'deviceRename', | ||
53 | - LONGITUDE_ATTRIBUTE = 'longitudeAttribute', | ||
54 | - LATITUDE_ATTRIBUTE = 'latitudeAttribute', | ||
55 | - | ||
56 | - COMMAND = 'command', | ||
57 | - COMMAND_TYPE = 'commandType', | ||
58 | - SERVICE = 'service', | ||
59 | -} | ||
60 | - | ||
61 | -export const isControlComponent = (frontId: FrontComponent) => { | ||
62 | - const list = [ | ||
63 | - FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, | ||
64 | - FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, | ||
65 | - FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, | ||
66 | - ]; | ||
67 | - return list.includes(frontId); | ||
68 | -}; | ||
69 | - | ||
70 | -export const isMapComponent = (frontId: FrontComponent) => { | ||
71 | - const list = [ | ||
72 | - FrontComponent.MAP_COMPONENT_TRACK_HISTORY, | ||
73 | - FrontComponent.MAP_COMPONENT_TRACK_REAL, | ||
74 | - ]; | ||
75 | - return list.includes(frontId); | ||
76 | -}; | ||
77 | - | ||
78 | -const isTcpProfile = (transportType: string) => { | ||
79 | - return transportType === TransportTypeEnum.TCP; | ||
80 | -}; | ||
81 | - | ||
82 | -export const basicSchema: FormSchema[] = [ | ||
83 | - { | ||
84 | - field: BasicConfigField.NAME, | ||
85 | - label: '组件名称', | ||
86 | - component: 'Input', | ||
87 | - // rules: [{ required: true, message: '组件名称为必填项' }], | ||
88 | - componentProps: { | ||
89 | - placeholder: '请输入组件名称', | ||
90 | - maxLength: 32, | ||
91 | - }, | ||
92 | - }, | ||
93 | - { | ||
94 | - field: BasicConfigField.REMARK, | ||
95 | - label: '组件备注', | ||
96 | - component: 'InputTextArea', | ||
97 | - // rules: [{ required: true, message: '组件备注为必填项' }], | ||
98 | - componentProps: { | ||
99 | - placeholder: '请输入组件备注', | ||
100 | - maxLength: 255, | ||
101 | - }, | ||
102 | - }, | ||
103 | -]; | ||
104 | - | ||
105 | -export const dataSourceSchema = (isEdit: boolean, frontId?: string): FormSchema[] => { | ||
106 | - // console.log(useSelectWidgetKeys()); | ||
107 | - // const isEdit = unref(useSelectWidgetMode()) === DataActionModeEnum.UPDATE; | ||
108 | - | ||
109 | - return [ | ||
110 | - { | ||
111 | - field: DataSourceField.IS_GATEWAY_DEVICE, | ||
112 | - component: 'Switch', | ||
113 | - label: '是否是网关设备', | ||
114 | - show: false, | ||
115 | - }, | ||
116 | - { | ||
117 | - field: DataSourceField.DEVICE_NAME, | ||
118 | - component: 'Input', | ||
119 | - label: '设备名', | ||
120 | - show: false, | ||
121 | - }, | ||
122 | - { | ||
123 | - field: DataSourceField.TRANSPORT_TYPE, | ||
124 | - component: 'Input', | ||
125 | - label: '设备配置类型', | ||
126 | - show: false, | ||
127 | - }, | ||
128 | - { | ||
129 | - field: DataSourceField.DEVICE_TYPE, | ||
130 | - component: 'ApiSelect', | ||
131 | - label: '设备类型', | ||
132 | - colProps: { span: 8 }, | ||
133 | - rules: [{ message: '请选择设备类型', required: true }], | ||
134 | - componentProps: ({ formActionType }) => { | ||
135 | - const { setFieldsValue } = formActionType; | ||
136 | - return { | ||
137 | - api: findDictItemByCode, | ||
138 | - params: { | ||
139 | - dictCode: 'device_type', | ||
140 | - }, | ||
141 | - valueField: 'itemValue', | ||
142 | - labelField: 'itemText', | ||
143 | - placeholder: '请选择设备类型', | ||
144 | - onChange: (value: DeviceTypeEnum) => { | ||
145 | - setFieldsValue({ | ||
146 | - [DataSourceField.IS_GATEWAY_DEVICE]: value === DeviceTypeEnum.GATEWAY, | ||
147 | - [DataSourceField.DEVICE_PROFILE_ID]: null, | ||
148 | - [DataSourceField.DEVICE_ID]: null, | ||
149 | - [DataSourceField.ATTRIBUTE]: null, | ||
150 | - [DataSourceField.TRANSPORT_TYPE]: null, | ||
151 | - }); | ||
152 | - }, | ||
153 | - getPopupContainer: () => document.body, | ||
154 | - }; | ||
155 | - }, | ||
156 | - }, | ||
157 | - { | ||
158 | - field: DataSourceField.DEVICE_PROFILE_ID, | ||
159 | - component: 'ApiSelect', | ||
160 | - label: '产品', | ||
161 | - colProps: { span: 8 }, | ||
162 | - rules: [{ required: true, message: '产品为必填项' }], | ||
163 | - componentProps: ({ formActionType, formModel }) => { | ||
164 | - const { setFieldsValue } = formActionType; | ||
165 | - const deviceType = formModel[DataSourceField.DEVICE_TYPE]; | ||
166 | - return { | ||
167 | - api: async () => { | ||
168 | - if (!deviceType) return []; | ||
169 | - const list = await getDeviceProfile(deviceType); | ||
170 | - if (isEdit) { | ||
171 | - const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | ||
172 | - const record = list.find((item) => item.id === deviceProfileId); | ||
173 | - setFieldsValue({ [DataSourceField.TRANSPORT_TYPE]: record?.transportType }); | ||
174 | - } | ||
175 | - return list; | ||
176 | - }, | ||
177 | - labelField: 'name', | ||
178 | - valueField: 'id', | ||
179 | - placeholder: '请选择产品', | ||
180 | - onChange: (_, option = {} as Record<'transportType', string>) => { | ||
181 | - setFieldsValue({ | ||
182 | - [DataSourceField.DEVICE_ID]: null, | ||
183 | - [DataSourceField.ATTRIBUTE]: null, | ||
184 | - [DataSourceField.TRANSPORT_TYPE]: option[DataSourceField.TRANSPORT_TYPE], | ||
185 | - }); | ||
186 | - }, | ||
187 | - getPopupContainer: () => document.body, | ||
188 | - }; | ||
189 | - }, | ||
190 | - }, | ||
191 | - { | ||
192 | - field: DataSourceField.ORIGINATION_ID, | ||
193 | - component: 'ApiTreeSelect', | ||
194 | - label: '组织', | ||
195 | - colProps: { span: 8 }, | ||
196 | - rules: [{ required: true, message: '组织为必填项' }], | ||
197 | - componentProps({ formActionType }) { | ||
198 | - const { setFieldsValue } = formActionType; | ||
199 | - return { | ||
200 | - placeholder: '请选择组织', | ||
201 | - api: async () => { | ||
202 | - const data = await getOrganizationList(); | ||
203 | - copyTransFun(data as any as any[]); | ||
204 | - return data; | ||
205 | - }, | ||
206 | - onChange() { | ||
207 | - setFieldsValue({ | ||
208 | - [DataSourceField.DEVICE_ID]: null, | ||
209 | - }); | ||
210 | - }, | ||
211 | - getPopupContainer: () => document.body, | ||
212 | - }; | ||
213 | - }, | ||
214 | - }, | ||
215 | - { | ||
216 | - field: DataSourceField.DEVICE_PROFILE_ID, | ||
217 | - component: 'Input', | ||
218 | - label: '', | ||
219 | - show: false, | ||
220 | - }, | ||
221 | - { | ||
222 | - field: DataSourceField.DEVICE_ID, | ||
223 | - component: 'ApiSelect', | ||
224 | - label: '设备', | ||
225 | - colProps: { span: 8 }, | ||
226 | - rules: [{ required: true, message: '设备名称为必填项' }], | ||
227 | - componentProps({ formModel, formActionType }) { | ||
228 | - const { setFieldsValue } = formActionType; | ||
229 | - const organizationId = formModel[DataSourceField.ORIGINATION_ID]; | ||
230 | - const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | ||
231 | - const deviceType = formModel[DataSourceField.DEVICE_TYPE]; | ||
232 | - | ||
233 | - return { | ||
234 | - api: async () => { | ||
235 | - if (organizationId) { | ||
236 | - try { | ||
237 | - const data = await getMeetTheConditionsDevice({ | ||
238 | - organizationId, | ||
239 | - deviceProfileId, | ||
240 | - deviceType, | ||
241 | - }); | ||
242 | - if (data) | ||
243 | - return data.map((item) => ({ | ||
244 | - ...item, | ||
245 | - label: item.alias || item.name, | ||
246 | - value: item.tbDeviceId, | ||
247 | - deviceType: item.deviceType, | ||
248 | - })); | ||
249 | - } catch (error) {} | ||
250 | - } | ||
251 | - return []; | ||
252 | - }, | ||
253 | - onChange(_value, record: MasterDeviceList) { | ||
254 | - setFieldsValue({ | ||
255 | - [DataSourceField.DEVICE_NAME]: record?.label, | ||
256 | - }); | ||
257 | - }, | ||
258 | - placeholder: '请选择设备', | ||
259 | - getPopupContainer: () => document.body, | ||
260 | - }; | ||
261 | - }, | ||
262 | - }, | ||
263 | - { | ||
264 | - field: DataSourceField.ATTRIBUTE, | ||
265 | - component: 'ApiSelect', | ||
266 | - label: '属性', | ||
267 | - colProps: { span: 8 }, | ||
268 | - rules: [{ required: true, message: '请选择属性' }], | ||
269 | - ifShow: ({ model }) => | ||
270 | - !(isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]) && isControlComponent(frontId!)), | ||
271 | - componentProps({ formModel }) { | ||
272 | - const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | ||
273 | - return { | ||
274 | - api: async () => { | ||
275 | - try { | ||
276 | - if (deviceProfileId) { | ||
277 | - return await getDeviceAttribute({ | ||
278 | - deviceProfileId, | ||
279 | - dataType: isControlComponent(frontId!) ? DataTypeEnum.IS_BOOL : undefined, | ||
280 | - }); | ||
281 | - } | ||
282 | - } catch (error) {} | ||
283 | - return []; | ||
284 | - }, | ||
285 | - placeholder: '请选择属性', | ||
286 | - getPopupContainer: () => document.body, | ||
287 | - }; | ||
288 | - }, | ||
289 | - }, | ||
290 | - { | ||
291 | - field: DataSourceField.COMMAND_TYPE, | ||
292 | - component: 'ApiSelect', | ||
293 | - label: '命令类型', | ||
294 | - defaultValue: CommandTypeEnum.CUSTOM.toString(), | ||
295 | - rules: [{ required: true, message: '请选择命令类型' }], | ||
296 | - colProps: { span: 8 }, | ||
297 | - ifShow: ({ model }) => | ||
298 | - isControlComponent(frontId!) && isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | ||
299 | - componentProps: ({ formActionType }) => { | ||
300 | - const { setFieldsValue } = formActionType; | ||
301 | - return { | ||
302 | - api: findDictItemByCode, | ||
303 | - params: { | ||
304 | - dictCode: 'custom_define', | ||
305 | - }, | ||
306 | - labelField: 'itemText', | ||
307 | - valueField: 'itemValue', | ||
308 | - placeholder: '请选择命令类型', | ||
309 | - onChange() { | ||
310 | - setFieldsValue({ [DataSourceField.COMMAND]: null, [DataSourceField.SERVICE]: null }); | ||
311 | - }, | ||
312 | - }; | ||
313 | - }, | ||
314 | - }, | ||
315 | - { | ||
316 | - field: DataSourceField.SERVICE, | ||
317 | - component: 'ApiSelect', | ||
318 | - label: '服务', | ||
319 | - colProps: { span: 8 }, | ||
320 | - rules: [{ required: true, message: '请选择服务' }], | ||
321 | - ifShow: ({ model }) => | ||
322 | - isControlComponent(frontId as FrontComponent) && | ||
323 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.SERVICE.toString() && | ||
324 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | ||
325 | - componentProps({ formModel, formActionType }) { | ||
326 | - const { setFieldsValue } = formActionType; | ||
327 | - const deviceProfileId = formModel[DataSourceField.DEVICE_PROFILE_ID]; | ||
328 | - const transportType = formModel[DataSourceField.TRANSPORT_TYPE]; | ||
329 | - if (isEdit && ![deviceProfileId, transportType].every(Boolean)) | ||
330 | - return { placeholder: '请选择服务', getPopupContainer: () => document.body }; | ||
331 | - return { | ||
332 | - api: async () => { | ||
333 | - try { | ||
334 | - if (deviceProfileId) { | ||
335 | - return await getDeviceService(deviceProfileId); | ||
336 | - } | ||
337 | - } catch (error) {} | ||
338 | - return []; | ||
339 | - }, | ||
340 | - placeholder: '请选择服务', | ||
341 | - getPopupContainer: () => document.body, | ||
342 | - onChange(value: string, options: ModelOfMatterParams) { | ||
343 | - const command = value ? (options.functionJson.inputData || [])[0].serviceCommand : null; | ||
344 | - setFieldsValue({ [DataSourceField.COMMAND]: command }); | ||
345 | - }, | ||
346 | - }; | ||
347 | - }, | ||
348 | - }, | ||
349 | - { | ||
350 | - field: DataSourceField.COMMAND, | ||
351 | - component: 'Input', | ||
352 | - label: '命令', | ||
353 | - colProps: { span: 8 }, | ||
354 | - rules: [{ required: true, message: '请输入下发命令' }], | ||
355 | - // 是控制组件 && 自定义命令 && 传输协议为TCP | ||
356 | - ifShow: ({ model }) => | ||
357 | - isControlComponent(frontId!) && | ||
358 | - model[DataSourceField.COMMAND_TYPE] === CommandTypeEnum.CUSTOM.toString() && | ||
359 | - model[DataSourceField.TRANSPORT_TYPE] && | ||
360 | - isTcpProfile(model[DataSourceField.TRANSPORT_TYPE]), | ||
361 | - componentProps: { | ||
362 | - placeholder: '请输入下发命令', | ||
363 | - }, | ||
364 | - }, | ||
365 | - { | ||
366 | - field: DataSourceField.DEVICE_RENAME, | ||
367 | - component: 'Input', | ||
368 | - label: '设备名', | ||
369 | - colProps: { span: 8 }, | ||
370 | - componentProps: { | ||
371 | - placeholder: '设备重命名', | ||
372 | - }, | ||
373 | - }, | ||
374 | - { | ||
375 | - field: DataSourceField.ATTRIBUTE_RENAME, | ||
376 | - component: 'Input', | ||
377 | - label: '属性', | ||
378 | - colProps: { span: 8 }, | ||
379 | - componentProps: { | ||
380 | - placeholder: '属性重命名', | ||
381 | - }, | ||
382 | - }, | ||
383 | - ]; | ||
384 | -}; |
src/views/visual/board/detail/config/historyTrend.config.ts
deleted
100644 → 0
1 | -import moment from 'moment'; | ||
2 | -import { Moment } from 'moment'; | ||
3 | -import { FormSchema } from '/@/components/Form'; | ||
4 | -import { ColEx } from '/@/components/Form/src/types'; | ||
5 | -import { useGridLayout } from '/@/hooks/component/useGridLayout'; | ||
6 | -import { | ||
7 | - getPacketIntervalByRange, | ||
8 | - getPacketIntervalByValue, | ||
9 | - intervalOption, | ||
10 | -} from '/@/views/device/localtion/cpns/TimePeriodForm/helper'; | ||
11 | -export enum QueryWay { | ||
12 | - LATEST = 'latest', | ||
13 | - TIME_PERIOD = 'timePeriod', | ||
14 | -} | ||
15 | - | ||
16 | -export enum SchemaFiled { | ||
17 | - DEVICE_ID = 'deviceId', | ||
18 | - WAY = 'way', | ||
19 | - TIME_PERIOD = 'timePeriod', | ||
20 | - KEYS = 'keys', | ||
21 | - DATE_RANGE = 'dataRange', | ||
22 | - START_TS = 'startTs', | ||
23 | - END_TS = 'endTs', | ||
24 | - INTERVAL = 'interval', | ||
25 | - LIMIT = 'limit', | ||
26 | - AGG = 'agg', | ||
27 | - ORDER_BY = 'orderBy', | ||
28 | -} | ||
29 | - | ||
30 | -export enum AggregateDataEnum { | ||
31 | - MIN = 'MIN', | ||
32 | - MAX = 'MAX', | ||
33 | - AVG = 'AVG', | ||
34 | - SUM = 'SUM', | ||
35 | - COUNT = 'COUNT', | ||
36 | - NONE = 'NONE', | ||
37 | -} | ||
38 | -export const formSchema = (): FormSchema[] => { | ||
39 | - return [ | ||
40 | - { | ||
41 | - field: SchemaFiled.DEVICE_ID, | ||
42 | - label: '设备名称', | ||
43 | - component: 'Select', | ||
44 | - rules: [{ required: true, message: '设备名称为必选项', type: 'string' }], | ||
45 | - componentProps({ formActionType }) { | ||
46 | - const { setFieldsValue } = formActionType; | ||
47 | - return { | ||
48 | - placeholder: '请选择设备', | ||
49 | - onChange() { | ||
50 | - setFieldsValue({ [SchemaFiled.KEYS]: null }); | ||
51 | - }, | ||
52 | - }; | ||
53 | - }, | ||
54 | - }, | ||
55 | - { | ||
56 | - field: SchemaFiled.WAY, | ||
57 | - label: '查询方式', | ||
58 | - component: 'RadioGroup', | ||
59 | - defaultValue: QueryWay.LATEST, | ||
60 | - componentProps({ formActionType }) { | ||
61 | - const { setFieldsValue } = formActionType; | ||
62 | - return { | ||
63 | - options: [ | ||
64 | - { label: '最后', value: QueryWay.LATEST }, | ||
65 | - { label: '时间段', value: QueryWay.TIME_PERIOD }, | ||
66 | - ], | ||
67 | - onChange(event: ChangeEvent) { | ||
68 | - (event.target as HTMLInputElement).value === QueryWay.LATEST | ||
69 | - ? setFieldsValue({ | ||
70 | - [SchemaFiled.DATE_RANGE]: [], | ||
71 | - [SchemaFiled.START_TS]: null, | ||
72 | - [SchemaFiled.END_TS]: null, | ||
73 | - }) | ||
74 | - : setFieldsValue({ [SchemaFiled.START_TS]: null }); | ||
75 | - }, | ||
76 | - getPopupContainer: () => document.body, | ||
77 | - }; | ||
78 | - }, | ||
79 | - }, | ||
80 | - { | ||
81 | - field: SchemaFiled.START_TS, | ||
82 | - label: '最后数据', | ||
83 | - component: 'Select', | ||
84 | - ifShow({ values }) { | ||
85 | - return values[SchemaFiled.WAY] === QueryWay.LATEST; | ||
86 | - }, | ||
87 | - componentProps({ formActionType }) { | ||
88 | - const { setFieldsValue } = formActionType; | ||
89 | - return { | ||
90 | - options: intervalOption, | ||
91 | - onChange() { | ||
92 | - setFieldsValue({ [SchemaFiled.INTERVAL]: null }); | ||
93 | - }, | ||
94 | - getPopupContainer: () => document.body, | ||
95 | - }; | ||
96 | - }, | ||
97 | - rules: [{ required: true, message: '最后数据为必选项', type: 'number' }], | ||
98 | - }, | ||
99 | - { | ||
100 | - field: SchemaFiled.DATE_RANGE, | ||
101 | - label: '时间段', | ||
102 | - component: 'RangePicker', | ||
103 | - ifShow({ values }) { | ||
104 | - return values[SchemaFiled.WAY] === QueryWay.TIME_PERIOD; | ||
105 | - }, | ||
106 | - rules: [{ required: true, message: '时间段为必选项' }], | ||
107 | - componentProps({ formActionType }) { | ||
108 | - const { setFieldsValue } = formActionType; | ||
109 | - let dates: Moment[] = []; | ||
110 | - return { | ||
111 | - showTime: { | ||
112 | - defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')], | ||
113 | - }, | ||
114 | - onCalendarChange(value: Moment[]) { | ||
115 | - dates = value; | ||
116 | - }, | ||
117 | - disabledDate(current: Moment) { | ||
118 | - if (!dates || dates.length === 0 || !current) { | ||
119 | - return false; | ||
120 | - } | ||
121 | - const diffDate = current.diff(dates[0], 'years', true); | ||
122 | - return Math.abs(diffDate) > 1; | ||
123 | - }, | ||
124 | - onChange() { | ||
125 | - dates = []; | ||
126 | - setFieldsValue({ [SchemaFiled.INTERVAL]: null }); | ||
127 | - }, | ||
128 | - getPopupContainer: () => document.body, | ||
129 | - }; | ||
130 | - }, | ||
131 | - colProps: useGridLayout(2, 2, 2, 2, 2, 2) as unknown as ColEx, | ||
132 | - }, | ||
133 | - { | ||
134 | - field: SchemaFiled.AGG, | ||
135 | - label: '数据聚合功能', | ||
136 | - component: 'Select', | ||
137 | - componentProps: { | ||
138 | - getPopupContainer: () => document.body, | ||
139 | - options: [ | ||
140 | - { label: '最小值', value: AggregateDataEnum.MIN }, | ||
141 | - { label: '最大值', value: AggregateDataEnum.MAX }, | ||
142 | - { label: '平均值', value: AggregateDataEnum.AVG }, | ||
143 | - { label: '求和', value: AggregateDataEnum.SUM }, | ||
144 | - { label: '计数', value: AggregateDataEnum.COUNT }, | ||
145 | - { label: '空', value: AggregateDataEnum.NONE }, | ||
146 | - ], | ||
147 | - }, | ||
148 | - }, | ||
149 | - { | ||
150 | - field: SchemaFiled.INTERVAL, | ||
151 | - label: '分组间隔', | ||
152 | - component: 'Select', | ||
153 | - dynamicRules: ({ model }) => { | ||
154 | - return [ | ||
155 | - { | ||
156 | - required: model[SchemaFiled.AGG] !== AggregateDataEnum.NONE, | ||
157 | - message: '分组间隔为必填项', | ||
158 | - type: 'number', | ||
159 | - }, | ||
160 | - ]; | ||
161 | - }, | ||
162 | - ifShow({ values }) { | ||
163 | - return values[SchemaFiled.AGG] !== AggregateDataEnum.NONE; | ||
164 | - }, | ||
165 | - componentProps({ formModel, formActionType }) { | ||
166 | - const options = | ||
167 | - formModel[SchemaFiled.WAY] === QueryWay.LATEST | ||
168 | - ? getPacketIntervalByValue(formModel[SchemaFiled.START_TS]) | ||
169 | - : getPacketIntervalByRange(formModel[SchemaFiled.DATE_RANGE]); | ||
170 | - if (formModel[SchemaFiled.AGG] !== AggregateDataEnum.NONE) { | ||
171 | - formActionType.setFieldsValue({ [SchemaFiled.LIMIT]: null }); | ||
172 | - } | ||
173 | - return { | ||
174 | - options, | ||
175 | - getPopupContainer: () => document.body, | ||
176 | - }; | ||
177 | - }, | ||
178 | - }, | ||
179 | - { | ||
180 | - field: SchemaFiled.LIMIT, | ||
181 | - label: '最大条数', | ||
182 | - component: 'InputNumber', | ||
183 | - ifShow({ values }) { | ||
184 | - return values[SchemaFiled.AGG] === AggregateDataEnum.NONE; | ||
185 | - }, | ||
186 | - helpMessage: ['根据查询条件,查出的数据条数不超过这个值'], | ||
187 | - componentProps() { | ||
188 | - return { | ||
189 | - max: 50000, | ||
190 | - min: 7, | ||
191 | - getPopupContainer: () => document.body, | ||
192 | - }; | ||
193 | - }, | ||
194 | - }, | ||
195 | - { | ||
196 | - field: SchemaFiled.KEYS, | ||
197 | - label: '设备属性', | ||
198 | - component: 'Select', | ||
199 | - componentProps: { | ||
200 | - getPopupContainer: () => document.body, | ||
201 | - }, | ||
202 | - }, | ||
203 | - ]; | ||
204 | -}; | ||
205 | - | ||
206 | -export function getHistorySearchParams(value: Recordable) { | ||
207 | - const { startTs, endTs, interval, agg, limit, way, keys, deviceId } = value; | ||
208 | - if (way === QueryWay.LATEST) { | ||
209 | - return { | ||
210 | - keys, | ||
211 | - entityId: deviceId, | ||
212 | - startTs: moment().subtract(startTs, 'ms').valueOf(), | ||
213 | - endTs: Date.now(), | ||
214 | - interval, | ||
215 | - agg, | ||
216 | - limit, | ||
217 | - }; | ||
218 | - } else { | ||
219 | - return { | ||
220 | - keys, | ||
221 | - entityId: deviceId, | ||
222 | - startTs: moment(startTs).valueOf(), | ||
223 | - endTs: moment(endTs).valueOf(), | ||
224 | - interval, | ||
225 | - agg, | ||
226 | - limit, | ||
227 | - }; | ||
228 | - } | ||
229 | -} |