From a78e352b672fd6b634c24e0d9cf184f56b846f6a Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 28 十月 2025 15:25:45 +0800
Subject: [PATCH] 库存预警,无用功能隐藏

---
 src/views/monitorManagement/areaControl/index.vue |  264 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 264 insertions(+), 0 deletions(-)

diff --git a/src/views/monitorManagement/areaControl/index.vue b/src/views/monitorManagement/areaControl/index.vue
new file mode 100644
index 0000000..1dd5a36
--- /dev/null
+++ b/src/views/monitorManagement/areaControl/index.vue
@@ -0,0 +1,264 @@
+<template>
+  <div class="app-container">
+    <el-row :gutter="16">
+      <el-col :span="16">
+        <el-card shadow="never" class="section-card">
+          <template #header>
+            <div class="card-header">
+              <span>鍖哄煙绠$悊锛堝弻閲嶉棬绂侊級</span>
+              <div class="header-actions">
+                <el-select v-model="selectedPlant" placeholder="閫夋嫨鍘傚尯" size="small" style="width: 160px" @change="filterZones">
+                  <el-option v-for="plant in plants" :key="plant.id" :label="plant.name" :value="plant.id" />
+                </el-select>
+                <el-switch v-model="onlyCritical" inline-prompt :active-text="'浠呭叧閿尯'" :inactive-text="'鍏ㄩ儴'" @change="filterZones" />
+              </div>
+            </div>
+          </template>
+          <el-table :data="filteredZones" border style="width: 100%" height="320">
+            <el-table-column type="index" width="60" label="搴忓彿" align="center" />
+            <el-table-column prop="name" label="鍖哄煙鍚嶇О" min-width="160" show-overflow-tooltip />
+            <el-table-column prop="zoneType" label="绫诲瀷" width="120" />
+            <el-table-column label="鍙岄棬鑱斿姩" width="120" align="center">
+              <template #default="{ row }">
+                <el-tag v-if="row.dualAccess" type="success">宸插惎鐢�</el-tag>
+                <el-tag v-else type="info">鏈惎鐢�</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="鍦ㄧ嚎浜烘暟" width="100" align="center">
+              <template #default="{ row }">{{ row.currentPersons }}</template>
+            </el-table-column>
+            <el-table-column label="瀹夊叏鐘舵��" width="140" align="center">
+              <template #default="{ row }">
+                <el-tag :type="row.status === '姝e父' ? 'success' : row.status === '棰勮' ? 'warning' : 'danger'">
+                  {{ row.status }}
+                </el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="鎿嶄綔" width="180" align="center" fixed="right">
+              <template #default="{ row }">
+                <el-button link type="primary" size="small" @click="toggleDual(row)">
+                  {{ row.dualAccess ? '鍋滅敤鍙岄棬' : '鍚敤鍙岄棬' }}
+                </el-button>
+                <el-button link type="success" size="small" @click="openAccessSim(row)">妯℃嫙寮�闂�</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+
+        <el-card shadow="never" class="section-card">
+          <template #header>
+            <div class="card-header">
+              <span>鍩硅鑱斿姩锛堟湭瀹屾垚/杩囨湡绂佹杩涘叆锛�</span>
+              <div class="header-actions">
+                <el-input v-model="accessSim.personId" placeholder="浜哄憳宸ュ彿" size="small" style="width: 140px" />
+                <el-select v-model="accessSim.targetZoneId" placeholder="閫夋嫨鐩爣鍖哄煙" size="small" style="width: 180px">
+                  <el-option v-for="z in zones" :key="z.id" :label="z.name" :value="z.id" />
+                </el-select>
+                <el-button type="primary" size="small" @click="simulateAccess">妫�楠屽噯鍏�</el-button>
+              </div>
+            </div>
+          </template>
+          <el-descriptions :column="3" border size="small" v-if="accessResult">
+            <el-descriptions-item label="宸ュ彿">{{ accessResult.person.id }}锛坽{ accessResult.person.dept }}锛�</el-descriptions-item>
+            <el-descriptions-item label="鍩硅鐘舵��">
+              <el-tag :type="accessResult.person.training.valid ? 'success' : 'danger'">
+                {{ accessResult.person.training.valid ? '鏈夋晥' : '澶辨晥/鏈畬鎴�' }}
+              </el-tag>
+            </el-descriptions-item>
+            <el-descriptions-item label="鐩爣鍖哄煙">{{ accessResult.zone.name }}</el-descriptions-item>
+            <el-descriptions-item label="鏈�杩戝煿璁�">{{ accessResult.person.training.lastDate }}</el-descriptions-item>
+            <el-descriptions-item label="閫傚矖璇佹湁鏁堟湡">{{ accessResult.person.training.expireDate }}</el-descriptions-item>
+            <el-descriptions-item label="鍑嗗叆缁撴灉">
+              <el-tag :type="accessResult.allowed ? 'success' : 'danger'">{{ accessResult.allowed ? '鍏佽杩涘叆' : '绂佹杩涘叆' }}</el-tag>
+            </el-descriptions-item>
+          </el-descriptions>
+          <el-empty v-else description="璇疯緭鍏ヤ汉鍛樹笌鍖哄煙杩涜妫�楠�" />
+        </el-card>
+      </el-col>
+
+      <el-col :span="8">
+        <el-card shadow="never" class="section-card">
+          <template #header>
+            <div class="card-header">
+              <span>浣╂埓璁惧婊炵暀鍛婅锛堝嵄闄╁尯瓒呮椂锛�</span>
+              <div class="header-actions">
+                <el-select v-model="stayThreshold" size="small" style="width: 140px">
+                  <el-option :value="10" label="闃堝�� 10 鍒嗛挓" />
+                  <el-option :value="20" label="闃堝�� 20 鍒嗛挓" />
+                  <el-option :value="30" label="闃堝�� 30 鍒嗛挓" />
+                </el-select>
+                <el-switch v-model="alarmOn" inline-prompt :active-text="'鍛婅寮�'" :inactive-text="'鍛婅鍏�'" />
+              </div>
+            </div>
+          </template>
+          <el-timeline style="max-height: 520px; overflow: auto">
+            <el-timeline-item v-for="(item, idx) in alarms" :key="idx" :type="item.level" :timestamp="item.time">
+              <div class="alarm-item">
+                <div class="title">
+                  {{ item.personId }} 路 {{ item.zoneName }} 路 婊炵暀 {{ item.stayMins }} 鍒嗛挓
+                </div>
+                <div class="desc">璁惧锛歿{ item.deviceId }}锛堜俊鍙峰己搴� {{ item.rssi }} dBm锛�</div>
+              </div>
+            </el-timeline-item>
+          </el-timeline>
+        </el-card>
+      </el-col>
+    </el-row>
+  </div>
+  <el-dialog v-model="doorSimVisible" title="闂ㄧ寮�闂ㄦā鎷�" width="420px">
+    <el-form :model="doorSim" label-width="90px">
+      <el-form-item label="鍖哄煙">
+        <el-input v-model="doorSim.zoneName" disabled />
+      </el-form-item>
+      <el-form-item label="闂ㄧ1">
+        <el-switch v-model="doorSim.door1" />
+      </el-form-item>
+      <el-form-item label="闂ㄧ2">
+        <el-switch v-model="doorSim.door2" />
+      </el-form-item>
+      <el-alert type="info" show-icon :closable="false" title="鍙岄棬鍧囦负寮�鍚柟鍙�氳" />
+    </el-form>
+    <template #footer>
+      <el-button @click="doorSimVisible = false">鍏抽棴</el-button>
+      <el-button type="primary" :disabled="!(doorSim.door1 && doorSim.door2)" @click="confirmPass">閫氳</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, reactive, computed, onMounted } from "vue";
+
+// 鍘傚尯涓庡尯鍩燂紙鐓ょ偔琛屼笟璇箟銆佸敖閲忚创杩戠湡瀹烇級
+const plants = ref([
+  { id: "P01", name: "涓�鍙烽�夌叅鍘�" },
+  { id: "P02", name: "浜屽彿娲楃叅鍒嗗巶" },
+]);
+const zones = ref([
+  { id: "Z01", plantId: "P01", name: "涓帶瀹�", zoneType: "鎺у埗瀹�", dualAccess: true, currentPersons: 4, status: "姝e父" },
+  { id: "Z02", plantId: "P01", name: "鐓ゅ満A鍖�", zoneType: "鍫嗗瓨鍖�", dualAccess: true, currentPersons: 12, status: "棰勮" },
+  { id: "Z03", plantId: "P01", name: "鍗遍櫓鍝佸簱", zoneType: "鍗卞寲鍝�", dualAccess: true, currentPersons: 1, status: "姝e父" },
+  { id: "Z04", plantId: "P01", name: "楂樺帇閰嶇數瀹�", zoneType: "鐢垫皵闂�", dualAccess: true, currentPersons: 2, status: "姝e父" },
+  { id: "Z05", plantId: "P02", name: "鐨甫寤婂寳娈�", zoneType: "杈撻�佸粖閬�", dualAccess: false, currentPersons: 5, status: "姝e父" },
+  { id: "Z06", plantId: "P02", name: "绛涘垎杞﹂棿", zoneType: "浣滀笟鍖�", dualAccess: false, currentPersons: 9, status: "棰勮" },
+]);
+
+const selectedPlant = ref(plants.value[0].id);
+const onlyCritical = ref(true);
+const filteredZones = ref([]);
+
+function filterZones() {
+  const data = zones.value.filter((z) => z.plantId === selectedPlant.value);
+  filteredZones.value = onlyCritical.value ? data.filter((z) => z.dualAccess) : data;
+}
+
+function toggleDual(row) {
+  row.dualAccess = !row.dualAccess;
+  filterZones();
+}
+
+// 闂ㄧ寮�闂ㄦā鎷�
+const doorSimVisible = ref(false);
+const doorSim = reactive({ zoneId: "", zoneName: "", door1: false, door2: false });
+function openAccessSim(row) {
+  doorSim.zoneId = row.id;
+  doorSim.zoneName = row.name;
+  doorSim.door1 = false;
+  doorSim.door2 = false;
+  doorSimVisible.value = true;
+}
+function confirmPass() {
+  doorSimVisible.value = false;
+}
+
+// 鍩硅鑱斿姩妯℃嫙
+const persons = ref([
+  { id: "EMP1001", dept: "鐢熶骇涓�闃�", training: { valid: true, lastDate: "2025-09-12", expireDate: "2026-09-12" } },
+  { id: "EMP1018", dept: "鏈虹數鐝�", training: { valid: false, lastDate: "2024-07-03", expireDate: "2025-07-03" } },
+  { id: "EMP1022", dept: "瀹夌洃绉�", training: { valid: true, lastDate: "2025-08-01", expireDate: "2026-08-01" } },
+]);
+const accessSim = reactive({ personId: "", targetZoneId: "" });
+const accessResult = ref(null);
+
+function simulateAccess() {
+  const person = persons.value.find((p) => p.id === accessSim.personId);
+  const zone = zones.value.find((z) => z.id === accessSim.targetZoneId);
+  if (!person || !zone) {
+    accessResult.value = null;
+    return;
+  }
+  const allowed = person.training.valid && (zone.zoneType !== "鍗卞寲鍝�" || person.dept === "瀹夌洃绉�");
+  accessResult.value = { allowed, person, zone };
+}
+
+// 浣╂埓璁惧婊炵暀鍛婅锛堝亣鏁版嵁瀹氭椂鎺ㄩ�侊級
+const stayThreshold = ref(20);
+const alarmOn = ref(true);
+const alarms = ref([
+  { time: "09:35", level: "warning", personId: "EMP1001", zoneName: "鐓ゅ満A鍖�", stayMins: 18, deviceId: "TAG-7A12", rssi: -67 },
+]);
+
+let timer = null;
+function pushMockAlarm() {
+  if (!alarmOn.value) return;
+  const candidates = [
+    { personId: "EMP1018", zoneName: "绛涘垎杞﹂棿", base: 12 },
+    { personId: "EMP1022", zoneName: "楂樺帇閰嶇數瀹�", base: 9 },
+    { personId: "EMP1001", zoneName: "鐓ゅ満A鍖�", base: 16 },
+  ];
+  const pick = candidates[Math.floor(Math.random() * candidates.length)];
+  const stay = pick.base + Math.floor(Math.random() * 10);
+  if (stay >= stayThreshold.value) {
+    const now = new Date();
+    const hh = String(now.getHours()).padStart(2, "0");
+    const mm = String(now.getMinutes()).padStart(2, "0");
+    alarms.value.unshift({
+      time: `${hh}:${mm}`,
+      level: stay >= stayThreshold.value + 10 ? "danger" : "warning",
+      personId: pick.personId,
+      zoneName: pick.zoneName,
+      stayMins: stay,
+      deviceId: `TAG-${Math.random().toString(16).slice(2, 6).toUpperCase()}`,
+      rssi: -60 - Math.floor(Math.random() * 15),
+    });
+    if (alarms.value.length > 30) alarms.value.pop();
+  }
+}
+
+onMounted(() => {
+  filterZones();
+  timer = setInterval(pushMockAlarm, 4500);
+});
+
+// 绂诲紑鏃舵竻鐞�
+if (import.meta.hot) {
+  import.meta.hot.dispose(() => {
+    if (timer) clearInterval(timer);
+  });
+}
+</script>
+
+<style scoped lang="scss">
+.section-card {
+  margin-bottom: 16px;
+}
+.card-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+.header-actions {
+  display: flex;
+  gap: 8px;
+  align-items: center;
+}
+.alarm-item .title {
+  font-weight: 600;
+  margin-bottom: 4px;
+}
+.alarm-item .desc {
+  color: #666;
+  font-size: 12px;
+}
+</style>
+
+

--
Gitblit v1.9.3