yanghao 18 часов назад
Родитель
Сommit
8d0a0ac25c

BIN
src/assets/imgs/green.png


BIN
src/assets/imgs/red.png


BIN
src/assets/imgs/导航按钮.png


+ 3 - 1
src/router/modules/remaining.ts

@@ -1810,7 +1810,9 @@ const remainingRouter: AppRouteRecordRaw[] = [
         meta: {
           title: '设备看板',
           noCache: true,
-          hidden: true
+          hidden: true,
+          canTo: true,
+          activeMenu: '/kanban/monitor/kanban'
         },
         component: () => import('@/views/pms/monitor/kanban.vue')
       },

+ 52 - 2
src/views/pms/monitor/data-row.vue

@@ -8,7 +8,14 @@
   >
     <span class="text-cyan-200">{{ label }}</span>
     <div class="flex items-center gap-2">
-      <span class="font-mono text-white font-bold">{{ value }}</span>
+      <!-- 对于运行状态类的字段,显示状态指示器 -->
+      <template v-if="isStatusField(label)">
+        <span class="flex items-center">
+          <span :class="['w-2 h-2 rounded-full inline-block mr-1', getStatusClass(value)]"></span>
+          {{ getStatusText(value) }}
+        </span>
+      </template>
+      <span v-else class="font-mono text-white font-bold">{{ formattedValue }}</span>
       <button
         v-if="button"
         style="border-radius: 5px"
@@ -34,7 +41,50 @@ interface Props {
   compact?: boolean
 }
 
-withDefaults(defineProps<Props>(), {
+const props = withDefaults(defineProps<Props>(), {
   compact: false
 })
+
+// 判断是否为状态字段
+const isStatusField = (label: string) => {
+  return (
+    label.includes('运行状态') ||
+    label.includes('加载状态') ||
+    label.includes('PSA运行状态') ||
+    label.toLowerCase().includes('status')
+  )
+}
+
+// 获取状态类名
+const getStatusClass = (statusValue: string | number) => {
+  return statusValue === '1' || statusValue === 1 || statusValue === 'true'
+    ? 'status-active'
+    : 'status-inactive'
+}
+
+// 获取状态文本
+const getStatusText = (statusValue: string | number) => {
+  return statusValue === '1' || statusValue === 1 || statusValue === 'true' ? '运行中' : '停止'
+}
+
+// 格式化非状态字段的值
+const formattedValue = computed(() => {
+  // 如果是状态字段,则不显示原始值
+  if (isStatusField(props.label)) {
+    return ''
+  }
+  return props.value
+})
 </script>
+
+<style scoped>
+.status-active {
+  background: rgb(34, 197, 94); /* 绿色 */
+  box-shadow: 0 0 8px rgba(34, 197, 94, 0.8);
+}
+
+.status-inactive {
+  background: rgb(239, 68, 68); /* 红色 */
+  box-shadow: 0 0 8px rgba(239, 68, 68, 0.8);
+}
+</style>

+ 1 - 1
src/views/pms/monitor/index.vue

@@ -119,7 +119,7 @@ const deptList = ref<Tree[]>([]) // 树形结构
 import { useRouter } from 'vue-router'
 const router = useRouter()
 
-defineOptions({ name: 'IotDeviceComplete' })
+defineOptions({ name: 'IotDeviceMonitor' })
 
 const loading = ref(true) // 列表的加载中
 

+ 57 - 32
src/views/pms/monitor/kanban.vue

@@ -1,28 +1,25 @@
 <template>
   <div
-    class="bg-[#08092d] text-cyan-100 p-4 relative h-screen overflow-hidden flex flex-col font-mono"
+    class="bg-[#08092d] text-cyan-100 p-4 pt-0 relative h-screen overflow-hidden flex flex-col font-mono"
     :class="{ 'fullscreen-layout': isFullscreen }"
   >
-    <!-- Animated background grid -->
-    <div class="absolute inset-0 opacity-20">
-      <div class="absolute inset-0 grid-pattern"></div>
-    </div>
-
-    <!-- Scanning line effect -->
-    <div class="absolute inset-0 pointer-events-none">
-      <div class="scan-line"></div>
-    </div>
-
+    <!-- Header -->
     <!-- Header -->
     <div class="relative z-10 mb-2 flex-shrink-0">
-      <div class="flex items-center justify-center border-b-2 border-cyan-500/30 pb-2 gap-8">
-        <h1 class="text-xl font-bold text-cyan-300 flex-6 tracking-wider">
+      <div class="header-bg relative flex items-center justify-center py-3">
+        <div class="absolute left-10 top-1/2 -translate-y-1/2 flex items-center gap-4 text-sm pt-2">
+          <div class="return-btn" @click="goBack">
+            <Icon icon="ep:back" /> <span class="pl-2 font-bold">返回</span>
+          </div>
+        </div>
+        <!-- 标题居中 -->
+        <h1 class="text-3xl font-bold text-[#ffac31] tracking-wider text-center">
           {{ dataInfo.groupName }}
         </h1>
 
-        <div class="flex flex-4 items-center gap-8 text-sm">
+        <!-- 操作按钮绝对定位到右侧 -->
+        <div class="absolute right-4 top-1/2 -translate-y-1/2 flex items-center gap-4 text-sm pt-1">
           <div
-            style="border: 0.5px solid #085b77"
             class="px-4 py-1.5 bg-cyan-500/10 border border-cyan-500/30 skew-x-[-12deg] hover:bg-cyan-500/20 transition-all cursor-pointer"
           >
             <span class="pr-4">部门名称</span>
@@ -55,7 +52,7 @@
 
         <div class="flex-1 min-h-0 flex flex-col">
           <div
-            class="flex bg-[#08092d] justify-between items-center panel-title mb-1 flex-shrink-0 pl-5"
+            class="flex bg-[#08092d] justify-between items-center panel-title mb-1 flex-shrink-0 pl-[5%]"
           >
             <h4 class="text-sm pt-1">PSA核心参数</h4>
           </div>
@@ -72,7 +69,7 @@
 
         <!-- 空气处理参数 -->
         <div class="flex-1 min-h-0 flex flex-col">
-          <div class="flex justify-between items-center panel-title mb-1 flex-shrink-0 pl-5">
+          <div class="flex justify-between items-center panel-title mb-1 flex-shrink-0 pl-[5%]">
             <h4 class="text-sm pt-1">空气处理撬参数</h4>
           </div>
           <div class="space-y-1 panel overflow-y-auto overflow-x-hidden flex-1 min-h-0">
@@ -88,7 +85,7 @@
 
         <!-- 1800中压机参数 -->
         <div class="flex-1 min-h-0 flex flex-col">
-          <div class="flex justify-between items-center panel-title flex-shrink-0 pl-5">
+          <div class="flex justify-between items-center panel-title flex-shrink-0 pl-[5%]">
             <h4 class="text-sm pt-1">中压机参数</h4>
           </div>
           <div class="space-y-1 panel px-6 overflow-y-auto overflow-x-hidden flex-1 min-h-0">
@@ -104,18 +101,19 @@
       </div>
 
       <div class="col-span-6 flex flex-col gap-3 min-h-0">
-        <div class="panel relative overflow-hidden flex-1 min-h-0">
+        <!-- 模型查看器 - 固定高度 -->
+        <div class="panel relative overflow-hidden flex-shrink-0" style="height: 30vh">
           <div class="relative h-full flex items-center justify-center">
             <ModelViewer />
           </div>
         </div>
 
-        <!-- 1050空压机参数 -->
-        <div class="flex-shrink-0">
-          <div class="flex justify-between items-center panel-title flex-shrink-0 pl-6">
+        <!-- 空压机参数 - 占满剩余空间 -->
+        <div class="flex-1 flex flex-col min-h-0" style="min-height: 0">
+          <div class="flex justify-between items-center panel-title flex-shrink-0 pl-[5%]">
             <h4 class="text-sm pt-1">空压机参数</h4>
           </div>
-          <div class="grid panel grid-cols-5 gap-2">
+          <div class="grid panel grid-cols-5 gap-2 flex-1 min-h-0" style="overflow-y: auto">
             <div v-for="(compressor, i) in compressorData" :key="i" class="space-y-0.5 text-center">
               <div class="text-cyan-300 font-bold text-xs mb-1">{{ `空压机${i + 1}` }}</div>
               <div class="text-xs" v-for="item in compressor" :key="item.modelName">
@@ -142,7 +140,7 @@
       <div class="col-span-3 min-h-0">
         <div class="h-full flex flex-col">
           <div
-            class="flex panel-title items-center justify-between border-b border-cyan-500/30 pb-1 flex-shrink-0 pl-5"
+            class="flex panel-title items-center justify-between border-b border-cyan-500/30 pb-1 flex-shrink-0 pl-[5%]"
           >
             <h3 class="text-cyan-300 text-sm font-bold pt-1">液驱压缩机参数</h3>
           </div>
@@ -168,9 +166,10 @@ import { ref, onMounted, nextTick, onUnmounted } from 'vue'
 import DataRow from './data-row.vue'
 import ModelViewer from './ModelViewer.vue'
 import { IotDeviceApi } from '@/api/pms/device'
-import { useRoute } from 'vue-router'
+import { useRoute, useRouter } from 'vue-router'
 
 const route = useRoute()
+const router = useRouter()
 
 defineOptions({
   name: 'Kanban'
@@ -221,12 +220,6 @@ const formatValue = (value: string, unit: string) => {
   return unit ? `${value} ${unit}` : value
 }
 
-// 根据modelName获取值
-const getValueByModelName = (data: any[], modelName: string) => {
-  const item = data.find((i) => i.modelName === modelName)
-  return item ? item.value : '0'
-}
-
 const toggleFullscreen = async () => {
   if (!document.fullscreenElement) {
     // 进入全屏
@@ -292,6 +285,12 @@ const fetchData = async () => {
   }
 }
 
+const goBack = () => {
+  router.push({
+    name: 'IotDeviceMonitor'
+  })
+}
+
 onMounted(async () => {
   document.addEventListener('fullscreenchange', handleFullscreenChange)
   await fetchData()
@@ -329,6 +328,14 @@ onUnmounted(() => {
   animation: gridMove 20s linear infinite;
 } */
 
+.header-bg {
+  background-image: url('https://hebbkx1anhila5yf.public.blob.vercel-storage.com/image-94sPHckfYzuaReFVWlt6620hJz4EKF.png');
+  background-size: 100% 100%;
+  background-repeat: no-repeat;
+  background-position: center;
+  min-height: 50px;
+}
+
 .scan-line {
   position: absolute;
   width: 100%;
@@ -362,7 +369,9 @@ onUnmounted(() => {
 
   padding-bottom: 0.25rem;
   background-image: url('../../../assets/imgs/border-title.png');
-  background-size: cover;
+  background-repeat: no-repeat;
+  /* 修改这里 */
+  background-size: 100% 100%; /* 宽高都填满 */
   flex-shrink: 0;
 }
 
@@ -421,6 +430,7 @@ onUnmounted(() => {
   width: 100vw !important;
   margin: 0 !important;
   padding: 1rem !important;
+  padding-top: 0 !important;
   position: fixed !important;
   top: 0 !important;
   left: 0 !important;
@@ -428,4 +438,19 @@ onUnmounted(() => {
   max-width: 100vw !important;
   max-height: 100vh !important;
 }
+
+.return-btn {
+  background-image: url('../../../assets/imgs/导航按钮.png');
+  background-size: 100% 100%;
+  background-position: center;
+  padding: 0.75rem 1rem;
+
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  width: 150px;
+  height: 36px;
+  line-height: 46px;
+}
 </style>