DeviceDetailDialog.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305
  1. <template>
  2. <el-dialog
  3. v-model="visible"
  4. :title="
  5. (deviceTypeOptions?.find((v: DeviceTypeVO) => v.deviceType == device?.deviceType)
  6. ?.deviceTypeName || '-') + ' - 详细信息'
  7. "
  8. width="700px"
  9. append-to-body
  10. class="large-screen-dialog"
  11. >
  12. <div class="device-detail-content" v-if="device">
  13. <div class="device-detail-header">
  14. <div class="device-icon-xlarge">
  15. <Icon :icon="getDeviceInfo(device).icon" />
  16. </div>
  17. <div class="device-detail-info">
  18. <h3>
  19. {{
  20. deviceTypeOptions?.find((v: DeviceTypeVO) => v.deviceType == device?.deviceType)
  21. ?.deviceTypeName || '-'
  22. }}
  23. </h3>
  24. <p>设备类型: {{ device.deviceType }}</p>
  25. <p>安装位置: {{ device.installPosition }}</p>
  26. <p>
  27. 设备状态:
  28. <el-tag :type="textStatusMap[device.status]">
  29. {{ device.status }}
  30. </el-tag>
  31. </p>
  32. </div>
  33. </div>
  34. <div class="device-data-detail">
  35. <h4>设备数据</h4>
  36. <div class="data-grid" v-if="device.indicatorInfo && device.indicatorInfo?.length">
  37. <div class="data-item" v-for="(item, value) in device.indicatorInfo" :key="value">
  38. <span class="data-label">{{ item.name }}</span>
  39. <span class="data-value">{{ item.value }}</span>
  40. </div>
  41. </div>
  42. <el-empty v-else description="暂无设备数据" :image-size="40" />
  43. </div>
  44. <div class="device-history">
  45. <h4>历史记录</h4>
  46. <div class="history-list" v-if="device.historyInfo && device.historyInfo?.length">
  47. <div class="history-item" v-for="(record, index) in device.historyInfo" :key="index">
  48. <span class="history-time">{{
  49. record.happensAt ? formatToDateTime(record.happensAt) : ''
  50. }}</span>
  51. <span class="history-event">{{ record.content }}</span>
  52. </div>
  53. </div>
  54. <el-empty v-else description="暂无历史记录" :image-size="40" />
  55. </div>
  56. </div>
  57. <template #footer>
  58. <el-button @click="closeDialog" size="large">关闭</el-button>
  59. </template>
  60. </el-dialog>
  61. </template>
  62. <script lang="ts" setup>
  63. import { computed } from 'vue'
  64. import { formatToDateTime } from '@/utils/dateUtil'
  65. interface CommonVo {
  66. name: string
  67. value: string
  68. }
  69. interface HistoryInfoVo {
  70. happensAt: string
  71. content: string
  72. }
  73. interface Device {
  74. deviceType: string
  75. installPosition: string
  76. status: string
  77. indicatorInfo: CommonVo[]
  78. historyInfo: HistoryInfoVo[]
  79. }
  80. interface DeviceTypeVO {
  81. deviceType: string
  82. deviceTypeName: string
  83. displayOrder: number
  84. }
  85. interface Props {
  86. visible: boolean
  87. device: Device | null
  88. deviceTypeOptions?: DeviceTypeVO[]
  89. }
  90. const props = defineProps<Props>()
  91. const emit = defineEmits<{
  92. 'update:visible': [value: boolean]
  93. }>()
  94. const textStatusMap = {
  95. 在线: 'success',
  96. 离线: 'danger',
  97. 警告: 'warning'
  98. }
  99. const deviceTypeMap: Record<
  100. string,
  101. {
  102. name: string
  103. icon: string
  104. color: string
  105. }
  106. > = {
  107. ['health_band']: { name: '健康监测手环', icon: 'mdi:watch-variant', color: '#ff6b6b' },
  108. ['smart_mattress']: { name: '智能床垫', icon: 'mdi:bed-queen', color: '#4ecdc4' },
  109. ['security_camera']: { name: '安防摄像头', icon: 'mdi:cctv', color: '#45aaf2' },
  110. ['blood_pressure_monitor']: {
  111. name: '血压监测仪',
  112. icon: 'mdi:heart-pulse',
  113. color: '#a55eea'
  114. },
  115. ['emergency_button']: {
  116. name: '紧急呼叫按钮',
  117. icon: 'mdi:alarm-light',
  118. color: '#fd9644'
  119. },
  120. ['smoke_sensor']: { name: '烟雾传感器', icon: 'mdi:smoke-detector', color: '#26de81' },
  121. ['water_sensor']: { name: '水浸传感器', icon: 'mdi:water-alert', color: '#26de81' },
  122. ['infrared_sensor']: {
  123. name: '人体红外传感器',
  124. icon: 'mdi:motion-sensor',
  125. color: '#26de81'
  126. },
  127. ['door_sensor']: { name: '门磁传感器', icon: 'mdi:door-closed', color: '#26de81' },
  128. ['gas_sensor']: { name: '燃气传感器', icon: 'mdi:gas-cylinder', color: '#26de81' },
  129. ['temperature_sensor']: {
  130. name: '温度传感器',
  131. icon: 'mdi:thermometer',
  132. color: '#ff6b6b'
  133. },
  134. ['humidity_sensor']: {
  135. name: '湿度传感器',
  136. icon: 'mdi:water-percent',
  137. color: '#48dbfb'
  138. },
  139. ['fall_detection_sensor']: {
  140. name: '跌倒检测传感器',
  141. icon: 'mdi:human-falling',
  142. color: '#ff9ff3'
  143. },
  144. ['pill_box']: {
  145. name: '智能药盒',
  146. icon: 'mdi:pill',
  147. color: '#1dd1a1'
  148. },
  149. ['oxygen_saturation_monitor']: {
  150. name: '血氧监测仪',
  151. icon: 'mdi:heart-pulse',
  152. color: '#a55eea'
  153. },
  154. ['glucose_meter']: {
  155. name: '血糖仪',
  156. icon: 'mdi:needle',
  157. color: '#fed330'
  158. },
  159. ['sleep_radar']: {
  160. name: '睡眠雷达',
  161. icon: 'mdi:radar',
  162. color: '#26de81'
  163. },
  164. ['alarm_controller']: {
  165. name: '报警主机',
  166. icon: 'mdi:shield-home',
  167. color: '#fd9644'
  168. }
  169. }
  170. const visible = computed({
  171. get: () => props.visible,
  172. set: (value) => emit('update:visible', value)
  173. })
  174. const getDeviceInfo = (device: Device) => {
  175. return (
  176. deviceTypeMap[device.deviceType] || {
  177. name: '未知设备',
  178. icon: 'mdi:help-circle-outline',
  179. color: '#a5b1c2'
  180. }
  181. )
  182. }
  183. const closeDialog = () => {
  184. visible.value = false
  185. }
  186. </script>
  187. <style lang="scss" scoped>
  188. $text-light: #fff;
  189. $text-gray: #8a8f98;
  190. $primary-color: #1a73e8;
  191. .device-detail-content {
  192. .device-detail-header {
  193. display: flex;
  194. padding-bottom: 20px;
  195. margin-bottom: 25px;
  196. border-bottom: 1px solid rgb(255 255 255 / 10%);
  197. align-items: center;
  198. gap: 20px;
  199. }
  200. .device-icon-xlarge {
  201. display: flex;
  202. width: 80px;
  203. height: 80px;
  204. background: rgb(26 115 232 / 20%);
  205. border-radius: 16px;
  206. align-items: center;
  207. justify-content: center;
  208. flex-shrink: 0;
  209. :deep(svg),
  210. :deep(span) {
  211. width: 32px !important;
  212. height: 32px !important;
  213. }
  214. }
  215. .device-detail-info {
  216. flex: 1;
  217. h3 {
  218. margin-bottom: 10px;
  219. font-size: 24px;
  220. }
  221. p {
  222. margin-bottom: 5px;
  223. color: $text-gray;
  224. }
  225. }
  226. .device-data-detail {
  227. margin-bottom: 25px;
  228. h4 {
  229. margin-bottom: 15px;
  230. font-size: 18px;
  231. }
  232. .data-grid {
  233. display: grid;
  234. grid-template-columns: repeat(2, 1fr);
  235. gap: 15px;
  236. }
  237. .data-item {
  238. display: flex;
  239. justify-content: space-between;
  240. padding: 12px 15px;
  241. background: rgb(255 255 255 / 5%);
  242. border-radius: 8px;
  243. .data-label {
  244. color: $text-gray;
  245. }
  246. .data-value {
  247. font-weight: 600;
  248. }
  249. }
  250. }
  251. .device-history {
  252. h4 {
  253. margin-bottom: 15px;
  254. font-size: 18px;
  255. }
  256. .history-list {
  257. max-height: 200px;
  258. overflow-y: auto;
  259. }
  260. .history-item {
  261. display: flex;
  262. justify-content: space-between;
  263. padding: 10px 15px;
  264. border-bottom: 1px solid rgb(255 255 255 / 5%);
  265. .history-time {
  266. font-size: 14px;
  267. color: $text-gray;
  268. }
  269. }
  270. }
  271. }
  272. </style>