liujie
2026-08-05 c9935678a74d53d90b13f4da898fa84ec82f77b6
update
2个文件已修改
360 ■■■■■ 已修改文件
ruoyi-ui/src/assets/styles/visitor-admin.scss 259 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/views/visitor-admin/device/index.vue 101 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ruoyi-ui/src/assets/styles/visitor-admin.scss
@@ -388,6 +388,265 @@
  }
}
.visitor-device-page {
  padding: 14px 16px 18px;
  .visitor-card {
    min-height: calc(100vh - 98px);
    padding: 20px 20px 28px;
    border-radius: 10px;
    box-shadow: 0 8px 24px rgba(34, 69, 126, 0.06);
  }
  .visitor-header {
    min-height: 28px;
    margin-bottom: 16px;
    h1 {
      font-size: 18px;
      font-weight: 800;
    }
  }
  .visitor-side-layout {
    grid-template-columns: 214px minmax(0, 1fr);
    gap: 16px;
  }
  .visitor-side {
    min-height: 604px;
    padding: 16px 14px;
    border-color: #dfe9f6;
    border-radius: 10px;
    box-shadow: none;
    h2 {
      margin-bottom: 10px;
      font-size: 14px;
      font-weight: 600;
    }
    .el-input__inner {
      height: 30px;
      border-radius: 8px;
      font-size: 12px;
    }
    .el-tree {
      margin-top: 12px;
      font-size: 12px;
    }
    .el-tree-node__content {
      height: 30px;
      margin: 3px 0;
      border-radius: 6px;
    }
    .el-tree-node__expand-icon {
      padding: 4px 5px 4px 3px;
      color: #8da1b8;
      font-size: 12px;
    }
    .el-tree-node__expand-icon.is-leaf {
      color: transparent;
    }
    .area-tree-node {
      min-width: 0;
      display: inline-flex;
      align-items: center;
      color: #334963;
    }
    .area-tree-node-icon {
      width: 16px;
      margin-right: 6px;
      color: #2f73ff;
      font-size: 14px;
      text-align: center;
    }
    .area-tree-label {
      min-width: 0;
      overflow: hidden;
      text-overflow: ellipsis;
      white-space: nowrap;
    }
  }
  .device-panel {
    min-width: 0;
    > h2 {
      margin: 18px 0 8px;
      color: #1f2d3d;
      font-size: 14px;
      font-weight: 600;
    }
  }
  .visitor-query {
    min-height: 80px;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    column-gap: 28px;
    row-gap: 12px;
    margin-bottom: 14px;
    padding: 18px 18px;
    border-color: #dfe9f6;
    border-radius: 10px;
    background: #fbfdff;
    .el-form-item {
      display: inline-flex;
      align-items: center;
      margin: 0;
    }
    .el-form-item__label {
      padding-right: 8px;
      line-height: 30px;
      color: #1f2d3d;
      font-weight: 700;
    }
    .el-form-item__content {
      display: inline-flex;
      align-items: center;
      line-height: 30px;
    }
    .el-form-item:nth-child(1) .el-input,
    .el-form-item:nth-child(2) .el-input {
      width: 176px;
    }
    .el-form-item:nth-child(3) .el-select {
      width: 164px;
    }
    .el-input__inner {
      height: 30px;
      border-radius: 6px;
      font-size: 12px;
    }
    .el-button {
      min-width: 52px;
      height: 30px;
      padding: 0 14px;
      border-radius: 6px;
      font-size: 12px;
      font-weight: 700;
    }
    .el-button + .el-button {
      margin-left: 12px;
    }
  }
  .visitor-toolbar {
    min-height: 42px;
    margin-bottom: 10px;
    .el-button {
      min-width: 100px;
      height: 34px;
      padding: 0 18px;
      border-radius: 8px;
      font-size: 13px;
      font-weight: 700;
    }
  }
  .visitor-table.el-table {
    border-color: #e2ebf5;
    border-radius: 8px;
    th.el-table__cell {
      padding: 7px 0;
      font-size: 12px;
      font-weight: 700;
    }
    td.el-table__cell {
      padding: 7px 0;
      font-size: 12px;
    }
    .cell {
      line-height: 18px;
      padding-left: 14px;
      padding-right: 14px;
    }
    .el-button--text {
      padding: 0;
      font-size: 12px;
    }
    .el-dropdown {
      margin-left: 14px;
    }
  }
  .status-pill {
    min-width: 44px;
    height: 20px;
    padding: 0 9px;
    font-size: 12px;
  }
}
.visitor-device-more-menu {
  min-width: 92px;
  padding: 8px 0;
  border-color: #edf2f8;
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(23, 43, 77, 0.14);
  .el-dropdown-menu__item {
    height: 32px;
    display: flex;
    align-items: center;
    padding: 0 14px;
    color: #334963;
    font-size: 12px;
    font-weight: 700;
    line-height: 32px;
    i {
      width: 16px;
      margin-right: 8px;
      font-size: 14px;
      text-align: center;
    }
    .el-icon-refresh-left {
      color: #2f73ff;
    }
    .el-icon-view {
      color: var(--visitor-success);
    }
    .el-icon-circle-close {
      color: #708195;
    }
    &.danger-item {
      color: var(--visitor-danger);
      .el-icon-delete {
        color: var(--visitor-danger);
      }
    }
  }
}
.status-pill {
  min-width: 52px;
  height: 24px;
ruoyi-ui/src/views/visitor-admin/device/index.vue
@@ -1,5 +1,5 @@
<template>
  <main class="visitor-page">
  <main class="visitor-page visitor-device-page">
    <section class="visitor-card">
      <header class="visitor-header">
        <h1>{{ pageTitle }}管理</h1>
@@ -19,7 +19,12 @@
            default-expand-all
            highlight-current
            @node-click="handleAreaClick"
          />
          >
            <span slot-scope="{ node, data }" class="area-tree-node">
              <i :class="areaNodeIconClass(data)" />
              <span class="area-tree-label">{{ node.label }}</span>
            </span>
          </el-tree>
        </aside>
        <section class="device-panel">
@@ -44,23 +49,20 @@
          </el-form>
          <div v-if="!isMiddlePlatformReadonly && hasDevicePerm('add')" class="visitor-toolbar">
            <el-button type="primary" @click="handleAdd">新增设备</el-button>
            <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增设备</el-button>
          </div>
          <el-table
            v-loading="loading"
            class="visitor-table"
            :data="deviceList"
            @selection-change="handleSelectionChange"
          >
            <el-table-column type="selection" width="48" align="center" />
            <el-table-column label="序号" width="64" align="center">
              <template slot-scope="scope">{{ scope.$index + 1 }}</template>
            </el-table-column>
            <el-table-column label="设备名称" prop="deviceName" min-width="120" show-overflow-tooltip />
            <el-table-column label="品牌" prop="brand" min-width="100" show-overflow-tooltip />
            <el-table-column label="设备ID" prop="deviceId" min-width="150" show-overflow-tooltip />
            <el-table-column v-if="deviceType === 'machine'" label="百度SDK指纹" prop="fingerprint" min-width="160" show-overflow-tooltip />
            <el-table-column label="所属区域" prop="areaName" min-width="130" show-overflow-tooltip />
            <el-table-column label="启用状态" align="center" width="100">
              <template slot-scope="scope">
@@ -78,10 +80,16 @@
                <el-button v-if="hasDevicePerm('edit')" type="text" @click="handleUpdate(scope.row)">编辑</el-button>
                <el-dropdown v-if="hasAnyDevicePerm(['status', 'logout', 'remove'])" trigger="click" @command="command => handleMoreCommand(command, scope.row)">
                  <el-button type="text">更多<i class="el-icon-arrow-down el-icon--right" /></el-button>
                  <el-dropdown-menu slot="dropdown">
                    <el-dropdown-item v-if="hasDevicePerm('status')" command="status">{{ scope.row.enabled ? '禁用' : '启用' }}</el-dropdown-item>
                    <el-dropdown-item v-if="hasDevicePerm('logout')" command="logout">注销</el-dropdown-item>
                    <el-dropdown-item v-if="hasDevicePerm('remove')" command="delete" class="danger-item">删除</el-dropdown-item>
                  <el-dropdown-menu slot="dropdown" class="visitor-device-more-menu">
                    <el-dropdown-item v-if="hasDevicePerm('logout')" command="logout">
                      <i class="el-icon-refresh-left" />注销
                    </el-dropdown-item>
                    <el-dropdown-item v-if="hasDevicePerm('status')" command="status">
                      <i :class="scope.row.enabled ? 'el-icon-circle-close' : 'el-icon-view'" />{{ scope.row.enabled ? '禁用' : '启用' }}
                    </el-dropdown-item>
                    <el-dropdown-item v-if="hasDevicePerm('remove')" command="delete" class="danger-item">
                      <i class="el-icon-delete" />删除
                    </el-dropdown-item>
                  </el-dropdown-menu>
                </el-dropdown>
              </template>
@@ -94,16 +102,16 @@
      </div>
    </section>
    <el-dialog :title="title" :visible.sync="open" width="654px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="92px">
    <el-dialog :title="title" :visible.sync="open" width="654px" append-to-body custom-class="visitor-device-dialog">
      <el-form ref="form" :model="form" :rules="rules" label-position="top">
        <el-form-item label="区域" prop="areaId">
          <el-cascader
            v-model="form.areaId"
            :options="areaTree"
            :options="modalAreaOptions"
            :props="areaCascaderProps"
            clearable
            filterable
            placeholder="下拉菜单"
            placeholder="请选择"
          />
        </el-form-item>
        <el-form-item label="设备名称" prop="deviceName">
@@ -119,7 +127,7 @@
          <el-input v-model="form.remark" placeholder="请输入" />
        </el-form-item>
        <el-form-item label="启用状态" prop="enabled">
          <el-switch v-model="form.enabled" active-text="启用" inactive-text="禁用" />
          <el-switch v-model="form.enabled" active-color="#13ce66" inactive-color="#dcdfe6" />
        </el-form-item>
      </el-form>
      <div slot="footer" class="dialog-footer">
@@ -190,6 +198,10 @@
    // 目的:返回当前设备类型的页面标题前缀。
    pageTitle() {
      return this.deviceType === 'door' ? '门禁' : '访客机'
    },
    // 目的:弹窗区域选择只保留一级和二级,避免二级继续展开空面板。
    modalAreaOptions() {
      return this.normalizeModalAreaOptions(this.areaTree)
    }
  },
  watch: {
@@ -224,6 +236,21 @@
        this.areaTree = []
      })
    },
    // 目的:按弹窗交互要求移除二级以下区域和叶子节点空 children。
    normalizeModalAreaOptions(list, level = 1) {
      return (list || []).map(item => {
        const option = {
          ...item
        }
        const children = Array.isArray(item.children) ? item.children : []
        if (level < 2 && children.length) {
          option.children = this.normalizeModalAreaOptions(children, level + 1)
        } else {
          delete option.children
        }
        return option
      })
    },
    // 目的:按当前设备类型和查询条件加载分页列表。
    getList() {
      this.loading = true
@@ -247,6 +274,11 @@
    // 目的:根据数据库在线状态枚举返回对应的标签样式。
    onlineClass(value) {
      return onlineStatusClass(value)
    },
    // 目的:根据区域是否有子级展示目录或文档图标,匹配区域树层级语义。
    areaNodeIconClass(data) {
      const hasChildren = Array.isArray(data.children) && data.children.length > 0
      return hasChildren ? 'el-icon-folder area-tree-node-icon is-folder' : 'el-icon-document area-tree-node-icon is-file'
    },
    // 目的:按区域名称关键字过滤区域树节点。
    filterAreaNode(value, data) {
@@ -375,3 +407,42 @@
  }
}
</script>
<style lang="scss">
.visitor-device-dialog {
  .el-dialog__body {
    padding: 18px 24px 24px;
  }
  .el-form {
    padding: 0 8px;
  }
  .el-form-item {
    margin-bottom: 18px;
  }
  .el-form-item__label {
    padding: 0 0 8px;
    color: #303133;
    font-weight: 600;
    line-height: 20px;
    text-align: left;
  }
  .el-form-item__content {
    line-height: 1;
  }
  .el-cascader,
  .el-input,
  .el-select {
    width: 100%;
  }
  .el-switch {
    display: inline-flex;
    align-items: center;
  }
}
</style>