Переглянути джерело

修改 车闸、人闸界面 bug

11868 6 днів тому
батько
коміт
5b79b9c7a6

+ 2 - 0
package.json

@@ -41,6 +41,7 @@
     "axios": "0.21.0",
     "clipboard": "2.0.6",
     "core-js": "3.8.1",
+    "dayjs": "^1.11.18",
     "docx-preview": "^0.1.4",
     "echarts": "^4.9.0",
     "element-ui": "2.15.6",
@@ -53,6 +54,7 @@
     "js-beautify": "1.13.0",
     "js-cookie": "2.2.1",
     "jsencrypt": "3.0.0-rc.1",
+    "lodash": "^4.17.21",
     "mammoth": "^1.4.21",
     "moment": "^2.30.1",
     "nprogress": "0.2.0",

+ 274 - 0
src/views/doorcarManage/InOutRecords/VehicleAccessRecordEditDialog.vue

@@ -0,0 +1,274 @@
+<template>
+  <el-dialog
+    :visible.sync="localVisible"
+    :title="formData.id ? '编辑名单' : '新增名单'"
+    @close="handleCancel"
+    custom-class="custom-dialog"
+  >
+    <el-form
+      ref="editForm"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      label-position="right"
+      class="nameList-form"
+    >
+      <!-- 基本信息 -->
+      <div class="form-row">
+        <el-form-item label="设备ID" prop="deviceId">
+          <el-select v-model="formData.deviceId" placeholder="请选择设备">
+            <el-option
+              v-for="device in devices"
+              :key="device.id"
+              :label="device.name"
+              :value="device.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="车牌号" prop="plateNumber">
+          <el-input v-model="formData.plateNumber" placeholder="请输入车牌号" />
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="事件类型" prop="eventType">
+          <el-radio-group v-model="formData.eventType">
+            <el-radio label="in">进</el-radio>
+            <el-radio label="out">出</el-radio>
+          </el-radio-group>
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="抓拍时间" prop="captureTime">
+          <el-date-picker
+            v-model="formData.captureTime"
+            type="datetime"
+            placeholder="选择时间"
+            style="width: 100%"
+          />
+        </el-form-item>
+        <el-form-item label="图片地址">
+          <el-input v-model="formData.photoUrl" placeholder="可输入或留空" />
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="备注">
+          <el-input
+            v-model="formData.remark"
+            type="textarea"
+            placeholder="可选填"
+          />
+        </el-form-item>
+      </div>
+    </el-form>
+
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">保存</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import request from "@/utils/request";
+
+export default {
+  name: "NameListEditDialog",
+  props: {
+    visible: Boolean,
+    accessRecordData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  data() {
+    return {
+      localVisible: this.visible, // ✅ 本地副本
+      devices: [],
+      loading: false,
+      formData: {
+        id: null,
+        deviceId: null,
+        plateNumber: "",
+        eventType: "",
+        photoUrl: "",
+        captureTime: null,
+        remark: "",
+      },
+      formRules: {
+        plateNumber: [
+          { required: true, message: "请输入车牌号", trigger: "blur" },
+        ],
+        eventType: [
+          { required: true, message: "请选择事件类型", trigger: "change" },
+        ],
+        captureTime: [
+          { required: true, message: "请选择抓拍时间", trigger: "change" },
+        ],
+        deviceId: [
+          { required: true, message: "请选择设备", trigger: "change" },
+        ],
+      },
+    };
+  },
+  watch: {
+    visible(val) {
+      this.localVisible = val; // ✅ 同步父组件的变化
+    },
+    localVisible(val) {
+      this.$emit("update:visible", val); // ✅ 通知父组件更新
+    },
+    accessRecordData: {
+      immediate: true,
+      handler(newVal) {
+        // console.log("newVal", newVal);
+        if (!newVal || Object.keys(newVal).length === 0) {
+          this.resetForm();
+          console.log("新增名单", this.formData);
+        } else {
+          this.formData = {
+            ...newVal,
+          };
+          console.log("编辑名单", this.formData);
+        }
+      },
+    },
+  },
+  mounted() {
+    this.getDeviceNameInfo();
+  },
+  methods: {
+    parseJsonArray(field) {
+      if (!field) return [];
+      if (Array.isArray(field)) return field;
+      try {
+        return JSON.parse(field);
+      } catch (e) {
+        return [];
+      }
+    },
+    resetForm() {
+      this.formData = {
+        id: null,
+        deviceId: null,
+        plateNumber: "",
+        eventType: "",
+        photoUrl: "",
+        captureTime: null,
+        remark: "",
+      };
+    },
+    handleCancel() {
+      this.localVisible = false; // ✅ 修改本地值,不触发 prop 报错
+      this.$emit("cancel");
+    },
+    handleSubmit() {
+      this.$refs.editForm.validate((valid) => {
+        if (!valid) {
+          this.$message.error("请检查表单填写项");
+          return false;
+        }
+        // 提交时将数组转 JSON 字符串
+        const data = {
+          ...this.formData,
+        };
+        console.log("data", data);
+        this.$emit("submit", data);
+      });
+    },
+    /** ========== 获取设备列表 ========== */
+    async getDeviceNameInfo() {
+      try {
+        this.loading = true;
+        const res = await request({
+          url: "/device/listDeviceName",
+          method: "GET",
+        });
+        const list = res?.data?.list || res?.list || [];
+        this.devices = list;
+        console.log("设备列表:", list);
+        if (list.length > 0 && !this.formData.deviceId) {
+          this.formData.deviceId = list[0].id;
+        }
+      } catch (e) {
+        console.error("获取设备信息失败:", e);
+        this.$message.error("获取设备列表失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>
+
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 800px !important;
+}
+
+/* 标签文字颜色深色背景下可读 */
+.custom-dialog .el-dialog__title {
+  color: #fff;
+}
+
+.custom-dialog .el-dialog__header {
+  background-color: #003c68;
+  color: #ffffff;
+}
+
+.custom-dialog .el-dialog__headerbtn {
+  color: #ffffff;
+}
+
+.nameList-form {
+  background-color: #003c68;
+  padding: 15px 20px;
+}
+
+/* 两列布局 */
+.form-row {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 10px;
+}
+
+/* 标签文字颜色深色背景下可读 */
+.nameList-form .el-form-item__label {
+  color: #fff;
+}
+
+/* 输入框文字颜色 */
+.nameList-form .el-input__inner,
+.nameList-form .el-select .el-input__inner,
+.nameList-form .el-input-number__input {
+  color: #000;
+  background-color: #fff;
+}
+
+/* 修改输入框 placeholder 文字颜色为黑色 */
+.nameList-form input::placeholder,
+.nameList-form .el-input__inner::placeholder {
+  color: #dbe1e7 !important;
+  opacity: 1; /* 避免部分浏览器透明度影响 */
+}
+
+/* 针对 select 的 placeholder(实际上是 input) */
+.nameList-form .el-select .el-input__inner::placeholder {
+  color: #dbe1e7 !important;
+}
+
+/* 针对 el-input-number */
+.nameList-form .el-input-number__input::placeholder {
+  color: #dbe1e7 !important;
+}
+
+/* 对话框底部按钮右对齐 */
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 385 - 0
src/views/doorcarManage/InOutRecords/index.vue

@@ -0,0 +1,385 @@
+<template>
+  <div style="padding: 10px">
+    <!-- 搜索和操作 -->
+    <div style="height: 50px; display: flex; align-items: center; gap: 10px">
+      <!-- 选择设备 -->
+      <el-select
+        v-model="queryCondition.selectedDeviceId"
+        placeholder="选择设备"
+        size="small"
+        style="width: 150px"
+        @change="fetchData"
+      >
+        <el-option
+          v-for="device in devices"
+          :key="device.id"
+          :label="device.name"
+          :value="device.id"
+        />
+      </el-select>
+      <el-input
+        v-model="queryCondition.inputPlateNumber"
+        placeholder="输入车牌号"
+        size="small"
+        clearable
+        style="width: 150px"
+        @keyup.enter.native="fetchData"
+      />
+      <el-select
+        v-model="queryCondition.selectedEventType"
+        placeholder="事件类型"
+        size="small"
+        clearable
+        style="width: 120px"
+        @change="fetchData"
+      >
+        <el-option label="全部" value="" />
+        <el-option label="进" value="enter" />
+        <el-option label="出" value="exit" />
+      </el-select>
+      <!-- 搜索时间范围 -->
+      <el-date-picker
+        v-model="queryCondition.startTime"
+        type="datetime"
+        placeholder="开始时间"
+        size="small"
+        clearable
+        style="width: 200px"
+        @change="fetchData"
+      />
+      <el-date-picker
+        v-model="queryCondition.endTime"
+        type="datetime"
+        placeholder="结束时间"
+        size="small"
+        clearable
+        style="width: 200px"
+        @change="fetchData"
+      />
+      <el-button type="primary" size="small" @click="fetchData">查询</el-button>
+      <!--      <el-button type="success" size="small" @click="handleAdd">新增</el-button>-->
+      <el-button
+        type="danger"
+        size="small"
+        :disabled="!multipleSelection.length"
+        @click="handleBatchDelete"
+        >批量删除</el-button
+      >
+    </div>
+
+    <!-- 表格 -->
+    <el-table
+      :data="tableData"
+      style="width: 100%; margin-top: 10px"
+      border
+      :header-cell-style="changeHeaderCellStyle"
+      @selection-change="handleSelectionChange"
+      v-loading="loading"
+      element-loading-text="加载中..."
+    >
+      <el-table-column type="selection" width="50" align="center" />
+      <el-table-column label="序号" width="80" align="center" sortable>
+        <template #default="scope">
+          {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="plateNumber"
+        label="车牌号"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="deviceId"
+        label="设备Id"
+        align="center"
+        min-width="80"
+        sortable
+      />
+      <el-table-column
+        prop="eventType"
+        label="事件类型"
+        align="center"
+        min-width="60"
+        sortable
+      >
+        <template #default="{ row }">{{
+          row.eventType === "enter" ? "进" : "出"
+        }}</template>
+      </el-table-column>
+      <el-table-column
+        prop="captureTime"
+        label="抓拍时间"
+        align="center"
+        min-width="150"
+        sortable
+      />
+      <el-table-column label="图片" align="center" min-width="120">
+        <template #default="{ row }">
+          <el-image
+            v-if="row.photoUrl"
+            :src="row.photoUrl"
+            :preview-src-list="[row.photoUrl]"
+            style="width: 60px; height: 40px; border-radius: 4px"
+          />
+          <span v-else>-</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="remark"
+        label="备注"
+        align="center"
+        min-width="150"
+      />
+      <el-table-column label="操作" fixed="right" align="center" width="160">
+        <template #default="{ row }">
+          <!--          <el-button type="text" size="small" @click="handleEdit(row)">编辑</el-button>-->
+          <el-button type="danger" size="small" @click="handleDelete(row)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- ===== 分页 ===== -->
+    <div class="pagination">
+      <el-pagination
+        background
+        layout="total, prev, pager, next, jumper, sizes"
+        :current-page="currentPage"
+        :page-size="pageSize"
+        :total="total"
+        :page-sizes="[10, 20, 50, 100]"
+        @current-change="handlePageChange"
+        @size-change="handleSizeChange"
+      />
+    </div>
+    <!--    &lt;!&ndash; 新增/编辑弹窗 &ndash;&gt;-->
+    <!--    <VehicleAccessRecordEditDialog-->
+    <!--      v-if="dialogVisible"-->
+    <!--      :visible.sync="dialogVisible"-->
+    <!--      :access-record-data="currentAccessRecordData"-->
+    <!--      @submit="handleAccessRecordSubmit"-->
+    <!--      @cancel="handleAccessRecordCancel"-->
+    <!--    />-->
+  </div>
+</template>
+
+<script>
+import request from "@/utils/request";
+import VehicleAccessRecordEditDialog from "./VehicleAccessRecordEditDialog.vue";
+import { debounce } from "lodash";
+
+export default {
+  name: "VehicleAccessRecord",
+  components: {
+    VehicleAccessRecordEditDialog,
+  },
+  data() {
+    return {
+      // 搜索条件
+      queryCondition: {
+        selectedDeviceId: null,
+        inputPlateNumber: "",
+        selectedEventType: "",
+        startTime: null, // 开始时间
+        endTime: null, // 结束时间
+      },
+      devices: [],
+      // 表格数据
+      tableData: [],
+      loading: false,
+      currentPage: 1,
+      pageSize: 10,
+      total: 0,
+      // 多选
+      multipleSelection: [],
+      // 弹窗
+      dialogVisible: false,
+      currentAccessRecordData: [],
+    };
+  },
+  mounted() {
+    this.getDeviceNameInfo();
+    this.fetchData();
+  },
+  methods: {
+    // 查询记录
+    async _fetchData() {
+      this.loading = true;
+      try {
+        if (!this.queryCondition.selectedDeviceId) {
+          this.$message.info("未选择设备,将查询所有设备数据");
+        }
+
+        const data = {
+          ...this.queryCondition,
+          page: this.currentPage,
+          size: this.pageSize,
+        };
+        console.log("查询参数", data);
+        const res = await request({
+          url: "/carCamera/accessRecord/list",
+          method: "POST",
+          data: data,
+        });
+        // 判断返回结构
+        if (res && res.list) {
+          this.tableData = res.list || [];
+          this.total = res.total || 0;
+        } else {
+          this.tableData = [];
+          this.total = 0;
+          this.$message.warning("未获取到数据");
+        }
+      } catch (error) {
+        console.error(error);
+        this.$message.error("获取数据失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+    // 包裹防抖函数,延迟 300ms
+    fetchData: debounce(function () {
+      this.fetchData();
+    }, 300),
+    // 分页
+    handlePageChange(page) {
+      this.currentPage = page;
+      this.fetchData();
+    },
+    handleSizeChange(size) {
+      this.pageSize = size;
+      this.currentPage = 1;
+      this.fetchData();
+    },
+    // 表格样式
+    changeHeaderCellStyle() {
+      return "background: #004279; color: #fff;";
+    },
+    // 分页控制
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    // 新增
+    handleAdd() {
+      this.currentAccessRecordData = {};
+      this.dialogVisible = true;
+    },
+    // 编辑
+    handleEdit(row) {
+      this.currentAccessRecordData = { ...row };
+      this.dialogVisible = true;
+    },
+    // 保存(新增/修改)
+    handleSubmit() {
+      this.$refs.formRef.validate(async (valid) => {
+        if (!valid) return;
+        const url = this.form.id
+          ? "/carCamera/accessRecord/update"
+          : "/carCamera/accessRecord/add";
+        try {
+          const res = await request.post(url, this.form);
+          if (res.success) {
+            this.$message.success("保存成功");
+            this.dialogVisible = false;
+            this.fetchData();
+          } else {
+            this.$message.error(res.msg || "保存失败");
+          }
+        } catch (e) {
+          console.error(e);
+          this.$message.error("请求失败");
+        }
+      });
+    },
+    // 删除单条
+    async handleDelete(row) {
+      this.$confirm(
+        `确定要删除车牌号为【${row.plateNumber}】的记录吗?`,
+        "提示",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        },
+      )
+        .then(async () => {
+          const res = await request.post("/carCamera/accessRecord/delete", {
+            ids: [row.id],
+          });
+          if (res.success) {
+            this.$message.success("删除成功");
+            this.fetchData();
+          } else {
+            this.$message.error(res.msg || "删除失败");
+          }
+        })
+        .catch(() => {});
+    },
+    // 批量删除
+    async handleBatchDelete() {
+      if (!this.multipleSelection.length) return;
+      const ids = this.multipleSelection.map((i) => i.id);
+      this.$confirm(`确定要删除选中的 ${ids.length} 条记录吗?`, "提示", {
+        type: "warning",
+      })
+        .then(async () => {
+          const res = await request.post("/carCamera/accessRecord/delete", {
+            ids,
+          });
+          if (res.success) {
+            this.$message.success("删除成功");
+            this.fetchData();
+          } else {
+            this.$message.error(res.msg || "删除失败");
+          }
+        })
+        .catch(() => {});
+    },
+    // 重置弹窗表单
+    resetDialogForm() {
+      this.form = {};
+      this.$refs.formRef && this.$refs.formRef.clearValidate();
+    },
+    // 获取设备列表
+    async getDeviceNameInfo() {
+      try {
+        this.loading = true;
+        const res = await request({
+          url: "/device/listDeviceName",
+          method: "GET",
+        });
+        const list = res?.data?.list || res?.list || [];
+        this.devices = list;
+        if (list.length > 0) {
+          this.queryCondition.selectedDeviceId = list[0].id;
+          this.fetchData();
+        }
+      } catch (e) {
+        console.error("获取设备信息失败:", e);
+        this.$message.error("获取设备列表失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>
+
+<style scoped>
+.vehicle-access-container {
+  padding: 10px;
+}
+.toolbar {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  margin-bottom: 10px;
+}
+.pagination {
+  text-align: right;
+  margin-top: 15px;
+}
+</style>

+ 341 - 0
src/views/doorcarManage/eventManage/EventDetailDialog.vue

@@ -0,0 +1,341 @@
+<template>
+  <el-dialog
+    title="事件详情"
+    :visible.sync="visible"
+    @close="handleCancel"
+    custom-class="custom-dialog"
+  >
+    <el-descriptions
+      :column="2"
+      border
+      :label-style="{ width: '200px', fontWeight: '600', color: '#333' }"
+      :content-style="{ width: '300px' }"
+    >
+      <el-descriptions-item label="人脸比对库唯一标识ID">{{
+        detail.FDID
+      }}</el-descriptions-item>
+      <el-descriptions-item label="检测阈值[0-100]">{{
+        detail.thresholdValue
+      }}</el-descriptions-item>
+      <el-descriptions-item label="人员出生日期">{{
+        detail.bornTime
+      }}</el-descriptions-item>
+      <el-descriptions-item label="姓名">{{
+        detail.name
+      }}</el-descriptions-item>
+      <el-descriptions-item label="性别">
+        {{ genderMap[detail.sex] || detail.sex || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="省份">{{
+        detail.province
+      }}</el-descriptions-item>
+      <el-descriptions-item label="城市">{{
+        detail.city
+      }}</el-descriptions-item>
+      <el-descriptions-item label="证件类型">{{
+        detail.certificateType
+      }}</el-descriptions-item>
+      <el-descriptions-item label="证件号">{{
+        detail.certificateNumber
+      }}</el-descriptions-item>
+      <el-descriptions-item label="人脸图片">
+        <el-image
+          v-if="detail.picURL"
+          :src="detail.picURL"
+          fit="cover"
+          style="width: 120px; height: 120px; border-radius: 8px"
+        />
+        <span v-else>无图片</span>
+      </el-descriptions-item>
+      <el-descriptions-item label="图片ID">{{
+        detail.PID
+      }}</el-descriptions-item>
+      <el-descriptions-item
+        label="人员扩展信息列表"
+        content-style="padding: 0;"
+      >
+        <el-table
+          v-if="
+            detail.PersonInfoExtendList && detail.PersonInfoExtendList.length
+          "
+          :data="detail.PersonInfoExtendList"
+          border
+          size="small"
+          style="width: 100%; max-width: 500px"
+        >
+          <el-table-column
+            prop="id"
+            label="序号"
+            min-width="50"
+            align="center"
+          />
+          <el-table-column
+            prop="name"
+            label="扩展名称"
+            min-width="50"
+            align="center"
+          />
+          <el-table-column
+            prop="value"
+            label="扩展内容"
+            min-width="100"
+            align="center"
+          />
+          <el-table-column
+            prop="enable"
+            label="启用状态"
+            align="center"
+            min-width="50"
+          >
+            <template #default="{ row }">
+              <el-tag :type="row.enable ? 'success' : 'info'">
+                {{ row.enable ? "启用" : "禁用" }}
+              </el-tag>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div v-else style="color: #999">无人员扩展信息</div>
+      </el-descriptions-item>
+      <el-descriptions-item label="相似度[0.0,100.0]">{{
+        detail.similarity
+      }}</el-descriptions-item>
+      <el-descriptions-item label="建模状态" content-style="padding: 0;">
+        <el-table
+          v-if="detail.ModelingStatus"
+          :data="[detail.ModelingStatus]"
+          border
+          size="small"
+          style="width: 100%; max-width: 500px"
+        >
+          <el-table-column
+            prop="FDID"
+            label="人脸库ID"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="PID"
+            label="图片ID"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="name"
+            label="图片名称"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="status"
+            label="建模状态"
+            min-width="60"
+            align="center"
+          >
+            <template #default="{ row }">
+              <el-tag
+                :type="
+                  row.status === 'success'
+                    ? 'success'
+                    : row.status === 'failed'
+                      ? 'danger'
+                      : 'info'
+                "
+              >
+                {{ row.status }}
+              </el-tag>
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="reason"
+            label="失败原因"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="customHumanID"
+            label="客户自定义人员ID"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="customFaceLibID"
+            label="客户自定义人脸库ID"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="modeData"
+            label="模型数据(Base64)"
+            min-width="60"
+            align="center"
+            show-overflow-tooltip
+          />
+        </el-table>
+        <div v-else style="color: #999">无建模状态信息</div>
+      </el-descriptions-item>
+      <el-descriptions-item label="是否不保存原始人脸库图片">{{
+        detail.isNoSaveFDPicture
+      }}</el-descriptions-item>
+      <el-descriptions-item label="人员ID">{{
+        detail.humanId
+      }}</el-descriptions-item>
+      <el-descriptions-item label="建模状态(旧)">{{
+        detail.modelStatus
+      }}</el-descriptions-item>
+      <el-descriptions-item label="人脸比对成功总次数">{{
+        detail.FMCount
+      }}</el-descriptions-item>
+      <el-descriptions-item label="入库时间">{{
+        detail.importTime
+      }}</el-descriptions-item>
+      <el-descriptions-item label="底库图片唯一标识ID">{{
+        detail.UUPID
+      }}</el-descriptions-item>
+      <el-descriptions-item label="出现频次">{{
+        detail.occurrences
+      }}</el-descriptions-item>
+      <el-descriptions-item label="IMSI采集信息" content-style="padding: 0;">
+        <el-table
+          v-if="detail.IMSIList && detail.IMSIList.length"
+          :data="detail.IMSIList"
+          border
+          size="small"
+          style="width: 100%; max-width: 500px"
+        >
+          <el-table-column
+            prop="IMSI"
+            label="IMSI号码"
+            min-width="60"
+            align="center"
+          />
+          <el-table-column
+            prop="count"
+            label="比中次数"
+            min-width="30"
+            align="center"
+          />
+          <el-table-column
+            prop="lastFMTime"
+            label="最近比对时间"
+            min-width="50"
+            align="center"
+          />
+        </el-table>
+        <div v-else style="color: #999">无IMSI采集信息</div>
+      </el-descriptions-item>
+      <el-descriptions-item label="最近比对成功时间">{{
+        detail.lastFMTime
+      }}</el-descriptions-item>
+    </el-descriptions>
+
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="$emit('update:visible', false)">关闭</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "EventDetailDialog",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false,
+    },
+    detail: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  data() {
+    return {
+      userTypeMap: {
+        normal: "普通人(主人)",
+        visitor: "访客",
+        blackList: "黑名单",
+      },
+      genderMap: {
+        male: "男",
+        female: "女",
+        unknown: "未知",
+      },
+      verifyModeMap: {
+        cardAndPw: "刷卡+密码",
+        card: "刷卡",
+        cardOrPw: "刷卡或密码",
+        fp: "指纹",
+        fpAndPw: "指纹+密码",
+        fpOrCard: "指纹或刷卡",
+        fpAndCard: "指纹+刷卡",
+        fpAndCardAndPw: "指纹+刷卡+密码",
+        faceOrFpOrCardOrPw: "人脸或指纹或刷卡或密码",
+        faceAndFp: "人脸+指纹",
+        faceAndPw: "人脸+密码",
+        faceAndCard: "人脸+刷卡",
+        face: "人脸",
+        employeeNoAndPw: "工号+密码",
+        fpOrPw: "指纹或密码",
+        employeeNoAndFp: "工号+指纹",
+        employeeNoAndFpAndPw: "工号+指纹+密码",
+        faceAndFpAndCard: "人脸+指纹+刷卡",
+        faceAndPwAndFp: "人脸+密码+指纹",
+        employeeNoAndFace: "工号+人脸",
+        faceOrfaceAndCard: "人脸或人脸+刷卡",
+        fpOrface: "指纹或人脸",
+        cardOrfaceOrPw: "刷卡或人脸或密码",
+        cardOrFace: "刷卡或人脸",
+        cardOrFaceOrFp: "刷卡或人脸或指纹",
+        employeeNoAndFaceAndPw: "工号+人脸+密码",
+        cardOrFaceOrFaceAndCard: "刷卡或人脸或人脸+刷卡",
+      },
+    };
+  },
+  methods: {
+    handleCancel() {
+      // this.$emit("cancel");
+    },
+  },
+};
+</script>
+
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 1000px !important;
+}
+
+/*.user-detail-dialog .el-descriptions {*/
+/*  background: #fafafa;*/
+/*  border-radius: 8px;*/
+/*  padding: 10px 15px;*/
+/*}*/
+
+/*.user-detail-dialog .el-descriptions__label {*/
+/*  width: 180px !important;*/
+/*  font-weight: 600;*/
+/*  color: #333;*/
+/*  background: #f5f7fa;*/
+/*  text-align: right;*/
+/*}*/
+
+/*.user-detail-dialog .el-descriptions__content {*/
+/*  color: #555;*/
+/*  word-break: break-all;*/
+/*}*/
+
+/*.door-plan {*/
+/*  background: #fff;*/
+/*  border: 1px solid #ebeef5;*/
+/*  border-radius: 6px;*/
+/*  padding: 6px;*/
+/*  margin-top: 4px;*/
+/*  font-size: 13px;*/
+/*  color: #606266;*/
+/*}*/
+
+/*.dialog-footer {*/
+/*  text-align: right;*/
+/*  padding-top: 10px;*/
+/*  border-top: 1px solid #ebeef5;*/
+/*}*/
+</style>

+ 338 - 0
src/views/doorcarManage/eventManage/EventSearchDialog.vue

@@ -0,0 +1,338 @@
+<template>
+  <el-dialog
+    :visible.sync="visible"
+    title="查询事件数据"
+    @close="handleCancel"
+    custom-class="custom-dialog"
+  >
+    <el-form
+      :model="formData"
+      ref="searchForm"
+      label-width="180px"
+      class="user-form"
+    >
+      <!-- 主次类型 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="报警主类型">
+            <el-input v-model="formData.dwMajor" placeholder="0-全部" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="报警次类型">
+            <el-input v-model="formData.dwMinor" placeholder="0-全部" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 时间范围 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="开始时间">
+            <el-date-picker
+              v-model="formData.struStartTime"
+              type="datetime"
+              placeholder="请选择开始时间"
+              value-format="yyyy-MM-dd HH:mm:ss"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="结束时间">
+            <el-date-picker
+              v-model="formData.struEndTime"
+              type="datetime"
+              placeholder="请选择结束时间"
+              value-format="yyyy-MM-dd HH:mm:ss"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 卡号、姓名 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="卡号">
+            <el-input v-model="formData.byCardNo" placeholder="为空默认全部" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="持卡人姓名">
+            <el-input v-model="formData.byName" placeholder="为空默认全部" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 图片与时间类型 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="是否带图片 (0-不带,1-带)">
+            <el-select v-model="formData.byPicEnable" placeholder="选择">
+              <el-option label="不带图片" :value="0" />
+              <el-option label="带图片" :value="1" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="时间类型">
+            <el-select v-model="formData.byTimeType" placeholder="选择">
+              <el-option label="设备本地时间(0)" :value="0" />
+              <el-option label="UTC时间(1)" :value="1" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 流水号 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="起始流水号">
+            <el-input
+              v-model="formData.dwBeginSerialNo"
+              placeholder="0 默认全部"
+            />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="结束流水号">
+            <el-input
+              v-model="formData.dwEndSerialNo"
+              placeholder="0 默认全部"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- IOT通道与归纳类型 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="IOT通道号">
+            <el-input v-model="formData.dwIOTChannelNo" placeholder="0-无效" />
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="归纳事件类型">
+            <el-select
+              v-model="formData.wInductiveEventType"
+              placeholder="选择归纳事件类型(0-无效)"
+              filterable
+              clearable
+              style="width: 100%"
+            >
+              <el-option :label="'0 - 无效(按主次类型区分)'" :value="0" />
+              <el-option-group label="HIKVISION门禁主机">
+                <el-option label="1 - 认证通过" :value="1" />
+                <el-option label="2 - 认证失败" :value="2" />
+                <el-option label="3 - 开门动作" :value="3" />
+                <el-option label="4 - 关门动作" :value="4" />
+                <el-option label="5 - 门异常" :value="5" />
+                <el-option label="6 - 远程操作" :value="6" />
+                <el-option label="7 - 校时事件" :value="7" />
+                <el-option label="8 - 设备异常事件" :value="8" />
+                <el-option label="9 - 设备恢复正常事件" :value="9" />
+                <el-option label="10 - 报警事件" :value="10" />
+                <el-option label="11 - 报警恢复事件" :value="11" />
+                <el-option label="12 - 呼叫中心" :value="12" />
+              </el-option-group>
+
+              <el-option-group label="HIKVISION可视对讲">
+                <el-option label="1 - 防拆报警" :value="101" />
+                <el-option label="2 - 劫持报警" :value="102" />
+                <el-option label="3 - 多次密码开锁失败" :value="103" />
+                <el-option label="4 - 门没开报警" :value="104" />
+                <el-option label="5 - 门没关报警" :value="105" />
+                <el-option label="6 - 通话对讲报警" :value="106" />
+                <el-option label="7 - 密码开锁" :value="107" />
+                <el-option label="8 - 劫持开锁" :value="108" />
+                <el-option label="9 - 刷卡开锁" :value="109" />
+                <el-option label="10 - 户主开锁" :value="110" />
+                <el-option label="11 - 中心平台开锁" :value="111" />
+                <el-option label="12 - 设备对讲" :value="112" />
+              </el-option-group>
+
+              <el-option-group label="HIKVISION报警主机">
+                <el-option label="1 - 防区事件" :value="201" />
+                <el-option label="2 - 主机事件" :value="202" />
+              </el-option-group>
+
+              <el-option-group label="第三方 GJD 报警主机">
+                <el-option label="1 - 对象侦测报警" :value="301" />
+                <el-option label="2 - 人为防拆报警" :value="302" />
+                <el-option label="3 - 弱光检测报警" :value="303" />
+                <el-option label="4 - 定时报警" :value="304" />
+                <el-option label="5 - 温度检测报警" :value="305" />
+              </el-option-group>
+
+              <el-option-group label="第三方 Luminite 报警主机">
+                <el-option label="1 - 检测到移动" :value="401" />
+                <el-option label="2 - 检测到干扰" :value="402" />
+                <el-option label="3 - 传感器遮挡" :value="403" />
+                <el-option label="4 - 震动报警" :value="404" />
+                <el-option label="5 - 传感器问题" :value="405" />
+                <el-option label="6 - 位置移动" :value="406" />
+                <el-option label="7 - 电池不足" :value="407" />
+                <el-option label="8 - 手动冲突数据" :value="408" />
+                <el-option label="9 - 接收干扰信息" :value="409" />
+                <el-option label="10 - 防拆弹簧松弛" :value="410" />
+              </el-option-group>
+
+              <el-option-group label="第三方 OPTEX 报警主机">
+                <el-option label="1 - 远&近事件" :value="501" />
+                <el-option label="2 - 远距离事件" :value="502" />
+                <el-option label="3 - 近距离事件" :value="503" />
+                <el-option label="4 - 缓慢移动检测" :value="504" />
+                <el-option label="5 - 干扰报警" :value="505" />
+                <el-option label="6 - 防遮挡报警" :value="506" />
+                <el-option label="7 - 防旋转报警" :value="507" />
+                <el-option label="8 - 异常电路激活" :value="508" />
+                <el-option label="9 - 污物覆盖" :value="509" />
+                <el-option label="10 - 传感器异常" :value="510" />
+                <el-option label="11 - 设备故障" :value="511" />
+                <el-option label="12 - 设备不在线" :value="512" />
+                <el-option label="13 - 设备已连接" :value="513" />
+                <el-option label="14~25 - 区域A/B报警" :value="514" />
+              </el-option-group>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 搜索方式与监控点 -->
+      <el-row :gutter="20">
+        <el-col :span="12">
+          <el-form-item label="搜索方式">
+            <el-select v-model="formData.bySearchType" placeholder="选择方式">
+              <el-option label="0-保留" :value="0" />
+              <el-option
+                label="1-按事件源搜索(此时通道号为非视频通道号)"
+                :value="1"
+              />
+              <el-option label="2-按监控点ID搜索" :value="2" />
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="12">
+          <el-form-item label="监控点ID">
+            <el-input
+              v-model="formData.szMonitorID"
+              placeholder="由设备序列号、通道类型、编号组成,例如门禁点:设备序列号+“DOOR”+门编号"
+            />
+          </el-form-item>
+        </el-col>
+      </el-row>
+
+      <!-- 工号 -->
+      <el-row>
+        <el-col :span="12">
+          <el-form-item label="工号(人员ID)">
+            <el-input v-model="formData.byEmployeeNo" />
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <!-- 底部 -->
+    <div slot="footer" class="dialog-footer">
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="submitForm">查询</el-button>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+export default {
+  name: "EventSearchDialog",
+  props: {
+    visible: Boolean,
+    eventData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  data() {
+    return {
+      formData: {
+        dwMajor: 0,
+        dwMinor: 0,
+        struStartTime: "",
+        struEndTime: "",
+        byCardNo: "",
+        byName: "",
+        byPicEnable: 0,
+        byTimeType: 0,
+        dwBeginSerialNo: 0,
+        dwEndSerialNo: 0,
+        dwIOTChannelNo: 0,
+        wInductiveEventType: 0,
+        bySearchType: 0,
+        szMonitorID: "",
+        byEmployeeNo: "",
+      },
+    };
+  },
+  watch: {
+    eventData: {
+      immediate: true,
+      handler(newVal) {
+        // if (!newVal || Object.keys(newVal).length === 0) {
+        //   // 新增设备
+        console.log("newVal", newVal);
+        this.resetForm();
+        // } else {
+        //   this.formData = {
+        //     ...this.formData,
+        //     ...newVal,
+        //     doors: this.parseJsonArray(newVal.doors),
+        //     channels: this.parseJsonArray(newVal.channels)
+        //   };
+        // }
+      },
+    },
+  },
+  methods: {
+    resetForm() {
+      this.formData = {
+        dwMajor: 0,
+        dwMinor: 0,
+        struStartTime: "",
+        struEndTime: "",
+        byCardNo: "",
+        byName: "",
+        byPicEnable: 0,
+        byTimeType: 0,
+        dwBeginSerialNo: 0,
+        dwEndSerialNo: 0,
+        dwIOTChannelNo: 0,
+        wInductiveEventType: 0,
+        bySearchType: 0,
+        szMonitorID: "",
+        byEmployeeNo: "",
+      };
+    },
+    handleCancel() {
+      // this.$emit("cancel");
+    },
+    submitForm() {
+      const data = {
+        ...this.formData,
+        dwSize: 0, // SDK要求填结构体大小,实际由底层计算,这里仅示意
+      };
+      console.log("📡 事件查询请求体:", JSON.stringify(data, null, 2));
+      this.$emit("event:search", data);
+    },
+  },
+};
+</script>
+
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 1000px !important;
+}
+
+.user-form .el-input,
+.user-form .el-select,
+.user-form .el-date-picker {
+  width: 100%;
+}
+</style>

+ 382 - 40
src/views/doorcarManage/eventManage/index.vue

@@ -1,58 +1,400 @@
 <template>
-  <div>
-    <el-form :inline="true" :model="filters">
-      <el-form-item label="车牌号">
-        <el-input v-model="filters.plate_number" placeholder="输入车牌号" />
-      </el-form-item>
-      <el-form-item label="时间范围">
-        <el-date-picker
-          v-model="filters.time_range"
-          type="datetimerange"
-          range-separator="至"
-          start-placeholder="开始时间"
-          end-placeholder="结束时间"
+  <div style="padding: 10px">
+    <!-- 搜索栏 -->
+    <div style="margin-bottom: 10px">
+      <!-- 选择设备 -->
+      <el-select
+        v-model="selectedDeviceId"
+        placeholder="选择设备"
+        size="small"
+        style="width: 150px"
+        clearable
+      >
+        <el-option
+          style="color: black"
+          v-for="device in devices"
+          :key="device.id"
+          :label="device.name"
+          :value="device.id"
         />
-      </el-form-item>
-      <el-button type="primary" @click="loadData">查询</el-button>
-    </el-form>
-
-    <el-table :data="tableData" border style="width:100%; margin-top:10px" v-loading="loading">
-      <el-table-column prop="plate_number" label="车牌号" />
-      <el-table-column prop="event_time" label="事件时间" />
-      <el-table-column prop="direction" label="进出方向" />
-      <el-table-column prop="channel" label="通道/道闸" />
-      <el-table-column prop="event_type" label="事件类型" />
-      <el-table-column label="抓拍图片">
+      </el-select>
+      <el-button
+        type="primary"
+        @click="handleSearch()"
+        style="margin-left: 10px"
+        >点击输入查询条件进行查询</el-button
+      >
+    </div>
+
+    <!-- 人员表格 -->
+    <el-table
+      v-loading="loading"
+      :data="tableData"
+      style="width: 100%; margin-top: 10px"
+      border
+      :header-cell-style="changeHeaderCellStyle"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" align="center" width="50" />
+      <el-table-column label="序号" align="center" min-width="50" sortable>
+        <template #default="scope">
+          {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="employeeNo"
+        label="序号"
+        width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="dwMajor"
+        label="报警主类型"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="dwMinor"
+        label="报警次类型"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="wInductiveEventType"
+        label="归纳事件类型"
+        min-width="100"
+        align="center"
+      >
         <template #default="{ row }">
-          <img v-if="row.picture" :src="row.picture" style="height:50px" />
+          {{ (getInductiveEventName(row.wInductiveEventType), "access") }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="struTime"
+        label="事件时间"
+        min-width="200"
+        align="center"
+      >
+        <template #default="{ row }">
+          {{ formatNetDvrTime(row.struTime) }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="sNetUser"
+        label="用户"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="struRemoteHostAddr"
+        label="设备IP"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column label="操作" fixed="right" width="100" align="center">
+        <template slot-scope="scope">
+          <el-button type="text" size="small" @click="viewFaceInfo(scope.row)"
+            >查看</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
+    <!-- 分页 -->
+    <div style="margin-top: 10px; text-align: right">
+      <el-pagination
+        background
+        layout="total, sizes, prev, pager, next, jumper"
+        :total="total"
+        :current-page.sync="currentPage"
+        :page-size="pageSize"
+        :page-sizes="[5, 10, 20, 50, 100]"
+        @size-change="handleSizeChange"
+        @current-change="handlePageChange"
+      />
+    </div>
+    <!-- 搜索弹窗 -->
+    <event-search-dialog
+      :visible.sync="searchVisible"
+      :event-data="currentEvent"
+      @submit="handleEventSubmit"
+      @cancel="handleEventCancel"
+    />
+    <!-- 查看详情弹窗 -->
+    <event-detail-dialog
+      :visible.sync="viewVisible"
+      :event-data="currentEvent"
+    />
   </div>
 </template>
 
 <script>
-// import { getVehicleEvents } from '@/api/event'
+import request from "@/utils/request";
+import EventSearchDialog from "./EventSearchDialog.vue";
+import EventDetailDialog from "./EventDetailDialog.vue";
 
 export default {
+  name: "EventManage",
+  components: {
+    EventSearchDialog,
+    EventDetailDialog,
+  },
   data() {
     return {
-      filters: { plate_number: "", time_range: [] },
-      tableData: [],
-      loading: false
-    }
+      multipleSelection: [],
+      currentEvent: null,
+      loading: false,
+      devices: [], // 设备列表
+      selectedDeviceId: null, // 当前选择的设备
+
+      tableData: [
+        {
+          dwMajor: 1,
+          dwMinor: 2,
+          wInductiveEventType: 3,
+          struTime: {
+            dwYear: 2025,
+            dwMonth: 10,
+            dwDay: 24,
+            dwHour: 19,
+            dwMinute: 30,
+            dwSecond: 45,
+          },
+          sNetUser: "admin",
+          struRemoteHostAddr: "192.168.1.100",
+        },
+        {
+          dwMajor: 1,
+          dwMinor: 5,
+          wInductiveEventType: 1,
+          struTime: {
+            dwYear: 2025,
+            dwMonth: 10,
+            dwDay: 24,
+            dwHour: 20,
+            dwMinute: 15,
+            dwSecond: 10,
+          },
+          sNetUser: "operator",
+          struRemoteHostAddr: "192.168.1.101",
+        },
+      ],
+      total: 0,
+      currentPage: 1,
+      pageSize: 10,
+
+      searchVisible: false,
+      viewVisible: false,
+    };
+  },
+  mounted() {
+    this.getDeviceNameInfo();
   },
   methods: {
-    async loadData() {
-      this.loading = true
-      const params = {
-        plate_number: this.filters.plate_number,
-        start_time: this.filters.time_range[0],
-        end_time: this.filters.time_range[1]
+    async handleEventSubmit(eventData) {
+      console.log(eventData);
+      // const url = deviceData.id ? "/device/update" : "/device/add";
+      // const res = await request({ url, method: "POST", data: deviceData });
+      this.searchVisible = false;
+      // await this.$message(res.data);
+      // await this.getDeviceInfo();
+    },
+    handleEventCancel() {
+      this.searchVisible = false;
+    },
+    viewFaceInfo(row) {
+      this.currentEvent = row;
+      this.viewVisible = true;
+    },
+    formatNetDvrTime(time) {
+      if (!time) return "-";
+      const { dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond } = time;
+      return (
+        `${dwYear}-${String(dwMonth).padStart(2, "0")}-${String(dwDay).padStart(2, "0")} ` +
+        `${String(dwHour).padStart(2, "0")}:${String(dwMinute).padStart(2, "0")}:${String(dwSecond).padStart(2, "0")}`
+      );
+    },
+    getInductiveEventName(type, deviceType = "access") {
+      const maps = {
+        // 1️⃣ HIKVISION 门禁主机
+        access: {
+          0: "无效",
+          1: "认证通过",
+          2: "认证失败",
+          3: "开门动作",
+          4: "关门动作",
+          5: "门异常",
+          6: "远程操作",
+          7: "校时事件",
+          8: "设备异常事件",
+          9: "设备恢复正常事件",
+          10: "报警事件",
+          11: "报警恢复事件",
+          12: "呼叫中心",
+        },
+
+        // 2️⃣ HIKVISION 可视对讲
+        intercom: {
+          1: "防拆报警",
+          2: "劫持报警",
+          3: "多次密码开锁失败报警",
+          4: "门未开报警",
+          5: "门未关报警",
+          6: "通话对讲报警",
+          7: "密码开锁",
+          8: "劫持开锁",
+          9: "刷卡开锁",
+          10: "户主开锁",
+          11: "中心平台开锁",
+          12: "设备对讲",
+        },
+
+        // 3️⃣ HIKVISION 报警主机
+        alarm: {
+          1: "防区事件",
+          2: "主机事件",
+        },
+
+        // 4️⃣ 第三方 GJD 报警主机
+        gjd: {
+          1: "对象侦测报警",
+          2: "人为防拆报警",
+          3: "弱光检测报警",
+          4: "定时报警(周期性)",
+          5: "温度检测报警",
+        },
+
+        // 5️⃣ 第三方 Luminite 报警主机
+        luminite: {
+          1: "探测到移动",
+          2: "传感器被干扰",
+          3: "信号被遮挡",
+          4: "检测到震动",
+          5: "传感器故障",
+          6: "传感器位置移动",
+          7: "电池不足",
+          8: "手动冲突数据",
+          9: "接收机收到干扰信息",
+          10: "防拆弹簧松弛",
+        },
+
+        // 6️⃣ 第三方 OPTEX 报警主机
+        optex: {
+          1: "远&近发生事件",
+          2: "远距离事件",
+          3: "近距离事件",
+          4: "缓慢移动检测",
+          5: "干扰检测",
+          6: "防遮挡报警",
+          7: "防旋转报警",
+          8: "异常电路激活",
+          9: "污物覆盖(自检异常)",
+          10: "传感器异常",
+          11: "设备故障",
+          12: "设备不在线",
+          13: "设备已连接",
+          14: "区域A1事件",
+          15: "区域A2事件",
+          16: "区域B1事件",
+          17: "区域B2事件",
+          18: "区域A11事件",
+          19: "区域A12事件",
+          20: "区域A21事件",
+          21: "区域A22事件",
+          22: "区域B11事件",
+          23: "区域B12事件",
+          24: "区域B21事件",
+          25: "区域B22事件",
+        },
+      };
+
+      // 防御性处理
+      if (!type) return "无效";
+      const map = maps[deviceType] || maps.access;
+      return map[type] || `未知(${type})`;
+    },
+    handleSearch() {
+      console.log("查询");
+      this.currentEvent = {};
+      this.searchVisible = true;
+    },
+    // 表格样式修改
+    changeHeaderCellStyle(row, column, rowIndex, columnIndex) {
+      if (row.columnIndex === 0) {
+        return "background: #004279 ; color:#fff;"; // 修改的样式
+      } else {
+        return "background: #004279 ;color:#fff; ";
       }
-      // this.tableData = await getVehicleEvents(params)
-      this.loading = false
-    }
-  }
-}
+    },
+    // 监听多选变化
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    // 切换页码
+    handlePageChange(page) {
+      this.currentPage = page;
+      // this.searchUserInfo()
+    },
+    // 修改每页条数
+    handleSizeChange(size) {
+      this.pageSize = size;
+      this.currentPage = 1; // 切换条数后回到第一页
+      // this.searchUserInfo()
+    },
+    async getDeviceNameInfo() {
+      try {
+        this.loading = true;
+        const res = await request({
+          url: "/device/listDeviceName",
+          method: "GET",
+        });
+        // 判断返回结构
+        if (res && res.list) {
+          this.devices = res.list;
+          this.total = res.total || 0;
+          console.log("设备列表:", this.devices);
+          if (this.devices.length > 0) {
+            this.selectedDeviceId = this.devices[0].id;
+            console.log("选中的设备ID:", this.selectedDeviceId);
+            // 获取该设备的人员信息
+            // this.searchUserInfo()
+          }
+        } else {
+          this.devices = [];
+          this.total = 0;
+          this.$message.warning("未获取到设备数据");
+        }
+      } catch (error) {
+        console.error("获取设备信息失败:", error);
+        this.$message.error("获取设备列表失败,请检查网络或后端接口!");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
 </script>
+
+<style scoped>
+.car-event-page {
+  display: flex;
+  padding: 10px;
+}
+.query-panel {
+  width: 280px;
+  padding: 10px;
+  border-right: 1px solid #ddd;
+}
+.table-panel {
+  flex: 1;
+  padding: 10px;
+}
+.quick-btns {
+  display: flex;
+  gap: 6px;
+  margin: 6px 0;
+}
+.pagination {
+  margin-top: 10px;
+  text-align: right;
+}
+</style>

+ 329 - 0
src/views/doorcarManage/whitelistTable/NameListEditDialog.vue

@@ -0,0 +1,329 @@
+<template>
+  <el-dialog
+    :visible.sync="localVisible"
+    :title="formData.id ? '编辑名单' : '新增名单'"
+    @close="handleCancel"
+    custom-class="custom-dialog"
+  >
+    <el-form
+      ref="editForm"
+      :model="formData"
+      :rules="formRules"
+      label-width="100px"
+      label-position="right"
+      class="nameList-form"
+    >
+      <!-- 基本信息 -->
+      <div class="form-row">
+        <el-form-item label="设备ID" prop="deviceId">
+          <el-select v-model="formData.deviceId" placeholder="请选择设备">
+            <el-option
+              v-for="device in devices"
+              :key="device.id"
+              :label="device.name"
+              :value="device.id"
+            />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="车牌号" prop="plateNumber">
+          <el-input v-model="formData.plateNumber" placeholder="请输入车牌号" />
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="卡号" prop="cardNo">
+          <el-input v-model="formData.cardNo" placeholder="请输入卡号" />
+        </el-form-item>
+        <el-form-item label="通道号" prop="channel">
+          <el-input-number v-model="formData.channel" :min="1" :max="16" />
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="车牌类型" prop="plateType">
+          <el-select v-model="formData.plateType" placeholder="选择类型">
+            <el-option label="普通蓝牌" :value="0" />
+            <el-option label="新能源" :value="1" />
+            <el-option label="黄牌" :value="2" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="车牌颜色" prop="plateColor">
+          <el-select v-model="formData.plateColor" placeholder="选择颜色">
+            <el-option label="蓝色" :value="0" />
+            <el-option label="黄色" :value="1" />
+            <el-option label="黑色" :value="2" />
+            <el-option label="白色" :value="3" />
+          </el-select>
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="名单类型" prop="type">
+          <el-select v-model="formData.type" placeholder="请选择">
+            <el-option
+              v-for="type in types"
+              :key="type.value"
+              :label="type.label"
+              :value="type.value"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+
+      <div class="form-row">
+        <el-form-item label="开始时间" prop="accessStartTime">
+          <el-date-picker
+            v-model="formData.accessStartTime"
+            type="datetime"
+            placeholder="选择开始时间"
+          />
+        </el-form-item>
+        <el-form-item label="结束时间" prop="accessEndTime">
+          <el-date-picker
+            v-model="formData.accessEndTime"
+            type="datetime"
+            placeholder="选择结束时间"
+          />
+        </el-form-item>
+      </div>
+    </el-form>
+
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleSubmit">保存</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import request from "@/utils/request";
+
+export default {
+  name: "NameListEditDialog",
+  props: {
+    visible: Boolean,
+    nameListData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
+  data() {
+    return {
+      localVisible: this.visible, // ✅ 本地副本
+      devices: [],
+      loading: false,
+      types: [
+        { label: "白名单", value: "whitelist" },
+        { label: "黑名单", value: "blacklist" },
+      ],
+      formData: {
+        id: null,
+        deviceId: null,
+        plateNumber: "",
+        type: "whitelist",
+        accessStartTime: null,
+        accessEndTime: null,
+        cardNo: "",
+        channel: 1,
+        plateType: 0,
+        plateColor: 0,
+      },
+      formRules: {
+        plateNumber: [
+          { required: true, message: "请输入车牌号", trigger: "blur" },
+        ],
+        type: [
+          { required: true, message: "请选择名单类型", trigger: "change" },
+        ],
+        deviceId: [
+          { required: true, message: "请选择设备", trigger: "change" },
+        ],
+        accessStartTime: [
+          { required: true, message: "请选择开始时间", trigger: "change" },
+        ],
+        accessEndTime: [
+          {
+            required: true,
+            message: "请选择结束时间",
+            trigger: "change",
+          },
+          {
+            validator: (rule, value, callback) => {
+              const start = this.formData.accessStartTime
+                ? new Date(this.formData.accessStartTime)
+                : null;
+              const end = value ? new Date(value) : null;
+              if (start && end && end <= start) {
+                callback(new Error("结束时间必须大于开始时间"));
+              } else {
+                callback();
+              }
+            },
+            trigger: "change",
+          },
+        ],
+      },
+    };
+  },
+  watch: {
+    visible(val) {
+      this.localVisible = val; // ✅ 同步父组件的变化
+    },
+    localVisible(val) {
+      this.$emit("update:visible", val); // ✅ 通知父组件更新
+    },
+    nameListData: {
+      immediate: true,
+      handler(newVal) {
+        // console.log("newVal", newVal);
+        if (!newVal || Object.keys(newVal).length === 0) {
+          this.resetForm();
+          console.log("新增名单", this.formData);
+        } else {
+          this.formData = {
+            ...newVal,
+          };
+          console.log("编辑名单", this.formData);
+        }
+      },
+    },
+  },
+  mounted() {
+    this.getDeviceNameInfo();
+  },
+  methods: {
+    parseJsonArray(field) {
+      if (!field) return [];
+      if (Array.isArray(field)) return field;
+      try {
+        return JSON.parse(field);
+      } catch (e) {
+        return [];
+      }
+    },
+    resetForm() {
+      this.formData = {
+        id: null,
+        deviceId: null,
+        plateNumber: "",
+        type: "whitelist",
+        accessStartTime: null,
+        accessEndTime: null,
+        cardNo: "",
+        channel: 1,
+        plateType: 0,
+        plateColor: 0,
+      };
+    },
+    handleCancel() {
+      this.localVisible = false; // ✅ 修改本地值,不触发 prop 报错
+      this.$emit("cancel");
+    },
+    handleSubmit() {
+      this.$refs.editForm.validate((valid) => {
+        if (!valid) {
+          this.$message.error("请检查表单填写项");
+          return false;
+        }
+        // 提交时将数组转 JSON 字符串
+        const data = {
+          ...this.formData,
+        };
+        console.log("data", data);
+        this.$emit("submit", data);
+      });
+    },
+    /** ========== 获取设备列表 ========== */
+    async getDeviceNameInfo() {
+      try {
+        this.loading = true;
+        const res = await request({
+          url: "/device/listDeviceName",
+          method: "GET",
+        });
+        const list = res?.data?.list || res?.list || [];
+        this.devices = list;
+        console.log("设备列表:", list);
+        if (list.length > 0 && !this.formData.deviceId) {
+          this.formData.deviceId = list[0].id;
+        }
+      } catch (e) {
+        console.error("获取设备信息失败:", e);
+        this.$message.error("获取设备列表失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+  },
+};
+</script>
+
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 800px !important;
+}
+
+/* 标签文字颜色深色背景下可读 */
+.custom-dialog .el-dialog__title {
+  color: #fff;
+}
+
+.custom-dialog .el-dialog__header {
+  background-color: #003c68;
+  color: #ffffff;
+}
+
+.custom-dialog .el-dialog__headerbtn {
+  color: #ffffff;
+}
+
+.nameList-form {
+  background-color: #003c68;
+  padding: 15px 20px;
+}
+
+/* 两列布局 */
+.form-row {
+  display: flex;
+  gap: 20px;
+  margin-bottom: 10px;
+}
+
+/* 标签文字颜色深色背景下可读 */
+.nameList-form .el-form-item__label {
+  color: #fff;
+}
+
+/* 输入框文字颜色 */
+.nameList-form .el-input__inner,
+.nameList-form .el-select .el-input__inner,
+.nameList-form .el-input-number__input {
+  color: #000;
+  background-color: #fff;
+}
+
+/* 修改输入框 placeholder 文字颜色为黑色 */
+.nameList-form input::placeholder,
+.nameList-form .el-input__inner::placeholder {
+  color: #dbe1e7 !important;
+  opacity: 1; /* 避免部分浏览器透明度影响 */
+}
+
+/* 针对 select 的 placeholder(实际上是 input) */
+.nameList-form .el-select .el-input__inner::placeholder {
+  color: #dbe1e7 !important;
+}
+
+/* 针对 el-input-number */
+.nameList-form .el-input-number__input::placeholder {
+  color: #dbe1e7 !important;
+}
+
+/* 对话框底部按钮右对齐 */
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+</style>

+ 335 - 162
src/views/doorcarManage/whitelistTable/index.vue

@@ -1,40 +1,158 @@
 <template>
-  <div>
-    <el-button type="primary" @click="openDialog()">新增名单</el-button>
+  <div style="padding: 10px">
+    <!-- 搜索和操作 -->
+    <div style="height: 50px; display: flex; align-items: center; gap: 10px">
+      <!-- 选择设备 -->
+      <el-select
+        v-model="selectedDeviceId"
+        placeholder="选择设备"
+        size="small"
+        style="width: 150px"
+        clearable
+        @change="handleDeviceChange"
+      >
+        <el-option
+          v-for="device in devices"
+          :key="device.id"
+          :label="device.name"
+          :value="device.id"
+        />
+      </el-select>
+      <!-- 选择名单类型 -->
+      <el-select
+        v-model="selectedType"
+        placeholder="选择名单类型"
+        size="small"
+        style="width: 150px"
+        clearable
+        @change="handleTypeChange"
+      >
+        <el-option
+          v-for="type in types"
+          :key="type.value"
+          :label="type.label"
+          :value="type.value"
+        />
+      </el-select>
+      <!-- 输入车牌号 -->
+      <el-input
+        v-model="inputPlateNumber"
+        placeholder="输入车牌号"
+        size="small"
+        style="width: 150px"
+        clearable
+        @keyup.enter.native="handleSearch"
+      />
+      <el-button type="primary" size="small" @click="handleSearch"
+        >查询</el-button
+      >
+      <el-button type="primary" size="small" @click="handleAdd">新增</el-button>
+      <el-button
+        :disabled="multipleSelection.length === 0"
+        type="danger"
+        size="small"
+        @click="handleBatchDelete"
+        >批量删除</el-button
+      >
+    </div>
 
+    <!-- 表格 -->
     <el-table
       :data="tableData"
-      border
       style="width: 100%; margin-top: 10px"
+      border
+      :header-cell-style="changeHeaderCellStyle"
+      @selection-change="handleSelectionChange"
       v-loading="loading"
+      element-loading-text="加载中..."
     >
-      <el-table-column prop="plate_number" label="序号" align="center" min-width="50" />
-      <el-table-column prop="plate_number" label="车牌号" align="center"  min-width="100" />
+      <el-table-column type="selection" align="center" width="50" />
+      <el-table-column label="序号" align="center" min-width="50" sortable>
+        <template #default="scope">
+          {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="deviceId"
+        label="设备ID"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="plateNumber"
+        label="车牌号"
+        align="center"
+        min-width="120"
+        sortable
+      />
+      <el-table-column
+        prop="cardNo"
+        label="卡号"
+        align="center"
+        min-width="120"
+        sortable
+      />
+      <el-table-column
+        prop="channel"
+        label="通道号"
+        align="center"
+        min-width="80"
+        sortable
+      />
+      <el-table-column
+        prop="plateType"
+        label="车牌类型"
+        align="center"
+        min-width="100"
+        :formatter="formatPlateType"
+        sortable
+      />
+      <el-table-column
+        prop="plateColor"
+        label="车牌颜色"
+        align="center"
+        min-width="100"
+        :formatter="formatPlateColor"
+        sortable
+      />
       <el-table-column
         prop="type"
         label="名单类型"
         :formatter="formatType"
         align="center"
         min-width="100"
+        sortable
       />
       <el-table-column
-        prop="access_start_time"
+        prop="accessStartTime"
         label="开始时间"
         :formatter="formatTime"
         align="center"
-        min-width="200"
+        min-width="150"
+        sortable
       />
       <el-table-column
-        prop="access_end_time"
+        prop="accessEndTime"
         label="结束时间"
         :formatter="formatTime"
         align="center"
-        min-width="200"
+        min-width="150"
+        sortable
       />
-      <el-table-column label="操作" align="center" width="180">
+      <el-table-column
+        label="操作"
+        fixed="right"
+        align="center"
+        min-width="120"
+      >
         <template #default="{ row }">
-          <el-button size="small" type="primary" @click="editRow(row)">编辑</el-button>
-          <el-button size="small" type="danger" @click="deleteRow(row)">删除</el-button>
+          <el-button type="primary" size="small" @click="handleEdit(row)"
+            >编辑</el-button
+          >
+          <el-button type="danger" size="small" @click="handleDelete(row)"
+            >删除</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
@@ -42,205 +160,260 @@
     <!-- 分页 -->
     <div style="margin-top: 15px; text-align: right">
       <el-pagination
-        v-if="total > 0"
         background
-        layout="prev, pager, next"
-        :total="total"
+        :current-page="currentPage"
         :page-size="pageSize"
+        :total="total"
+        :page-sizes="[10, 15, 20, 25, 30]"
+        layout="total, prev, pager, next, jumper, sizes"
         @current-change="handlePageChange"
+        @size-change="handleSizeChange"
       />
     </div>
-
-    <!-- 弹窗 -->
-    <el-dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
-      <el-form :model="form" :rules="rules" ref="formRef" label-width="100px">
-        <el-form-item label="车牌号" prop="plate_number">
-          <el-input v-model="form.plate_number" />
-        </el-form-item>
-        <el-form-item label="名单类型" prop="type">
-          <el-select v-model="form.type" placeholder="请选择">
-            <el-option label="白名单" value="whitelist" />
-            <el-option label="黑名单" value="blacklist" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="生效时间" prop="access_start_time">
-          <el-date-picker
-            v-model="form.access_start_time"
-            type="datetime"
-            placeholder="选择开始时间"
-            style="width: 100%"
-          />
-        </el-form-item>
-        <el-form-item label="结束时间" prop="access_end_time">
-          <el-date-picker
-            v-model="form.access_end_time"
-            type="datetime"
-            placeholder="选择结束时间"
-            style="width: 100%"
-          />
-        </el-form-item>
-      </el-form>
-      <template #footer>
-        <el-button @click="dialogVisible = false">取消</el-button>
-        <el-button type="primary" @click="saveRow">保存</el-button>
-      </template>
-    </el-dialog>
+    <!-- 新增/编辑弹窗 -->
+    <NameListEditDialog
+      v-if="dialogVisible"
+      :visible.sync="dialogVisible"
+      :name-list-data="currentNameListData"
+      @submit="handleNameListSubmit"
+      @cancel="handleNameListCancel"
+    />
   </div>
 </template>
 
 <script>
-// ==== 模拟API ====
-// 实际项目中你应替换为:
-// import { getWhitelist, addWhitelist, updateWhitelist, removeWhitelist } from '@/api/whitelist'
-import axios from "axios";
+import request from "@/utils/request";
+import dayjs from "dayjs";
+import NameListEditDialog from "./NameListEditDialog.vue";
 
 export default {
+  name: "NameListManagement",
+  components: {
+    NameListEditDialog,
+  },
   data() {
     return {
-      tableData: [
-        {
-          id: 1,
-          plate_number: "粤A12345",
-          type: "whitelist",
-          access_start_time: "2025-10-01 08:00:00",
-          access_end_time: "2025-12-31 18:00:00"
-        },
-        {
-          id: 2,
-          plate_number: "粤B88888",
-          type: "blacklist",
-          access_start_time: "2025-09-01 00:00:00",
-          access_end_time: "2026-01-01 00:00:00"
-        },
-        {
-          id: 3,
-          plate_number: "粤C66666",
-          type: "whitelist",
-          access_start_time: "2025-10-10 07:30:00",
-          access_end_time: "2025-12-10 19:00:00"
-        }
+      // 下拉数据
+      devices: [],
+      types: [
+        { label: "白名单", value: "whitelist" },
+        { label: "黑名单", value: "blacklist" },
       ],
+      // 搜索条件
+      selectedDeviceId: "",
+      selectedType: "",
+      inputPlateNumber: "",
+      // 表格数据
+      tableData: [],
       loading: false,
       total: 0,
       pageSize: 10,
       currentPage: 1,
-
+      // 多选
+      multipleSelection: [],
+      // 弹窗
       dialogVisible: false,
-      dialogTitle: "新增名单",
-      editId: null,
-
-      form: {
-        plate_number: "",
-        type: "whitelist",
-        access_start_time: "",
-        access_end_time: "",
-      },
-
-      rules: {
-        plate_number: [{ required: true, message: "请输入车牌号", trigger: "blur" }],
-        type: [{ required: true, message: "请选择名单类型", trigger: "change" }],
-        access_start_time: [{ required: true, message: "请选择开始时间", trigger: "change" }],
-        access_end_time: [{ required: true, message: "请选择结束时间", trigger: "change" }],
-      },
+      dialogTitle: "",
+      saving: false,
+      currentNameListData: {},
     };
   },
-
   mounted() {
-    // this.loadData();
+    this.getDeviceNameInfo();
   },
-
   methods: {
-    // ===== 获取列表 =====
-    async loadData() {
+    handleDeviceChange() {
+      // 切换设备时重置其他搜索条件
+      this.selectedType = "";
+      this.inputPlateNumber = "";
+      this.currentPage = 1; // 重置分页
+      this.fetchData();
+    },
+    handleTypeChange() {
+      this.currentPage = 1; // 重置分页
+      this.fetchData();
+    },
+    // 获取设备列表
+    async getDeviceNameInfo() {
+      try {
+        this.loading = true;
+        const res = await request({
+          url: "/device/listDeviceName",
+          method: "GET",
+        });
+        const list = res?.data?.list || res?.list || [];
+        this.devices = list;
+        if (list.length > 0) {
+          this.selectedDeviceId = list[0].id;
+          this.fetchData();
+        }
+      } catch (e) {
+        console.error("获取设备信息失败:", e);
+        this.$message.error("获取设备列表失败");
+      } finally {
+        this.loading = false;
+      }
+    },
+    // 获取名单列表
+    async fetchData() {
+      if (!this.selectedDeviceId) {
+        this.tableData = [];
+        return;
+      }
       this.loading = true;
       try {
-        const { data } = await axios.get("/api/whitelist", {
-          params: { page: this.currentPage, pageSize: this.pageSize },
+        const data = {
+          deviceId: this.selectedDeviceId,
+          type: this.selectedType,
+          plateNumber: this.inputPlateNumber,
+          page: this.currentPage,
+          pageSize: this.pageSize,
+        };
+        console.log("名单列表参数", data);
+        const res = await request({
+          url: "/carCamera/nameList/list",
+          method: "GET",
+          params: data,
         });
-        this.tableData = data.records;
-        this.total = data.total;
+        this.tableData = res?.records || [];
+        this.total = res?.total || 0;
+        console.log("名单列表", this.tableData);
       } catch (e) {
-        console.error(e);
+        console.error("获取名单失败", e);
       } finally {
         this.loading = false;
       }
     },
-
-    // ===== 打开新增弹窗 =====
-    openDialog() {
-      console.log("openDialog")
-      this.dialogTitle = "新增名单";
-      this.form = {
-        plate_number: "",
-        type: "whitelist",
-        access_start_time: "",
-        access_end_time: "",
-      };
-      this.editId = null;
+    // 查询按钮
+    handleSearch() {
+      this.currentPage = 1;
+      this.fetchData();
+    },
+    // 新增名单
+    handleAdd() {
+      this.currentNameListData = {};
       this.dialogVisible = true;
-      console.log("openDialog this.dialogVisible", this.dialogVisible)
     },
-
-    // ===== 编辑 =====
-    editRow(row) {
-      this.dialogTitle = "编辑名单";
-      this.form = { ...row };
-      this.editId = row.id;
+    // 编辑名单
+    handleEdit(row) {
+      this.currentNameListData = { ...row };
       this.dialogVisible = true;
     },
-
-    // ===== 保存 =====
-    async saveRow() {
-      this.$refs.formRef.validate(async (valid) => {
-        if (!valid) return;
-        try {
-          if (this.editId) {
-            await axios.put(`/api/whitelist/${this.editId}`, this.form);
-            this.$message.success("修改成功");
-          } else {
-            await axios.post("/api/whitelist", this.form);
-            this.$message.success("新增成功");
-          }
-          this.dialogVisible = false;
-          this.loadData();
-        } catch (e) {
-          console.error(e);
-          this.$message.error("保存失败");
-        }
-      });
+    async handleNameListSubmit(nameListData) {
+      const url = nameListData.id
+        ? "/carCamera/nameList/update"
+        : "/carCamera/nameList/add";
+      const res = await request({ url, method: "POST", data: nameListData });
+      console.log("保存结果", res);
+      if (res.success || res.code === 0) {
+        this.$message.success("保存成功");
+        this.dialogVisible = false;
+        await this.fetchData();
+      } else {
+        this.$message.error(res.msg || "保存失败");
+      }
     },
-
-    // ===== 删除 =====
-    async deleteRow(row) {
-      try {
-        await this.$confirm(`确定要删除车牌 ${row.plate_number} 吗?`, "提示", {
+    handleNameListCancel() {
+      this.dialogVisible = false;
+    },
+    // 删除名单(单条或批量统一)
+    handleDelete(row) {
+      this.$confirm(
+        `确定要删除车牌号为【${row.plateNumber}】的名单吗?`,
+        "提示",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
           type: "warning",
+        },
+      )
+        .then(() => this.deleteIds([row.id]))
+        .catch(() => this.$message.info("已取消删除"));
+    },
+    handleBatchDelete() {
+      const ids = this.multipleSelection.map((i) => i.id);
+      if (!ids.length) {
+        this.$message.warning("请先选择要删除的名单");
+        return;
+      }
+      this.$confirm(
+        `确定要删除选中的 ${ids.length} 条名单吗?`,
+        "批量删除确认",
+        {
+          confirmButtonText: "确定",
+          cancelButtonText: "取消",
+          type: "warning",
+        },
+      )
+        .then(() => this.deleteIds(ids))
+        .catch(() => this.$message.info("已取消删除"));
+    },
+    // 通用删除方法
+    async deleteIds(ids) {
+      if (!ids.length) return;
+      try {
+        const res = await request({
+          url: "/carCamera/nameList/delete",
+          method: "POST",
+          data: { ids, deviceId: this.selectedDeviceId }, // 注意:统一传 ids 数组
         });
-        await axios.delete(`/api/whitelist/${row.id}`);
-        this.$message.success("删除成功");
-        this.loadData();
+        if (res.success) {
+          this.$message.success("删除成功");
+          this.fetchData();
+          this.multipleSelection = [];
+        } else {
+          this.$message.error(res.msg || "删除失败");
+        }
       } catch (e) {
-        if (e !== "cancel") this.$message.error("删除失败");
+        console.error("删除失败", e);
+        this.$message.error("请求出错,请稍后重试");
       }
     },
-
-    // ===== 分页 =====
+    // 表格样式
+    changeHeaderCellStyle() {
+      return "background: #004279; color: #fff;";
+    },
+    // 分页控制
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
     handlePageChange(page) {
       this.currentPage = page;
-      this.loadData();
+      this.fetchData();
     },
-
-    // ===== 格式化类型 =====
+    handleSizeChange(size) {
+      this.pageSize = size;
+      this.currentPage = 1;
+      this.fetchData();
+    },
+    // 格式化函数
     formatType(row) {
-      return row.type === "whitelist" ? "白名单" : "黑名单";
+      const map = {
+        whitelist: "白名单",
+        blacklist: "黑名单",
+      };
+      return map[row.type] || "-";
     },
-
-    // ===== 格式化时间 =====
+    // 时间格式化
     formatTime(row, column, cellValue) {
-      if (!cellValue) return "";
-      const date = new Date(cellValue);
-      return date.toLocaleString();
+      return cellValue ? dayjs(cellValue).format("YYYY-MM-DD HH:mm:ss") : "";
+    },
+    // 车牌颜色格式化
+    formatPlateColor(row) {
+      const map = { 0: "蓝色", 1: "黄色", 2: "黑色", 3: "白色" };
+      return map[row.plateColor] || "-";
+    },
+    // 车牌类型格式化
+    formatPlateType(row) {
+      const map = { 0: "普通蓝牌", 1: "新能源", 2: "黄牌" };
+      return map[row.plateType] || "-";
     },
   },
 };
 </script>
+
+<style scoped>
+.nameList-edit-dialog .el-form-item {
+  margin-bottom: 18px;
+}
+</style>

+ 240 - 0
src/views/doormanManage/InOutRecords/index.vue

@@ -0,0 +1,240 @@
+<template>
+  <div style="padding: 10px">
+    <el-row gutter="10" style="margin-bottom: 10px">
+      <el-col :span="4">
+        <el-select
+          v-model="selectedDeviceId"
+          placeholder="选择设备"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="device in devices"
+            :key="device.id"
+            :label="device.name"
+            :value="device.id"
+          />
+        </el-select>
+      </el-col>
+      <el-col :span="4">
+        <el-input v-model="inputName" placeholder="姓名" />
+      </el-col>
+      <el-col :span="4">
+        <el-select
+          v-model="eventType"
+          placeholder="进出类型"
+          style="width: 100%"
+        >
+          <el-option label="全部" :value="''" />
+          <el-option label="进入" :value="'in'" />
+          <el-option label="离开" :value="'out'" />
+        </el-select>
+      </el-col>
+      <el-col :span="6">
+        <el-date-picker
+          v-model="timeRange"
+          type="datetimerange"
+          start-placeholder="开始时间"
+          end-placeholder="结束时间"
+          style="width: 100%"
+        />
+      </el-col>
+      <el-col :span="4">
+        <el-button type="primary" @click="fetchData">查询</el-button>
+        <el-button type="success" @click="openAddDialog">新增</el-button>
+        <el-button
+          type="danger"
+          @click="batchDelete"
+          :disabled="multipleSelection.length === 0"
+          >批量删除</el-button
+        >
+      </el-col>
+    </el-row>
+
+    <el-table
+      :data="tableData"
+      border
+      v-loading="loading"
+      @selection-change="handleSelectionChange"
+    >
+      <el-table-column type="selection" width="50" />
+      <el-table-column prop="name" label="姓名" min-width="120" />
+      <el-table-column prop="deviceId" label="设备" min-width="100" />
+      <el-table-column prop="eventType" label="类型" min-width="80" />
+      <el-table-column prop="captureTime" label="抓拍时间" min-width="180" />
+      <el-table-column prop="photoUrl" label="照片" min-width="120">
+        <template #default="{ row }">
+          <img v-if="row.photoUrl" :src="row.photoUrl" width="60" />
+        </template>
+      </el-table-column>
+      <el-table-column prop="remark" label="备注" min-width="150" />
+      <el-table-column label="操作" width="180">
+        <template #default="{ row }">
+          <el-button type="text" size="small" @click="openEditDialog(row)"
+            >编辑</el-button
+          >
+          <el-button type="text" size="small" @click="deleteRecord(row.id)"
+            >删除</el-button
+          >
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <el-pagination
+      background
+      layout="total, prev, pager, next, jumper, sizes"
+      :total="total"
+      :current-page.sync="currentPage"
+      :page-size.sync="pageSize"
+      @current-change="fetchData"
+      @size-change="fetchData"
+      style="margin-top: 10px; text-align: right"
+    />
+
+    <!-- 新增/编辑弹窗 -->
+    <el-dialog :title="dialogTitle" :visible.sync="dialogVisible">
+      <el-form :model="form">
+        <el-form-item label="姓名"
+          ><el-input v-model="form.name"
+        /></el-form-item>
+        <el-form-item label="设备"
+          ><el-select v-model="form.deviceId">
+            <el-option
+              v-for="device in devices"
+              :key="device.id"
+              :label="device.name"
+              :value="device.id"
+            /> </el-select
+        ></el-form-item>
+        <el-form-item label="类型"
+          ><el-select v-model="form.eventType">
+            <el-option label="进入" value="in" />
+            <el-option label="离开" value="out" /> </el-select
+        ></el-form-item>
+        <el-form-item label="抓拍时间"
+          ><el-date-picker v-model="form.captureTime" type="datetime"
+        /></el-form-item>
+        <el-form-item label="照片"
+          ><el-input v-model="form.photoUrl"
+        /></el-form-item>
+        <el-form-item label="备注"
+          ><el-input v-model="form.remark"
+        /></el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="saveRecord">保存</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import request from "@/utils/request";
+export default {
+  data() {
+    return {
+      devices: [],
+      selectedDeviceId: null,
+      inputName: "",
+      eventType: "",
+      timeRange: [],
+      tableData: [],
+      total: 0,
+      currentPage: 1,
+      pageSize: 10,
+      loading: false,
+      multipleSelection: [],
+      dialogVisible: false,
+      dialogTitle: "新增",
+      form: {},
+      editId: null,
+    };
+  },
+  mounted() {
+    this.getDevices();
+  },
+  methods: {
+    async getDevices() {
+      const res = await request({
+        url: "/device/listDeviceName",
+        method: "GET",
+      });
+      this.devices = res.list || [];
+    },
+    handleSelectionChange(val) {
+      this.multipleSelection = val;
+    },
+    async fetchData() {
+      this.loading = true;
+      try {
+        const params = {
+          deviceId: this.selectedDeviceId,
+          name: this.inputName,
+          eventType: this.eventType,
+          page: this.currentPage,
+          pageSize: this.pageSize,
+        };
+        if (this.timeRange.length === 2) {
+          params.startTime = this.timeRange[0];
+          params.endTime = this.timeRange[1];
+        }
+        const res = await request({
+          url: "/peopleDoor/accessRecord/list",
+          method: "GET",
+          params,
+        });
+        this.tableData = res.records || [];
+        this.total = res.total || 0;
+      } finally {
+        this.loading = false;
+      }
+    },
+    openAddDialog() {
+      this.dialogTitle = "新增";
+      this.dialogVisible = true;
+      this.form = {};
+      this.editId = null;
+    },
+    openEditDialog(row) {
+      this.dialogTitle = "编辑";
+      this.dialogVisible = true;
+      this.form = { ...row };
+      this.editId = row.id;
+    },
+    async saveRecord() {
+      const url = this.editId
+        ? "/peopleDoor/accessRecord/update"
+        : "/peopleDoor/accessRecord/add";
+      const res = await request({ url, method: "POST", data: this.form });
+      if (res.success) {
+        this.dialogVisible = false;
+        this.fetchData();
+      }
+    },
+    async deleteRecord(id) {
+      if (confirm("确定删除吗?")) {
+        const res = await request({
+          url: "/peopleDoor/accessRecord/delete",
+          method: "POST",
+          data: { id },
+        });
+        if (res.success) this.fetchData();
+      }
+    },
+    async batchDelete() {
+      if (confirm("确定批量删除吗?")) {
+        const ids = this.multipleSelection.map((i) => i.id);
+        const res = await request({
+          url: "/peopleDoor/accessRecord/batchDelete",
+          method: "POST",
+          data: { ids },
+        });
+        if (res.success) {
+          this.fetchData();
+          this.multipleSelection = [];
+        }
+      }
+    },
+  },
+};
+</script>

+ 126 - 50
src/views/doormanManage/deviceManage/index.vue

@@ -12,45 +12,127 @@
         @keyup.enter.native="handleSearch"
       />
       <el-button type="primary" size="small" @click="handleSearch"
-      >查询</el-button
+        >查询</el-button
       >
       <el-button type="primary" size="small" @click="handleAdd">新增</el-button>
     </div>
 
     <el-table
       :data="devices"
-      style="width: 100%;
-      margin-top: 10px"
+      style="width: 100%; margin-top: 10px"
       border
       :header-cell-style="changeHeaderCellStyle"
       v-loading="loading"
     >
-      <el-table-column type="index" label="序号" width="60" align="center">
-        <template slot-scope="scope">{{ scope.$index + 1 }}</template>
+      <el-table-column label="序号" align="center" min-width="50" sortable>
+        <template #default="scope">
+          {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
+        </template>
       </el-table-column>
-      <el-table-column prop="id" label="设备ID" align="center" width="60" />
-      <el-table-column prop="name" label="设备名称" align="center" min-width="100" />
-      <el-table-column prop="serialNumber" label="设备序列号" align="center" min-width="100" />
-      <el-table-column prop="type" label="设备类型" align="center" min-width="100" />
-      <el-table-column prop="location" label="设备位置" align="center" min-width="150" />
-      <el-table-column prop="ip" label="设备IP" align="center" min-width="100" />
-      <el-table-column prop="port" label="设备Port" align="center" min-width="100" />
-      <el-table-column prop="account" label="账号" align="center" min-width="100" />
-      <el-table-column prop="password" label="密码" align="center" min-width="100" />
-      <el-table-column prop="status" label="设备状态" align="center" min-width="100" />
-      <el-table-column prop="firmwareVersion" label="固件版本" align="center" min-width="100" />
-      <el-table-column prop="notes" label="备注" align="center" min-width="100" />
-      <el-table-column label="操作" fixed="right" align="center" min-width="100">
+      <el-table-column
+        prop="id"
+        label="设备ID"
+        align="center"
+        width="60"
+        sortable
+      />
+      <el-table-column
+        prop="name"
+        label="设备名称"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="serialNumber"
+        label="设备序列号"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="type"
+        label="设备类型"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="location"
+        label="设备位置"
+        align="center"
+        min-width="150"
+        sortable
+      />
+      <el-table-column
+        prop="ip"
+        label="设备IP"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="port"
+        label="设备Port"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="account"
+        label="账号"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="password"
+        label="密码"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="status"
+        label="设备状态"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="firmwareVersion"
+        label="固件版本"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        prop="notes"
+        label="备注"
+        align="center"
+        min-width="100"
+        sortable
+      />
+      <el-table-column
+        label="操作"
+        fixed="right"
+        align="center"
+        min-width="100"
+      >
         <template #default="{ row }">
-          <el-button type="text" size="small" @click="handleEdit(row)">编辑</el-button>
-          <el-button type="text" size="small" @click="handleDelete(row)">删除</el-button>
+          <el-button type="primary" size="small" @click="handleEdit(row)"
+            >编辑</el-button
+          >
+          <el-button type="danger" size="small" @click="handleDelete(row)"
+            >删除</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
     <!-- 分页 -->
     <el-pagination
       class="custom-pagination"
-      style="margin-top: 20px; text-align: right;"
+      style="margin-top: 20px; text-align: right"
       :current-page="currentPage"
       :page-size="pageSize"
       :total="total"
@@ -75,8 +157,10 @@ import DeviceEditDialog from "./DeviceEditDialog.vue";
 import request from "@/utils/request";
 
 export default {
-  name: 'DeviceManagement',
-  components: { DeviceEditDialog },
+  name: "DeviceManagement",
+  components: {
+    DeviceEditDialog,
+  },
   data() {
     return {
       searchDeviceName: "",
@@ -89,36 +173,28 @@ export default {
 
       dialogVisible: false,
       currentDeviceData: {},
-      rules: {
-        model: [{ required: true, message: '请输入设备型号', trigger: 'blur' }],
-        location: [{ required: true, message: '请输入设备位置', trigger: 'blur' }],
-        ip: [{ required: true, message: '请输入设备IP', trigger: 'blur' }],
-        port: [{ required: true, message: '请输入设备Port', trigger: 'blur' }],
-        account: [{ required: true, message: '请输入账号', trigger: 'blur' }],
-        password: [{ required: true, message: '请输入密码', trigger: 'blur' }]
-      },
-    }
+    };
   },
   mounted() {
-    this.getDeviceInfo()
+    this.getDeviceInfo();
   },
   methods: {
     handleSearch() {
       this.currentPage = 1;
       this.getDeviceInfo();
     },
-    handleAdd(){
+    handleAdd() {
       this.currentDeviceData = {
-        name: '',
-        model: '',
-        location: '',
-        ip: '',
-        port: '',
-        account: '',
-        password: '',
+        name: "",
+        model: "",
+        location: "",
+        ip: "",
+        port: "",
+        account: "",
+        password: "",
         syncMode: 0,
-        syncFromDeviceId: null
-      }
+        syncFromDeviceId: null,
+      };
       this.dialogVisible = true;
     },
     handleEdit(row) {
@@ -162,7 +238,7 @@ export default {
     },
     async getDeviceInfo() {
       try {
-        this.loading = true
+        this.loading = true;
         const res = await request({
           url: "/device/list",
           method: "POST",
@@ -186,15 +262,15 @@ export default {
         console.error("获取设备信息失败:", error);
         this.$message.error("获取设备列表失败,请检查网络或后端接口!");
       } finally {
-        this.loading = false
+        this.loading = false;
       }
     },
-  }
-}
+  },
+};
 </script>
 
 <style scoped>
-
-.dialog-footer { text-align: right; }
-
+.dialog-footer {
+  text-align: right;
+}
 </style>

+ 42 - 34
src/views/doormanManage/eventManage/EventDetailDialog.vue

@@ -2,10 +2,8 @@
   <el-dialog
     title="事件详情"
     :visible.sync="visible"
-    width="1000px"
-    height="800px"
-    class="user-detail-dialog"
-    @close="$emit('view:visible', false)"
+    @close="handleCancel"
+    custom-class="custom-dialog"
   >
     <el-descriptions
       :column="2"
@@ -292,42 +290,52 @@ export default {
       },
     };
   },
+  methods: {
+    handleCancel() {
+      // this.$emit("cancel");
+    },
+  },
 };
 </script>
 
-<style scoped>
-.user-detail-dialog .el-descriptions {
-  background: #fafafa;
-  border-radius: 8px;
-  padding: 10px 15px;
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 1000px !important;
 }
 
-.user-detail-dialog .el-descriptions__label {
-  width: 180px !important;
-  font-weight: 600;
-  color: #333;
-  background: #f5f7fa;
-  text-align: right;
-}
+/*.user-detail-dialog .el-descriptions {*/
+/*  background: #fafafa;*/
+/*  border-radius: 8px;*/
+/*  padding: 10px 15px;*/
+/*}*/
 
-.user-detail-dialog .el-descriptions__content {
-  color: #555;
-  word-break: break-all;
-}
+/*.user-detail-dialog .el-descriptions__label {*/
+/*  width: 180px !important;*/
+/*  font-weight: 600;*/
+/*  color: #333;*/
+/*  background: #f5f7fa;*/
+/*  text-align: right;*/
+/*}*/
 
-.door-plan {
-  background: #fff;
-  border: 1px solid #ebeef5;
-  border-radius: 6px;
-  padding: 6px;
-  margin-top: 4px;
-  font-size: 13px;
-  color: #606266;
-}
+/*.user-detail-dialog .el-descriptions__content {*/
+/*  color: #555;*/
+/*  word-break: break-all;*/
+/*}*/
 
-.dialog-footer {
-  text-align: right;
-  padding-top: 10px;
-  border-top: 1px solid #ebeef5;
-}
+/*.door-plan {*/
+/*  background: #fff;*/
+/*  border: 1px solid #ebeef5;*/
+/*  border-radius: 6px;*/
+/*  padding: 6px;*/
+/*  margin-top: 4px;*/
+/*  font-size: 13px;*/
+/*  color: #606266;*/
+/*}*/
+
+/*.dialog-footer {*/
+/*  text-align: right;*/
+/*  padding-top: 10px;*/
+/*  border-top: 1px solid #ebeef5;*/
+/*}*/
 </style>

+ 90 - 32
src/views/doormanManage/eventManage/EventSearchDialog.vue

@@ -1,27 +1,26 @@
 <template>
   <el-dialog
-    title="查询事件数据"
     :visible.sync="visible"
-    width="1200px"
-    class="event-search-dialog"
-    @close="$emit('view:visible', false)"
+    title="查询事件数据"
+    @close="handleCancel"
+    custom-class="custom-dialog"
   >
     <el-form
-      :model="form"
+      :model="formData"
       ref="searchForm"
-      label-width="220px"
+      label-width="180px"
       class="user-form"
     >
       <!-- 主次类型 -->
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="报警主类型">
-            <el-input v-model="form.dwMajor" placeholder="0-全部" />
+            <el-input v-model="formData.dwMajor" placeholder="0-全部" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="报警次类型">
-            <el-input v-model="form.dwMinor" placeholder="0-全部" />
+            <el-input v-model="formData.dwMinor" placeholder="0-全部" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -31,7 +30,7 @@
         <el-col :span="12">
           <el-form-item label="开始时间">
             <el-date-picker
-              v-model="form.struStartTime"
+              v-model="formData.struStartTime"
               type="datetime"
               placeholder="请选择开始时间"
               value-format="yyyy-MM-dd HH:mm:ss"
@@ -41,7 +40,7 @@
         <el-col :span="12">
           <el-form-item label="结束时间">
             <el-date-picker
-              v-model="form.struEndTime"
+              v-model="formData.struEndTime"
               type="datetime"
               placeholder="请选择结束时间"
               value-format="yyyy-MM-dd HH:mm:ss"
@@ -54,12 +53,12 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="卡号">
-            <el-input v-model="form.byCardNo" placeholder="为空默认全部" />
+            <el-input v-model="formData.byCardNo" placeholder="为空默认全部" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="持卡人姓名">
-            <el-input v-model="form.byName" placeholder="为空默认全部" />
+            <el-input v-model="formData.byName" placeholder="为空默认全部" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -68,7 +67,7 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="是否带图片 (0-不带,1-带)">
-            <el-select v-model="form.byPicEnable" placeholder="选择">
+            <el-select v-model="formData.byPicEnable" placeholder="选择">
               <el-option label="不带图片" :value="0" />
               <el-option label="带图片" :value="1" />
             </el-select>
@@ -76,7 +75,7 @@
         </el-col>
         <el-col :span="12">
           <el-form-item label="时间类型">
-            <el-select v-model="form.byTimeType" placeholder="选择">
+            <el-select v-model="formData.byTimeType" placeholder="选择">
               <el-option label="设备本地时间(0)" :value="0" />
               <el-option label="UTC时间(1)" :value="1" />
             </el-select>
@@ -88,12 +87,18 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="起始流水号">
-            <el-input v-model="form.dwBeginSerialNo" placeholder="0 默认全部" />
+            <el-input
+              v-model="formData.dwBeginSerialNo"
+              placeholder="0 默认全部"
+            />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="结束流水号">
-            <el-input v-model="form.dwEndSerialNo" placeholder="0 默认全部" />
+            <el-input
+              v-model="formData.dwEndSerialNo"
+              placeholder="0 默认全部"
+            />
           </el-form-item>
         </el-col>
       </el-row>
@@ -102,13 +107,13 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="IOT通道号">
-            <el-input v-model="form.dwIOTChannelNo" placeholder="0-无效" />
+            <el-input v-model="formData.dwIOTChannelNo" placeholder="0-无效" />
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="归纳事件类型">
             <el-select
-              v-model="form.wInductiveEventType"
+              v-model="formData.wInductiveEventType"
               placeholder="选择归纳事件类型(0-无效)"
               filterable
               clearable
@@ -196,16 +201,22 @@
       <el-row :gutter="20">
         <el-col :span="12">
           <el-form-item label="搜索方式">
-            <el-select v-model="form.bySearchType" placeholder="选择方式">
+            <el-select v-model="formData.bySearchType" placeholder="选择方式">
               <el-option label="0-保留" :value="0" />
-              <el-option label="1-按事件源搜索(此时通道号为非视频通道号)" :value="1" />
+              <el-option
+                label="1-按事件源搜索(此时通道号为非视频通道号)"
+                :value="1"
+              />
               <el-option label="2-按监控点ID搜索" :value="2" />
             </el-select>
           </el-form-item>
         </el-col>
         <el-col :span="12">
           <el-form-item label="监控点ID">
-            <el-input v-model="form.szMonitorID" placeholder="由设备序列号、通道类型、编号组成,例如门禁点:设备序列号+“DOOR”+门编号" />
+            <el-input
+              v-model="formData.szMonitorID"
+              placeholder="由设备序列号、通道类型、编号组成,例如门禁点:设备序列号+“DOOR”+门编号"
+            />
           </el-form-item>
         </el-col>
       </el-row>
@@ -214,7 +225,7 @@
       <el-row>
         <el-col :span="12">
           <el-form-item label="工号(人员ID)">
-            <el-input v-model="form.byEmployeeNo" />
+            <el-input v-model="formData.byEmployeeNo" />
           </el-form-item>
         </el-col>
       </el-row>
@@ -222,8 +233,8 @@
 
     <!-- 底部 -->
     <div slot="footer" class="dialog-footer">
-      <el-button @click="handleClose">取 消</el-button>
-      <el-button type="primary" @click="submitForm">查 询</el-button>
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="submitForm">查询</el-button>
     </div>
   </el-dialog>
 </template>
@@ -231,10 +242,16 @@
 <script>
 export default {
   name: "EventSearchDialog",
-  props: { visible: Boolean },
+  props: {
+    visible: Boolean,
+    eventData: {
+      type: Object,
+      default: () => ({}),
+    },
+  },
   data() {
     return {
-      form: {
+      formData: {
         dwMajor: 0,
         dwMinor: 0,
         struStartTime: "",
@@ -253,10 +270,51 @@ export default {
       },
     };
   },
+  watch: {
+    eventData: {
+      immediate: true,
+      handler(newVal) {
+        // if (!newVal || Object.keys(newVal).length === 0) {
+        //   // 新增设备
+        console.log("newVal", newVal);
+        this.resetForm();
+        // } else {
+        //   this.formData = {
+        //     ...this.formData,
+        //     ...newVal,
+        //     doors: this.parseJsonArray(newVal.doors),
+        //     channels: this.parseJsonArray(newVal.channels)
+        //   };
+        // }
+      },
+    },
+  },
   methods: {
+    resetForm() {
+      this.formData = {
+        dwMajor: 0,
+        dwMinor: 0,
+        struStartTime: "",
+        struEndTime: "",
+        byCardNo: "",
+        byName: "",
+        byPicEnable: 0,
+        byTimeType: 0,
+        dwBeginSerialNo: 0,
+        dwEndSerialNo: 0,
+        dwIOTChannelNo: 0,
+        wInductiveEventType: 0,
+        bySearchType: 0,
+        szMonitorID: "",
+        byEmployeeNo: "",
+      };
+    },
+    handleCancel() {
+      // this.$emit("cancel");
+    },
     submitForm() {
       const data = {
-        ...this.form,
+        ...this.formData,
         dwSize: 0, // SDK要求填结构体大小,实际由底层计算,这里仅示意
       };
       console.log("📡 事件查询请求体:", JSON.stringify(data, null, 2));
@@ -266,12 +324,12 @@ export default {
 };
 </script>
 
-<style scoped>
-.user-form {
-  background-color: #0a427c;
-  padding: 10px 20px;
-  border-radius: 8px;
+<style>
+.custom-dialog {
+  background-color: #003c68;
+  width: 1000px !important;
 }
+
 .user-form .el-input,
 .user-form .el-select,
 .user-form .el-date-picker {

+ 168 - 99
src/views/doormanManage/eventManage/index.vue

@@ -18,7 +18,12 @@
           :value="device.id"
         />
       </el-select>
-      <el-button type="primary" @click="searchEventInfo()" style="margin-left: 10px">点击输入查询条件进行查询</el-button>
+      <el-button
+        type="primary"
+        @click="handleSearch()"
+        style="margin-left: 10px"
+        >点击输入查询条件进行查询</el-button
+      >
     </div>
 
     <!-- 人员表格 -->
@@ -30,28 +35,70 @@
       :header-cell-style="changeHeaderCellStyle"
       @selection-change="handleSelectionChange"
     >
-      <el-table-column prop="employeeNo" label="序号" width="100" align="center" />
-      <el-table-column prop="dwMajor" label="报警主类型" min-width="100" align="center" />
-      <el-table-column prop="dwMinor" label="报警次类型" min-width="100" align="center" />
-      <el-table-column prop="wInductiveEventType" label="归纳事件类型" min-width="100" align="center">
+      <el-table-column type="selection" align="center" width="50" />
+      <el-table-column label="序号" align="center" min-width="50" sortable>
+        <template #default="scope">
+          {{ (currentPage - 1) * pageSize + scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column
+        prop="employeeNo"
+        label="序号"
+        width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="dwMajor"
+        label="报警主类型"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="dwMinor"
+        label="报警次类型"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="wInductiveEventType"
+        label="归纳事件类型"
+        min-width="100"
+        align="center"
+      >
         <template #default="{ row }">
-          {{ getInductiveEventName(row.wInductiveEventType), "access" }}
+          {{ (getInductiveEventName(row.wInductiveEventType), "access") }}
         </template>
       </el-table-column>
-      <el-table-column prop="struTime" label="事件时间" min-width="200" align="center">
+      <el-table-column
+        prop="struTime"
+        label="事件时间"
+        min-width="200"
+        align="center"
+      >
         <template #default="{ row }">
           {{ formatNetDvrTime(row.struTime) }}
         </template>
       </el-table-column>
-      <el-table-column prop="sNetUser" label="用户" min-width="100" align="center" />
-      <el-table-column prop="struRemoteHostAddr" label="设备IP" min-width="100" align="center" />
+      <el-table-column
+        prop="sNetUser"
+        label="用户"
+        min-width="100"
+        align="center"
+      />
+      <el-table-column
+        prop="struRemoteHostAddr"
+        label="设备IP"
+        min-width="100"
+        align="center"
+      />
       <el-table-column label="操作" fixed="right" width="100" align="center">
         <template slot-scope="scope">
-          <el-button type="text" size="small" @click="viewFaceInfo(scope.row)">查看</el-button>
+          <el-button type="text" size="small" @click="viewFaceInfo(scope.row)"
+            >查看</el-button
+          >
         </template>
       </el-table-column>
     </el-table>
-
     <!-- 分页 -->
     <div style="margin-top: 10px; text-align: right">
       <el-pagination
@@ -65,11 +112,18 @@
         @current-change="handlePageChange"
       />
     </div>
-
-    <event-search-dialog :visible.sync="searchVisible" :detail="currentEvent" /><!-- 查看详情弹窗 -->
-
+    <!-- 搜索弹窗 -->
+    <event-search-dialog
+      :visible.sync="searchVisible"
+      :event-data="currentEvent"
+      @submit="handleEventSubmit"
+      @cancel="handleEventCancel"
+    />
     <!-- 查看详情弹窗 -->
-    <event-detail-dialog :visible.sync="viewVisible" :detail="currentEvent" />
+    <event-detail-dialog
+      :visible.sync="viewVisible"
+      :event-data="currentEvent"
+    />
   </div>
 </template>
 
@@ -86,6 +140,7 @@ export default {
   },
   data() {
     return {
+      multipleSelection: [],
       currentEvent: null,
       loading: false,
       devices: [], // 设备列表
@@ -129,123 +184,137 @@ export default {
 
       searchVisible: false,
       viewVisible: false,
-
-    }
+    };
   },
   mounted() {
     this.getDeviceNameInfo();
   },
   methods: {
+    async handleEventSubmit(eventData) {
+      console.log(eventData);
+      // const url = deviceData.id ? "/device/update" : "/device/add";
+      // const res = await request({ url, method: "POST", data: deviceData });
+      this.searchVisible = false;
+      // await this.$message(res.data);
+      // await this.getDeviceInfo();
+    },
+    handleEventCancel() {
+      this.searchVisible = false;
+    },
+    viewFaceInfo(row) {
+      this.currentEvent = row;
+      this.viewVisible = true;
+    },
     formatNetDvrTime(time) {
-      if (!time) return '-'
-      const { dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond } = time
-      return `${dwYear}-${String(dwMonth).padStart(2, '0')}-${String(dwDay).padStart(2, '0')} ` +
-        `${String(dwHour).padStart(2, '0')}:${String(dwMinute).padStart(2, '0')}:${String(dwSecond).padStart(2, '0')}`
+      if (!time) return "-";
+      const { dwYear, dwMonth, dwDay, dwHour, dwMinute, dwSecond } = time;
+      return (
+        `${dwYear}-${String(dwMonth).padStart(2, "0")}-${String(dwDay).padStart(2, "0")} ` +
+        `${String(dwHour).padStart(2, "0")}:${String(dwMinute).padStart(2, "0")}:${String(dwSecond).padStart(2, "0")}`
+      );
     },
-    getInductiveEventName(type, deviceType = 'access') {
+    getInductiveEventName(type, deviceType = "access") {
       const maps = {
         // 1️⃣ HIKVISION 门禁主机
         access: {
-          0: '无效',
-          1: '认证通过',
-          2: '认证失败',
-          3: '开门动作',
-          4: '关门动作',
-          5: '门异常',
-          6: '远程操作',
-          7: '校时事件',
-          8: '设备异常事件',
-          9: '设备恢复正常事件',
-          10: '报警事件',
-          11: '报警恢复事件',
-          12: '呼叫中心',
+          0: "无效",
+          1: "认证通过",
+          2: "认证失败",
+          3: "开门动作",
+          4: "关门动作",
+          5: "门异常",
+          6: "远程操作",
+          7: "校时事件",
+          8: "设备异常事件",
+          9: "设备恢复正常事件",
+          10: "报警事件",
+          11: "报警恢复事件",
+          12: "呼叫中心",
         },
 
         // 2️⃣ HIKVISION 可视对讲
         intercom: {
-          1: '防拆报警',
-          2: '劫持报警',
-          3: '多次密码开锁失败报警',
-          4: '门未开报警',
-          5: '门未关报警',
-          6: '通话对讲报警',
-          7: '密码开锁',
-          8: '劫持开锁',
-          9: '刷卡开锁',
-          10: '户主开锁',
-          11: '中心平台开锁',
-          12: '设备对讲',
+          1: "防拆报警",
+          2: "劫持报警",
+          3: "多次密码开锁失败报警",
+          4: "门未开报警",
+          5: "门未关报警",
+          6: "通话对讲报警",
+          7: "密码开锁",
+          8: "劫持开锁",
+          9: "刷卡开锁",
+          10: "户主开锁",
+          11: "中心平台开锁",
+          12: "设备对讲",
         },
 
         // 3️⃣ HIKVISION 报警主机
         alarm: {
-          1: '防区事件',
-          2: '主机事件',
+          1: "防区事件",
+          2: "主机事件",
         },
 
         // 4️⃣ 第三方 GJD 报警主机
         gjd: {
-          1: '对象侦测报警',
-          2: '人为防拆报警',
-          3: '弱光检测报警',
-          4: '定时报警(周期性)',
-          5: '温度检测报警',
+          1: "对象侦测报警",
+          2: "人为防拆报警",
+          3: "弱光检测报警",
+          4: "定时报警(周期性)",
+          5: "温度检测报警",
         },
 
         // 5️⃣ 第三方 Luminite 报警主机
         luminite: {
-          1: '探测到移动',
-          2: '传感器被干扰',
-          3: '信号被遮挡',
-          4: '检测到震动',
-          5: '传感器故障',
-          6: '传感器位置移动',
-          7: '电池不足',
-          8: '手动冲突数据',
-          9: '接收机收到干扰信息',
-          10: '防拆弹簧松弛',
+          1: "探测到移动",
+          2: "传感器被干扰",
+          3: "信号被遮挡",
+          4: "检测到震动",
+          5: "传感器故障",
+          6: "传感器位置移动",
+          7: "电池不足",
+          8: "手动冲突数据",
+          9: "接收机收到干扰信息",
+          10: "防拆弹簧松弛",
         },
 
         // 6️⃣ 第三方 OPTEX 报警主机
         optex: {
-          1: '远&近发生事件',
-          2: '远距离事件',
-          3: '近距离事件',
-          4: '缓慢移动检测',
-          5: '干扰检测',
-          6: '防遮挡报警',
-          7: '防旋转报警',
-          8: '异常电路激活',
-          9: '污物覆盖(自检异常)',
-          10: '传感器异常',
-          11: '设备故障',
-          12: '设备不在线',
-          13: '设备已连接',
-          14: '区域A1事件',
-          15: '区域A2事件',
-          16: '区域B1事件',
-          17: '区域B2事件',
-          18: '区域A11事件',
-          19: '区域A12事件',
-          20: '区域A21事件',
-          21: '区域A22事件',
-          22: '区域B11事件',
-          23: '区域B12事件',
-          24: '区域B21事件',
-          25: '区域B22事件',
+          1: "远&近发生事件",
+          2: "远距离事件",
+          3: "近距离事件",
+          4: "缓慢移动检测",
+          5: "干扰检测",
+          6: "防遮挡报警",
+          7: "防旋转报警",
+          8: "异常电路激活",
+          9: "污物覆盖(自检异常)",
+          10: "传感器异常",
+          11: "设备故障",
+          12: "设备不在线",
+          13: "设备已连接",
+          14: "区域A1事件",
+          15: "区域A2事件",
+          16: "区域B1事件",
+          17: "区域B2事件",
+          18: "区域A11事件",
+          19: "区域A12事件",
+          20: "区域A21事件",
+          21: "区域A22事件",
+          22: "区域B11事件",
+          23: "区域B12事件",
+          24: "区域B21事件",
+          25: "区域B22事件",
         },
-      }
+      };
 
       // 防御性处理
-      if (!type) return '无效'
-      const map = maps[deviceType] || maps.access
-      return map[type] || `未知(${type})`
+      if (!type) return "无效";
+      const map = maps[deviceType] || maps.access;
+      return map[type] || `未知(${type})`;
     },
-    searchEventInfo() {
+    handleSearch() {
       console.log("查询");
-      this.currentEvent = {}; // 空对象代替 null
-      // this.currentUser = {}; // 空对象代替 null
-      // this.currentUser = null; // 空对象代替 null
+      this.currentEvent = {};
       this.searchVisible = true;
     },
     // 表格样式修改
@@ -301,8 +370,8 @@ export default {
         this.loading = false;
       }
     },
-  }
-}
+  },
+};
 </script>
 
 <style scoped>

+ 6 - 2
src/views/doormanManage/faceInfoManage/index.vue

@@ -35,7 +35,8 @@
       >
       <el-button type="primary" @click="addFaceInfo()" style="margin-left: 10px"
         >新增</el-button
-      ><el-button
+      >
+      <el-button
         :disabled="multipleSelection.length <= 0"
         type="danger"
         @click="batchDeleteFaceInfo()"
@@ -129,7 +130,10 @@
       />
     </div>
 
-    <face-search-dialog :visible.sync="searchVisible" :detail="currentFace" /><!-- 查看详情弹窗 -->
+    <face-search-dialog
+      :visible.sync="searchVisible"
+      :detail="currentFace"
+    /><!-- 查看详情弹窗 -->
 
     <!-- 查看详情弹窗 -->
     <face-detail-dialog :visible.sync="viewVisible" :detail="currentFace" />

+ 23 - 17
src/views/monitoring/manage/index.vue

@@ -37,8 +37,7 @@
     <!-- 视频表格 -->
     <el-table
       :data="tableData"
-      style="width: 100%;
-      margin-top: 10px"
+      style="width: 100%; margin-top: 10px"
       border
       :header-cell-style="changeHeaderCellStyle"
     >
@@ -137,7 +136,7 @@
     <!-- 分页 -->
     <el-pagination
       class="custom-pagination"
-      style="margin-top: 20px; text-align: right;"
+      style="margin-top: 20px; text-align: right"
       :current-page="currentPage"
       :page-size="pageSize"
       :total="total"
@@ -146,7 +145,6 @@
       @current-change="handlePageChange"
       @size-change="handleSizeChange"
     />
-
     <!-- 新增/编辑弹窗 -->
     <VideoEditDialog
       :visible.sync="dialogVisible"
@@ -163,24 +161,28 @@ import request from "@/utils/request";
 
 export default {
   name: "VideoManage",
-  components: { VideoEditDialog },
+  components: {
+    VideoEditDialog,
+  },
   data() {
     return {
+      // 搜索条件
       searchName: "",
       searchDeviceType: "",
-
+      // 设备类型
       deviceTypeOptions: [
         { label: "网络摄像机 (IPC)", value: "IPC" },
         { label: "人脸门禁终端 (MINMOE)", value: "MINMOE" },
         { label: "车牌识别一体机 (LPR_GATE)", value: "LPR_GATE" },
-        { label: "通道控制设备 / 摆闸 (TURNSTILE)", value: "TURNSTILE" }
+        { label: "通道控制设备 / 摆闸 (TURNSTILE)", value: "TURNSTILE" },
       ],
-
+      // 表格数据
       tableData: [],
+      loading: false,
       currentPage: 1,
       pageSize: 10,
       total: 0,
-
+      // 弹窗
       dialogVisible: false,
       currentVideoData: {},
     };
@@ -250,22 +252,24 @@ export default {
       this.dialogVisible = false;
     },
     async getVideoInfo() {
+      this.loading = true;
       try {
+        const data = {
+          name: this.searchName?.trim() || null, // 去掉多余空格,避免 "" 导致条件错乱
+          deviceType: this.searchDeviceType || null, // 为空时传 null
+          page: this.currentPage,
+          size: this.pageSize,
+        };
+        console.log("查询参数", data);
         // 发起分页+查询请求
         const res = await request({
           url: "/video/list",
           method: "POST",
-          data: {
-            page: this.currentPage,
-            size: this.pageSize,
-            name: this.searchName?.trim() || null, // 去掉多余空格,避免 "" 导致条件错乱
-            deviceType: this.searchDeviceType || null, // 为空时传 null
-          },
+          data: data,
         });
-
         // 判断返回结构
         if (res && res.list) {
-          this.tableData = res.list;
+          this.tableData = res.list || [];
           this.total = res.total || 0;
         } else {
           this.tableData = [];
@@ -275,6 +279,8 @@ export default {
       } catch (error) {
         console.error("获取视频信息失败:", error);
         this.$message.error("获取视频列表失败,请检查网络或后端接口!");
+      } finally {
+        this.loading = false;
       }
     },
     // 格式化设备类型