Jelajahi Sumber

feat(设备详情): 添加全屏查看点位图与呼吸率功能,并优化界面显示;

- 新增全屏模态框组件用于展示设备点位图
- 修改报警提示文字使其更明确
- 调整呼吸率曲线标题字体大小
- 优化设备列表在线数量显示样式
- 修复搜索条件变化时的类型检查问题
- 添加全屏查看按钮及相关样式
- 调整项目名称为ln-web
liujia 2 bulan lalu
induk
melakukan
3db11f69ab

+ 1 - 1
package.json

@@ -1,5 +1,5 @@
 {
-  "name": "vue-project",
+  "name": "ln-web",
   "version": "0.3.9",
   "private": true,
   "type": "module",

+ 1 - 1
src/views/device/detail/components/breathLineChart/index.vue

@@ -40,7 +40,7 @@ onMounted(() => {
         text: '呼吸率曲线', // 主标题
         left: 'center',
         top: 20,
-        textStyle: { fontSize: 16 },
+        textStyle: { fontSize: 14 },
       },
       {
         text: 'BPM: 0 次/分钟', // 实时呼吸率显示

+ 1 - 1
src/views/device/detail/components/deviceAreaConfig/index.vue

@@ -2,7 +2,7 @@
   <a-spin :spinning="spinning">
     <a-alert
       v-if="areaAvailable"
-      message="检测区域范围未配置或数值较小,建议通过设备配置调整参数!"
+      message="检测区域范围未配置或数值较小,请在设备配置调整参数!"
       banner
     />
     <furnitureCard

+ 57 - 0
src/views/device/detail/components/fullViewModal/index.vue

@@ -0,0 +1,57 @@
+<template>
+  <div ref="mod">
+    <a-modal
+      :get-container="() => $refs.mod"
+      :open="props.open"
+      :title="props.title ? props.title + ' 设备实时信息' : ''"
+      @cancel="cancel"
+      :footer="null"
+    >
+      <slot></slot>
+    </a-modal>
+  </div>
+</template>
+
+<script setup lang="ts">
+defineOptions({
+  name: 'FullViewModal',
+})
+
+type Props = {
+  open: boolean
+  title?: string
+}
+const emit = defineEmits<{
+  (e: 'update:open', value: boolean): void
+}>()
+
+const props = withDefaults(defineProps<Props>(), {
+  open: false,
+  title: '',
+})
+
+// 关闭弹窗
+const cancel = () => {
+  emit('update:open', false)
+}
+</script>
+
+<style scoped lang="less">
+:deep(.ant-modal) {
+  max-width: 100%;
+  top: 0;
+  padding-bottom: 0;
+  margin: 0;
+  .ant-modal-content {
+    display: flex;
+    flex-direction: column;
+    height: calc(100vh);
+    width: calc(100vw);
+    border-radius: 0;
+  }
+  .ant-modal-body {
+    flex: 1;
+    gap: 50px;
+  }
+}
+</style>

+ 134 - 24
src/views/device/detail/index.vue

@@ -1,15 +1,18 @@
 <template>
   <a-spin :spinning="spinning">
     <div class="deviceDetail">
-      <info-card title="点位图">
+      <info-card title="实时点位图">
         <template #extra>
-          <a-button type="primary" size="small" @click="roomConfigHandler('area')">
-            区域配置
-          </a-button>
+          <a-space>
+            <a-button type="primary" size="small" @click="roomConfigHandler('area')">
+              区域配置
+            </a-button>
+            <div class="areaZoom"> <FullscreenOutlined @click="openFullView = true" /> </div>
+          </a-space>
         </template>
         <a-alert
           v-if="areaAvailable"
-          message="检测区域范围未配置或数值较小,建议通过设备配置调整参数!"
+          message="检测区域范围未配置或数值较小,请在设备配置调整参数!"
           banner
           style="margin-bottom: 10px"
         />
@@ -83,6 +86,78 @@
         </div>
       </info-card>
 
+      <FullViewModal v-model:open="openFullView" :title="detailState.devName">
+        <div class="fullView">
+          <div class="pointTitle">实时点位图</div>
+          <div
+            class="radarBox"
+            :style="{
+              width: `${detailState?.length || 400}px`,
+              height: `${detailState?.width || 400}px`,
+            }"
+          >
+            <template v-if="targets && Object.keys(targets).length > 0">
+              <template v-for="t in targets" :key="t.id">
+                <div
+                  class="target-dot"
+                  :style="{
+                    position: 'absolute',
+                    width: '18px',
+                    height: '18px',
+                    background: t.id === 0 ? 'red' : t.id === 1 ? 'blue' : 'green',
+                    borderRadius: '50%',
+                    transform: `translate3d(${t.displayX + 200}px, ${-t.displayY + 200}px, 0) translate(-50%, -50%)`,
+                    zIndex: 10,
+                    transition: 'transform 1s linear',
+                    willChange: 'transform',
+                  }"
+                >
+                  <span
+                    style="
+                      color: #fff;
+                      font-size: 12px;
+                      font-weight: 600;
+                      position: absolute;
+                      left: 50%;
+                      top: 50%;
+                      transform: translate(-50%, -50%);
+                      pointer-events: none;
+                    "
+                  >
+                    {{ t.id + 1 }}
+                  </span>
+                </div>
+              </template>
+            </template>
+            <div>
+              <furniture-icon
+                v-for="(item, index) in furnitureItems"
+                :key="index"
+                :icon="item.type"
+                :width="item.width"
+                :height="item.length"
+                :style="{
+                  left: `${item.left}px`,
+                  top: `${item.top}px`,
+                  position: 'absolute',
+                  rotate: `${item.rotate}deg`,
+                  cursor: 'default',
+                  pointerEvents: 'none',
+                }"
+                :draggable="false"
+              />
+            </div>
+          </div>
+
+          <div
+            v-if="furnitureItems && furnitureItems.some((item) => item.type === 'bed')"
+            class="breathLine"
+          >
+            <BreathLineChart :data="breathRpmList"></BreathLineChart>
+          </div>
+        </div>
+      </FullViewModal>
+
       <info-card title="基本信息">
         <template #extra>
           <a-button type="primary" size="small" @click="roomConfigHandler('base')">
@@ -112,7 +187,14 @@
         </info-item>
         <info-item label="归属租户">{{ detailState.tenantName }}</info-item>
         <info-item label="统计信息">
-          <a-button type="link" size="small" @click="viewDeviceHistoryInfo"> 点击查看 </a-button>
+          <a-button
+            v-if="detailState.clientId"
+            type="link"
+            size="small"
+            @click="viewDeviceHistoryInfo"
+          >
+            点击查看
+          </a-button>
         </info-item>
       </info-card>
 
@@ -176,6 +258,8 @@ import deviceConfigDrawer from './components/deviceConfig/index.vue'
 import deviceStatsDrawer from './components/deviceStatsDrawer/index.vue'
 import BreathLineChart from './components/breathLineChart/index.vue'
 import { formatDateTime } from '@/utils'
+import { FullscreenOutlined } from '@ant-design/icons-vue'
+import FullViewModal from './components/fullViewModal/index.vue'
 
 defineOptions({
   name: 'DeviceDetail',
@@ -463,6 +547,8 @@ onUnmounted(() => {
   if (mqttClient) mqttClient.end()
   if (mqttTimeout) clearTimeout(mqttTimeout)
 })
+
+const openFullView = ref(false)
 </script>
 
 <style scoped lang="less">
@@ -478,25 +564,48 @@ onUnmounted(() => {
     border-radius: 10px;
     display: flex;
     flex-direction: row;
+  }
+}
 
-    .radarBox {
-      position: relative;
-      background-image:
-        linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
-        linear-gradient(to right, rgba(0, 0, 0, 0.1) 1px, transparent 1px);
-      background-size: 20px 20px;
-      border: 1px solid rgba(0, 0, 0, 0.8);
-      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
-      overflow: hidden;
-
-      .furniture-item {
-        position: absolute;
-        user-select: none;
-        cursor: move;
-        width: 30px;
-        height: 30px;
-      }
-    }
+.radarBox {
+  position: relative;
+  background-image:
+    linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px),
+    linear-gradient(to right, rgba(0, 0, 0, 0.1) 1px, transparent 1px);
+  background-size: 20px 20px;
+  border: 1px solid rgba(0, 0, 0, 0.8);
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  overflow: hidden;
+
+  .furniture-item {
+    position: absolute;
+    user-select: none;
+    cursor: move;
+    width: 30px;
+    height: 30px;
+  }
+}
+
+.areaZoom {
+  font-size: 16px;
+  font-weight: 600;
+  cursor: pointer;
+  &:hover {
+    color: #1890ff;
+  }
+}
+
+.fullView {
+  margin-top: 50px;
+  .radarBox {
+    margin: auto;
+  }
+
+  .pointTitle {
+    font-size: 16px;
+    font-weight: 600;
+    margin-bottom: 10px;
+    text-align: center;
   }
 }
 
@@ -505,5 +614,6 @@ onUnmounted(() => {
   flex-shrink: 0;
   flex-grow: 1;
   flex-basis: 350px;
+  position: relative;
 }
 </style>

+ 4 - 3
src/views/device/list/index.vue

@@ -52,7 +52,8 @@
         <div class="tableCard-header-title">
           <span>设备列表</span>
           <span class="subtitle"
-            >设备在线数量:{{ onlineDeviceTotal }} / {{ allDeviceTotal }} (台)
+            >设备在线数量:<span style="color: #389e0d">{{ onlineDeviceTotal }}</span> /
+            {{ allDeviceTotal }} (台)
           </span>
         </div>
         <div class="tableCard-header-extra">
@@ -247,8 +248,8 @@ const searchHandler = async () => {
 
 // 搜索条件变化时清空搜索结果
 const clearHandler = (e: InputEvent) => {
-  console.log('clearHandler', e, e.target, e.target?.value)
-  if (!e.target?.value) {
+  console.log('clearHandler', e, e.target, (e.target as HTMLInputElement)?.value)
+  if (!(e.target as HTMLInputElement)?.value) {
     fetchList()
   }
 }