You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

351 lines
9.8 KiB

3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
3 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
2 months ago
3 months ago
2 months ago
  1. <script setup lang="ts">
  2. import { pauseCraft, resumeCraft, stopCraft } from 'apis/crafts'
  3. import { trayTube } from 'apis/home'
  4. import errorIcon from 'assets/images/error.svg'
  5. import ingIcon from 'assets/images/ing.svg'
  6. import successIcon from 'assets/images/success.svg'
  7. import waitIcon from 'assets/images/wait.svg'
  8. import CountDown from 'components/common/Countdown.vue'
  9. import { useHomeStore } from 'stores/homeStore'
  10. import { useSystemStore } from 'stores/systemStore'
  11. import { computed, ref } from 'vue'
  12. const props = withDefaults(defineProps<{ data: System.HeatArea }>(), {
  13. data: () => ({
  14. moduleCode: 'heat_module_01',
  15. trayUp: 1,
  16. trayStatus: 0,
  17. fanOpen: false,
  18. heating: false,
  19. heatingType: 'constant',
  20. capExist: false,
  21. temperature: 0,
  22. targetTemperature: 0,
  23. }),
  24. })
  25. const emits = defineEmits(['selectChange', 'setTemperature'])
  26. const homeStore = useHomeStore()
  27. const systemStore = useSystemStore()
  28. const mousedownHandle = async (e: Event) => {
  29. let event
  30. if ('touches' in e) {
  31. event = (e.touches as TouchList)[0]
  32. }
  33. else {
  34. event = e
  35. }
  36. if (event.target!.classList!.contains('tube-inner')) {
  37. const num = event.target!.getAttribute('index')
  38. await trayTube({
  39. trayUuid: tray.value!.uuid,
  40. tubes: [
  41. {
  42. tubeNum: Number(num),
  43. exists: !tray.value?.tubes.find(t => t.tubeNum === Number(num))?.exists,
  44. },
  45. ],
  46. })
  47. }
  48. }
  49. const activeTubeBox = ref(false)
  50. const tubeSelect = () => {
  51. emits('selectChange')
  52. }
  53. const hearInfo = computed(() => {
  54. return homeStore.heatAreaList.find(item => item.value === props.data.moduleCode)
  55. })
  56. const craft = computed(() => {
  57. return systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode)?.crafts
  58. })
  59. const craftSteps = computed(() => {
  60. const steps = systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode)?.crafts?.craft?.steps
  61. return steps ? JSON.parse(steps) : undefined
  62. })
  63. console.log(craftSteps.value)
  64. const tray = computed(() => {
  65. return systemStore.systemStatus.tray?.find(item => item.heatModuleId === props.data.moduleCode)
  66. })
  67. const pauseCraftHandle = async () => {
  68. await pauseCraft({
  69. heatId: props.data.moduleCode,
  70. })
  71. }
  72. const resumeCraftHandle = async () => {
  73. await resumeCraft({
  74. heatId: props.data.moduleCode,
  75. })
  76. }
  77. const stopCraftHandle = async () => {
  78. await stopCraft({
  79. heatId: props.data.moduleCode,
  80. })
  81. }
  82. defineExpose({
  83. activeTubeBox,
  84. })
  85. </script>
  86. <template>
  87. <div class="tube" :class="{ 'tube-active': hearInfo?.selected, 'tube-shadow': data.trayUp === 1 }">
  88. <div class="header">
  89. <span>{{ hearInfo?.label }}</span>
  90. <el-tag v-show="!data.trayStatus" type="info">
  91. 空置
  92. </el-tag>
  93. <el-tag v-show="data.trayStatus" type="success">
  94. 已放置
  95. </el-tag>
  96. </div>
  97. <div class="tube-item" :class="{ 'tube-item-heat': ['warm_up', 'thermostatic'].includes(data.heatingType), 'tube-item-constant': data.heatingType === 'constant', 'tube-item-fan': data.fanOpen }" @click.prevent="mousedownHandle" @touch.prevent="mousedownHandle">
  98. <div v-if="data.trayStatus === 0" class="tube-disable" />
  99. <div
  100. v-if="craft?.state"
  101. class="status" :class="{
  102. 'status-success': craft?.state === 'FINISHED',
  103. 'status-wait': craft?.state === 'READY',
  104. 'status-PAUSED': craft?.state === 'PAUSED',
  105. 'status-error': craft?.state === 'ERROR',
  106. 'status-ing': craft?.state === 'RUNNING',
  107. }"
  108. >
  109. <img v-if="craft?.state === 'FINISHED'" :src="successIcon" alt="">
  110. <img v-if="craft?.state === 'RUNNING'" :src="ingIcon" alt="">
  111. <img v-if="craft?.state === 'READY'" :src="waitIcon" alt="">
  112. <img v-if="craft?.state === 'PAUSED'" :src="waitIcon" alt="">
  113. <img v-if="craft?.state === 'ERROR'" :src="errorIcon" alt="">
  114. <span class="status-name">{{ craft?.craft?.name || ' ' }}</span>
  115. <span v-if="craft?.state === 'RUNNING'" class="status-text">工艺执行中</span>
  116. <span v-if="craft?.state === 'READY'" class="status-text">工艺等待执行</span>
  117. <span v-if="craft?.state === 'PAUSED'" class="status-text">工艺已暂停</span>
  118. <span v-if="craft?.state === 'ERROR'" class="status-text">工艺执行错误</span>
  119. <span v-if="craft?.state === 'FINISHED'" class="status-text">工艺执行完毕</span>
  120. <el-tooltip v-if="craft?.state === 'RUNNING' && craftSteps && craftSteps[craft.currentIndex || 0]?.params?.description" :content="`${craftSteps[craft.currentIndex || 0].params.description}`" placement="top" trigger="click">
  121. <div class="status-description">
  122. {{ craftSteps[craft.currentIndex || 0].params.description }}
  123. </div>
  124. </el-tooltip>
  125. <div class="status-operation">
  126. <ft-button v-if="craft?.state === 'RUNNING'" type="primary" size="small" :click-handle="pauseCraftHandle">
  127. 暂停
  128. </ft-button>
  129. <ft-button v-if="craft?.state === 'PAUSED'" type="primary" size="small" :click-handle="resumeCraftHandle">
  130. 继续
  131. </ft-button>
  132. <ft-button v-if="['RUNNING', 'PAUSED'].includes(craft?.state)" size="small" :click-handle="stopCraftHandle">
  133. 停止
  134. </ft-button>
  135. <span v-if="craft?.state === 'FINISHED'">请取走托盘</span>
  136. </div>
  137. </div>
  138. <span v-for="item in 16" :key="item" class="tube-inner" :class="{ 'tube-inner-active': tray?.tubes.find(tu => tu.tubeNum === item)?.exists }" :index="item" />
  139. </div>
  140. <div class="temperature-box">
  141. <span>
  142. <span>当前温度: </span>
  143. <span>{{ data.temperature || '--' }}</span>
  144. <span></span>
  145. </span>
  146. <span v-show="data.heatingType === 'warm_up'" style="color: #FF4500;font-weight: bold ">预热中</span>
  147. <span v-show="data.heatingType === 'thermostatic'" style="color: #FF4500;;font-weight: bold ">加热中</span>
  148. <span v-show="data.heatingType === 'constant'" style="color: #eccbb6;font-weight: bold ">恒温中</span>
  149. <span v-show="data.fanOpen" style="color: #6CD3FF;font-weight: bold ">降温中</span>
  150. </div>
  151. <CountDown v-if="data.startHeatTime && data.targetTime" :current="new Date().getTime()" :start-time="data.startHeatTime" :duration="data.targetTime" />
  152. <div class="footer">
  153. <div class="tem-box">
  154. <span :class="{ 'active-footer': hearInfo?.selected }"> {{ data.targetTemperature || '--' }}</span>
  155. </div>
  156. <ft-button size="small" :type="hearInfo?.selected ? 'primary' : 'default'" @click="tubeSelect">
  157. 选择
  158. </ft-button>
  159. </div>
  160. </div>
  161. </template>
  162. <style scoped lang="scss">
  163. .tube-active {
  164. border-color: #275EFB !important;
  165. }
  166. .tube-shadow {
  167. box-shadow: 0 0 10px rgba(0,0,0,0.9);
  168. }
  169. .tube {
  170. box-sizing: border-box;
  171. width: 100%;
  172. height: 95%;
  173. background: #E9F3FF;
  174. border-radius: 10px;
  175. padding: 10px;
  176. font-size: 14px;
  177. display: flex;
  178. flex-direction: column;
  179. justify-content: space-between;
  180. border: 2px solid #E9F3FF;
  181. transition: all 0.3s;
  182. position: relative;
  183. overflow: hidden;
  184. .header {
  185. display: flex;
  186. justify-content: space-between;
  187. align-items: center;
  188. color: #4D6882;
  189. }
  190. .tube-item-heat {
  191. background: #FF4500 !important;
  192. }
  193. .tube-item-fan {
  194. background: #6CD3FF !important;
  195. }
  196. .tube-item-constant {
  197. background: #F0E5DE !important;
  198. }
  199. .tube-item {
  200. padding: 5px;
  201. background: #384D5D;
  202. border-radius: 10px;
  203. display: grid;
  204. grid-template-columns: repeat(4, 1fr);
  205. grid-template-rows: repeat(4, 1fr);
  206. grid-gap: 5px;
  207. position: relative;
  208. .tube-disable {
  209. position: absolute;
  210. width: 100%;
  211. height: 100%;
  212. top: 0;
  213. left: 0;
  214. background: rgba(255,255,255,0.9);
  215. border-radius: 9px;
  216. }
  217. .tube-inner {
  218. display: inline-block;
  219. width: 25px;
  220. height: 25px;
  221. border-radius: 50%;
  222. background: #fff;
  223. margin: 2px;
  224. transition: background 0.5s;
  225. }
  226. .tube-inner-active {
  227. background: #26D574;
  228. border: 1px solid #fff;
  229. }
  230. }
  231. .temperature-box {
  232. display: flex;
  233. justify-content: space-between;
  234. background: #fff;
  235. padding: 5px;
  236. border-radius: 5px;
  237. font-size: 12px;
  238. }
  239. .footer {
  240. display: flex;
  241. justify-content: space-between;
  242. align-items: center;
  243. font-weight: bold;
  244. color: #4D6882;
  245. .active-footer {
  246. color: #1562B7;
  247. }
  248. .ft-button {
  249. margin-right: 0;
  250. }
  251. }
  252. }
  253. .status {
  254. position: absolute;
  255. top: 0;
  256. left: 0;
  257. width: 100%;
  258. height: 100%;
  259. background: rgba(255,255,255,0.9);
  260. z-index: 1;
  261. display: flex;
  262. flex-direction: column;
  263. justify-content: flex-start;
  264. align-items: center;
  265. border-radius: 10px;
  266. img {
  267. width: 22px;
  268. margin: 5px 0;
  269. }
  270. .status-name {
  271. font-size: 14px;
  272. }
  273. .status-text {
  274. font-size: 16px;
  275. font-weight: 700;
  276. }
  277. .status-operation {
  278. width: 100%;
  279. margin-top: auto;
  280. margin-bottom: 10px;
  281. display: flex;
  282. justify-content: space-around;
  283. .ft-button {
  284. margin-right: 5px;
  285. }
  286. }
  287. }
  288. .status-success {
  289. border: 1px solid #14A656;
  290. }
  291. .status-wait {
  292. background: rgba(242,235,231, 0.9);
  293. border: 1px solid #EE8223;
  294. color: #EE8223;
  295. }
  296. .status-PAUSED {
  297. background: rgba(242,235,231, 0.9);
  298. border: 1px solid #EE8223;
  299. color: #EE8223;
  300. }
  301. .status-error {
  302. background: rgba(232,212,222, 0.9);
  303. border: 1px solid #DF1515;
  304. color: #DF1515;
  305. }
  306. .status-ing {
  307. background: rgba(205,223,255, 0.9);
  308. border: 1px solid #0256FF;
  309. color: #0256FF;
  310. }
  311. .tem-box {
  312. display: flex;
  313. align-items: center;
  314. .el-icon {
  315. margin-left: 5px;
  316. }
  317. }
  318. .status-description {
  319. white-space: nowrap;
  320. overflow: hidden;
  321. text-overflow: ellipsis;
  322. max-width: 95%;
  323. padding: 0 3px;
  324. display: inline-block;
  325. background: #fff;
  326. border-radius: 2px;
  327. margin: 2px 0;
  328. }
  329. </style>