Commit 106ae3768443387c14b8ad6c38625c684347f852

Authored by loveumiko
2 parents 3df887c7 77fe68ab

Merge branch 'main_dev' of http://git.yunteng.com/yunteng/thingskit-front into fix/DEFECT-1362

@@ -18,4 +18,5 @@ export interface Platform { @@ -18,4 +18,5 @@ export interface Platform {
18 copyright: string; 18 copyright: string;
19 presentedOurselves: string; 19 presentedOurselves: string;
20 domain: string; 20 domain: string;
  21 + icon: string;
21 } 22 }
@@ -49,13 +49,6 @@ @@ -49,13 +49,6 @@
49 return userStore.platInfo?.logo; 49 return userStore.platInfo?.logo;
50 }); 50 });
51 const getTitle = computed(() => { 51 const getTitle = computed(() => {
52 - // 设置icon  
53 - let link = (document.querySelector("link[rel*='icon']") ||  
54 - document.createElement('link')) as HTMLLinkElement;  
55 - link.type = 'image/x-icon';  
56 - link.rel = 'shortcut icon';  
57 - link.href = userStore.platInfo?.icon ?? '/favicon.ico';  
58 - document.getElementsByTagName('head')[0].appendChild(link);  
59 return userStore.platInfo?.name ?? title; 52 return userStore.platInfo?.name ?? title;
60 }); 53 });
61 </script> 54 </script>
@@ -67,6 +60,7 @@ @@ -67,6 +60,7 @@
67 padding-left: 7px; 60 padding-left: 7px;
68 cursor: pointer; 61 cursor: pointer;
69 transition: all 0.2s ease; 62 transition: all 0.2s ease;
  63 +
70 &.light { 64 &.light {
71 border-bottom: 1px solid @border-color-base; 65 border-bottom: 1px solid @border-color-base;
72 } 66 }
@@ -113,7 +113,7 @@ @@ -113,7 +113,7 @@
113 componentProps: { 113 componentProps: {
114 options: [ 114 options: [
115 { label: `${boolClose}-0`, value: 0 }, 115 { label: `${boolClose}-0`, value: 0 },
116 - { label: `${boolOpen}-0`, value: 1 }, 116 + { label: `${boolOpen}-1`, value: 1 },
117 ], 117 ],
118 onChange: (value: string) => { 118 onChange: (value: string) => {
119 syncValue(identifier, value); 119 syncValue(identifier, value);
@@ -397,22 +397,6 @@ @@ -397,22 +397,6 @@
397 ); 397 );
398 }); 398 });
399 } 399 }
400 - /**  
401 - * 只针对表格分页和组织列表分页的Tree(不是通过弹窗显示,默认是关闭的并且图标显示)  
402 - * 如果是其他弹窗出来的Tree(会造成默认是关闭的并且图标显示),则在对应页面重写css样式即可  
403 - * <style scoped lang="less">  
404 - :deep(.vben-basic-tree) {  
405 - width: 100% !important;  
406 - }  
407 - :deep(.is-unflod) {  
408 - display: none !important;  
409 - }  
410 - :deep(.is-flod) {  
411 - display: none !important;  
412 - }  
413 - </style>  
414 - TODO下次优化通过传配置值来动态显示那些页面需要默认展开或收起  
415 - */  
416 return () => { 400 return () => {
417 const { title, helpMessage, toolbar, search, checkable } = props; 401 const { title, helpMessage, toolbar, search, checkable } = props;
418 const showTitle = title || toolbar || search || slots.headerTitle; 402 const showTitle = title || toolbar || search || slots.headerTitle;
@@ -454,51 +438,6 @@ @@ -454,51 +438,6 @@
454 }); 438 });
455 </script> 439 </script>
456 <style lang="less"> 440 <style lang="less">
457 - // 使用媒体查询兼容 1920 1280 1024 800  
458 - @media screen and (max-width: 1980px) {  
459 - .fold-left {  
460 - z-index: 1;  
461 - cursor: pointer;  
462 - position: absolute;  
463 - top: 0.85rem;  
464 - left: 1.1vw;  
465 - }  
466 -  
467 - .fold-right {  
468 - z-index: 1;  
469 - cursor: pointer;  
470 - position: absolute;  
471 - top: 0.85rem;  
472 - left: 18.2vw;  
473 - }  
474 - }  
475 - @media screen and (max-width: 1280px) {  
476 - .fold-right {  
477 - z-index: 1;  
478 - cursor: pointer;  
479 - position: absolute;  
480 - top: 0.85rem;  
481 - left: 17.5vw;  
482 - }  
483 - }  
484 - @media screen and (max-width: 1024px) {  
485 - .fold-right {  
486 - z-index: 1;  
487 - cursor: pointer;  
488 - position: absolute;  
489 - top: 0.85rem;  
490 - left: 14.2vw;  
491 - }  
492 - }  
493 - @media screen and (max-width: 800px) {  
494 - .fold-right {  
495 - z-index: 1;  
496 - cursor: pointer;  
497 - position: absolute;  
498 - top: 0.85rem;  
499 - left: 18vw;  
500 - }  
501 - }  
502 @prefix-cls: ~'@{namespace}-basic-tree'; 441 @prefix-cls: ~'@{namespace}-basic-tree';
503 442
504 .@{prefix-cls} { 443 .@{prefix-cls} {
@@ -35,6 +35,8 @@ export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__'; @@ -35,6 +35,8 @@ export const APP_SESSION_CACHE_KEY = 'COMMON__SESSION__KEY__';
35 35
36 export const PLATFORM = 'PLATFORM'; 36 export const PLATFORM = 'PLATFORM';
37 37
  38 +export const PLATFORM_INFO_CACHE_KEY = 'PLATFORM_INFO';
  39 +
38 export const MENU_LIST = 'MENU_LIST'; 40 export const MENU_LIST = 'MENU_LIST';
39 export enum CacheTypeEnum { 41 export enum CacheTypeEnum {
40 SESSION, 42 SESSION,
@@ -71,7 +71,6 @@ @@ -71,7 +71,6 @@
71 71
72 import { useUserStore } from '/@/store/modules/user'; 72 import { useUserStore } from '/@/store/modules/user';
73 import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin'; 73 import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
74 - import { useDesign } from '/@/hooks/web/useDesign';  
75 import { getPlatForm } from '/@/api/oem'; 74 import { getPlatForm } from '/@/api/oem';
76 import { createLocalStorage } from '/@/utils/cache'; 75 import { createLocalStorage } from '/@/utils/cache';
77 76
@@ -80,8 +79,7 @@ @@ -80,8 +79,7 @@
80 const FormItem = Form.Item; 79 const FormItem = Form.Item;
81 const InputPassword = Input.Password; 80 const InputPassword = Input.Password;
82 const { t } = useI18n(); 81 const { t } = useI18n();
83 - const { notification, createMessage } = useMessage();  
84 - const { prefixCls } = useDesign('login'); 82 + const { notification } = useMessage();
85 const userStore = useUserStore(); 83 const userStore = useUserStore();
86 84
87 const { setLoginState, getLoginState } = useLoginState(); 85 const { setLoginState, getLoginState } = useLoginState();
@@ -115,7 +113,7 @@ @@ -115,7 +113,7 @@
115 username: data.account, 113 username: data.account,
116 mode: 'modal', //不要默认的错误提示 114 mode: 'modal', //不要默认的错误提示
117 }) 115 })
118 - .catch((data) => { 116 + .catch(() => {
119 //登录失败返回的html,所以提示框什么都没有 117 //登录失败返回的html,所以提示框什么都没有
120 //去掉提示框 118 //去掉提示框
121 // createMessage.error(data.message); 119 // createMessage.error(data.message);
@@ -129,13 +127,6 @@ @@ -129,13 +127,6 @@
129 const res = await getPlatForm(); 127 const res = await getPlatForm();
130 storage.set('platformInfo', res); 128 storage.set('platformInfo', res);
131 userStore.setPlatInfo(res); 129 userStore.setPlatInfo(res);
132 - // 设置icon  
133 - let link = (document.querySelector("link[rel*='icon']") ||  
134 - document.createElement('link')) as HTMLLinkElement;  
135 - link.type = 'image/x-icon';  
136 - link.rel = 'shortcut icon';  
137 - link.href = res.icon ?? '/favicon.ico';  
138 - document.getElementsByTagName('head')[0].appendChild(link);  
139 130
140 var _hmt = _hmt || []; 131 var _hmt = _hmt || [];
141 (function () { 132 (function () {
1 <template> 1 <template>
2 - <div  
3 - style="background-size: 100% 100%"  
4 - :style="{ backgroundImage: 'url(' + (logoUrl !== undefined ? logoUrl : '') + ')' }"  
5 - :class="prefixCls"  
6 - class="relative w-full h-full px-4"  
7 - > 2 + <div :class="prefixCls" class="relative w-full h-full px-4">
8 <AppLocalePicker 3 <AppLocalePicker
9 class="absolute text-white top-4 right-4 enter-x xl:text-gray-600" 4 class="absolute text-white top-4 right-4 enter-x xl:text-gray-600"
10 :showText="false" 5 :showText="false"
@@ -36,12 +31,8 @@ @@ -36,12 +31,8 @@
36 {{ defaultTitle }} 31 {{ defaultTitle }}
37 </div> 32 </div>
38 </div> 33 </div>
39 - <div v-if="ifCustom" class="my-auto">  
40 - <img  
41 - :alt="title"  
42 - src="../../../assets/svg/thingskit-login-background.svg"  
43 - class="w-1/2 -mt-16 -enter-x"  
44 - /> 34 + <div v-if="show" class="my-auto">
  35 + <img :alt="title" :src="defaultBackgroundImage" class="w-1/2 -mt-16 -enter-x" />
45 <div class="mt-10 font-medium text-white -enter-x"> 36 <div class="mt-10 font-medium text-white -enter-x">
46 <span class="inline-block mt-4 text-3xl"> {{ t('sys.login.signInTitle') }}</span> 37 <span class="inline-block mt-4 text-3xl"> {{ t('sys.login.signInTitle') }}</span>
47 </div> 38 </div>
@@ -67,7 +58,6 @@ @@ -67,7 +58,6 @@
67 </template> 58 </template>
68 <script lang="ts" setup> 59 <script lang="ts" setup>
69 import { ref, onMounted } from 'vue'; 60 import { ref, onMounted } from 'vue';
70 - // import { AppLogo } from '/@/components/Application';  
71 import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application'; 61 import { AppLocalePicker, AppDarkModeToggle } from '/@/components/Application';
72 import LoginForm from './LoginForm.vue'; 62 import LoginForm from './LoginForm.vue';
73 import ForgetPasswordForm from './ForgetPasswordForm.vue'; 63 import ForgetPasswordForm from './ForgetPasswordForm.vue';
@@ -77,13 +67,9 @@ @@ -77,13 +67,9 @@
77 import { useI18n } from '/@/hooks/web/useI18n'; 67 import { useI18n } from '/@/hooks/web/useI18n';
78 import { useDesign } from '/@/hooks/web/useDesign'; 68 import { useDesign } from '/@/hooks/web/useDesign';
79 import { useLocaleStore } from '/@/store/modules/locale'; 69 import { useLocaleStore } from '/@/store/modules/locale';
80 - // import { useUserStore } from '/@/store/modules/user';  
81 - import { getPlatForm } from '/@/api/oem/index';  
82 - import defaultShowLogoImg from '/@/assets/svg/login-bg.svg';  
83 - import defaultShowLogoDarkImg from '/@/assets/svg/login-bg-dark.svg';  
84 import { useTitle } from '@vueuse/core'; 70 import { useTitle } from '@vueuse/core';
85 - import { createLocalStorage } from '/@/utils/cache';  
86 - // import {usePlatform} from '/@/views/system/customize/hook/usePlatformInfo' 71 + import defaultBackgroundImage from '/@/assets/svg/thingskit-login-background.svg';
  72 + import { getPlatFormInfo } from '../../system/customize/hook/usePlatformInfo';
87 73
88 defineProps({ 74 defineProps({
89 sessionTimeout: { 75 sessionTimeout: {
@@ -93,88 +79,22 @@ @@ -93,88 +79,22 @@
93 const { title } = useGlobSetting(); 79 const { title } = useGlobSetting();
94 const defaultTitle = ref(''); 80 const defaultTitle = ref('');
95 const defaultLogo = ref(''); 81 const defaultLogo = ref('');
96 - const logoUrl = ref('');  
97 - // const userStore = useUserStore();  
98 - const storage = createLocalStorage();  
99 -  
100 - onMounted(async () => {  
101 - let res = storage.get('platformInfo');  
102 - if (res === '' || res === null) {  
103 - res = await getPlatForm();  
104 - }  
105 - logoUrl.value = res?.background;  
106 - defaultTitle.value = res?.name || title;  
107 - defaultLogo.value = res?.logo;  
108 - let link = (document.querySelector("link[rel*='icon']") ||  
109 - document.createElement('link')) as HTMLLinkElement;  
110 - link.type = 'image/x-icon';  
111 - link.rel = 'shortcut icon';  
112 - link.href = res?.icon ?? '/favicon.ico';  
113 - document.getElementsByTagName('head')[0].appendChild(link);  
114 - let defaultLogoBg = document.createElement('style');  
115 - let defaultLogoDarkBg = document.createElement('style');  
116 - if (logoUrl.value !== undefined) {  
117 - //企业自定义  
118 - ifCustom.value = false;  
119 - //默认图片  
120 - defaultLogoBg.innerHTML = `.vben-login::before{  
121 - background-image:url("");  
122 - position:absolute;  
123 - }`;  
124 - //切换黑暗模式图片  
125 - defaultLogoDarkBg.innerHTML = `html[data-theme='dark'] .vben-login::before{  
126 - background-image:url("");  
127 - position:absolute;  
128 - }`;  
129 - } else {  
130 - logoUrl.value = 'url(' + defaultShowLogoImg + ')';  
131 - //默认图片  
132 - defaultLogoBg.innerHTML = `.vben-login::before{  
133 - background-image:url(${defaultShowLogoImg});  
134 - position:absolute;  
135 - }`;  
136 - //切换黑暗模式图片  
137 - defaultLogoDarkBg.innerHTML = `html[data-theme='dark'] .vben-login::before{  
138 - background-image:url(${defaultShowLogoDarkImg});  
139 - position:absolute;  
140 - }`;  
141 - }  
142 - document.head.appendChild(defaultLogoBg);  
143 - document.head.appendChild(defaultLogoDarkBg);  
144 - });  
145 82
146 - // const userStore = useUserStore();  
147 -  
148 - const ifCustom = ref(true);  
149 - // const getLogo = computed(() => {  
150 - // return userStore.platInfo?.logo;  
151 - // });  
152 - // const getTitle = computed(() => {  
153 - // // 设置icon  
154 - // let link = (document.querySelector("link[rel*='icon']") ||  
155 - // document.createElement('link')) as HTMLLinkElement;  
156 - // link.type = 'image/x-icon';  
157 - // link.rel = 'shortcut icon';  
158 - // link.href = userStore.platInfo?.icon ?? '/favicon.ico';  
159 - // document.getElementsByTagName('head')[0].appendChild(link);  
160 - // // logoUrl.value = userStore.platInfo?.background;  
161 - // // if (logoUrl.value !== undefined) {  
162 - // // ifCustom.value = false;  
163 - // // } else {  
164 - // // logoUrl.value = 'url(' + defaultShowLogoImg + ')';  
165 - // // }  
166 - // return userStore.platInfo?.name ?? title;  
167 - // });  
168 - // const globSetting = useGlobSetting();  
169 const { prefixCls } = useDesign('login'); 83 const { prefixCls } = useDesign('login');
170 const { t } = useI18n(); 84 const { t } = useI18n();
171 const localeStore = useLocaleStore(); 85 const localeStore = useLocaleStore();
172 const showLocale = localeStore.getShowPicker; 86 const showLocale = localeStore.getShowPicker;
173 - // const title = computed(() => globSetting?.title ?? '');  
174 87
175 onMounted(() => { 88 onMounted(() => {
176 useTitle('ThingsKit 物联网平台'); 89 useTitle('ThingsKit 物联网平台');
177 }); 90 });
  91 +
  92 + const show = ref(false);
  93 +
  94 + onMounted(() => {
  95 + const platform = getPlatFormInfo();
  96 + show.value = !platform.background;
  97 + });
178 </script> 98 </script>
179 <style lang="less"> 99 <style lang="less">
180 @prefix-cls: ~'@{namespace}-login'; 100 @prefix-cls: ~'@{namespace}-login';
@@ -71,7 +71,6 @@ @@ -71,7 +71,6 @@
71 71
72 import { useUserStore } from '/@/store/modules/user'; 72 import { useUserStore } from '/@/store/modules/user';
73 import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin'; 73 import { LoginStateEnum, useLoginState, useFormRules, useFormValid } from './useLogin';
74 - import { useDesign } from '/@/hooks/web/useDesign';  
75 import { getPlatForm } from '/@/api/oem'; 74 import { getPlatForm } from '/@/api/oem';
76 import { createLocalStorage, createSessionStorage } from '/@/utils/cache'; 75 import { createLocalStorage, createSessionStorage } from '/@/utils/cache';
77 76
@@ -81,7 +80,6 @@ @@ -81,7 +80,6 @@
81 const InputPassword = Input.Password; 80 const InputPassword = Input.Password;
82 const { t } = useI18n(); 81 const { t } = useI18n();
83 const { notification, createMessage } = useMessage(); 82 const { notification, createMessage } = useMessage();
84 - const { prefixCls } = useDesign('login');  
85 const userStore = useUserStore(); 83 const userStore = useUserStore();
86 84
87 const { setLoginState, getLoginState } = useLoginState(); 85 const { setLoginState, getLoginState } = useLoginState();
@@ -137,13 +135,6 @@ @@ -137,13 +135,6 @@
137 const res = await getPlatForm(); 135 const res = await getPlatForm();
138 storage.set('platformInfo', res); 136 storage.set('platformInfo', res);
139 userStore.setPlatInfo(res); 137 userStore.setPlatInfo(res);
140 - // 设置icon  
141 - let link = (document.querySelector("link[rel*='icon']") ||  
142 - document.createElement('link')) as HTMLLinkElement;  
143 - link.type = 'image/x-icon';  
144 - link.rel = 'shortcut icon';  
145 - link.href = res.icon ?? '/favicon.ico';  
146 - document.getElementsByTagName('head')[0].appendChild(link);  
147 138
148 var _hmt = _hmt || []; 139 var _hmt = _hmt || [];
149 (function () { 140 (function () {
@@ -38,7 +38,7 @@ @@ -38,7 +38,7 @@
38 > 38 >
39 </template> 39 </template>
40 <template #uploadText> 40 <template #uploadText>
41 - <div class="box-outline"> 支持.ICON格式,建议尺寸为16*16px </div> 41 + <div class="box-outline"> 支持.ICO格式,建议尺寸为16*16px </div>
42 </template> 42 </template>
43 </ContentUploadText> 43 </ContentUploadText>
44 </template> 44 </template>
@@ -149,9 +149,10 @@ @@ -149,9 +149,10 @@
149 } 149 }
150 } 150 }
151 const beforeUploadIconPic = (file: FileItem) => { 151 const beforeUploadIconPic = (file: FileItem) => {
152 - const isJpgOrPng = file.type === 'image/x-icon'; 152 + console.log(file);
  153 + const isJpgOrPng = file.type === 'image/vnd.microsoft.icon';
153 if (!isJpgOrPng) { 154 if (!isJpgOrPng) {
154 - createMessage.error('只能上传.icon图片文件!'); 155 + createMessage.error('只能上传.ico图片文件!');
155 } 156 }
156 const isLt2M = (file.size as number) / 1024 < 500; 157 const isLt2M = (file.size as number) / 1024 < 500;
157 if (!isLt2M) { 158 if (!isLt2M) {
1 import { getPlatForm } from '/@/api/oem'; 1 import { getPlatForm } from '/@/api/oem';
2 -import { createLocalStorage } from '/@/utils/cache'; 2 +import lightThemeBgImage from '/@/assets/svg/login-bg.svg';
  3 +import darkThemeBgImage from '/@/assets/svg/login-bg-dark.svg';
  4 +import { createStorage } from '/@/utils/cache';
  5 +import { Platform } from '/@/api/oem/model';
  6 +import { PLATFORM_INFO_CACHE_KEY } from '/@/enums/cacheEnum';
3 7
4 enum DefaultPlatform { 8 enum DefaultPlatform {
5 LOGO = '/resource/img/logo.png', 9 LOGO = '/resource/img/logo.png',
@@ -7,19 +11,15 @@ enum DefaultPlatform { @@ -7,19 +11,15 @@ enum DefaultPlatform {
7 ICO = '/favicon.ico', 11 ICO = '/favicon.ico',
8 } 12 }
9 13
  14 +const storage = createStorage();
  15 +
  16 +export const getPlatFormInfo = () => storage.get(PLATFORM_INFO_CACHE_KEY) as Platform;
  17 +export const setPlatFormInfo = (info: Recordable) => storage.set(PLATFORM_INFO_CACHE_KEY, info);
  18 +
10 export const usePlatform = async () => { 19 export const usePlatform = async () => {
11 - const platformInfo = await getPlatForm();  
12 - const storage = createLocalStorage();  
13 - if (  
14 - platformInfo === '' ||  
15 - platformInfo === null ||  
16 - Object.getOwnPropertyNames(platformInfo).length === 0  
17 - ) {  
18 - storage.set('platformInfo', 1234);  
19 - } else {  
20 - storage.set('platformInfo', platformInfo);  
21 - } 20 + const platformInfo = (await getPlatForm()) || {};
22 21
  22 + setPlatFormInfo(platformInfo);
23 const createLoadingEffect = () => { 23 const createLoadingEffect = () => {
24 const wrap = document.createElement('div'); 24 const wrap = document.createElement('div');
25 wrap.setAttribute('class', 'app-loading-wrap'); 25 wrap.setAttribute('class', 'app-loading-wrap');
@@ -50,7 +50,7 @@ export const usePlatform = async () => { @@ -50,7 +50,7 @@ export const usePlatform = async () => {
50 const replaceSiteIco = () => { 50 const replaceSiteIco = () => {
51 const linkEl = document.querySelectorAll('link[rel*="icon"]'); 51 const linkEl = document.querySelectorAll('link[rel*="icon"]');
52 linkEl.forEach((item) => { 52 linkEl.forEach((item) => {
53 - item.setAttribute('href', platformInfo.logo || DefaultPlatform.ICO); 53 + item.setAttribute('href', platformInfo.icon || DefaultPlatform.ICO);
54 }); 54 });
55 }; 55 };
56 56
@@ -60,9 +60,31 @@ export const usePlatform = async () => { @@ -60,9 +60,31 @@ export const usePlatform = async () => {
60 loadingWrapper?.appendChild(createLoadingEffect()); 60 loadingWrapper?.appendChild(createLoadingEffect());
61 }; 61 };
62 62
  63 + const setBackgroundImage = () => {
  64 + const { background } = platformInfo;
  65 + const styleEl = document.createElement('style');
  66 +
  67 + styleEl.innerHTML = `
  68 + .vben-login::before{
  69 + background-image:url("${background || lightThemeBgImage}");
  70 + position:absolute;
  71 + margin-left: ${background ? 0 : '-48%'} !important;
  72 + }
  73 + html[data-theme='dark'] .vben-login::before{
  74 + background-image:url("${background || darkThemeBgImage}");
  75 + position:absolute;
  76 + margin-left: ${background ? 0 : '-48%'} !important;
  77 + }
  78 + `;
  79 +
  80 + console.log(styleEl);
  81 + document.head.appendChild(styleEl);
  82 + };
  83 +
63 const bootstrap = () => { 84 const bootstrap = () => {
64 replaceSiteIco(); 85 replaceSiteIco();
65 replaceLoadingEffect(); 86 replaceLoadingEffect();
  87 + setBackgroundImage();
66 }; 88 };
67 89
68 bootstrap(); 90 bootstrap();
@@ -16,6 +16,7 @@ @@ -16,6 +16,7 @@
16 toolbar 16 toolbar
17 ref="treeRef" 17 ref="treeRef"
18 :treeData="treeData" 18 :treeData="treeData"
  19 + @check="handleCheckClick"
19 :replace-fields="{ title: 'name', key: 'id' }" 20 :replace-fields="{ title: 'name', key: 'id' }"
20 :checkedKeys="roleMenus" 21 :checkedKeys="roleMenus"
21 title="菜单分配" 22 title="菜单分配"
@@ -58,6 +59,7 @@ @@ -58,6 +59,7 @@
58 const treeRef = ref<Nullable<TreeActionType>>(); 59 const treeRef = ref<Nullable<TreeActionType>>();
59 const checked = ref<string[]>([]); //需要选中的节点 60 const checked = ref<string[]>([]); //需要选中的节点
60 const spinning = ref(false); 61 const spinning = ref(false);
  62 + const checkedKeysWithHalfChecked = ref<string[]>([]);
61 63
62 const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({ 64 const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
63 labelWidth: 100, 65 labelWidth: 100,
@@ -126,7 +128,9 @@ @@ -126,7 +128,9 @@
126 roleId.value = data.record.id; 128 roleId.value = data.record.id;
127 129
128 //通过角色id去获取角色对应的菜单的ids 130 //通过角色id去获取角色对应的菜单的ids
129 - roleMenus.value = await getMenusIdsByRoleId(data.record.id); 131 + checkedKeysWithHalfChecked.value = roleMenus.value = await getMenusIdsByRoleId(
  132 + data.record.id
  133 + );
130 excludeHalfCheckedKeys(unref(treeData)); 134 excludeHalfCheckedKeys(unref(treeData));
131 await nextTick(); 135 await nextTick();
132 unref(treeRef)?.setCheckedKeys(roleMenus.value); 136 unref(treeRef)?.setCheckedKeys(roleMenus.value);
@@ -147,14 +151,13 @@ @@ -147,14 +151,13 @@
147 setDrawerProps({ confirmLoading: true }); 151 setDrawerProps({ confirmLoading: true });
148 const { createMessage } = useMessage(); 152 const { createMessage } = useMessage();
149 try { 153 try {
150 - const menu = (unref(treeRef)?.getCheckedKeys() as string[]) || [];  
151 const values = await validate(); 154 const values = await validate();
152 const req = { 155 const req = {
153 id: roleId.value, 156 id: roleId.value,
154 name: values.name, 157 name: values.name,
155 remark: values.remark, 158 remark: values.remark,
156 status: values.status, 159 status: values.status,
157 - menu, 160 + menu: unref(checkedKeysWithHalfChecked),
158 }; 161 };
159 if (req.menu == undefined) return createMessage.error('请勾选权限菜单'); 162 if (req.menu == undefined) return createMessage.error('请勾选权限菜单');
160 saveOrUpdateRoleInfoWithMenu(req).then(() => { 163 saveOrUpdateRoleInfoWithMenu(req).then(() => {
@@ -239,6 +242,13 @@ @@ -239,6 +242,13 @@
239 return needExcludeKeys; 242 return needExcludeKeys;
240 }; 243 };
241 244
  245 + const handleCheckClick = (selectedKeys: string[], event: CheckEvent) => {
  246 + checkedKeysWithHalfChecked.value = [
  247 + ...selectedKeys,
  248 + ...(event.halfCheckedKeys as string[]),
  249 + ];
  250 + };
  251 +
242 return { 252 return {
243 spinning, 253 spinning,
244 registerDrawer, 254 registerDrawer,
@@ -248,6 +258,7 @@ @@ -248,6 +258,7 @@
248 treeData, 258 treeData,
249 roleMenus, 259 roleMenus,
250 treeRef, 260 treeRef,
  261 + handleCheckClick,
251 }; 262 };
252 }, 263 },
253 }); 264 });
@@ -15,6 +15,7 @@ @@ -15,6 +15,7 @@
15 :treeData="treeData" 15 :treeData="treeData"
16 :replaceFields="{ title: 'name', key: 'id' }" 16 :replaceFields="{ title: 'name', key: 'id' }"
17 :checkedKeys="roleMenus" 17 :checkedKeys="roleMenus"
  18 + @check="handleCheckClick"
18 checkable 19 checkable
19 toolbar 20 toolbar
20 ref="treeRef" 21 ref="treeRef"
@@ -30,7 +31,7 @@ @@ -30,7 +31,7 @@
30 import { BasicForm, useForm } from '/@/components/Form/index'; 31 import { BasicForm, useForm } from '/@/components/Form/index';
31 import { formSchema } from './role.data'; 32 import { formSchema } from './role.data';
32 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer'; 33 import { BasicDrawer, useDrawerInner } from '/@/components/Drawer';
33 - import { BasicTree, TreeActionType, TreeItem } from '/@/components/Tree'; 34 + import { BasicTree, CheckEvent, TreeActionType, TreeItem } from '/@/components/Tree';
34 const { t } = useI18n(); //加载国际化 35 const { t } = useI18n(); //加载国际化
35 // 加载菜单数据 36 // 加载菜单数据
36 import { getAdminMenuList, getMenuList, getMenusIdsByRoleId } from '/@/api/sys/menu'; 37 import { getAdminMenuList, getMenuList, getMenusIdsByRoleId } from '/@/api/sys/menu';
@@ -58,6 +59,7 @@ @@ -58,6 +59,7 @@
58 const treeRef = ref<Nullable<TreeActionType>>(null); 59 const treeRef = ref<Nullable<TreeActionType>>(null);
59 const checked = ref<string[]>([]); //需要选中的节点 60 const checked = ref<string[]>([]); //需要选中的节点
60 const spinning = ref(false); 61 const spinning = ref(false);
  62 + const checkedKeysWithHalfChecked = ref<string[]>([]);
61 63
62 const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({ 64 const [registerForm, { resetFields, setFieldsValue, validate }] = useForm({
63 labelWidth: 90, 65 labelWidth: 90,
@@ -85,7 +87,6 @@ @@ -85,7 +87,6 @@
85 const roleType = RoleEnum.TENANT_ADMIN; 87 const roleType = RoleEnum.TENANT_ADMIN;
86 try { 88 try {
87 spinning.value = true; 89 spinning.value = true;
88 - // 需要在setFieldsValue之前先填充treeData,否则Tree组件可能会报key not exist警告  
89 90
90 if (!unref(treeData).length) { 91 if (!unref(treeData).length) {
91 // 获取全部的菜单 92 // 获取全部的菜单
@@ -108,7 +109,10 @@ @@ -108,7 +109,10 @@
108 roleId.value = data.record.id; 109 roleId.value = data.record.id;
109 110
110 //通过角色id去获取角色对应的菜单的ids 111 //通过角色id去获取角色对应的菜单的ids
111 - roleMenus.value = await getMenusIdsByRoleId(data.record.id); 112 + checkedKeysWithHalfChecked.value = roleMenus.value = await getMenusIdsByRoleId(
  113 + data.record.id
  114 + );
  115 +
112 excludeHalfCheckedKeys(unref(treeData)); 116 excludeHalfCheckedKeys(unref(treeData));
113 117
114 await nextTick(); 118 await nextTick();
@@ -125,10 +129,8 @@ @@ -125,10 +129,8 @@
125 const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色')); 129 const getTitle = computed(() => (!unref(isUpdate) ? '新增角色' : '编辑角色'));
126 130
127 async function handleSubmit() { 131 async function handleSubmit() {
128 - setDrawerProps({ loading: true });  
129 - setDrawerProps({ confirmLoading: true }); 132 + setDrawerProps({ loading: true, confirmLoading: true });
130 const { createMessage } = useMessage(); 133 const { createMessage } = useMessage();
131 - const menu = (unref(treeRef)?.getCheckedKeys() as string[]) || [];  
132 try { 134 try {
133 const values = await validate(); 135 const values = await validate();
134 const req = { 136 const req = {
@@ -137,7 +139,7 @@ @@ -137,7 +139,7 @@
137 remark: values.remark, 139 remark: values.remark,
138 status: values.status, 140 status: values.status,
139 roleType: RoleEnum.TENANT_ADMIN, 141 roleType: RoleEnum.TENANT_ADMIN,
140 - menu, 142 + menu: unref(checkedKeysWithHalfChecked),
141 }; 143 };
142 if (req.menu == undefined) return createMessage.error('请勾选权限菜单'); 144 if (req.menu == undefined) return createMessage.error('请勾选权限菜单');
143 const res = await saveOrUpdateRoleInfoWithMenu(req); 145 const res = await saveOrUpdateRoleInfoWithMenu(req);
@@ -226,6 +228,13 @@ @@ -226,6 +228,13 @@
226 return needExcludeKeys; 228 return needExcludeKeys;
227 }; 229 };
228 230
  231 + const handleCheckClick = (selectedKeys: string[], event: CheckEvent) => {
  232 + checkedKeysWithHalfChecked.value = [
  233 + ...selectedKeys,
  234 + ...(event.halfCheckedKeys as string[]),
  235 + ];
  236 + };
  237 +
229 return { 238 return {
230 spinning, 239 spinning,
231 registerDrawer, 240 registerDrawer,
@@ -235,6 +244,7 @@ @@ -235,6 +244,7 @@
235 treeData, 244 treeData,
236 roleMenus, 245 roleMenus,
237 treeRef, 246 treeRef,
  247 + handleCheckClick,
238 }; 248 };
239 }, 249 },
240 }); 250 });
@@ -75,7 +75,7 @@ @@ -75,7 +75,7 @@
75 75
76 createSelectWidgetModeContext(currentMode); 76 createSelectWidgetModeContext(currentMode);
77 77
78 - const currentRecord = ref<WidgetDataType>(); 78 + const currentRecord = ref<Nullable<Recordable>>({});
79 79
80 const [registerModal, { closeModal }] = useModalInner( 80 const [registerModal, { closeModal }] = useModalInner(
81 (params: ModalParamsType<WidgetDataType>) => { 81 (params: ModalParamsType<WidgetDataType>) => {
@@ -105,6 +105,7 @@ @@ -105,6 +105,7 @@
105 if (activeKey === TabKeyEnum.VISUAL) { 105 if (activeKey === TabKeyEnum.VISUAL) {
106 dataSource.value = (dataSourceFormEl.value?.getFormValues() as DataSourceType[]) || []; 106 dataSource.value = (dataSourceFormEl.value?.getFormValues() as DataSourceType[]) || [];
107 } 107 }
  108 + currentRecord.value = basicInfoFromEl.value?.getFormValues() || {};
108 }; 109 };
109 110
110 const handleNewRecord = () => { 111 const handleNewRecord = () => {
@@ -22,6 +22,10 @@ @@ -22,6 +22,10 @@
22 22
23 const wrapId = `bai-map-${buildUUID()}`; 23 const wrapId = `bai-map-${buildUUID()}`;
24 24
  25 + const getDeviceId = computed(() => {
  26 + return props.config.option.dataSource?.at(0)?.deviceId;
  27 + });
  28 +
25 /** 29 /**
26 * @description 经度key 30 * @description 经度key
27 */ 31 */
@@ -40,14 +44,18 @@ @@ -40,14 +44,18 @@
40 return !!(value && !isNaN(value as unknown as number)); 44 return !!(value && !isNaN(value as unknown as number));
41 }; 45 };
42 46
43 - const updateFn: MultipleDataFetchUpdateFn = (message) => { 47 + const updateFn: MultipleDataFetchUpdateFn = (message, deviceId) => {
  48 + if (unref(getDeviceId) !== deviceId) return;
  49 +
44 const { data = {} } = message; 50 const { data = {} } = message;
45 51
46 - const lngData = data[unref(getLngKey)] || []; 52 + const bindMessage = data[deviceId];
  53 +
  54 + const lngData = bindMessage[unref(getLngKey)] || [];
47 const [lngLatest] = lngData; 55 const [lngLatest] = lngData;
48 const [, lng] = lngLatest; 56 const [, lng] = lngLatest;
49 57
50 - const latData = data[unref(getLatKey)] || []; 58 + const latData = bindMessage[unref(getLatKey)] || [];
51 const [latLatest] = latData; 59 const [latLatest] = latData;
52 const [, lat] = latLatest; 60 const [, lat] = latLatest;
53 61
@@ -9,40 +9,48 @@ @@ -9,40 +9,48 @@
9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName'; 9 import { DeviceName } from '/@/views/visual/commonComponents/DeviceName';
10 import { unref } from 'vue'; 10 import { unref } from 'vue';
11 import { onMounted } from 'vue'; 11 import { onMounted } from 'vue';
  12 + import { useReceiveMessage } from '../../../hook/useReceiveMessage';
  13 + import { buildUUID } from '/@/utils/uuid';
  14 + import { useReceiveValue } from '../../../hook/useReceiveValue';
12 15
13 const props = defineProps<{ 16 const props = defineProps<{
14 config: ComponentPropsConfigType<typeof option>; 17 config: ComponentPropsConfigType<typeof option>;
15 }>(); 18 }>();
16 19
17 - const percentList = ref<any[]>([ 20 + interface PercentType {
  21 + value: number;
  22 + fontColor: string;
  23 + backgroundColor: string;
  24 + unit: string;
  25 + id: string;
  26 + attribute?: string;
  27 + attributeRename?: string;
  28 + deviceId?: string;
  29 + }
  30 +
  31 + const defaultValue: PercentType[] = [
18 { 32 {
19 value: 20, 33 value: 20,
20 fontColor: '#19eff', 34 fontColor: '#19eff',
21 backgroundColor: '#19eff', 35 backgroundColor: '#19eff',
22 - color: '#000',  
23 - fontSize: '16px',  
24 unit: '℃', 36 unit: '℃',
25 - id: 1, 37 + id: buildUUID(),
26 }, 38 },
27 { 39 {
28 value: 66, 40 value: 66,
29 fontColor: '#1E8667', 41 fontColor: '#1E8667',
30 backgroundColor: '#1E8667', 42 backgroundColor: '#1E8667',
31 - color: '#000',  
32 - fontSize: '16px',  
33 unit: '℃', 43 unit: '℃',
34 - id: 2, 44 + id: buildUUID(),
35 }, 45 },
36 { 46 {
37 value: 49, 47 value: 49,
38 fontColor: '#2196F3', 48 fontColor: '#2196F3',
39 backgroundColor: '#2196F3', 49 backgroundColor: '#2196F3',
40 - color: '#000',  
41 - fontSize: '16px',  
42 unit: '℃', 50 unit: '℃',
43 - id: 3, 51 + id: buildUUID(),
44 }, 52 },
45 - ]); 53 + ];
46 54
47 const getDesign = computed(() => { 55 const getDesign = computed(() => {
48 const { persetOption = {}, option } = props.config; 56 const { persetOption = {}, option } = props.config;
@@ -52,65 +60,52 @@ @@ -52,65 +60,52 @@
52 fontColor: presetFontColor, 60 fontColor: presetFontColor,
53 backgroundColor: persetBackgroundColor, 61 backgroundColor: persetBackgroundColor,
54 } = persetOption || {}; 62 } = persetOption || {};
  63 +
55 return { 64 return {
56 - dataSource: dataSource?.map((item) => {  
57 - const { unit, fontColor, fontSize, backgroundColor } = item.componentInfo || {};  
58 - const { attribute, attributeRename } = item; 65 + dataSource: dataSource.map((item) => {
  66 + const { unit, fontColor, backgroundColor } = item.componentInfo || {};
  67 + const { attribute, attributeRename, deviceId } = item;
59 return { 68 return {
60 unit: unit ?? presetUnit, 69 unit: unit ?? presetUnit,
61 fontColor: fontColor ?? presetFontColor, 70 fontColor: fontColor ?? presetFontColor,
62 - fontSize,  
63 backgroundColor: backgroundColor ?? persetBackgroundColor, 71 backgroundColor: backgroundColor ?? persetBackgroundColor,
64 attribute, 72 attribute,
65 attributeRename, 73 attributeRename,
66 - }; 74 + id: deviceId,
  75 + } as PercentType;
67 }), 76 }),
68 }; 77 };
69 }); 78 });
  79 + const percentList = ref(
  80 + props.config.option.dataSource ? unref(getDesign).dataSource : defaultValue
  81 + );
  82 + const { forEachGroupMessage } = useReceiveMessage();
70 83
71 - const newPercentList = ref<any[]>([]);  
72 -  
73 - const updateFn: MultipleDataFetchUpdateFn = (message) => {  
74 - const { data = {} } = message;  
75 - const { dataSource } = unref(getDesign);  
76 - newPercentList.value = dataSource.map((item) => {  
77 - const { attribute, fontSize, attributeRename, fontColor, unit, backgroundColor } = item;  
78 -  
79 - const [latest] = data[attribute] || [];  
80 - const [_timespan, value] = latest || [];  
81 - return {  
82 - value: Number(value),  
83 - name: attributeRename ?? attribute,  
84 - fontSize,  
85 - unit,  
86 - backgroundColor: backgroundColor,  
87 - fontColor: fontColor,  
88 - }; 84 + const { getNumberValue } = useReceiveValue();
  85 + const updateFn: MultipleDataFetchUpdateFn = (message, deviceId, attribute) => {
  86 + forEachGroupMessage(message, deviceId, attribute, (attribute, value) => {
  87 + percentList.value.forEach((item) => {
  88 + if (item.id === deviceId && item.attribute === attribute) {
  89 + item.value = getNumberValue(value);
  90 + }
  91 + });
89 }); 92 });
90 - // console.log(numberList, 'numberList');  
91 }; 93 };
92 94
93 - onMounted(() => {  
94 - newPercentList.value = percentList.value;  
95 - !props.config.option.uuid;  
96 - }); 95 + onMounted(() => {});
97 96
98 useMultipleDataFetch(props, updateFn); 97 useMultipleDataFetch(props, updateFn);
99 -  
100 - // const { getScale } = useComponentScale(props);  
101 </script> 98 </script>
102 99
103 <template> 100 <template>
104 <main class="w-full h-full flex flex-col justify-center"> 101 <main class="w-full h-full flex flex-col justify-center">
105 <DeviceName :config="config" /> 102 <DeviceName :config="config" />
106 103
107 - <div  
108 - v-for="item in newPercentList"  
109 - :key="item.id"  
110 - class="flex flex-col ml-3 mr-3 items-stretch"  
111 - > 104 + <div v-for="item in percentList" :key="item.id" class="flex flex-col ml-3 mr-3 items-stretch">
112 <div class="flex justify-between"> 105 <div class="flex justify-between">
113 - <div class="text-gray-500 text-lg truncate">{{ item.name || '温度' }}</div> 106 + <div class="text-gray-500 text-lg truncate">
  107 + {{ item.attributeRename || item.attribute || '温度' }}
  108 + </div>
114 <span class="text-lg" :style="{ color: item.fontColor }" 109 <span class="text-lg" :style="{ color: item.fontColor }"
115 >{{ item.value }} {{ item.unit }}</span 110 >{{ item.value }} {{ item.unit }}</span
116 > 111 >
@@ -302,7 +302,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => { @@ -302,7 +302,9 @@ export const commonDataSourceSchemas = (): FormSchema[] => {
302 placeholder: '请选择服务', 302 placeholder: '请选择服务',
303 getPopupContainer: () => document.body, 303 getPopupContainer: () => document.body,
304 onChange(value: string, options: ModelOfMatterParams) { 304 onChange(value: string, options: ModelOfMatterParams) {
305 - const command = value ? (options.functionJson.inputData || [])[0].serviceCommand : null; 305 + const command = value
  306 + ? (options.functionJson.inputData || [])[0]?.serviceCommand
  307 + : null;
306 setFieldsValue({ [DataSourceField.COMMAND]: command }); 308 setFieldsValue({ [DataSourceField.COMMAND]: command });
307 }, 309 },
308 }; 310 };
  1 +import { ReceiveGroupMessageType } from '../index.type';
  2 +
  3 +export const useReceiveMessage = () => {
  4 + const forEachGroupMessage = (
  5 + message: ReceiveGroupMessageType,
  6 + deviceId: string,
  7 + attributes: string[],
  8 + Fn: (attribute: string, value: any, timespan: number) => void
  9 + ) => {
  10 + const { data = {} } = message;
  11 + const bindMessage = data[deviceId];
  12 +
  13 + for (const attribute of attributes) {
  14 + const [latest] = bindMessage[attribute] || [];
  15 + const [timespan, value] = latest || [];
  16 + Fn?.(attribute, value, timespan);
  17 + }
  18 + };
  19 +
  20 + return { forEachGroupMessage };
  21 +};
  1 +export const useReceiveValue = () => {
  2 + const getNumberValue = (value: any, defaultValue = 0) => {
  3 + const newValue = isNaN(value) ? defaultValue : Number(value);
  4 + return newValue;
  5 + };
  6 +
  7 + return { getNumberValue };
  8 +};
@@ -31,11 +31,13 @@ export function useSendCommand() { @@ -31,11 +31,13 @@ export function useSendCommand() {
31 params = customCommand.command!; 31 params = customCommand.command!;
32 } 32 }
33 33
  34 + console.log(params);
  35 +
34 // 控制按钮下发命令为0 或 1 36 // 控制按钮下发命令为0 或 1
35 await sendCommandOneway({ 37 await sendCommandOneway({
36 deviceId, 38 deviceId,
37 value: { 39 value: {
38 - params: params, 40 + params: params || null,
39 persistent: true, 41 persistent: true,
40 additionalInfo: { 42 additionalInfo: {
41 cmdType: 'API', 43 cmdType: 'API',
@@ -10,6 +10,7 @@ import { @@ -10,6 +10,7 @@ import {
10 DataFetchUpdateFn, 10 DataFetchUpdateFn,
11 EntityTypeEnum, 11 EntityTypeEnum,
12 MultipleDataFetchUpdateFn, 12 MultipleDataFetchUpdateFn,
  13 + ReceiveGroupMessageType,
13 ReceiveMessageType, 14 ReceiveMessageType,
14 ScopeTypeEnum, 15 ScopeTypeEnum,
15 SubscribeMessageItemType, 16 SubscribeMessageItemType,
@@ -115,6 +116,20 @@ class Subscriber { @@ -115,6 +116,20 @@ class Subscriber {
115 } as ReceiveMessageType; 116 } as ReceiveMessageType;
116 } 117 }
117 118
  119 + getGroupScopeMessage(message: ReceiveMessageType, attribute: string[], deviceId: string) {
  120 + const result = this.getScopeMessage(message, attribute);
  121 +
  122 + return {
  123 + ...result,
  124 + data: {
  125 + [deviceId]: result.data,
  126 + },
  127 + latestValues: {
  128 + [deviceId]: result.latestValues,
  129 + },
  130 + } as ReceiveGroupMessageType;
  131 + }
  132 +
118 trackUpdate(uuid: string, fn: Fn) { 133 trackUpdate(uuid: string, fn: Fn) {
119 if (!uuid || !fn) return; 134 if (!uuid || !fn) return;
120 this.componentUpdateFnMap.set(uuid, fn); 135 this.componentUpdateFnMap.set(uuid, fn);
@@ -144,7 +159,7 @@ class Subscriber { @@ -144,7 +159,7 @@ class Subscriber {
144 const { attributes, fn } = item; 159 const { attributes, fn } = item;
145 try { 160 try {
146 if (!fn) return; 161 if (!fn) return;
147 - fn?.(this.getScopeMessage(message, attributes), attributes); 162 + fn?.(this.getGroupScopeMessage(message, attributes, deviceId), deviceId, attributes);
148 } catch (error) { 163 } catch (error) {
149 console.error(`deviceId: ${deviceId}`); 164 console.error(`deviceId: ${deviceId}`);
150 throw error; 165 throw error;
@@ -253,23 +268,47 @@ export const useMultipleDataFetch = ( @@ -253,23 +268,47 @@ export const useMultipleDataFetch = (
253 props: { config: ComponentPropsConfigType }, 268 props: { config: ComponentPropsConfigType },
254 updateFn: MultipleDataFetchUpdateFn 269 updateFn: MultipleDataFetchUpdateFn
255 ) => { 270 ) => {
256 - const getBindAttributes = computed(() => {  
257 - const attributes = props.config.option.dataSource?.map((item) => item.attribute);  
258 - return [...new Set(attributes)];  
259 - }); 271 + const getDataSourceGroup = computed(() => {
  272 + const { config } = props;
  273 + const { option } = config;
  274 + const { dataSource } = option || {};
260 275
261 - if (!unref(getBindAttributes).length) return; 276 + const group: Record<string, DataSource[]> = {};
262 277
263 - const getDeviceId = computed(() => {  
264 - return props.config.option.dataSource?.at(0)?.deviceId; 278 + dataSource?.forEach((item) => {
  279 + const { deviceId } = item;
  280 + if (group[deviceId]) {
  281 + group[deviceId].push(item);
  282 + } else {
  283 + group[deviceId] = [item];
  284 + }
  285 + });
  286 +
  287 + return group;
265 }); 288 });
266 289
  290 + if (!Object.keys(unref(getDataSourceGroup)).length) return;
  291 + // const getBindAttributes = computed(() => {
  292 + // const attributes = props.config.option.dataSource?.map((item) => item.attribute);
  293 + // return [...new Set(attributes)];
  294 + // });
  295 +
  296 + // if (!unref(getBindAttributes).length) return;
  297 +
  298 + // const getDeviceId = computed(() => {
  299 + // return props.config.option.dataSource?.at(0)?.deviceId;
  300 + // });
  301 +
267 watch( 302 watch(
268 - () => getDeviceId, 303 + () => getDataSourceGroup,
269 () => { 304 () => {
270 - subscriber.trackUpdateGroup(unref(getDeviceId)!, {  
271 - attributes: unref(getBindAttributes),  
272 - fn: updateFn, 305 + Object.keys(unref(getDataSourceGroup)).forEach((key) => {
  306 + const item = unref(getDataSourceGroup)[key];
  307 + const attributes = [...new Set(item.map((item) => item.attribute))];
  308 + subscriber.trackUpdateGroup(key, {
  309 + attributes,
  310 + fn: updateFn,
  311 + });
273 }); 312 });
274 }, 313 },
275 { 314 {
@@ -277,5 +316,5 @@ export const useMultipleDataFetch = ( @@ -277,5 +316,5 @@ export const useMultipleDataFetch = (
277 } 316 }
278 ); 317 );
279 318
280 - return { getDeviceId, getBindAttributes }; 319 + return {};
281 }; 320 };
@@ -162,6 +162,18 @@ export interface ReceiveMessageType { @@ -162,6 +162,18 @@ export interface ReceiveMessageType {
162 latestValues: Record<string, number>; 162 latestValues: Record<string, number>;
163 } 163 }
164 164
  165 +export interface ReceiveGroupMessageType {
  166 + subscriptionId: number;
  167 + errorCode: number;
  168 + errorMsg: Nullable<string>;
  169 + data: {
  170 + string: Record<string, [number, string][]>;
  171 + };
  172 + latestValues: {
  173 + string: Record<string, number>;
  174 + };
  175 +}
  176 +
165 export enum EntityTypeEnum { 177 export enum EntityTypeEnum {
166 DEVICE = 'DEVICE', 178 DEVICE = 'DEVICE',
167 } 179 }
@@ -172,7 +184,11 @@ export enum ScopeTypeEnum { @@ -172,7 +184,11 @@ export enum ScopeTypeEnum {
172 184
173 export type DataFetchUpdateFn = (message: ReceiveMessageType, attr: string) => void; 185 export type DataFetchUpdateFn = (message: ReceiveMessageType, attr: string) => void;
174 186
175 -export type MultipleDataFetchUpdateFn = (message: ReceiveMessageType, attr: string[]) => void; 187 +export type MultipleDataFetchUpdateFn = (
  188 + message: ReceiveGroupMessageType,
  189 + deviceId: string,
  190 + attr: string[]
  191 +) => void;
176 192
177 // 旧 组件key 193 // 旧 组件key
178 // TEXT_COMPONENT_1 = 'text-component-1', 194 // TEXT_COMPONENT_1 = 'text-component-1',
@@ -27,6 +27,8 @@ @@ -27,6 +27,8 @@
27 import { HistoryTrendModal } from './components/HistoryTrendModal'; 27 import { HistoryTrendModal } from './components/HistoryTrendModal';
28 import { useSocket } from '../packages/hook/useSocket'; 28 import { useSocket } from '../packages/hook/useSocket';
29 import { watch } from 'vue'; 29 import { watch } from 'vue';
  30 + import { useRootSetting } from '/@/hooks/setting/useRootSetting';
  31 + import { ThemeEnum } from '/@/enums/appEnum';
30 32
31 const props = defineProps<{ 33 const props = defineProps<{
32 value?: Recordable; 34 value?: Recordable;
@@ -72,12 +74,15 @@ @@ -72,12 +74,15 @@
72 74
73 useSocket(dataSource); 75 useSocket(dataSource);
74 76
  77 + const { getDarkMode } = useRootSetting();
75 watch( 78 watch(
76 getIsSharePage, 79 getIsSharePage,
77 (value) => { 80 (value) => {
78 if (value) { 81 if (value) {
  82 + console.log(unref(getDarkMode));
79 const root = document.querySelector('#app'); 83 const root = document.querySelector('#app');
80 - (root as HTMLDivElement).style.backgroundColor = '#F5F5F5'; 84 + (root as HTMLDivElement).style.backgroundColor =
  85 + unref(getDarkMode) === ThemeEnum.LIGHT ? '#F5F5F5' : '#1b1b1b';
81 } 86 }
82 }, 87 },
83 { immediate: true } 88 { immediate: true }