feat(rfid): 新增RFID设备和读取记录管理视图
- 实现RFID设备列表、查询、添加、修改、删除及导出功能 - 实现读取记录列表、查询、添加、修改、删除及导出功能 - 设备管理支持位置选择、在线状态、告警状态及标识筛选 - 读取记录支持设备选择、读取状态、条码信息、记录时间、告警标志等筛选 - 首页新增设备统计卡片显示设备总数、在线、离线及告警设备数量 - 首页新增设备列表及告警状态快速查看与页面跳转功能main
parent
748754c82c
commit
ba37248df3
@ -1,165 +1,254 @@
|
||||
<template>
|
||||
<div class="app-container home">
|
||||
<el-row :gutter="20">
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>RuoYi-Vue-Plus多租户管理系统</h2>
|
||||
<p>
|
||||
RuoYi-Vue-Plus 是基于 RuoYi-Vue 针对 分布式集群 场景升级(不兼容原框架)
|
||||
<br />
|
||||
* 前端开发框架 Vue3、TS、Element Plus<br />
|
||||
* 后端开发框架 Spring Boot<br />
|
||||
* 容器框架 Undertow 基于 Netty 的高性能容器<br />
|
||||
* 权限认证框架 Sa-Token 支持多终端认证系统<br />
|
||||
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
|
||||
* 缓存数据库 Redis 适配 6.X 最低 4.X<br />
|
||||
* 数据库框架 Mybatis-Plus 快速 CRUD 增加开发效率<br />
|
||||
* 数据库框架 p6spy 更强劲的 SQL 分析<br />
|
||||
* 多数据源框架 dynamic-datasource 支持主从与多种类数据库异构<br />
|
||||
* 序列化框架 Jackson 统一使用 jackson 高效可靠<br />
|
||||
* Redis客户端 Redisson 性能强劲、API丰富<br />
|
||||
* 分布式限流 Redisson 全局、请求IP、集群ID 多种限流<br />
|
||||
* 分布式锁 Lock4j 注解锁、工具锁 多种多样<br />
|
||||
* 分布式幂等 Lock4j 基于分布式锁实现<br />
|
||||
* 分布式链路追踪 SkyWalking 支持链路追踪、网格分析、度量聚合、可视化<br />
|
||||
* 分布式任务调度 SnailJob 高性能 高可靠 易扩展<br />
|
||||
* 文件存储 Minio 本地存储<br />
|
||||
* 文件存储 七牛、阿里、腾讯 云存储<br />
|
||||
* 监控框架 SpringBoot-Admin 全方位服务监控<br />
|
||||
* 校验框架 Validation 增强接口安全性 严谨性<br />
|
||||
* Excel框架 FastExcel(原Alibaba EasyExcel) 性能优异 扩展性强<br />
|
||||
* 文档框架 SpringDoc、javadoc 无注解零入侵基于java注释<br />
|
||||
* 工具类框架 Hutool、Lombok 减少代码冗余 增加安全性<br />
|
||||
* 代码生成器 适配MP、SpringDoc规范化代码 一键生成前后端代码<br />
|
||||
* 部署方式 Docker 容器编排 一键部署业务集群<br />
|
||||
* 国际化 SpringMessage Spring标准国际化方案<br />
|
||||
</p>
|
||||
<p><b>当前版本:</b> <span>v5.5.1</span></p>
|
||||
<p>
|
||||
<el-tag type="danger">¥免费开源</el-tag>
|
||||
</p>
|
||||
<p>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Vue-Plus')">访问码云</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Vue-Plus')">访问GitHub</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-vue-plus/changlog')"
|
||||
>更新日志</el-button
|
||||
>
|
||||
</p>
|
||||
<!-- 统计卡片 -->
|
||||
<el-row :gutter="16" class="mb-4">
|
||||
<el-col :xs="12" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-card-inner">
|
||||
<div class="stat-icon bg-primary"><el-icon :size="28"><Monitor /></el-icon></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">设备总数</div>
|
||||
<div class="stat-value">{{ stats.totalDevices || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
|
||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||
<h2>RuoYi-Cloud-Plus多租户微服务管理系统</h2>
|
||||
<p>
|
||||
RuoYi-Cloud-Plus 微服务通用权限管理系统 重写 RuoYi-Cloud 全方位升级(不兼容原框架)
|
||||
<br />
|
||||
* 前端开发框架 Vue3、TS、Element UI<br />
|
||||
* 后端开发框架 Spring Boot<br />
|
||||
* 微服务开发框架 Spring Cloud、Spring Cloud Alibaba<br />
|
||||
* 容器框架 Undertow 基于 XNIO 的高性能容器<br />
|
||||
* 权限认证框架 Sa-Token、Jwt 支持多终端认证系统<br />
|
||||
* 关系数据库 MySQL 适配 8.X 最低 5.7<br />
|
||||
* 关系数据库 Oracle 适配 11g 12c<br />
|
||||
* 关系数据库 PostgreSQL 适配 13 14<br />
|
||||
* 关系数据库 SQLServer 适配 2017 2019<br />
|
||||
* 缓存数据库 Redis 适配 6.X 最低 5.X<br />
|
||||
* 分布式注册中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
|
||||
* 分布式配置中心 Alibaba Nacos 采用2.X 基于GRPC通信高性能<br />
|
||||
* 服务网关 Spring Cloud Gateway 响应式高性能网关<br />
|
||||
* 负载均衡 Spring Cloud Loadbalancer 负载均衡处理<br />
|
||||
* RPC远程调用 Apache Dubbo 原生态使用体验、高性能<br />
|
||||
* 分布式限流熔断 Alibaba Sentinel 无侵入、高扩展<br />
|
||||
* 分布式事务 Alibaba Seata 无侵入、高扩展 支持 四种模式<br />
|
||||
* 分布式消息队列 Apache Kafka 高性能高速度<br />
|
||||
* 分布式消息队列 Apache RocketMQ 高可用功能多样<br />
|
||||
* 分布式消息队列 RabbitMQ 支持各种扩展插件功能多样性<br />
|
||||
* 分布式搜索引擎 ElasticSearch 业界知名<br />
|
||||
* 分布式链路追踪 Apache SkyWalking 链路追踪、网格分析、度量聚合、可视化<br />
|
||||
* 分布式日志中心 ELK 业界成熟解决方案<br />
|
||||
* 分布式监控 Prometheus、Grafana 全方位性能监控<br />
|
||||
* 其余与 Vue 版本一致<br />
|
||||
</p>
|
||||
<p><b>当前版本:</b> <span>v2.5.1</span></p>
|
||||
<p>
|
||||
<el-tag type="danger">¥免费开源</el-tag>
|
||||
</p>
|
||||
<p>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://gitee.com/dromara/RuoYi-Cloud-Plus')">访问码云</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://github.com/dromara/RuoYi-Cloud-Plus')">访问GitHub</el-button>
|
||||
<el-button type="primary" icon="Cloudy" plain @click="goTarget('https://plus-doc.dromara.org/#/ruoyi-cloud-plus/changlog')"
|
||||
>更新日志</el-button
|
||||
>
|
||||
</p>
|
||||
<el-col :xs="12" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-card-inner">
|
||||
<div class="stat-icon bg-success"><el-icon :size="28"><CircleCheck /></el-icon></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">在线设备</div>
|
||||
<div class="stat-value text-success">{{ stats.onlineDevices || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-card-inner">
|
||||
<div class="stat-icon bg-warning"><el-icon :size="28"><CircleClose /></el-icon></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">离线设备</div>
|
||||
<div class="stat-value text-warning">{{ stats.offlineDevices || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="12" :sm="12" :md="6">
|
||||
<el-card shadow="hover" class="stat-card">
|
||||
<div class="stat-card-inner">
|
||||
<div class="stat-icon bg-danger"><el-icon :size="28"><Warning /></el-icon></div>
|
||||
<div class="stat-content">
|
||||
<div class="stat-title">告警设备</div>
|
||||
<div class="stat-value text-danger">{{ stats.alarmDevices || 0 }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 设备列表与告警记录 -->
|
||||
<el-row :gutter="16">
|
||||
<el-col :xs="24" :md="14">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>设备列表</span>
|
||||
<el-button type="primary" link @click="$router.push('/base/rfidDevice')">查看更多</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="deviceList" border stripe max-height="400" empty-text="暂无数据">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="deviceCode" label="设备编号" min-width="120" />
|
||||
<el-table-column prop="deviceName" label="设备名称" min-width="120" />
|
||||
<el-table-column prop="locationAlias" label="所在位置" min-width="100">
|
||||
<template #default="{ row }">{{ row.locationAlias || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="onlineStatus" label="在线状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.onlineStatus === '1' ? 'success' : 'info'" size="small">
|
||||
{{ row.onlineStatus === '1' ? '在线' : '离线' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="alarmStatus" label="告警状态" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.alarmStatus === '1' ? 'danger' : 'success'" size="small">
|
||||
{{ row.alarmStatus === '1' ? '告警' : '正常' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :xs="24" :md="10">
|
||||
<el-card shadow="hover">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span>告警记录</span>
|
||||
<el-button type="primary" link @click="$router.push('/base/rfidReadRecord')">查看更多</el-button>
|
||||
</div>
|
||||
</template>
|
||||
<el-table :data="alarmList" border stripe max-height="400" empty-text="暂无数据">
|
||||
<el-table-column type="index" label="序号" width="60" align="center" />
|
||||
<el-table-column prop="deviceName" label="设备" min-width="100">
|
||||
<template #default="{ row }">{{ row.deviceName || row.deviceCode || '-' }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="recordTime" label="时间" min-width="140">
|
||||
<template #default="{ row }">{{ formatRecordTime(row.recordTime) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="alarmLevel" label="级别" width="80" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.alarmLevel === '2' ? 'danger' : 'warning'" size="small">
|
||||
{{ row.alarmLevel === '2' ? '严重' : (row.alarmLevel === '1' ? '一般' : '-') }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="alarmAction" label="行为" min-width="80">
|
||||
<template #default="{ row }">{{ row.alarmAction || '-' }}</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Index" lang="ts">
|
||||
const goTarget = (url: string) => {
|
||||
window.open(url, '__blank');
|
||||
import { getDashboardStats } from "@/api/rfid/dashboard";
|
||||
import { getRfidDeviceList } from "@/api/rfid/rfidDevice";
|
||||
import { getRfidReadRecordList } from "@/api/rfid/rfidReadRecord";
|
||||
import type { RfidDeviceVO } from "@/api/rfid/rfidDevice/types";
|
||||
import type { RfidReadRecordVO } from "@/api/rfid/rfidReadRecord/types";
|
||||
|
||||
const stats = ref({
|
||||
totalDevices: 0,
|
||||
onlineDevices: 0,
|
||||
offlineDevices: 0,
|
||||
alarmDevices: 0
|
||||
});
|
||||
|
||||
const deviceList = ref<RfidDeviceVO[]>([]);
|
||||
const alarmList = ref<RfidReadRecordVO[]>([]);
|
||||
|
||||
const loadStats = async () => {
|
||||
try {
|
||||
const res = await getDashboardStats();
|
||||
if (res.data) {
|
||||
stats.value = res.data;
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("获取统计数据失败", e);
|
||||
}
|
||||
};
|
||||
|
||||
const loadDeviceList = async () => {
|
||||
try {
|
||||
const res: any = await getRfidDeviceList({ isMarked: "1" } as any);
|
||||
deviceList.value = res.data || [];
|
||||
} catch (e) {
|
||||
console.error("获取设备列表失败", e);
|
||||
}
|
||||
};
|
||||
|
||||
const loadAlarmList = async () => {
|
||||
try {
|
||||
const res: any = await getRfidReadRecordList({ alarmFlag: "1" } as any);
|
||||
alarmList.value = res.data || [];
|
||||
} catch (e) {
|
||||
console.error("获取告警记录失败", e);
|
||||
}
|
||||
};
|
||||
|
||||
const formatRecordTime = (val: any) => {
|
||||
if (!val) {
|
||||
return "-";
|
||||
}
|
||||
return String(val).replace("T", " ");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
loadStats();
|
||||
loadDeviceList();
|
||||
loadAlarmList();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.home {
|
||||
blockquote {
|
||||
padding: 10px 20px;
|
||||
margin: 0 0 20px;
|
||||
font-size: 17.5px;
|
||||
border-left: 5px solid #eee;
|
||||
}
|
||||
hr {
|
||||
margin-top: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 0;
|
||||
border-top: 1px solid #eee;
|
||||
}
|
||||
.col-item {
|
||||
margin-bottom: 20px;
|
||||
padding: 16px;
|
||||
|
||||
.mb-4 {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
font-family: 'open sans', 'Helvetica Neue', Helvetica, Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
color: #676a6c;
|
||||
overflow-x: hidden;
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
h4 {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-top: 10px;
|
||||
font-size: 26px;
|
||||
font-weight: 100;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 10px;
|
||||
|
||||
b {
|
||||
font-weight: 700;
|
||||
// 统计卡片样式
|
||||
.stat-card {
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.update-log {
|
||||
ol {
|
||||
display: block;
|
||||
list-style-type: decimal;
|
||||
margin-block-start: 1em;
|
||||
margin-block-end: 1em;
|
||||
margin-inline-start: 0;
|
||||
margin-inline-end: 0;
|
||||
padding-inline-start: 40px;
|
||||
.stat-card-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.stat-icon {
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
border-radius: 8px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #fff;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
|
||||
&.bg-primary { background: linear-gradient(135deg, #409eff, #53a8ff); }
|
||||
&.bg-success { background: linear-gradient(135deg, #67c23a, #85ce61); }
|
||||
&.bg-warning { background: linear-gradient(135deg, #e6a23c, #ebb563); }
|
||||
&.bg-danger { background: linear-gradient(135deg, #f56c6c, #f78989); }
|
||||
}
|
||||
|
||||
.stat-content {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.stat-title {
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
|
||||
&.text-success { color: #67c23a; }
|
||||
&.text-warning { color: #e6a23c; }
|
||||
&.text-danger { color: #f56c6c; }
|
||||
}
|
||||
}
|
||||
|
||||
// 卡片头部样式
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
span {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
}
|
||||
}
|
||||
|
||||
// 表格容器
|
||||
:deep(.el-card__body) {
|
||||
padding: 16px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Loading…
Reference in New Issue