DeviceControl.vue 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. <script setup lang="ts">
  2. import { computed, onUnmounted, ref, watch } from 'vue';
  3. import { useRequest } from '@/hooks/request';
  4. import { t } from '@/i18n';
  5. import { getGroupModuleInfo } from '@/api';
  6. import { addUnit } from '@/utils';
  7. import { VisualModuleType } from '@/constants';
  8. import AdvancedSettings from './AdvancedSettings.vue';
  9. import AIOptimization from './AIOptimization.vue';
  10. import AIStartStop from './AIStartStop.vue';
  11. import type { Component, CSSProperties } from 'vue';
  12. import type { GroupModuleInfo } from '@/types';
  13. interface Props {
  14. info: GroupModuleInfo;
  15. }
  16. const props = defineProps<Props>();
  17. const { handleRequest } = useRequest();
  18. const moduleInfo = ref<GroupModuleInfo>(props.info);
  19. let moduleTimer: number | undefined;
  20. const getModuleInfo = () => {
  21. clearTimeout(moduleTimer);
  22. handleRequest(async () => {
  23. moduleInfo.value = await getGroupModuleInfo({
  24. groupId: props.info.groupId,
  25. moduleType: VisualModuleType.Module2D,
  26. });
  27. });
  28. moduleTimer = window.setTimeout(getModuleInfo, 3000);
  29. };
  30. interface ConfigItem {
  31. label: string;
  32. component: Component;
  33. style?: CSSProperties;
  34. }
  35. const activeConfigIndex = ref(-1);
  36. const configs = computed<ConfigItem[]>(() => {
  37. return [
  38. { label: t('realTimeMonitor.aiStartStop'), component: AIStartStop },
  39. { label: t('realTimeMonitor.aiOptimization'), component: AIOptimization },
  40. {
  41. label: t('common.advancedSettings'),
  42. component: AdvancedSettings,
  43. style: {
  44. paddingBlock: addUnit(16),
  45. },
  46. },
  47. ];
  48. });
  49. const showCtrlPanel = computed(() => {
  50. return activeConfigIndex.value !== -1;
  51. });
  52. watch(activeConfigIndex, () => {
  53. // 仅打开智能启停面板时定时获取数据
  54. if (activeConfigIndex.value !== 0) {
  55. clearTimeout(moduleTimer);
  56. } else {
  57. getModuleInfo();
  58. }
  59. });
  60. const toggleConfig = (index: number) => {
  61. if (activeConfigIndex.value === index) {
  62. closeCtrlPanel();
  63. } else {
  64. activeConfigIndex.value = index;
  65. }
  66. };
  67. const closeCtrlPanel = () => {
  68. activeConfigIndex.value = -1;
  69. };
  70. onUnmounted(() => {
  71. clearTimeout(moduleTimer);
  72. });
  73. defineExpose({
  74. closeCtrlPanel,
  75. });
  76. </script>
  77. <template>
  78. <div class="ctrl-button-conatiner top-1/2 flex flex-col -translate-y-1/2" @click.stop>
  79. <div
  80. :class="['ctrl-button', { 'ctrl-button-active': activeConfigIndex === index }]"
  81. v-for="(item, index) in configs"
  82. :key="index"
  83. :style="item.style"
  84. @click="toggleConfig(index)"
  85. >
  86. <div class="corner-border lt-border"></div>
  87. <div class="corner-border rt-border"></div>
  88. <div class="corner-border lb-border"></div>
  89. <div class="corner-border rb-border"></div>
  90. {{ item.label }}
  91. </div>
  92. <div class="ctrl-panel" v-if="showCtrlPanel">
  93. <div class="ctrl-panel-title">{{ configs[activeConfigIndex].label }}</div>
  94. <component :is="configs[activeConfigIndex].component" :info="moduleInfo" />
  95. </div>
  96. </div>
  97. </template>
  98. <style lang="scss" scoped>
  99. .ctrl-button-conatiner {
  100. position: fixed;
  101. right: 16px;
  102. margin-inline: 16px;
  103. }
  104. .ctrl-button {
  105. position: relative;
  106. width: 32px;
  107. height: 96px;
  108. padding: 8px;
  109. font-size: 14px;
  110. line-height: 16px;
  111. color: #fff;
  112. cursor: pointer;
  113. background-color: #323a47;
  114. border: 1px solid rgb(134 141 152 / 40%);
  115. box-shadow: inset 0 0 3px 0 rgb(255 255 255 / 24%);
  116. & + & {
  117. margin-top: 16px;
  118. }
  119. }
  120. .ctrl-button-active {
  121. background-color: var(--antd-color-primary);
  122. border: 1px solid rgb(255 255 255 / 50%);
  123. box-shadow: inset 0 0 6px 1px rgb(255 255 255 / 70%);
  124. .corner-border {
  125. --corner-border-color: #fff;
  126. }
  127. }
  128. .ctrl-panel {
  129. position: absolute;
  130. top: -147px;
  131. right: 48px;
  132. width: 464px;
  133. height: 630px;
  134. padding-block: 24px;
  135. background: rgb(30 37 48 / 50%);
  136. backdrop-filter: blur(12px);
  137. border: 1px solid rgb(255 255 255 / 24%);
  138. border-radius: 8px;
  139. }
  140. .ctrl-panel-title {
  141. margin-bottom: 16px;
  142. margin-left: 24px;
  143. font-size: 16px;
  144. font-weight: 500;
  145. line-height: 22px;
  146. color: #fff;
  147. }
  148. .ctrl-panel-scroll-container {
  149. height: calc(100% - 38px);
  150. padding-left: 24px;
  151. overflow: hidden;
  152. }
  153. :deep(.ctrl-panel-scroll-content) {
  154. height: calc(100% - 52px);
  155. padding-right: 20px;
  156. margin-right: 4px;
  157. overflow: hidden auto;
  158. &::-webkit-scrollbar {
  159. width: 6px;
  160. height: 6px;
  161. }
  162. &::-webkit-scrollbar-thumb {
  163. cursor: pointer;
  164. background: #55585e;
  165. border-radius: 3px;
  166. }
  167. &::-webkit-scrollbar-corner {
  168. display: none;
  169. }
  170. }
  171. :deep(.ant-segmented) {
  172. padding: 4px;
  173. margin-bottom: 16px;
  174. color: #fff;
  175. background-color: rgb(255 255 255 / 8%);
  176. border-radius: 4px;
  177. .ant-segmented-group > div {
  178. background-color: var(--antd-color-primary);
  179. }
  180. .ant-segmented-item-label {
  181. display: flex;
  182. align-items: center;
  183. padding-inline: 0;
  184. span {
  185. padding-inline: 12px;
  186. }
  187. }
  188. .ant-segmented-item-selected {
  189. color: #fff;
  190. background-color: var(--antd-color-primary);
  191. box-shadow: none;
  192. }
  193. .ant-segmented-item {
  194. + .ant-segmented-item {
  195. margin-left: 8px;
  196. }
  197. &:hover:not(.ant-segmented-item-selected, .ant-segmented-item-disabled) {
  198. color: #fff;
  199. &::after {
  200. background-color: var(--antd-color-primary-opacity-15);
  201. }
  202. }
  203. }
  204. .ant-segmented-item-disabled,
  205. .ant-segmented-item-disabled:hover,
  206. .ant-segmented-item-disabled:focus {
  207. color: rgb(255 255 255 / 25%);
  208. }
  209. }
  210. :deep(.ant-switch) {
  211. background: rgb(255 255 255 / 25%);
  212. &:hover:not(.ant-switch-disabled) {
  213. background: rgb(255 255 255 / 45%);
  214. }
  215. &.ant-switch-checked {
  216. background: var(--antd-color-primary);
  217. &:hover:not(.ant-switch-disabled) {
  218. background: var(--antd-color-primary-hover);
  219. }
  220. }
  221. }
  222. </style>