Showing
59 changed files
with
22 additions
and
6145 deletions
... | ... | @@ -6,8 +6,6 @@ import { |
6 | 6 | EXCEPTION_COMPONENT, |
7 | 7 | PAGE_NOT_FOUND_NAME, |
8 | 8 | } from '/@/router/constant'; |
9 | -import { DATA_BOARD_SHARE_URL } from '../../views/visual/board/config/config'; | |
10 | - | |
11 | 9 | // 404 on a page |
12 | 10 | export const PAGE_NOT_FOUND_ROUTE: AppRouteRecordRaw = { |
13 | 11 | path: '/:path(.*)*', |
... | ... | @@ -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 | 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 | 3 | import { mainOutRoutes } from './mainOut'; |
4 | 4 | import { PageEnum } from '/@/enums/pageEnum'; |
5 | 5 | import { t } from '/@/hooks/web/useI18n'; |
... | ... | @@ -86,6 +86,5 @@ export const basicRoutes = [ |
86 | 86 | ...mainOutRoutes, |
87 | 87 | REDIRECT_ROUTE, |
88 | 88 | PAGE_NOT_FOUND_ROUTE, |
89 | - DATA_BOARD_SHARE, | |
90 | 89 | PUBLIC_PAGE_ROUTER, |
91 | 90 | ]; | ... | ... |
... | ... | @@ -161,7 +161,7 @@ const transform: AxiosTransform = { |
161 | 161 | throw new Error(error); |
162 | 162 | } |
163 | 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 | 18 | TABLE_CHART_MODE_LIST, |
19 | 19 | EnumTableChartMode, |
20 | 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 | 23 | interface DeviceDetail { |
24 | 24 | tbDeviceId: string; | ... | ... |
... | ... | @@ -7,8 +7,8 @@ import { getDeviceAttributes } from '/@/api/dataBoard'; |
7 | 7 | import { DeviceAttributeRecord } from '/@/api/dataBoard/model'; |
8 | 8 | import { dateUtil } from '/@/utils/dateUtil'; |
9 | 9 | import { isArray } from '/@/utils/is'; |
10 | -import { QueryWay, SchemaFiled } from '/@/views/visual/board/detail/config/historyTrend.config'; | |
11 | 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 | 13 | interface DeviceOption { |
14 | 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 | 1 | export enum MoreActionEvent { |
4 | 2 | EDIT = 'edit', |
5 | 3 | COPY = 'copy', |
... | ... | @@ -27,9 +25,6 @@ export const DEFAULT_WIDGET_HEIGHT = 6; |
27 | 25 | export const DEFAULT_MIN_HEIGHT = 5; |
28 | 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 | 28 | export const isBataBoardSharePage = (url: string) => { |
34 | 29 | const reg = /^\/data\/board\/share/g; |
35 | 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 | -} |
1 | -import { dateUtil } from '/@/utils/dateUtil'; | |
2 | - | |
3 | -export interface RadioRecord { | |
4 | - width: number; | |
5 | - height: number; | |
6 | - isLess: boolean; | |
7 | - radio: number; | |
8 | -} | |
9 | - | |
10 | -export const DEFAULT_ANIMATION_INTERVAL = 2000; | |
11 | - | |
12 | -export const DEFAULT_RADIO_RECORD: RadioRecord = { | |
13 | - width: 200, | |
14 | - height: 140, | |
15 | - isLess: false, | |
16 | - radio: 1, | |
17 | -}; | |
18 | - | |
19 | 1 | export const DEFAULT_DATE_FORMAT = 'YYYY-MM-DD HH:mm:ss'; |
20 | - | |
21 | -export const getUpdateTime = (updateTime?: string) => { | |
22 | - return updateTime ? dateUtil(updateTime).format(DEFAULT_DATE_FORMAT) : '暂无更新时间'; | |
23 | -}; | |
24 | - | |
25 | -export const calcScale = ( | |
26 | - width: number, | |
27 | - height: number, | |
28 | - widthRadio: number, | |
29 | - heightRadio: number | |
30 | -): RadioRecord => { | |
31 | - width = width * (widthRadio / 100); | |
32 | - height = height * (heightRadio / 100); | |
33 | - | |
34 | - const temp = width > height ? height ** 2 : width ** 2; | |
35 | - | |
36 | - const isLess = temp < 300 * 300; | |
37 | - | |
38 | - const radio = temp / (300 * 300); | |
39 | - | |
40 | - return { | |
41 | - width, | |
42 | - height, | |
43 | - isLess, | |
44 | - radio, | |
45 | - }; | |
46 | -}; | |
47 | - | |
48 | -export const fontSize = ({ | |
49 | - radioRecord, | |
50 | - basic, | |
51 | - max, | |
52 | - min, | |
53 | -}: { | |
54 | - radioRecord: RadioRecord; | |
55 | - basic: number; | |
56 | - max?: number; | |
57 | - min?: number; | |
58 | -}) => { | |
59 | - const { radio } = radioRecord; | |
60 | - let res = basic * radio; | |
61 | - if (max && res > max) res = max; | |
62 | - if (min && res < min) res = min; | |
63 | - return res + 'px'; | |
64 | -}; | ... | ... |
src/views/visual/board/detail/config/visualOptions.ts
deleted
100644 → 0
1 | -import { FrontComponent, GradientColor } from '../../const/const'; | |
2 | -import { FormSchema } from '/@/components/Form'; | |
3 | - | |
4 | -export interface VisualOptionParams { | |
5 | - [visualOptionField.FONT_COLOR]: string; | |
6 | - [visualOptionField.UNIT]: string; | |
7 | - [visualOptionField.ICON_COLOR]: string; | |
8 | - [visualOptionField.ICON]: string; | |
9 | - [visualOptionField.FIRST_PHASE_COLOR]: string; | |
10 | - [visualOptionField.SECOND_PHASE_COLOR]: string; | |
11 | - [visualOptionField.THIRD_PHASE_COLOR]: string; | |
12 | - [visualOptionField.FIRST_PHASE_VALUE]: string; | |
13 | - [visualOptionField.SECOND_PHASE_VALUE]: string; | |
14 | - [visualOptionField.THIRD_PHASE_VALUE]: string; | |
15 | - [visualOptionField.SHOW_DEVICE_NAME]: string; | |
16 | -} | |
17 | - | |
18 | -export enum visualOptionField { | |
19 | - FONT_COLOR = 'fontColor', | |
20 | - UNIT = 'unit', | |
21 | - ICON_COLOR = 'iconColor', | |
22 | - ICON = 'icon', | |
23 | - FIRST_PHASE_COLOR = 'firstPhaseColor', | |
24 | - SECOND_PHASE_COLOR = 'secondPhaseColor', | |
25 | - THIRD_PHASE_COLOR = 'thirdPhaseColor', | |
26 | - FIRST_PHASE_VALUE = 'firstPhaseValue', | |
27 | - SECOND_PHASE_VALUE = 'secondPhaseValue', | |
28 | - THIRD_PHASE_VALUE = 'thirdPhaseValue', | |
29 | - SHOW_DEVICE_NAME = 'showDeviceName', | |
30 | -} | |
31 | - | |
32 | -export const modeOne: FormSchema[] = [ | |
33 | - { | |
34 | - field: visualOptionField.FONT_COLOR, | |
35 | - label: '数值字体颜色', | |
36 | - component: 'ColorPicker', | |
37 | - changeEvent: 'update:value', | |
38 | - componentProps: { | |
39 | - defaultValue: '#000', | |
40 | - }, | |
41 | - }, | |
42 | - { | |
43 | - field: visualOptionField.SHOW_DEVICE_NAME, | |
44 | - label: '显示设备名称', | |
45 | - component: 'Checkbox', | |
46 | - }, | |
47 | -]; | |
48 | - | |
49 | -export const modeTwo: FormSchema[] = [ | |
50 | - { | |
51 | - field: visualOptionField.FONT_COLOR, | |
52 | - label: '数值字体颜色', | |
53 | - component: 'ColorPicker', | |
54 | - changeEvent: 'update:value', | |
55 | - componentProps: { | |
56 | - defaultValue: '#000', | |
57 | - }, | |
58 | - }, | |
59 | - { | |
60 | - field: visualOptionField.UNIT, | |
61 | - label: '数值单位', | |
62 | - component: 'Input', | |
63 | - componentProps: { | |
64 | - placeholder: '请输入数值单位', | |
65 | - }, | |
66 | - }, | |
67 | - { | |
68 | - field: visualOptionField.ICON_COLOR, | |
69 | - label: '图标颜色', | |
70 | - component: 'ColorPicker', | |
71 | - changeEvent: 'update:value', | |
72 | - componentProps: { | |
73 | - defaultValue: '#367BFF', | |
74 | - }, | |
75 | - }, | |
76 | - { | |
77 | - field: visualOptionField.ICON, | |
78 | - label: '图标', | |
79 | - component: 'IconDrawer', | |
80 | - changeEvent: 'update:value', | |
81 | - componentProps({ formModel }) { | |
82 | - const color = formModel[visualOptionField.ICON_COLOR]; | |
83 | - return { | |
84 | - color, | |
85 | - }; | |
86 | - }, | |
87 | - }, | |
88 | - { | |
89 | - field: visualOptionField.SHOW_DEVICE_NAME, | |
90 | - label: '显示设备名称', | |
91 | - component: 'Checkbox', | |
92 | - }, | |
93 | -]; | |
94 | - | |
95 | -export const modeThree: FormSchema[] = [ | |
96 | - { | |
97 | - field: visualOptionField.FONT_COLOR, | |
98 | - label: '数值字体颜色', | |
99 | - component: 'ColorPicker', | |
100 | - changeEvent: 'update:value', | |
101 | - componentProps: { | |
102 | - defaultValue: '#000', | |
103 | - }, | |
104 | - }, | |
105 | - { | |
106 | - field: visualOptionField.UNIT, | |
107 | - label: '数值单位', | |
108 | - component: 'Input', | |
109 | - componentProps: { | |
110 | - placeholder: '请输入数值单位', | |
111 | - }, | |
112 | - }, | |
113 | - { | |
114 | - field: visualOptionField.FIRST_PHASE_COLOR, | |
115 | - label: '一阶段颜色', | |
116 | - component: 'ColorPicker', | |
117 | - changeEvent: 'update:value', | |
118 | - componentProps: { | |
119 | - defaultValue: GradientColor.FIRST, | |
120 | - }, | |
121 | - }, | |
122 | - { | |
123 | - field: visualOptionField.FIRST_PHASE_VALUE, | |
124 | - label: '一阶段阀值', | |
125 | - component: 'InputNumber', | |
126 | - componentProps: { | |
127 | - placeholder: '请输入一阶段阀值', | |
128 | - min: 0, | |
129 | - }, | |
130 | - }, | |
131 | - { | |
132 | - field: visualOptionField.SECOND_PHASE_COLOR, | |
133 | - label: '二阶段颜色', | |
134 | - component: 'ColorPicker', | |
135 | - changeEvent: 'update:value', | |
136 | - componentProps: { | |
137 | - defaultValue: GradientColor.SECOND, | |
138 | - }, | |
139 | - }, | |
140 | - { | |
141 | - field: visualOptionField.SECOND_PHASE_VALUE, | |
142 | - label: '二阶段阀值', | |
143 | - component: 'InputNumber', | |
144 | - componentProps: ({ formModel }) => { | |
145 | - return { | |
146 | - placeholder: '请输入二阶段阀值', | |
147 | - min: formModel[visualOptionField.FIRST_PHASE_VALUE], | |
148 | - }; | |
149 | - }, | |
150 | - }, | |
151 | - { | |
152 | - field: visualOptionField.THIRD_PHASE_COLOR, | |
153 | - label: '三阶段颜色', | |
154 | - component: 'ColorPicker', | |
155 | - changeEvent: 'update:value', | |
156 | - componentProps: { | |
157 | - defaultValue: GradientColor.THIRD, | |
158 | - }, | |
159 | - }, | |
160 | - { | |
161 | - field: visualOptionField.THIRD_PHASE_VALUE, | |
162 | - label: '三阶段阀值', | |
163 | - component: 'InputNumber', | |
164 | - componentProps: ({ formModel }) => { | |
165 | - return { | |
166 | - placeholder: '请输入三阶段阀值', | |
167 | - min: formModel[visualOptionField.SECOND_PHASE_VALUE], | |
168 | - }; | |
169 | - }, | |
170 | - }, | |
171 | - { | |
172 | - field: visualOptionField.SHOW_DEVICE_NAME, | |
173 | - label: '显示设备名称', | |
174 | - component: 'Checkbox', | |
175 | - }, | |
176 | -]; | |
177 | - | |
178 | -export const modeFour: FormSchema[] = [ | |
179 | - { | |
180 | - field: visualOptionField.FONT_COLOR, | |
181 | - label: '数值字体颜色', | |
182 | - component: 'ColorPicker', | |
183 | - changeEvent: 'update:value', | |
184 | - componentProps: { | |
185 | - defaultValue: '#000', | |
186 | - }, | |
187 | - }, | |
188 | - { | |
189 | - field: visualOptionField.UNIT, | |
190 | - label: '数值单位', | |
191 | - component: 'Input', | |
192 | - componentProps: { | |
193 | - placeholder: '请输入数值单位', | |
194 | - }, | |
195 | - }, | |
196 | - { | |
197 | - field: visualOptionField.SHOW_DEVICE_NAME, | |
198 | - label: '显示设备名称', | |
199 | - component: 'Checkbox', | |
200 | - }, | |
201 | -]; | |
202 | - | |
203 | -export const modeFive: FormSchema[] = [ | |
204 | - { | |
205 | - field: visualOptionField.FONT_COLOR, | |
206 | - label: '数值字体颜色', | |
207 | - component: 'ColorPicker', | |
208 | - changeEvent: 'update:value', | |
209 | - componentProps: { | |
210 | - defaultValue: '#000', | |
211 | - }, | |
212 | - }, | |
213 | - { | |
214 | - field: visualOptionField.ICON_COLOR, | |
215 | - label: '图标颜色', | |
216 | - component: 'ColorPicker', | |
217 | - changeEvent: 'update:value', | |
218 | - componentProps: { | |
219 | - defaultValue: '#367BFF', | |
220 | - }, | |
221 | - }, | |
222 | - { | |
223 | - field: visualOptionField.ICON, | |
224 | - label: '图标', | |
225 | - component: 'IconDrawer', | |
226 | - changeEvent: 'update:value', | |
227 | - componentProps({ formModel }) { | |
228 | - const color = formModel[visualOptionField.ICON_COLOR]; | |
229 | - return { | |
230 | - color, | |
231 | - }; | |
232 | - }, | |
233 | - }, | |
234 | - { | |
235 | - field: visualOptionField.SHOW_DEVICE_NAME, | |
236 | - label: '显示设备名称', | |
237 | - component: 'Checkbox', | |
238 | - }, | |
239 | -]; | |
240 | - | |
241 | -export const schemasMap = new Map<FrontComponent, FormSchema[]>(); | |
242 | - | |
243 | -schemasMap.set(FrontComponent.TEXT_COMPONENT_1, modeOne); | |
244 | -schemasMap.set(FrontComponent.TEXT_COMPONENT_2, modeOne); | |
245 | -schemasMap.set(FrontComponent.TEXT_COMPONENT_3, modeOne); | |
246 | -schemasMap.set(FrontComponent.TEXT_COMPONENT_4, modeTwo); | |
247 | -schemasMap.set(FrontComponent.TEXT_COMPONENT_5, modeTwo); | |
248 | -schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_1, modeOne); | |
249 | -schemasMap.set(FrontComponent.INSTRUMENT_COMPONENT_2, modeThree); | |
250 | -schemasMap.set(FrontComponent.DIGITAL_DASHBOARD_COMPONENT, modeFour); | |
251 | -schemasMap.set(FrontComponent.CONTROL_COMPONENT_SWITCH_WITH_ICON, modeFive); | |
252 | -schemasMap.set(FrontComponent.CONTROL_COMPONENT_SLIDING_SWITCH, modeOne); | |
253 | -schemasMap.set(FrontComponent.CONTROL_COMPONENT_TOGGLE_SWITCH, modeOne); |
src/views/visual/board/detail/old-index.vue
deleted
100644 → 0
1 | -<script lang="ts" setup> | |
2 | - import { Button, PageHeader, Empty, Spin, Tooltip } from 'ant-design-vue'; | |
3 | - import { GridItem, GridLayout } from 'vue3-grid-layout'; | |
4 | - import { nextTick, onMounted, ref } from 'vue'; | |
5 | - import WidgetWrapper from '../components/WidgetWrapper/WidgetWrapper.vue'; | |
6 | - import BaseWidgetHeader from '../components/WidgetHeader/BaseWidgetHeader.vue'; | |
7 | - import { DropMenu } from '/@/components/Dropdown'; | |
8 | - import DataBindModal from './components/DataBindModal.vue'; | |
9 | - import { useModal } from '/@/components/Modal'; | |
10 | - import { | |
11 | - decode, | |
12 | - DEFAULT_MAX_COL, | |
13 | - DEFAULT_MIN_HEIGHT, | |
14 | - DEFAULT_MIN_WIDTH, | |
15 | - DEFAULT_WIDGET_HEIGHT, | |
16 | - DEFAULT_WIDGET_WIDTH, | |
17 | - MoreActionEvent, | |
18 | - VisualComponentPermission, | |
19 | - } from '../config/config'; | |
20 | - import { | |
21 | - addDataComponent, | |
22 | - deleteDataComponent, | |
23 | - getDataComponent, | |
24 | - updateDataBoardLayout, | |
25 | - } from '/@/api/dataBoard'; | |
26 | - import { useRoute, useRouter } from 'vue-router'; | |
27 | - import { computed, unref } from '@vue/reactivity'; | |
28 | - import { | |
29 | - ComponentInfoDetail, | |
30 | - DataComponentRecord, | |
31 | - DataSource, | |
32 | - Layout, | |
33 | - } from '/@/api/dataBoard/model'; | |
34 | - import { frontComponentMap } from '../components/help'; | |
35 | - import { calcScale } from './config/util'; | |
36 | - import { useMessage } from '/@/hooks/web/useMessage'; | |
37 | - import { DataBoardLayoutInfo } from '../types/type'; | |
38 | - import Authority from '/@/components/Authority/src/Authority.vue'; | |
39 | - import { useSocketConnect } from '../hook/useSocketConnect'; | |
40 | - import { buildUUID } from '/@/utils/uuid'; | |
41 | - import HistoryTrendModal from './components/HistoryTrendModal.vue'; | |
42 | - import trendIcon from '/@/assets/svg/trend.svg'; | |
43 | - import backIcon from '/@/assets/images/back.png'; | |
44 | - import backWhiteIcon from '/@/assets/images/backWhite.png'; | |
45 | - import { useCalcGridLayout } from '../hook/useCalcGridLayout'; | |
46 | - import { FrontComponent } from '../const/const'; | |
47 | - import { useScript } from '/@/hooks/web/useScript'; | |
48 | - import { BAI_DU_MAP_GL_LIB, BAI_DU_MAP_TRACK_ANIMATION } from '/@/utils/fnUtils'; | |
49 | - import { useAppStore } from '/@/store/modules/app'; | |
50 | - import { useRole } from '/@/hooks/business/useRole'; | |
51 | - | |
52 | - const userStore = useAppStore(); | |
53 | - | |
54 | - const getAceClass = computed((): string => userStore.getDarkMode); | |
55 | - | |
56 | - const props = defineProps<{ | |
57 | - value?: Recordable; | |
58 | - }>(); | |
59 | - | |
60 | - const ROUTE = useRoute(); | |
61 | - | |
62 | - const ROUTER = useRouter(); | |
63 | - | |
64 | - const { isCustomerUser } = useRole(); | |
65 | - const { toPromise: injectBaiDuMapLib } = useScript({ src: BAI_DU_MAP_GL_LIB }); | |
66 | - const { toPromise: injectBaiDuMapTrackAniMationLib } = useScript({ | |
67 | - src: BAI_DU_MAP_TRACK_ANIMATION, | |
68 | - }); | |
69 | - | |
70 | - const { createMessage, createConfirm } = useMessage(); | |
71 | - | |
72 | - const getBoardId = computed(() => { | |
73 | - return decode((ROUTE.params as { boardId: string }).boardId); | |
74 | - }); | |
75 | - | |
76 | - const getDataBoardName = computed(() => { | |
77 | - return decode((ROUTE.params as { boardName: string }).boardName || ''); | |
78 | - }); | |
79 | - | |
80 | - const getSharePageData = computed(() => { | |
81 | - return props.value!; | |
82 | - }); | |
83 | - | |
84 | - const getIsSharePage = computed(() => { | |
85 | - return ROUTE.matched.find((item) => item.path === '/share/:viewType/:id/:publicId'); | |
86 | - }); | |
87 | - | |
88 | - const widgetEl = new Map<string, Fn>(); | |
89 | - | |
90 | - const dataBoardList = ref<DataBoardLayoutInfo[]>([]); | |
91 | - | |
92 | - const draggable = ref(!unref(getIsSharePage)); | |
93 | - const resizable = ref(!unref(getIsSharePage)); | |
94 | - | |
95 | - const GirdLayoutColNum = DEFAULT_MAX_COL; | |
96 | - const GridLayoutMargin = 20; | |
97 | - | |
98 | - const handleBack = () => { | |
99 | - if (unref(getIsSharePage)) return; | |
100 | - ROUTER.go(-1); | |
101 | - }; | |
102 | - | |
103 | - function updateSize(i: string, _newH: number, _newW: number, newHPx: number, newWPx: number) { | |
104 | - newWPx = Number(newWPx); | |
105 | - newHPx = Number(newHPx); | |
106 | - | |
107 | - const data = dataBoardList.value.find((item) => item.i === i)!; | |
108 | - const length = data.record.dataSource.length || 0; | |
109 | - | |
110 | - const row = Math.floor(Math.pow(length, 0.5)); | |
111 | - const col = Math.floor(length / row); | |
112 | - let width = Math.floor(100 / col); | |
113 | - let height = Math.floor(100 / row); | |
114 | - | |
115 | - const WHRatio = newWPx / newHPx; | |
116 | - const HWRatio = newHPx / newWPx; | |
117 | - | |
118 | - if (WHRatio > 1.6) { | |
119 | - width = Math.floor(100 / length); | |
120 | - height = 100; | |
121 | - } | |
122 | - | |
123 | - if (HWRatio > 1.6) { | |
124 | - height = Math.floor(100 / length); | |
125 | - width = 100; | |
126 | - } | |
127 | - | |
128 | - data.width = newWPx; | |
129 | - data.height = newHPx; | |
130 | - | |
131 | - data.record.dataSource = data.record.dataSource.map((item) => { | |
132 | - if (!item.uuid) item.uuid = buildUUID(); | |
133 | - return { | |
134 | - ...item, | |
135 | - width, | |
136 | - height, | |
137 | - radio: calcScale(newWPx, newHPx, width, height), | |
138 | - }; | |
139 | - }); | |
140 | - | |
141 | - nextTick(() => { | |
142 | - const updateFn = widgetEl.get(i); | |
143 | - if (updateFn) updateFn(); | |
144 | - }); | |
145 | - } | |
146 | - | |
147 | - const itemResize = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => { | |
148 | - updateSize(i, newH, newW, newHPx, newWPx); | |
149 | - }; | |
150 | - | |
151 | - const itemResized = (i: string, newH: number, newW: number, newHPx: number, newWPx: number) => { | |
152 | - handleSaveLayoutInfo(); | |
153 | - updateSize(i, newH, newW, newHPx, newWPx); | |
154 | - }; | |
155 | - | |
156 | - const itemContainerResized = ( | |
157 | - i: string, | |
158 | - newH: number, | |
159 | - newW: number, | |
160 | - newHPx: number, | |
161 | - newWPx: number | |
162 | - ) => { | |
163 | - updateSize(i, newH, newW, newHPx, newWPx); | |
164 | - }; | |
165 | - | |
166 | - const itemMoved = (i: string) => { | |
167 | - handleSaveLayoutInfo(); | |
168 | - updateCharts(i); | |
169 | - }; | |
170 | - | |
171 | - const updateCharts = (i: string) => { | |
172 | - nextTick(() => { | |
173 | - const updateFn = widgetEl.get(i); | |
174 | - if (updateFn) updateFn(); | |
175 | - }); | |
176 | - }; | |
177 | - | |
178 | - const setComponentRef = (el: Element, record: DataBoardLayoutInfo) => { | |
179 | - if (widgetEl.has(record.i)) widgetEl.delete(record.i); | |
180 | - if (el && (el as unknown as { update: Fn }).update) | |
181 | - widgetEl.set(record.i, (el as unknown as { update: Fn }).update); | |
182 | - }; | |
183 | - | |
184 | - const [register, { openModal }] = useModal(); | |
185 | - | |
186 | - const handleMoreAction = (event: DropMenu, id: string) => { | |
187 | - if (event.event === MoreActionEvent.DELETE) { | |
188 | - createConfirm({ | |
189 | - iconType: 'warning', | |
190 | - content: '是否确认删除?', | |
191 | - onOk: () => handleDelete(id), | |
192 | - }); | |
193 | - } | |
194 | - if (event.event === MoreActionEvent.EDIT) handleUpdate(id); | |
195 | - if (event.event === MoreActionEvent.COPY) handleCopy(id); | |
196 | - }; | |
197 | - | |
198 | - const handleOpenCreatePanel = () => { | |
199 | - openModal(true, { isEdit: false }); | |
200 | - }; | |
201 | - | |
202 | - const getLayoutInfo = () => { | |
203 | - return unref(dataBoardList).map((item) => { | |
204 | - return { | |
205 | - id: item.i, | |
206 | - h: item.h, | |
207 | - w: item.w, | |
208 | - x: item.x, | |
209 | - y: item.y, | |
210 | - } as Layout; | |
211 | - }); | |
212 | - }; | |
213 | - | |
214 | - const handleSaveLayoutInfo = async () => { | |
215 | - try { | |
216 | - await updateDataBoardLayout({ | |
217 | - boardId: unref(getBoardId), | |
218 | - layout: getLayoutInfo(), | |
219 | - }); | |
220 | - } catch (error) {} | |
221 | - }; | |
222 | - | |
223 | - const { beginSendMessage } = useSocketConnect(dataBoardList); | |
224 | - | |
225 | - const getBasePageComponentData = async () => { | |
226 | - try { | |
227 | - return await getDataComponent(unref(getBoardId)); | |
228 | - } catch (error) {} | |
229 | - return {} as ComponentInfoDetail; | |
230 | - }; | |
231 | - | |
232 | - const getDataBoradDetail = async () => { | |
233 | - try { | |
234 | - return unref(getIsSharePage) ? unref(getSharePageData) : await getBasePageComponentData(); | |
235 | - } catch (error) { | |
236 | - return {} as ComponentInfoDetail; | |
237 | - } | |
238 | - }; | |
239 | - | |
240 | - const loading = ref(false); | |
241 | - const getDataBoardComponent = async () => { | |
242 | - try { | |
243 | - dataBoardList.value = []; | |
244 | - loading.value = true; | |
245 | - const data = await getDataBoradDetail(); | |
246 | - | |
247 | - if (!data.data.componentData) { | |
248 | - dataBoardList.value = []; | |
249 | - return; | |
250 | - } | |
251 | - dataBoardList.value = data.data.componentData.map((item) => { | |
252 | - const index = data.data.componentLayout.findIndex((each) => item.id === each.id); | |
253 | - let layout; | |
254 | - if (!~index) { | |
255 | - layout = {}; | |
256 | - } else { | |
257 | - layout = data.data.componentLayout[index]; | |
258 | - } | |
259 | - return { | |
260 | - i: item.id, | |
261 | - w: layout.w || DEFAULT_WIDGET_WIDTH, | |
262 | - h: layout.h || DEFAULT_WIDGET_HEIGHT, | |
263 | - x: layout.x || 0, | |
264 | - y: layout.y || 0, | |
265 | - record: item, | |
266 | - }; | |
267 | - }); | |
268 | - beginSendMessage(); | |
269 | - } catch (error) { | |
270 | - throw error; | |
271 | - } finally { | |
272 | - loading.value = false; | |
273 | - } | |
274 | - }; | |
275 | - | |
276 | - const handleUpdateComponent = async (id: string) => { | |
277 | - try { | |
278 | - loading.value = true; | |
279 | - const data = await getDataBoradDetail(); | |
280 | - const updateIndex = data.data.componentData.findIndex((item) => item.id === id); | |
281 | - const originalIndex = unref(dataBoardList).findIndex((item) => item.i === id); | |
282 | - | |
283 | - const newUpdateData = data.data.componentData[updateIndex]; | |
284 | - const originalData = unref(dataBoardList)[originalIndex]; | |
285 | - dataBoardList.value[originalIndex] = { | |
286 | - i: id, | |
287 | - w: originalData.w || DEFAULT_WIDGET_WIDTH, | |
288 | - h: originalData.h || DEFAULT_WIDGET_HEIGHT, | |
289 | - x: originalData.x || 0, | |
290 | - y: originalData.y || 0, | |
291 | - width: originalData.width, | |
292 | - height: originalData.height, | |
293 | - record: newUpdateData, | |
294 | - }; | |
295 | - | |
296 | - updateSize(id, 0, 0, originalData.height || 0, originalData.width || 0); | |
297 | - | |
298 | - beginSendMessage(); | |
299 | - } catch (error) { | |
300 | - } finally { | |
301 | - loading.value = false; | |
302 | - } | |
303 | - }; | |
304 | - | |
305 | - const getComponent = (record: DataComponentRecord) => { | |
306 | - const frontComponent = record.frontId; | |
307 | - const component = frontComponentMap.get(frontComponent as FrontComponent); | |
308 | - return component?.Component; | |
309 | - }; | |
310 | - | |
311 | - const getComponentConfig = ( | |
312 | - record: DataBoardLayoutInfo['record'], | |
313 | - dataSourceRecord: DataSource | DataSource[] | |
314 | - ) => { | |
315 | - const frontComponent = record.frontId; | |
316 | - const component = frontComponentMap.get(frontComponent as FrontComponent); | |
317 | - return component?.transformConfig(component.ComponentConfig || {}, record, dataSourceRecord); | |
318 | - }; | |
319 | - | |
320 | - const handleUpdate = async (id: string) => { | |
321 | - const record = unref(dataBoardList).find((item) => item.i === id); | |
322 | - openModal(true, { isEdit: true, record }); | |
323 | - }; | |
324 | - | |
325 | - const { calcLayoutInfo } = useCalcGridLayout(); | |
326 | - | |
327 | - const handleCopy = async (id: string) => { | |
328 | - const record = unref(dataBoardList).find((item) => item.i === id); | |
329 | - try { | |
330 | - const data = await addDataComponent({ | |
331 | - boardId: unref(getBoardId), | |
332 | - record: { | |
333 | - dataBoardId: unref(getBoardId), | |
334 | - frontId: record?.record.frontId, | |
335 | - name: record?.record.name, | |
336 | - remark: record?.record.remark, | |
337 | - dataSource: record?.record.dataSource, | |
338 | - }, | |
339 | - }); | |
340 | - createMessage.success('复制成功'); | |
341 | - const _id = data.data.id; | |
342 | - const layoutInfo = getLayoutInfo(); | |
343 | - | |
344 | - const newGridLayout = calcLayoutInfo(unref(dataBoardList), { | |
345 | - width: record?.w || DEFAULT_WIDGET_HEIGHT, | |
346 | - height: record?.h || DEFAULT_WIDGET_HEIGHT, | |
347 | - }); | |
348 | - layoutInfo.push({ | |
349 | - id: _id, | |
350 | - ...newGridLayout, | |
351 | - } as Layout); | |
352 | - | |
353 | - await updateDataBoardLayout({ | |
354 | - boardId: unref(getBoardId), | |
355 | - layout: layoutInfo, | |
356 | - }); | |
357 | - | |
358 | - await getDataBoardComponent(); | |
359 | - } catch (error) {} | |
360 | - }; | |
361 | - | |
362 | - const handleDelete = async (id: string) => { | |
363 | - try { | |
364 | - const dataBoardId = unref(dataBoardList).find((item) => item.i == id)?.record.dataBoardId; | |
365 | - if (!dataBoardId) return; | |
366 | - await deleteDataComponent({ dataBoardId, ids: [id] }); | |
367 | - createMessage.success('删除成功'); | |
368 | - await getDataBoardComponent(); | |
369 | - } catch (error) {} | |
370 | - }; | |
371 | - | |
372 | - const [registerHistoryDataModal, historyDataModalMethod] = useModal(); | |
373 | - | |
374 | - const handleOpenHistroyDataModal = (record: DataSource[]) => { | |
375 | - historyDataModalMethod.openModal(true, record); | |
376 | - }; | |
377 | - | |
378 | - const hasHistoryTrend = (item: DataBoardLayoutInfo) => { | |
379 | - return frontComponentMap.get(item.record.frontId as FrontComponent)?.hasHistoryTrend; | |
380 | - }; | |
381 | - | |
382 | - onMounted(async () => { | |
383 | - injectBaiDuMapLib(); | |
384 | - injectBaiDuMapTrackAniMationLib(); | |
385 | - getDataBoardComponent(); | |
386 | - }); | |
387 | -</script> | |
388 | - | |
389 | -<template> | |
390 | - <section class="flex flex-col overflow-hidden h-full w-full board-detail"> | |
391 | - <PageHeader v-if="!getIsSharePage"> | |
392 | - <template #title> | |
393 | - <div class="flex items-center"> | |
394 | - <img | |
395 | - :src="getAceClass === 'dark' ? backWhiteIcon : backIcon" | |
396 | - v-if="!getIsSharePage" | |
397 | - class="mr-3 cursor-pointer" | |
398 | - @click="handleBack" | |
399 | - /> | |
400 | - <span class="text-lg" color="#333">{{ getDataBoardName }}</span> | |
401 | - </div> | |
402 | - </template> | |
403 | - <template #extra> | |
404 | - <Authority :value="VisualComponentPermission.CREATE"> | |
405 | - <Button | |
406 | - v-if="!getIsSharePage && !isCustomerUser" | |
407 | - type="primary" | |
408 | - @click="handleOpenCreatePanel" | |
409 | - > | |
410 | - 创建组件 | |
411 | - </Button> | |
412 | - </Authority> | |
413 | - </template> | |
414 | - <div> | |
415 | - <span class="mr-3 text-sm" style="color: #666">已创建组件:</span> | |
416 | - <span style="color: #409eff"> {{ dataBoardList.length }}个</span> | |
417 | - </div> | |
418 | - </PageHeader> | |
419 | - <section class="flex-1"> | |
420 | - <Spin :spinning="loading"> | |
421 | - <GridLayout | |
422 | - v-model:layout="dataBoardList" | |
423 | - :col-num="GirdLayoutColNum" | |
424 | - :row-height="30" | |
425 | - :margin="[GridLayoutMargin, GridLayoutMargin]" | |
426 | - :is-draggable="draggable" | |
427 | - :is-resizable="resizable" | |
428 | - :vertical-compact="true" | |
429 | - :use-css-transforms="true" | |
430 | - style="width: 100%" | |
431 | - > | |
432 | - <GridItem | |
433 | - v-for="item in dataBoardList" | |
434 | - :key="item.i" | |
435 | - :static="item.static" | |
436 | - :x="item.x" | |
437 | - :y="item.y" | |
438 | - :w="item.w" | |
439 | - :h="item.h" | |
440 | - :i="item.i" | |
441 | - :min-h="DEFAULT_MIN_HEIGHT" | |
442 | - :min-w="DEFAULT_MIN_WIDTH" | |
443 | - :style="{ display: 'flex', flexWrap: 'wrap' }" | |
444 | - class="grid-item-layout" | |
445 | - @resized="itemResized" | |
446 | - @resize="itemResize" | |
447 | - @moved="itemMoved" | |
448 | - @container-resized="itemContainerResized" | |
449 | - drag-ignore-from=".no-drag" | |
450 | - > | |
451 | - <WidgetWrapper | |
452 | - :key="item.i" | |
453 | - :ref="(el: Element) => setComponentRef(el, item)" | |
454 | - :record="item.record" | |
455 | - :data-source="item.record.dataSource" | |
456 | - > | |
457 | - <template #header> | |
458 | - <BaseWidgetHeader | |
459 | - :record="item.record.dataSource" | |
460 | - :id="item.record.id" | |
461 | - :panel-name="item.record.name" | |
462 | - @action="handleMoreAction" | |
463 | - > | |
464 | - <template #moreAction> | |
465 | - <Tooltip v-if="!isCustomerUser" title="趋势"> | |
466 | - <img | |
467 | - :src="trendIcon" | |
468 | - v-if="!getIsSharePage && hasHistoryTrend(item)" | |
469 | - class="cursor-pointer w-4.5 h-4.5" | |
470 | - @click="handleOpenHistroyDataModal(item.record.dataSource)" | |
471 | - /> | |
472 | - </Tooltip> | |
473 | - </template> | |
474 | - </BaseWidgetHeader> | |
475 | - </template> | |
476 | - <template #controls="{ record, add, remove, update }"> | |
477 | - <component | |
478 | - :is="getComponent(item.record)" | |
479 | - :add="add" | |
480 | - :remove="remove" | |
481 | - :update="update" | |
482 | - :radio="record.radio || {}" | |
483 | - v-bind="getComponentConfig(item.record, record)" | |
484 | - :random="false" | |
485 | - /> | |
486 | - </template> | |
487 | - </WidgetWrapper> | |
488 | - </GridItem> | |
489 | - </GridLayout> | |
490 | - <Empty | |
491 | - v-if="!dataBoardList.length" | |
492 | - class="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2" | |
493 | - /> | |
494 | - </Spin> | |
495 | - </section> | |
496 | - <DataBindModal | |
497 | - :layout="dataBoardList" | |
498 | - @register="register" | |
499 | - @update="handleUpdateComponent" | |
500 | - @create="getDataBoardComponent" | |
501 | - /> | |
502 | - <HistoryTrendModal @register="registerHistoryDataModal" /> | |
503 | - </section> | |
504 | -</template> | |
505 | - | |
506 | -<style lang="less" scoped> | |
507 | - .vue-grid-item:not(.vue-grid-placeholder) { | |
508 | - background: #fff; | |
509 | - border: none !important; | |
510 | - | |
511 | - /* border: 1px solid black; */ | |
512 | - } | |
513 | - | |
514 | - .vue-grid-item .resizing { | |
515 | - opacity: 0.9; | |
516 | - } | |
517 | - | |
518 | - .vue-grid-item .static { | |
519 | - background: #cce; | |
520 | - } | |
521 | - | |
522 | - .vue-grid-item .text { | |
523 | - font-size: 24px; | |
524 | - text-align: center; | |
525 | - position: absolute; | |
526 | - top: 0; | |
527 | - bottom: 0; | |
528 | - left: 0; | |
529 | - right: 0; | |
530 | - margin: auto; | |
531 | - height: 100%; | |
532 | - width: 100%; | |
533 | - } | |
534 | - | |
535 | - .vue-grid-item .no-drag { | |
536 | - height: 100%; | |
537 | - width: 100%; | |
538 | - } | |
539 | - | |
540 | - .vue-grid-item .minMax { | |
541 | - font-size: 12px; | |
542 | - } | |
543 | - | |
544 | - .vue-grid-item .add { | |
545 | - cursor: pointer; | |
546 | - } | |
547 | - | |
548 | - .vue-draggable-handle { | |
549 | - position: absolute; | |
550 | - width: 20px; | |
551 | - height: 20px; | |
552 | - top: 0; | |
553 | - left: 0; | |
554 | - background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'><circle cx='5' cy='5' r='5' fill='#999999'/></svg>") | |
555 | - no-repeat; | |
556 | - background-position: bottom right; | |
557 | - padding: 0 8px 8px 0; | |
558 | - background-repeat: no-repeat; | |
559 | - background-origin: content-box; | |
560 | - box-sizing: border-box; | |
561 | - cursor: pointer; | |
562 | - } | |
563 | - | |
564 | - .grid-item-layout { | |
565 | - overflow: hidden; | |
566 | - border: 1px solid #eee !important; | |
567 | - background-color: #fcfcfc !important; | |
568 | - } | |
569 | - | |
570 | - .board-detail:deep(.ant-page-header) { | |
571 | - padding: 20px 20px 0 20px; | |
572 | - } | |
573 | - | |
574 | - .board-detail:deep(.ant-page-header-heading) { | |
575 | - height: 78px; | |
576 | - padding: 0 20px 0 20px; | |
577 | - box-sizing: border-box; | |
578 | - background-color: #fff; | |
579 | - } | |
580 | - | |
581 | - [data-theme='dark'] .board-detail:deep(.ant-page-header-heading) { | |
582 | - @apply bg-dark-900; | |
583 | - } | |
584 | - | |
585 | - .board-detail:deep(.ant-page-header-heading-extra) { | |
586 | - margin: 0; | |
587 | - line-height: 78px; | |
588 | - } | |
589 | - | |
590 | - .board-detail:deep(.ant-page-header-content) { | |
591 | - padding-top: 20px; | |
592 | - } | |
593 | - | |
594 | - :deep(.vue-resizable-handle) { | |
595 | - z-index: 99; | |
596 | - } | |
597 | -</style> |
src/views/visual/board/hook/useCalcGridLayout.ts
deleted
100644 → 0
1 | -import { unref } from 'vue'; | |
2 | -import { Layout } from 'vue3-grid-layout'; | |
3 | -import { DEFAULT_WIDGET_HEIGHT, DEFAULT_WIDGET_WIDTH, DEFAULT_MAX_COL } from '../config/config'; | |
4 | - | |
5 | -interface GapRecord { | |
6 | - maxGap: number; | |
7 | - startIndex: Nullable<number>; | |
8 | - endIndex: Nullable<number>; | |
9 | -} | |
10 | - | |
11 | -/** | |
12 | - * @description calculate where to place the component | |
13 | - * @returns | |
14 | - */ | |
15 | -export function useCalcGridLayout() { | |
16 | - const calcLayoutInfo = ( | |
17 | - layoutInfo: Layout[], | |
18 | - randomLayout = { width: DEFAULT_WIDGET_WIDTH, height: DEFAULT_WIDGET_HEIGHT } | |
19 | - ) => { | |
20 | - let maxWidth = 0; | |
21 | - let maxHeight = 0; | |
22 | - let maxWidthRecord = {} as unknown as Layout; | |
23 | - let maxHeightRecord = {} as unknown as Layout; | |
24 | - | |
25 | - for (const item of unref(layoutInfo)) { | |
26 | - const { x, y, h, w } = item; | |
27 | - if (x + w > maxWidth) { | |
28 | - maxWidth = x + w; | |
29 | - maxWidthRecord = item; | |
30 | - } | |
31 | - if (y + h > maxHeight) { | |
32 | - maxHeight = y + h; | |
33 | - maxHeightRecord = item; | |
34 | - } | |
35 | - } | |
36 | - | |
37 | - maxWidth = maxWidthRecord.x + maxWidthRecord.w; | |
38 | - maxHeight = maxHeightRecord.y + maxHeightRecord.h; | |
39 | - | |
40 | - const array = Array.from({ length: maxHeight }, (_value) => { | |
41 | - return Array.from({ length: maxWidth }); | |
42 | - }); | |
43 | - | |
44 | - for (const item of layoutInfo) { | |
45 | - const { x, y, w, h } = item; | |
46 | - | |
47 | - for (let i = 0; i < h; i++) { | |
48 | - const rowIndex = y + i > array.length - 1 ? array.length - 1 : y + i; | |
49 | - const colEnd = x + w; | |
50 | - const row = array[rowIndex]; | |
51 | - row.fill(true, x, colEnd); | |
52 | - } | |
53 | - } | |
54 | - | |
55 | - const checkAreaIsAvaliable = (rowIndex: number, rowRecord: GapRecord[]) => { | |
56 | - const { height } = randomLayout; | |
57 | - | |
58 | - for (const { startIndex: colStartIndex } of rowRecord) { | |
59 | - let record: GapRecord = { maxGap: 0, startIndex: null, endIndex: null }; | |
60 | - const heightGapRecord: GapRecord[] = []; | |
61 | - for (let i = 0; i < height; i++) { | |
62 | - const rowStartIndex = rowIndex + i > array.length - 1 ? array.length - 1 : rowIndex + i; | |
63 | - const row = array[rowStartIndex]; | |
64 | - const col = row[colStartIndex!]; | |
65 | - if (col) { | |
66 | - if (record.maxGap > 0) heightGapRecord.push(record); | |
67 | - record = { maxGap: 0, startIndex: null, endIndex: null }; | |
68 | - } | |
69 | - if (!col) { | |
70 | - record = { | |
71 | - maxGap: record.maxGap + 1, | |
72 | - startIndex: record.startIndex === null ? rowStartIndex : record.startIndex, | |
73 | - endIndex: rowStartIndex, | |
74 | - }; | |
75 | - } | |
76 | - if (i + 1 === height) if (record.maxGap > 0) heightGapRecord.push(record); | |
77 | - } | |
78 | - const minHeight = heightGapRecord.length | |
79 | - ? Math.min(...heightGapRecord.map((item) => item.maxGap)) | |
80 | - : 0; | |
81 | - if (minHeight >= height) { | |
82 | - let flag = true; | |
83 | - for (let colIndex = colStartIndex!; colIndex < record.endIndex!; colIndex++) { | |
84 | - for (let _rowIndex = rowIndex; _rowIndex < height; _rowIndex++) { | |
85 | - if (array[_rowIndex][colIndex]) { | |
86 | - flag = false; | |
87 | - break; | |
88 | - } | |
89 | - } | |
90 | - } | |
91 | - if (flag) return { y: rowIndex, x: colStartIndex!, flag: true }; | |
92 | - } | |
93 | - } | |
94 | - | |
95 | - return { flag: false, x: 0, y: 0 }; | |
96 | - }; | |
97 | - | |
98 | - for (let rowIndex = 0; rowIndex < array.length; rowIndex++) { | |
99 | - const row = array[rowIndex]; | |
100 | - let record: GapRecord = { maxGap: 0, startIndex: null, endIndex: null }; | |
101 | - const widthGapRecord: GapRecord[] = []; | |
102 | - | |
103 | - const { width } = unref(randomLayout); | |
104 | - | |
105 | - for (let colIndex = 0; colIndex < DEFAULT_MAX_COL; colIndex++) { | |
106 | - const col = row[colIndex]; | |
107 | - if (col) { | |
108 | - if (record.maxGap > 0) widthGapRecord.push(record); | |
109 | - record = { maxGap: 0, startIndex: null, endIndex: null }; | |
110 | - } | |
111 | - if (!col) { | |
112 | - record = { | |
113 | - maxGap: record.maxGap + 1, | |
114 | - startIndex: record.startIndex === null ? colIndex : record.startIndex, | |
115 | - endIndex: colIndex, | |
116 | - }; | |
117 | - } | |
118 | - if (colIndex + 1 === DEFAULT_MAX_COL) if (record.maxGap > 0) widthGapRecord.push(record); | |
119 | - } | |
120 | - | |
121 | - const maxWidth = widthGapRecord.length | |
122 | - ? Math.max(...widthGapRecord.map((item) => item.maxGap)) | |
123 | - : 0; | |
124 | - | |
125 | - if (maxWidth >= width) { | |
126 | - const maxRecordList = widthGapRecord.filter((item) => item.maxGap >= maxWidth); | |
127 | - const { flag, x, y } = checkAreaIsAvaliable(rowIndex, maxRecordList); | |
128 | - if (flag) return { x, y, w: randomLayout.width, h: randomLayout.height }; | |
129 | - } | |
130 | - } | |
131 | - | |
132 | - return { x: 0, y: array.length, w: randomLayout.width, h: randomLayout.height }; | |
133 | - }; | |
134 | - | |
135 | - return { calcLayoutInfo }; | |
136 | -} |
src/views/visual/board/hook/useSocketConnect.ts
deleted
100644 → 0
1 | -import { useWebSocket } from '@vueuse/core'; | |
2 | -import { Ref, unref } from 'vue'; | |
3 | -import { DataBoardLayoutInfo } from '../types/type'; | |
4 | -import { useGlobSetting } from '/@/hooks/setting'; | |
5 | -import { isNullAndUnDef } from '/@/utils/is'; | |
6 | -import { getJwtToken, getShareJwtToken } from '/@/utils/auth'; | |
7 | -import { isShareMode } from '/@/views/sys/share/hook'; | |
8 | - | |
9 | -interface SocketMessage { | |
10 | - tsSubCmds: SocketMessageItem[]; | |
11 | -} | |
12 | - | |
13 | -interface SocketMessageItem { | |
14 | - entityType: string; | |
15 | - entityId: string; | |
16 | - scope: string; | |
17 | - cmdId: number; | |
18 | - keys: string; | |
19 | -} | |
20 | - | |
21 | -interface GroupMappingRecord { | |
22 | - id: string; | |
23 | - recordIndex: number; | |
24 | - dataSourceIndex: number; | |
25 | - attribute: string; | |
26 | - deviceId: string; | |
27 | - slaveDeviceId: string; | |
28 | -} | |
29 | - | |
30 | -interface ResponseMessage { | |
31 | - subscriptionId: number; | |
32 | - errorCode: number; | |
33 | - errorMsg: Nullable<string>; | |
34 | - data: { | |
35 | - [key: string]: [[number, string]]; | |
36 | - }; | |
37 | - latestValues: { | |
38 | - [key: string]: number; | |
39 | - }; | |
40 | -} | |
41 | - | |
42 | -const generateMessage = (deviceId: string, cmdId: number, attr: string): SocketMessageItem => { | |
43 | - return { | |
44 | - entityType: 'DEVICE', | |
45 | - entityId: deviceId, | |
46 | - scope: 'LATEST_TELEMETRY', | |
47 | - cmdId, | |
48 | - keys: attr, | |
49 | - }; | |
50 | -}; | |
51 | - | |
52 | -export function useSocketConnect(dataSourceRef: Ref<DataBoardLayoutInfo[]>) { | |
53 | - const token = isShareMode() ? getShareJwtToken() : getJwtToken(); | |
54 | - | |
55 | - const cmdIdMapping = new Map<number, GroupMappingRecord[]>(); | |
56 | - | |
57 | - const groupMapping = new Map<string, GroupMappingRecord[]>(); | |
58 | - | |
59 | - const waitSendQueue: string[] = []; | |
60 | - | |
61 | - const { socketUrl } = useGlobSetting(); | |
62 | - | |
63 | - const config = { | |
64 | - server: `${socketUrl}${token}`, | |
65 | - }; | |
66 | - | |
67 | - // const getNeedUpdateValueById = (componentId: string, deviceId: string) => {}; | |
68 | - | |
69 | - const getNeedUpdateValueByIndex = (recordIndex: number, dataSourceIndex: number) => { | |
70 | - return unref(dataSourceRef)[recordIndex].record.dataSource[dataSourceIndex]; | |
71 | - }; | |
72 | - | |
73 | - const mergeGroup = (dataSourceRef: Ref<DataBoardLayoutInfo[]>) => { | |
74 | - for (let recordIndex = 0; recordIndex < unref(dataSourceRef).length; recordIndex++) { | |
75 | - const record = unref(dataSourceRef).at(recordIndex)!; | |
76 | - const dataSource = record?.record.dataSource; | |
77 | - for (let dataSourceIndex = 0; dataSourceIndex < dataSource.length; dataSourceIndex++) { | |
78 | - const dataDatum = dataSource.at(dataSourceIndex)!; | |
79 | - const { deviceId, slaveDeviceId, attribute } = dataDatum; | |
80 | - const groupMappingRecord: GroupMappingRecord = { | |
81 | - id: record.record.id, | |
82 | - recordIndex, | |
83 | - dataSourceIndex, | |
84 | - attribute, | |
85 | - deviceId, | |
86 | - slaveDeviceId, | |
87 | - }; | |
88 | - if (groupMapping.has(slaveDeviceId || deviceId)) { | |
89 | - const group = groupMapping.get(slaveDeviceId || deviceId); | |
90 | - group?.push(groupMappingRecord); | |
91 | - } else { | |
92 | - groupMapping.set(slaveDeviceId || deviceId, [groupMappingRecord]); | |
93 | - } | |
94 | - } | |
95 | - } | |
96 | - }; | |
97 | - | |
98 | - function generateGroupMessage() { | |
99 | - const messageList: SocketMessageItem[] = []; | |
100 | - let cmdId = 0; | |
101 | - groupMapping.forEach((value, key) => { | |
102 | - const message = generateMessage( | |
103 | - key, | |
104 | - cmdId, | |
105 | - Array.from(new Set(value.map((item) => item.attribute))).join(',') | |
106 | - ); | |
107 | - messageList.push(message); | |
108 | - setCmdId(cmdId, value); | |
109 | - cmdId++; | |
110 | - }); | |
111 | - return messageList; | |
112 | - } | |
113 | - | |
114 | - const { close, send, open, status } = useWebSocket(config.server, { | |
115 | - onConnected() { | |
116 | - if (waitSendQueue.length) { | |
117 | - waitSendQueue.forEach((string) => { | |
118 | - send(string); | |
119 | - }); | |
120 | - waitSendQueue.length = 0; | |
121 | - } | |
122 | - }, | |
123 | - onMessage(_ws, message) { | |
124 | - try { | |
125 | - const res: ResponseMessage = JSON.parse(message.data); | |
126 | - const { subscriptionId, data = {} } = res; | |
127 | - if (isNullAndUnDef(subscriptionId)) return; | |
128 | - const mappingRecord = cmdIdMapping.get(subscriptionId); | |
129 | - if (!mappingRecord || !data) return; | |
130 | - | |
131 | - for (const item of mappingRecord) { | |
132 | - const { attribute, recordIndex, dataSourceIndex } = item; | |
133 | - if (!attribute) continue; | |
134 | - const [[timespan, value]] = data[attribute]; | |
135 | - const record = getNeedUpdateValueByIndex(recordIndex, dataSourceIndex); | |
136 | - record.componentInfo.value = value; | |
137 | - record.componentInfo.updateTime = timespan; | |
138 | - } | |
139 | - return; | |
140 | - } catch (error) { | |
141 | - throw Error(error as string); | |
142 | - } | |
143 | - }, | |
144 | - // onDisconnected() { | |
145 | - // close(); | |
146 | - // }, | |
147 | - }); | |
148 | - | |
149 | - const setCmdId = (cmdId: number, record: GroupMappingRecord[]) => { | |
150 | - cmdIdMapping.set(cmdId, record); | |
151 | - }; | |
152 | - | |
153 | - const transformSocketMessageItem = () => { | |
154 | - mergeGroup(dataSourceRef); | |
155 | - return { | |
156 | - tsSubCmds: generateGroupMessage(), | |
157 | - } as SocketMessage; | |
158 | - }; | |
159 | - | |
160 | - const beginSendMessage = () => { | |
161 | - close(); | |
162 | - cmdIdMapping.clear(); | |
163 | - | |
164 | - // TODO current need use setTimeout delay 1 second to reconnect | |
165 | - setTimeout(() => { | |
166 | - open(); | |
167 | - const messageList = transformSocketMessageItem(); | |
168 | - | |
169 | - if (unref(status) !== 'OPEN') { | |
170 | - waitSendQueue.push(JSON.stringify(messageList)); | |
171 | - return; | |
172 | - } | |
173 | - send(JSON.stringify(messageList)); | |
174 | - }, 1000); | |
175 | - }; | |
176 | - | |
177 | - return { | |
178 | - close, | |
179 | - send, | |
180 | - open, | |
181 | - beginSendMessage, | |
182 | - }; | |
183 | -} |
src/views/visual/board/hook/useUpdateCenter.ts
deleted
100644 → 0
1 | -export type UpdateCenter = ReturnType<typeof useUpdateCenter>; | |
2 | - | |
3 | -export function useUpdateCenter() { | |
4 | - const eventCenter = new Map<string, Fn>(); | |
5 | - | |
6 | - const update = () => { | |
7 | - eventCenter.forEach((method) => { | |
8 | - method(); | |
9 | - }); | |
10 | - }; | |
11 | - | |
12 | - const add = (key: string, method: Fn) => { | |
13 | - if (eventCenter.has(key)) { | |
14 | - window.console.log(`Update Center Has Exist This Update Method(${key})`); | |
15 | - return; | |
16 | - } | |
17 | - eventCenter.set(key, method); | |
18 | - }; | |
19 | - | |
20 | - const remove = (key: string) => { | |
21 | - if (eventCenter.has(key)) eventCenter.delete(key); | |
22 | - }; | |
23 | - | |
24 | - return { update, add, remove }; | |
25 | -} |
src/views/visual/board/hook/useVisualBoardContext.ts
deleted
100644 → 0
1 | -import { inject, provide } from 'vue'; | |
2 | -import { UpdateCenter } from './useUpdateCenter'; | |
3 | - | |
4 | -const key = Symbol('visual-board-content'); | |
5 | - | |
6 | -type Instance = UpdateCenter; | |
7 | - | |
8 | -export function createVisualBoardContext(instance: Instance) { | |
9 | - provide(key, instance); | |
10 | -} | |
11 | - | |
12 | -export function useVisualBoardContext() { | |
13 | - return inject(key) as Instance; | |
14 | -} |
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | import { useMessage } from '/@/hooks/web/useMessage'; |
7 | 7 | import Dropdown from '/@/components/Dropdown/src/Dropdown.vue'; |
8 | 8 | import { DropMenu } from '/@/components/Dropdown'; |
9 | - import { DATA_BOARD_SHARE_URL, MoreActionEvent, VisualBoardPermission } from './config/config'; | |
9 | + import { MoreActionEvent, VisualBoardPermission } from './config/config'; | |
10 | 10 | import { useModal } from '/@/components/Modal'; |
11 | 11 | import PanelDetailModal from './components/PanelDetailModal.vue'; |
12 | 12 | import { getDataBoardList, deleteDataBoard, shareBoard } from '/@/api/dataBoard'; |
... | ... | @@ -25,6 +25,7 @@ |
25 | 25 | import { DataActionModeEnum } from '/@/enums/toolEnum'; |
26 | 26 | import { useRole } from '/@/hooks/business/useRole'; |
27 | 27 | import { useClipboard } from '@vueuse/core'; |
28 | + import { DATA_BOARD_SHARE_URL } from '../palette'; | |
28 | 29 | |
29 | 30 | const ListItem = List.Item; |
30 | 31 | const router = useRouter(); | ... | ... |
src/views/visual/board/types/type.ts
deleted
100644 → 0
1 | -import { Component } from 'vue'; | |
2 | -import { Layout } from 'vue3-grid-layout'; | |
3 | -import { FrontComponent, FrontComponentCategory, visualOptionField } from '../const/const'; | |
4 | -import { RadioRecord } from '../detail/config/util'; | |
5 | -import { DataComponentRecord, DataSource } from '/@/api/dataBoard/model'; | |
6 | - | |
7 | -export type FrontDataSourceRecord = DataSource; | |
8 | - | |
9 | -export type DataBoardLayoutInfo = Layout & { | |
10 | - record: DataComponentRecord; | |
11 | - width?: number; | |
12 | - height?: number; | |
13 | -}; | |
14 | - | |
15 | -export interface ComponentConfig { | |
16 | - Component: Component; | |
17 | - ComponentName?: string; | |
18 | - ComponentKey: FrontComponent; | |
19 | - ComponentConfig?: Recordable; | |
20 | - ComponentCategory: FrontComponentCategory; | |
21 | - isMultipleDataSource?: boolean; | |
22 | - hasHistoryTrend?: boolean; | |
23 | - hasSetting?: boolean; | |
24 | - transformConfig: ( | |
25 | - componentConfig: Recordable, | |
26 | - record: DataComponentRecord, | |
27 | - dataSourceRecord: DataSource | DataSource[] | |
28 | - ) => Recordable; | |
29 | -} | |
30 | - | |
31 | -export interface VisualOptionParams { | |
32 | - [visualOptionField.FONT_COLOR]: string; | |
33 | - [visualOptionField.UNIT]: string; | |
34 | - [visualOptionField.ICON_COLOR]: string; | |
35 | - [visualOptionField.ICON]: string; | |
36 | - [visualOptionField.FIRST_PHASE_COLOR]: string; | |
37 | - [visualOptionField.SECOND_PHASE_COLOR]: string; | |
38 | - [visualOptionField.THIRD_PHASE_COLOR]: string; | |
39 | - [visualOptionField.FIRST_PHASE_VALUE]: string; | |
40 | - [visualOptionField.SECOND_PHASE_VALUE]: string; | |
41 | - [visualOptionField.THIRD_PHASE_VALUE]: string; | |
42 | -} | |
43 | - | |
44 | -export interface VisualComponentProps<Layout = Recordable, Value = Recordable> { | |
45 | - value?: Value; | |
46 | - layout?: Layout; | |
47 | - radio?: RadioRecord; | |
48 | - random?: boolean; | |
49 | - add?: (key: string, method: Fn) => void; | |
50 | - update?: () => void; | |
51 | - remove?: (key: string) => void; | |
52 | -} |
1 | 1 | <script lang="ts" setup> |
2 | + import { commonDataSourceSchemas } from '../../packages/config/common.config'; | |
2 | 3 | import { CreateComponentType } from '../../packages/index.type'; |
3 | 4 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration'; | |
5 | 5 | import { |
6 | 6 | PublicComponentValueType, |
7 | 7 | PublicFormInstaceType, |
8 | 8 | } from '/@/views/visual/dataSourceBindPanel/index.type'; |
9 | 9 | |
10 | - const props = defineProps<{ | |
10 | + defineProps<{ | |
11 | 11 | values: PublicComponentValueType; |
12 | 12 | componentConfig: CreateComponentType; |
13 | 13 | }>(); |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | showActionButtonGroup: false, |
18 | 18 | layout: 'horizontal', |
19 | 19 | labelCol: { span: 0 }, |
20 | - schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key), | |
20 | + schemas: commonDataSourceSchemas(), | |
21 | 21 | }); |
22 | 22 | |
23 | 23 | const getFormValues = () => { | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { CreateComponentType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration'; | |
5 | 4 | import { |
6 | 5 | PublicComponentValueType, |
7 | 6 | PublicFormInstaceType, |
8 | 7 | } from '/@/views/visual/dataSourceBindPanel/index.type'; |
8 | + import { commonDataSourceSchemas } from '../../../config/common.config'; | |
9 | 9 | |
10 | - const props = defineProps<{ | |
10 | + defineProps<{ | |
11 | 11 | values: PublicComponentValueType; |
12 | 12 | componentConfig: CreateComponentType; |
13 | 13 | }>(); |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | showActionButtonGroup: false, |
18 | 18 | layout: 'horizontal', |
19 | 19 | labelCol: { span: 0 }, |
20 | - schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key), | |
20 | + schemas: commonDataSourceSchemas(), | |
21 | 21 | }); |
22 | 22 | |
23 | 23 | const getFormValues = () => { | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { CreateComponentType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration'; | |
5 | 4 | import { |
6 | 5 | PublicComponentValueType, |
7 | 6 | PublicFormInstaceType, |
8 | 7 | } from '/@/views/visual/dataSourceBindPanel/index.type'; |
8 | + import { commonDataSourceSchemas } from '../../../config/common.config'; | |
9 | 9 | |
10 | - const props = defineProps<{ | |
10 | + defineProps<{ | |
11 | 11 | values: PublicComponentValueType; |
12 | 12 | componentConfig: CreateComponentType; |
13 | 13 | }>(); |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | showActionButtonGroup: false, |
18 | 18 | layout: 'horizontal', |
19 | 19 | labelCol: { span: 0 }, |
20 | - schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key), | |
20 | + schemas: commonDataSourceSchemas(), | |
21 | 21 | }); |
22 | 22 | |
23 | 23 | const getFormValues = () => { | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { CreateComponentType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration'; | |
5 | 4 | import { |
6 | 5 | PublicComponentValueType, |
7 | 6 | PublicFormInstaceType, |
8 | 7 | } from '/@/views/visual/dataSourceBindPanel/index.type'; |
8 | + import { commonDataSourceSchemas } from '../../../config/common.config'; | |
9 | 9 | |
10 | - const props = defineProps<{ | |
10 | + defineProps<{ | |
11 | 11 | values: PublicComponentValueType; |
12 | 12 | componentConfig: CreateComponentType; |
13 | 13 | }>(); |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | showActionButtonGroup: false, |
18 | 18 | layout: 'horizontal', |
19 | 19 | labelCol: { span: 0 }, |
20 | - schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key), | |
20 | + schemas: commonDataSourceSchemas(), | |
21 | 21 | }); |
22 | 22 | |
23 | 23 | const getFormValues = () => { | ... | ... |
1 | 1 | <script lang="ts" setup> |
2 | 2 | import { CreateComponentType } from '/@/views/visual/packages/index.type'; |
3 | 3 | import { BasicForm, useForm } from '/@/components/Form'; |
4 | - import { dataSourceSchema } from '/@/views/visual/board/detail/config/basicConfiguration'; | |
5 | 4 | import { |
6 | 5 | PublicComponentValueType, |
7 | 6 | PublicFormInstaceType, |
8 | 7 | } from '/@/views/visual/dataSourceBindPanel/index.type'; |
8 | + import { commonDataSourceSchemas } from '../config/common.config'; | |
9 | 9 | |
10 | - const props = defineProps<{ | |
10 | + defineProps<{ | |
11 | 11 | values: PublicComponentValueType; |
12 | 12 | componentConfig: CreateComponentType; |
13 | 13 | }>(); |
... | ... | @@ -17,7 +17,7 @@ |
17 | 17 | showActionButtonGroup: false, |
18 | 18 | layout: 'horizontal', |
19 | 19 | labelCol: { span: 0 }, |
20 | - schemas: dataSourceSchema(false, props.componentConfig.componentConfig.key), | |
20 | + schemas: commonDataSourceSchemas(), | |
21 | 21 | }); |
22 | 22 | |
23 | 23 | const getFormValues = () => { | ... | ... |