SysWindCard.vue 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. <!-- eslint-disable vue/multi-word-component-names -->
  2. <template>
  3. <!-- 基准的画廊模块,通过不同的 type 展示不同的样式 -->
  4. <div ref="container" class="syswind__container w-full h-full">
  5. <!-- 画廊项的具体内容填充剩余宽度 -->
  6. <div
  7. v-for="item in data"
  8. :key="item.deviceID"
  9. class="syswind__card"
  10. @mouseenter="getTrigger(item.deviceID).onEnter($event)"
  11. @mousemove="getTrigger(item.deviceID).onMove($event)"
  12. @mouseleave="getTrigger(item.deviceID).onLeave()"
  13. >
  14. <!-- 数据解读悬浮按钮(公共组件) -->
  15. <DataInterpretationBtn
  16. v-if="getTrigger(item.deviceID).state.visible"
  17. :x="getTrigger(item.deviceID).state.x"
  18. :y="getTrigger(item.deviceID).state.y"
  19. @click="handleInterpret(item)"
  20. @btn-enter="getTrigger(item.deviceID).onBtnEnter()"
  21. @btn-leave="getTrigger(item.deviceID).onBtnLeave()"
  22. />
  23. <!-- 卡片内容 -->
  24. <div class="syswind__card__title font-bold" :title="getFormattedText(item, config.title)">{{ getFormattedText(item, config.title) }}</div>
  25. <div class="flex items-center justify-between syswind__card__vol_wrapper">
  26. <div class="syswind__card__vol_image" :class="{ overlimit: isVolumnOverLimit(item) }"></div>
  27. <div>
  28. <div
  29. v-for="(ctx, idx) in config.volumns"
  30. :key="`v${idx}`"
  31. class="flex justify-between pl-20px pr-10px syswind__card__vol_space"
  32. :class="{ overlimit_space: isVolumnOverLimit(item) }"
  33. >
  34. <span>{{ getFormattedText(item, ctx.label) }}</span>
  35. <span class="font-bold">{{ getFormattedText(item, ctx.value) }}</span>
  36. </div>
  37. </div>
  38. </div>
  39. <div class="flex items-center justify-between syswind__card__spd_wrapper">
  40. <div class="syswind__card__spd_image" :class="{ overlimit: isSpeedOverLimit(item) }"></div>
  41. <div>
  42. <div
  43. v-for="(ctx, idx) in config.speeds"
  44. :key="`s${idx}`"
  45. class="flex justify-between pl-20px pr-10px syswind__card__spd_space"
  46. :class="{
  47. overlimit_space: isSpeedOverLimit(item),
  48. }"
  49. >
  50. <span>{{ getFormattedText(item, ctx.label) }}</span>
  51. <span class="font-bold">{{ getFormattedText(item, ctx.value) }}</span>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. <!-- 数据解读弹框(公共组件) -->
  57. <DataInterpretationModal
  58. :visible="modalVisible"
  59. :loading="loading"
  60. :streaming-text="streamingText"
  61. :error="error"
  62. :thinking="thinking"
  63. :todos="todos"
  64. :executing-tools="executingTools"
  65. :tool-calls="toolCalls"
  66. @close="handleClose"
  67. />
  68. </div>
  69. </template>
  70. <script lang="ts" setup>
  71. import { defaultTo, inRange, first } from 'lodash-es';
  72. import { getFormattedText } from '../../hooks/helper';
  73. // import { onMounted, ref } from 'vue';
  74. // import { useEventListener, useScroll } from '@vueuse/core';
  75. // import { get } from 'lodash-es';
  76. // import { computed } from 'vue';
  77. import { useDataInterpretationList, DataInterpretationBtn, DataInterpretationModal } from '/@/views/ventAI/dataPicker';
  78. const props = defineProps<{
  79. config: {
  80. title: string;
  81. volumns: { value: string; label: string; lowerLimit: string }[];
  82. speeds: { value: string; label: string; lowerLimit: string; upperLimit: string }[];
  83. prop: string;
  84. };
  85. data: any[];
  86. }>();
  87. // defineEmits(['click']);
  88. // 判断val是否在low、high指定的范围内
  89. const { modalVisible, loading, streamingText, error, thinking, todos, executingTools, toolCalls, getTrigger, handleInterpret, handleClose } =
  90. useDataInterpretationList((item) => ({
  91. device_id: item.deviceID,
  92. device_name: item.strinstallpos,
  93. device_type: 'windrect',
  94. }));
  95. function isOverLimit(val: string, low: string = '', high: string = '') {
  96. const l = parseFloat(low);
  97. const h = parseFloat(high);
  98. const v = parseFloat(val);
  99. return !inRange(defaultTo(v, 0), defaultTo(l, -Infinity), defaultTo(h, Infinity));
  100. }
  101. function isVolumnOverLimit(item) {
  102. const ctx = first(props.config.volumns);
  103. if (!ctx) return false;
  104. return isOverLimit(getFormattedText(item, ctx.value), getFormattedText(item, ctx.lowerLimit));
  105. }
  106. function isSpeedOverLimit(item) {
  107. const ctx = first(props.config.speeds);
  108. if (!ctx) return false;
  109. return isOverLimit(getFormattedText(item, ctx.value), getFormattedText(item, ctx.lowerLimit), getFormattedText(item, ctx.upperLimit));
  110. }
  111. // const container = ref<HTMLElement>();
  112. // const { y, directions } = useScroll(container); // 当前滚动位置(响应式)
  113. // const STEP = 234;
  114. // useEventListener(
  115. // container,
  116. // 'scrollend',
  117. // (event) => {
  118. // console.log('debug rr', event);
  119. // event.preventDefault();
  120. // event.stopPropagation(); // 防止冒泡到外层
  121. // const direction = directions.top ? -1 : 1;
  122. // const targetY = y.value + direction * STEP;
  123. // container.value?.scrollTo({
  124. // top: targetY,
  125. // behavior: 'smooth',
  126. // });
  127. // },
  128. // { passive: false }
  129. // );
  130. </script>
  131. <style lang="less" scoped>
  132. @import '@/design/theme.less';
  133. @import '@/design/theme.less';
  134. @{theme-green} {
  135. .gallery {
  136. --image-gallery_I_2: url(/@/assets/images/themify/deepblue/home-container/configurable/dusthome/gallery_I_2.png);
  137. --image-gallery_I_1: url(/@/assets/images/themify/green/home-container/configurable/dusthome/gallery_I_1.png);
  138. }
  139. }
  140. .syswind__container {
  141. padding: 10px 30px;
  142. display: flex;
  143. flex-wrap: wrap;
  144. justify-content: space-between;
  145. overflow: auto;
  146. scroll-padding-top: 10px;
  147. scroll-snap-type: y mandatory;
  148. }
  149. .syswind__card {
  150. --image-syswind-space-red: url(/@/assets/images/home-container/configurable/preset/syswind-space-red.png);
  151. --image-syswind-space-blue: url(/@/assets/images/home-container/configurable/preset/syswind-space-blue.png);
  152. --image-syswind-space-green: url(/@/assets/images/home-container/configurable/preset/syswind-space-green.png);
  153. --image-syswind-border-blue: url(/@/assets/images/home-container/configurable/preset/syswind-border-blue.png);
  154. --image-syswind-border-green: url(/@/assets/images/home-container/configurable/preset/syswind-border-green.png);
  155. --image-syswind-module-title: url(/@/assets/images/home-container/configurable/preset/syswind-module-title.png);
  156. --image-syswind-module: url(/@/assets/images/home-container/configurable/preset/syswind-module.png);
  157. --image-syswind-icon-blue: url(/@/assets/images/home-container/configurable/preset/syswind-icon-blue.png);
  158. --image-syswind-icon-green: url(/@/assets/images/home-container/configurable/preset/syswind-icon-green.png);
  159. --image-syswind-icon-red: url(/@/assets/images/home-container/configurable/preset/syswind-icon-red.png);
  160. --image-syswind-icon-vol: url(/@/assets/images/home-container/configurable/preset/syswind-icon-vol.svg);
  161. --image-syswind-icon-vol-red: url(/@/assets/images/home-container/configurable/preset/syswind-icon-vol-red.svg);
  162. --image-syswind-icon-spd: url(/@/assets/images/home-container/configurable/preset/syswind-icon-spd.svg);
  163. --image-syswind-icon-spd-red: url(/@/assets/images/home-container/configurable/preset/syswind-icon-spd-red.svg);
  164. position: relative;
  165. background-image: var(--image-syswind-module);
  166. background-repeat: no-repeat;
  167. width: 320px;
  168. // width: 320px;
  169. height: 234px;
  170. margin: 0 0 10px 0;
  171. scroll-padding-top: 10px;
  172. scroll-snap-align: start;
  173. .syswind__card__title {
  174. background-image: var(--image-syswind-module-title);
  175. width: 250px;
  176. height: 24px;
  177. padding-left: 10px;
  178. padding-right: 20px;
  179. font-size: 16px;
  180. background-size: 100% 100%;
  181. overflow: hidden; /* 隐藏溢出内容 */
  182. white-space: nowrap; /* 强制文本不换行 */
  183. text-overflow: ellipsis; /* 显示省略号 */
  184. }
  185. .syswind__card__vol_image {
  186. background-image: var(--image-syswind-icon-green), var(--image-syswind-icon-vol);
  187. background-position: center, center;
  188. background-repeat: no-repeat;
  189. width: 76px;
  190. height: 70px;
  191. }
  192. .syswind__card__spd_image {
  193. background-image: var(--image-syswind-icon-blue), var(--image-syswind-icon-spd);
  194. background-position: center, center;
  195. background-repeat: no-repeat;
  196. width: 76px;
  197. height: 70px;
  198. }
  199. .syswind__card__vol_space {
  200. background-image: var(--image-syswind-space-green);
  201. background-repeat: no-repeat;
  202. background-position: bottom left;
  203. width: 208px;
  204. height: 28px;
  205. line-height: 28px;
  206. }
  207. .syswind__card__spd_space {
  208. background-image: var(--image-syswind-space-blue);
  209. background-repeat: no-repeat;
  210. background-position: bottom left;
  211. width: 208px;
  212. height: 28px;
  213. line-height: 28px;
  214. }
  215. .syswind__card__vol_wrapper {
  216. background-image: var(--image-syswind-border-green);
  217. height: 72px;
  218. background-position: right center;
  219. background-repeat: no-repeat;
  220. margin: 10px;
  221. padding-right: 10px;
  222. .overlimit {
  223. background-image: var(--image-syswind-icon-red), var(--image-syswind-icon-vol-red);
  224. }
  225. }
  226. .syswind__card__spd_wrapper {
  227. background-image: var(--image-syswind-border-blue);
  228. height: 100px;
  229. background-position: right center;
  230. background-repeat: no-repeat;
  231. margin: 10px;
  232. padding-right: 10px;
  233. .overlimit {
  234. background-image: var(--image-syswind-icon-red), var(--image-syswind-icon-spd-red);
  235. }
  236. }
  237. .overlimit_space {
  238. background-image: var(--image-syswind-space-red);
  239. }
  240. }
  241. </style>