LightMore.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419
  1. <template>
  2. <div class="more-box">
  3. <div class="light-more-top">
  4. <div class="left">
  5. <div class="light-box">
  6. <img
  7. :src="lampData.isOpen ? lampOpenIcon : lampCloseIcon"
  8. alt=""
  9. :style="lampData.isOpen ? { width: '58px', height: '62px' } : ''"
  10. />
  11. </div>
  12. <div class="light-name">{{ $t(`lamp.${lampData.lampStatusText}`) }}</div>
  13. </div>
  14. <div class="right">
  15. <div
  16. class="control"
  17. :ref="setRef"
  18. @click="handleSwitch('isOpen', true)"
  19. >
  20. {{ $t(`common.全开`) }}
  21. </div>
  22. <div
  23. class="control"
  24. :ref="setRef"
  25. @click="handleSwitch('isOpen', false)"
  26. >
  27. {{ $t(`common.全关`) }}
  28. </div>
  29. </div>
  30. </div>
  31. <div
  32. class="light-middle"
  33. v-if="lampData.isOpen"
  34. >
  35. <div class="bright-slider">
  36. <Slider
  37. :min="min"
  38. :max="max"
  39. type="light"
  40. v-model="lampData.brightValue"
  41. isFollow
  42. showValue
  43. suffixNormal
  44. suffix="%"
  45. @onStart="sendEvent(true)"
  46. @onEnd="setLampStatus('brightValue')"
  47. />
  48. </div>
  49. <!-- <div class="temp-slider">
  50. <LampSlider v-model="lampData.colorTempValue" @onEnd="setLampStatus('colorTempValue')" />
  51. </div> -->
  52. </div>
  53. <div
  54. class="divider"
  55. v-if="lampData.isOpen"
  56. >
  57. <img
  58. src="@/assets/svg/line.svg"
  59. alt=""
  60. />
  61. </div>
  62. <div class="light-bottom">
  63. <div
  64. class="item-box"
  65. :class="item.brightValue ? 'light-box-active' : ''"
  66. v-for="(item, index) in lampList"
  67. >
  68. <div class="name">{{ item.localName }}</div>
  69. <div style="width: 100rpx">
  70. <SwitchButton
  71. :loading="allLampStatus[item.id]?.loading"
  72. v-model="item.isOpen"
  73. @change="sigleLampChange('isOpen', item, 'single')"
  74. />
  75. </div>
  76. </div>
  77. </div>
  78. </div>
  79. </template>
  80. <script setup>
  81. import lampCloseIcon from '@/assets/taiguv1/svg/lamp_close_p_icon.svg';
  82. import lampOpenIcon from '@/assets/taiguv1/svg/lamp_open_p_icon.svg';
  83. import Slider from '@/components/slider/Slider.vue';
  84. import SwitchButton from '@/components/switch-button/SwitchButton.vue';
  85. import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue';
  86. import eventBus from '@/utils/eventBus';
  87. import useDeviceControl from '@/hooks/useDeviceControl';
  88. import { customRound } from '@/utils';
  89. import { useStore } from '@/store';
  90. import { type } from 'os';
  91. const store = useStore();
  92. const deviceControl = useDeviceControl();
  93. const min = 0;
  94. const max = 100;
  95. let closeUpdate = false;
  96. // 接收父组件传递的初始状态
  97. const props = defineProps({
  98. lampStatus: {
  99. type: Object,
  100. default: () => {
  101. return {
  102. isOpen: false,
  103. brightValue: 0,
  104. };
  105. },
  106. },
  107. equipList: {
  108. type: Array,
  109. default: () => {
  110. return [];
  111. },
  112. },
  113. });
  114. const controlBtn = ref([]);
  115. const setRef = (el) => {
  116. if (el) {
  117. if (!controlBtn.value.includes(el)) {
  118. controlBtn.value.push(el);
  119. }
  120. }
  121. };
  122. const lampList = ref(props.equipList || []);
  123. const lampData = ref(props.lampStatus);
  124. const allLampStatus = computed(() => store.state.taiguv1.lampSwitchStatus);
  125. // const lampSwitchStatus = computed(() => {
  126. // let statusText = ''
  127. // let switchStatus = false
  128. // let arr = props.equipList.filter(item => item.runStatus == 1)
  129. // if (arr.length == 0) {
  130. // statusText = '全部关闭'
  131. // switchStatus = false
  132. // } else {
  133. // statusText = arr.length > 0 && arr.length < props.equipList.length ? '部分开启' : '全部开启'
  134. // switchStatus = true
  135. // }
  136. // return {
  137. // statusText,
  138. // switchStatus
  139. // }
  140. // })
  141. watch(
  142. () => props.lampStatus,
  143. (newVal, oldVal) => {
  144. if (!newVal || closeUpdate) {
  145. return;
  146. }
  147. // newVal.brightValue = customRound(newVal.brightValue, 20);
  148. lampData.value = { ...newVal };
  149. },
  150. { deep: true } // 添加深度监听
  151. );
  152. watch(
  153. () => props.equipList,
  154. (newVal, oldVal) => {
  155. if (!newVal || closeUpdate) {
  156. return;
  157. }
  158. compareStatus(newVal);
  159. },
  160. { deep: true }
  161. );
  162. // 对比和store中开关状态
  163. const compareStatus = (data) => {
  164. lampList.value = data.map((item) => {
  165. const currentStatus = allLampStatus.value[item.id];
  166. let isOpen = item.brightValue !== 0;
  167. // 基础状态对象
  168. const baseStatus = {
  169. ...item,
  170. isOpen: isOpen,
  171. };
  172. if (!currentStatus) {
  173. return baseStatus;
  174. }
  175. // 如果最后切换状态与当前运行状态相同,重置状态
  176. if (currentStatus.lastSwitchStatus == isOpen) {
  177. store.dispatch('taiguv1/setLampStatus', {
  178. id: item.id,
  179. status: {
  180. loading: false,
  181. lastSwitchStatus: null,
  182. },
  183. });
  184. return baseStatus;
  185. }
  186. // 如果有待处理的切换状态,使用该状态
  187. return {
  188. ...item,
  189. isOpen: currentStatus.lastSwitchStatus ?? isOpen,
  190. };
  191. });
  192. };
  193. // 整体开关
  194. const handleSwitch = (type, value) => {
  195. lampData.value.isOpen = value;
  196. setLampStatus(type);
  197. };
  198. // 单个开关
  199. const sigleLampChange = (type, source, all) => {
  200. if (type == 'isOpen') {
  201. store.dispatch('taiguv1/setLampStatus', {
  202. id: source.id,
  203. status: {
  204. loading: true,
  205. lastSwitchStatus: source.isOpen,
  206. },
  207. });
  208. }
  209. if (all == 'single') {
  210. const params = deviceControl.assemblyLampCommand(source[type], type, source);
  211. deviceControl.sendCommands(params);
  212. }
  213. };
  214. const setLampStatus = (type) => {
  215. if (type == 'isOpen') {
  216. store.dispatch('taiguv1/setLampStatus', {
  217. id: 'all',
  218. status: {
  219. loading: true,
  220. lastSwitchStatus: lampData.value.isOpen,
  221. },
  222. });
  223. lampList.value.forEach((item) => {
  224. item.isOpen = lampData.value.isOpen;
  225. sigleLampChange(type, item, 'all');
  226. });
  227. }else {
  228. lampData.value[type]=customRound(lampData.value[type],20)
  229. }
  230. const params = deviceControl.assemblyLampCommand(lampData.value[type], type, lampList.value);
  231. deviceControl.sendCommands(params, () => {
  232. debouncedSendEvent(false);
  233. });
  234. };
  235. const sendEvent = (close) => {
  236. closeUpdate = close;
  237. eventBus.emit('close_deviece_timer', { close });
  238. };
  239. const debouncedSendEvent = deviceControl.debounce(sendEvent, 1500);
  240. onMounted(() => {
  241. nextTick(() => {
  242. controlBtn.value.forEach((button) => {
  243. button.addEventListener('touchstart', handleTouchStart);
  244. });
  245. });
  246. });
  247. // 添加 touchstart 处理函数
  248. const handleTouchStart = (event) => {
  249. const button = event.currentTarget;
  250. button.classList.add('active');
  251. setTimeout(() => {
  252. button.classList.remove('active');
  253. }, 200);
  254. };
  255. // 添加 onUnmounted 钩子
  256. onUnmounted(() => {
  257. controlBtn.value.forEach((button) => {
  258. button.removeEventListener('touchstart', handleTouchStart);
  259. });
  260. });
  261. </script>
  262. <style lang="scss" scoped>
  263. .more-box {
  264. .light-more-top {
  265. display: flex;
  266. align-items: center;
  267. margin-bottom: 36px;
  268. .left {
  269. margin-right: 36px;
  270. .light-box {
  271. width: 110px;
  272. height: 110px;
  273. background: rgba(255, 255, 255, 0.4);
  274. border-radius: 50%;
  275. text-align: center;
  276. margin-right: 27px;
  277. img {
  278. width: 36px;
  279. height: 36px;
  280. margin: 0 auto;
  281. margin-top: 37px;
  282. }
  283. }
  284. .light-name {
  285. //styleName: Chi/Body Large;
  286. margin-top: 12px;
  287. font-family: PingFang SC;
  288. width: 110px;
  289. font-size: 16px;
  290. font-weight: 300;
  291. line-height: 22px;
  292. letter-spacing: 0.02em;
  293. text-align: center;
  294. color: rgba(0, 20, 40, 1);
  295. }
  296. }
  297. .right {
  298. height: 92px;
  299. display: flex;
  300. flex-direction: column;
  301. justify-content: space-between;
  302. .control {
  303. width: 100px;
  304. height: 40px;
  305. line-height: 40px;
  306. border-radius: 50px;
  307. border-radius: 50px;
  308. background: #e1e1df;
  309. box-shadow: 0px 10px 20px 0px rgba(0, 0, 0, 0.05);
  310. color: #001428;
  311. //styleName: Chi/Body Small;
  312. font-family: PingFang SC;
  313. font-size: 12px;
  314. font-weight: 400;
  315. letter-spacing: 0.04em;
  316. text-align: center;
  317. }
  318. .control.active {
  319. background: rgba(255, 255, 255, 1);
  320. box-shadow: 0px 10px 20px 0px rgba(0, 20, 40, 0.1);
  321. }
  322. }
  323. }
  324. .light-middle {
  325. // height: 100px;
  326. // background: #fff;
  327. .bright-slider {
  328. width: 100%;
  329. height: 62px;
  330. background: rgba(255, 255, 255, 0.6);
  331. margin-bottom: 12px;
  332. border-radius: 12px;
  333. }
  334. .temp-slider {
  335. width: 100%;
  336. height: 58px;
  337. padding: 2px;
  338. border-radius: 12px;
  339. background: var(--White-White-60, rgba(255, 255, 255, 0.6));
  340. margin-bottom: 12px;
  341. }
  342. }
  343. .divider {
  344. height: 24px;
  345. display: flex;
  346. align-items: center;
  347. margin-bottom: 12px;
  348. }
  349. .light-bottom {
  350. // margin-top: 36px;
  351. .item-box {
  352. display: flex;
  353. justify-content: space-between;
  354. align-items: center;
  355. width: 300px;
  356. height: 68px;
  357. box-sizing: border-box;
  358. padding: 20px 24px;
  359. border-radius: 24px 24px 44px 24px;
  360. background: rgba(255, 255, 255, 0.2);
  361. box-shadow: 0px 10px 20px 0px rgba(0, 20, 40, 0.05);
  362. margin-bottom: 12px;
  363. .name {
  364. //styleName: Chi/Body Regular;
  365. font-family: PingFang SC;
  366. font-size: 14px;
  367. font-weight: 400;
  368. line-height: 19px;
  369. letter-spacing: 0.02em;
  370. color: rgba(0, 20, 40, 1);
  371. }
  372. }
  373. .light-box-active {
  374. background: rgba(255, 255, 255, 0.6);
  375. }
  376. }
  377. .switch-btn {
  378. margin-top: 0;
  379. width: 50px !important;
  380. height: 28px !important;
  381. border: none;
  382. }
  383. }
  384. </style>
  385. <style lang="scss">
  386. .more-box {
  387. .van-loading__spinner {
  388. color: $elActiveColor !important;
  389. }
  390. .van-switch__loading {
  391. top: 0;
  392. left: 0;
  393. width: 100%;
  394. height: 100%;
  395. line-height: 1;
  396. }
  397. .van-switch--disabled {
  398. opacity: 0.5;
  399. }
  400. }
  401. </style>