diff --git a/src/api/rfid/dashboard/index.ts b/src/api/rfid/dashboard/index.ts index 77b21fa..e96f39f 100644 --- a/src/api/rfid/dashboard/index.ts +++ b/src/api/rfid/dashboard/index.ts @@ -1,11 +1,92 @@ import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +import { + DashboardVO, + RealtimeStats, + DeviceStatusVO, + SuccessRateTrend, + StatisticsOverview, + AlarmStatVO +} from './types'; + +// ==================== 新增接口(按需刷新) ==================== /** - * 获取首页统计数据 + * 【新接口1】获取实时统计数据(按秒刷新) + * 包含:顶部统计(设备数量、在线数量、离线数量、告警数量)+ 告警统计列表 + * + * @param alarmLimit 告警列表限制数量(默认10条) */ -export const getDashboardStats = () => { +export const getRealtimeStats = (alarmLimit?: number): AxiosPromise => { return request({ - url: '/rfid/dashboard/stats', + url: '/rfid/dashboard/realtime', + method: 'get', + params: { alarmLimit } + }); +}; + +/** + * 【新接口2】获取设备状态列表(定时刷新) + * 包含每个设备的在线状态、告警状态及最新读取记录 + * + * @param locationId 位置ID(可选,用于按位置筛选) + */ +export const getDeviceStatusList = (locationId?: number): AxiosPromise => { + return request({ + url: '/rfid/dashboard/deviceStatus', + method: 'get', + params: { locationId } + }); +}; + +/** + * 【新接口3】获取成功率趋势(按小时统计) + * 不需要频繁刷新 + * + * @param type 类型:today-今日(默认),yesterday-昨日 + */ +export const getSuccessRateTrends = (type?: string): AxiosPromise => { + return request({ + url: '/rfid/dashboard/successRate', + method: 'get', + params: { type } + }); +}; + +// ==================== 原有接口(保留) ==================== + +/** + * 获取完整看板数据 + * + * @param locationId 位置ID(可选) + */ +export const getDashboardData = (locationId?: number): AxiosPromise => { + return request({ + url: '/rfid/dashboard/data', + method: 'get', + params: { locationId } + }); +}; + +/** + * 获取统计概览 + */ +export const getOverview = (): AxiosPromise => { + return request({ + url: '/rfid/dashboard/overview', method: 'get' - }) -} + }); +}; + +/** + * 获取告警统计列表 + * + * @param limit 限制数量(默认10条) + */ +export const getAlarmStats = (limit?: number): AxiosPromise => { + return request({ + url: '/rfid/dashboard/alarmStats', + method: 'get', + params: { limit } + }); +}; diff --git a/src/api/rfid/dashboard/types.ts b/src/api/rfid/dashboard/types.ts new file mode 100644 index 0000000..c24ce0d --- /dev/null +++ b/src/api/rfid/dashboard/types.ts @@ -0,0 +1,106 @@ +/** + * 看板数据类型定义 + */ + +/** + * 完整看板数据(原接口返回) + */ +export interface DashboardVO { + /** 统计概览 */ + overview: StatisticsOverview; + /** 设备状态列表 */ + deviceStatusList: DeviceStatusVO[]; + /** 成功率趋势数据 */ + successRateTrends: SuccessRateTrend[]; + /** 告警统计列表 */ + alarmStats: AlarmStatVO[]; +} + +/** + * 统计概览 + */ +export interface StatisticsOverview { + /** 设备总数 */ + deviceTotal: number; + /** 在线数量 */ + onlineCount: number; + /** 离线数量 */ + offlineCount: number; + /** 告警数量 */ + alarmCount: number; +} + +/** + * 告警统计 + */ +export interface AlarmStatVO { + /** 告警时间 */ + alarmTime: string; + /** 设备名称 */ + deviceName: string; + /** 位置 */ + location: string; + /** 告警级别 */ + alarmLevel: string; + /** 告警行为 */ + alarmAction: string; +} + +/** + * 实时统计数据(接口1返回) + * 按秒刷新:顶部统计 + 告警列表 + */ +export interface RealtimeStats { + /** 统计概览 */ + overview: StatisticsOverview; + /** 告警统计列表 */ + alarmStats: AlarmStatVO[]; +} + +/** + * 最新读取记录 + */ +export interface LatestReadRecord { + /** 工位名称 */ + stationName: string; + /** 条码信息 */ + barcode: string; + /** 读取时间 */ + recordTime: string; + /** 读取状态 (1-成功;0-失败) */ + readStatus: string; +} + +/** + * 设备状态(接口2返回) + * 定时刷新:设备树 + 设备最新读取记录 + */ +export interface DeviceStatusVO { + /** 设备ID */ + deviceId: number; + /** 设备编号 */ + deviceCode: string; + /** 设备名称 */ + deviceName: string; + /** 所属位置ID */ + locationId: number; + /** 位置别名 */ + locationAlias: string; + /** 在线状态 (1-在线;0-离线) */ + onlineStatus: string; + /** 告警状态 (0-正常;1-告警) */ + alarmStatus: string; + /** 最新读取记录 */ + latestRecord?: LatestReadRecord; +} + +/** + * 成功率趋势(接口3返回) + * 按小时统计 + */ +export interface SuccessRateTrend { + /** 时间点(小时,如 "09:00") */ + timePoint: string; + /** 成功率(百分比,如 98.5) */ + successRate: number | null; +} diff --git a/src/api/rfid/rfidReadRecord/index.ts b/src/api/rfid/rfidReadRecord/index.ts index cf30f03..5b8b07e 100644 --- a/src/api/rfid/rfidReadRecord/index.ts +++ b/src/api/rfid/rfidReadRecord/index.ts @@ -18,12 +18,14 @@ export const listRfidReadRecord = (query?: RfidReadRecordQuery): AxiosPromise => { +export const getRfidReadRecord = (id: string | number, queryDate?: string): AxiosPromise => { return request({ url: '/rfid/rfidReadRecord/' + id, - method: 'get' + method: 'get', + params: { queryDate } }); }; @@ -53,12 +55,14 @@ export const updateRfidReadRecord = (data: RfidReadRecordForm) => { /** * 删除读取记录 - * @param id + * @param id 主键 + * @param queryDate 查询日期(用于定位分表,格式:yyyy-MM-dd) */ -export const delRfidReadRecord = (id: string | number | Array) => { +export const delRfidReadRecord = (id: string | number | Array, queryDate?: string) => { return request({ url: '/rfid/rfidReadRecord/' + id, - method: 'delete' + method: 'delete', + params: { queryDate } }); }; diff --git a/src/api/rfid/rfidReadRecord/types.ts b/src/api/rfid/rfidReadRecord/types.ts index 1d7d729..e75605f 100644 --- a/src/api/rfid/rfidReadRecord/types.ts +++ b/src/api/rfid/rfidReadRecord/types.ts @@ -42,17 +42,17 @@ export interface RfidReadRecordVO { /** * 告警级别(1-一般;2-严重等) */ - alarmLevel: string; + alarmLevel?: string; /** * 告警类型 */ - alarmType: string; + alarmType?: string; /** * 告警行为 */ - alarmAction: string; + alarmAction?: string; } @@ -122,9 +122,14 @@ export interface RfidReadRecordQuery extends PageQuery { barcode?: string; /** - * 记录时间 + * 记录时间范围-开始 */ - recordTime?: string; + beginRecordTime?: string; + + /** + * 记录时间范围-结束 + */ + endRecordTime?: string; /** * 是否告警(0-否;1-是) @@ -146,6 +151,13 @@ export interface RfidReadRecordQuery extends PageQuery { */ alarmAction?: string; + /** + * 采样间隔(分钟) + * - null 或 <= 1:不采样,返回全部数据 + * - > 1:每 N 分钟取一条代表数据 + */ + samplingInterval?: number; + /** * 日期范围参数 */ diff --git a/src/views/rfid/rfidDevice/index.vue b/src/views/rfid/rfidDevice/index.vue index 8ae4ea3..8c8da00 100644 --- a/src/views/rfid/rfidDevice/index.vue +++ b/src/views/rfid/rfidDevice/index.vue @@ -72,43 +72,43 @@ 导出 - + - - - - - - - + + + + + + + - + - + - - - + + + - - + + @@ -247,6 +247,24 @@ const total = ref(0); const locationOptions = ref([]); +// 列显隐信息 +const columns = ref([ + { key: 0, label: '设备编号', visible: true }, + { key: 1, label: '设备名称', visible: true }, + { key: 2, label: '所在位置', visible: true }, + { key: 3, label: '设备地址', visible: true }, + { key: 4, label: '设备端口', visible: true }, + { key: 5, label: '读取频率', visible: true }, + { key: 6, label: '在线状态', visible: true }, + { key: 7, label: '告警状态', visible: true }, + { key: 8, label: '是否标识', visible: false }, + { key: 9, label: '备注', visible: false }, + { key: 10, label: '创建人', visible: false }, + { key: 11, label: '创建时间', visible: false }, + { key: 12, label: '更新人', visible: false }, + { key: 13, label: '更新时间', visible: false } +]); + const queryFormRef = ref(); const rfidDeviceFormRef = ref(); @@ -294,9 +312,6 @@ const data = reactive>({ } }, rules: { - id: [ - { required: true, message: "标识不能为空", trigger: "blur" } - ], deviceCode: [ { required: true, message: "设备编号不能为空", trigger: "blur" } ], @@ -403,10 +418,15 @@ const submitForm = () => { /** 删除按钮操作 */ const handleDelete = async (row?: RfidDeviceVO) => { const _ids = row?.id || ids.value; - await proxy?.$modal.confirm('是否确认删除设备信息编号为"' + _ids + '"的数据项?').finally(() => loading.value = false); - await delRfidDevice(_ids); - proxy?.$modal.msgSuccess("删除成功"); - await getList(); + try { + await proxy?.$modal.confirm('是否确认删除设备信息编号为"' + _ids + '"的数据项?'); + loading.value = true; + await delRfidDevice(_ids); + proxy?.$modal.msgSuccess("删除成功"); + await getList(); + } finally { + loading.value = false; + } } /** 导出按钮操作 */ diff --git a/src/views/rfid/rfidLocation/index.vue b/src/views/rfid/rfidLocation/index.vue index 1ad192e..ef14377 100644 --- a/src/views/rfid/rfidLocation/index.vue +++ b/src/views/rfid/rfidLocation/index.vue @@ -66,7 +66,7 @@ 展开/折叠 - + - - - - + + + + - + - - - + + + - - + + @@ -214,6 +214,19 @@ const rfidLocationList = ref([]); const rfidLocationOptions = ref([]); const buttonLoading = ref(false); const showSearch = ref(true); + +// 列显隐信息 +const columns = ref([ + { key: 0, label: '位置编号', visible: true }, + { key: 1, label: '位置别名', visible: true }, + { key: 2, label: '位置类型', visible: true }, + { key: 3, label: '是否标识', visible: false }, + { key: 4, label: '备注', visible: false }, + { key: 5, label: '创建人', visible: false }, + { key: 6, label: '创建时间', visible: false }, + { key: 7, label: '更新人', visible: false }, + { key: 8, label: '更新时间', visible: false } +]); const isExpandAll = ref(true); const loading = ref(false); @@ -257,9 +270,6 @@ const data = reactive>({ } }, rules: { - id: [ - { required: true, message: "标识不能为空", trigger: "blur" } - ], locationCode: [ { required: true, message: "位置编号不能为空", trigger: "blur" } ], @@ -373,11 +383,15 @@ const submitForm = () => { /** 删除按钮操作 */ const handleDelete = async (row: RfidLocationVO) => { - await proxy?.$modal.confirm('是否确认删除位置信息编号为"' + row.id + '"的数据项?'); - loading.value = true; - await delRfidLocation(row.id).finally(() => loading.value = false); - await getList(); - proxy?.$modal.msgSuccess("删除成功"); + try { + await proxy?.$modal.confirm('是否确认删除位置信息编号为"' + row.id + '"的数据项?'); + loading.value = true; + await delRfidLocation(row.id); + proxy?.$modal.msgSuccess("删除成功"); + await getList(); + } finally { + loading.value = false; + } } onMounted(() => { diff --git a/src/views/rfid/rfidReadRecord/index.vue b/src/views/rfid/rfidReadRecord/index.vue index 55b9559..3ec0abd 100644 --- a/src/views/rfid/rfidReadRecord/index.vue +++ b/src/views/rfid/rfidReadRecord/index.vue @@ -22,12 +22,15 @@ - - + @@ -61,6 +64,8 @@ - - - + + + - - + + - + - + - - + + + + @@ -202,10 +211,25 @@ const buttonLoading = ref(false); const loading = ref(true); const showSearch = ref(true); const ids = ref>([]); +// 当前选中行的记录时间(用于分表定位) +const selectedRecordTimes = ref([]); const single = ref(true); const multiple = ref(true); const total = ref(0); +// 列显隐信息 +const columns = ref([ + { key: 0, label: '设备编号', visible: true }, + { key: 1, label: '设备名称', visible: true }, + { key: 2, label: '读取状态', visible: true }, + { key: 3, label: '条码信息', visible: true }, + { key: 4, label: '记录时间', visible: true }, + { key: 5, label: '是否告警', visible: false }, + { key: 6, label: '告警级别', visible: false }, + { key: 7, label: '告警类型', visible: false }, + { key: 8, label: '告警行为', visible: false } +]); + const queryFormRef = ref(); const rfidReadRecordFormRef = ref(); @@ -215,6 +239,18 @@ const dialog = reactive({ }); const deviceOptions = ref([]); +// 记录时间范围(默认当天) +const dateRange = ref<[string, string] | []>([]); + +/** 初始化默认时间范围:当天 00:00:00 ~ 23:59:59 */ +const initDefaultDateRange = () => { + const today = new Date(); + const year = today.getFullYear(); + const month = String(today.getMonth() + 1).padStart(2, '0'); + const day = String(today.getDate()).padStart(2, '0'); + const dateStr = `${year}-${month}-${day}`; + dateRange.value = [`${dateStr} 00:00:00`, `${dateStr} 23:59:59`]; +}; const initFormData: RfidReadRecordForm = { id: undefined, @@ -235,7 +271,8 @@ const data = reactive>({ deviceId: undefined, readStatus: undefined, barcode: undefined, - recordTime: undefined, + beginRecordTime: undefined, + endRecordTime: undefined, alarmFlag: undefined, alarmLevel: undefined, alarmType: undefined, @@ -244,18 +281,13 @@ const data = reactive>({ } }, rules: { - id: [ - { required: true, message: "标识不能为空", trigger: "blur" } - ], deviceId: [ { required: true, message: "设备名称不能为空", trigger: "change" } ], readStatus: [ { required: true, message: "读取状态不能为空", trigger: "change" } ], - barcode: [ - { required: true, message: "条码信息不能为空", trigger: "blur" } - ], + // barcode: 读取成功时必填,读取失败时可为空(后端校验) recordTime: [ { required: true, message: "记录时间不能为空", trigger: "blur" } ], @@ -270,6 +302,14 @@ const { queryParams, form, rules } = toRefs(data); /** 查询读取记录列表 */ const getList = async () => { loading.value = true; + // 处理日期范围参数 + if (dateRange.value && dateRange.value.length === 2) { + queryParams.value.beginRecordTime = dateRange.value[0]; + queryParams.value.endRecordTime = dateRange.value[1]; + } else { + queryParams.value.beginRecordTime = undefined; + queryParams.value.endRecordTime = undefined; + } const res = await listRfidReadRecord(queryParams.value); rfidReadRecordList.value = res.rows; total.value = res.total; @@ -302,6 +342,8 @@ const handleQuery = () => { /** 重置按钮操作 */ const resetQuery = () => { + // 重置为当天时间范围 + initDefaultDateRange(); queryFormRef.value?.resetFields(); handleQuery(); } @@ -309,6 +351,8 @@ const resetQuery = () => { /** 多选框选中数据 */ const handleSelectionChange = (selection: RfidReadRecordVO[]) => { ids.value = selection.map(item => item.id); + // 保存选中行的记录时间,用于分表定位 + selectedRecordTimes.value = selection.map(item => item.recordTime?.substring(0, 10) || ''); single.value = selection.length != 1; multiple.value = !selection.length; } @@ -323,8 +367,10 @@ const handleAdd = () => { /** 修改按钮操作 */ const handleUpdate = async (row?: RfidReadRecordVO) => { reset(); - const _id = row?.id || ids.value[0] - const res = await getRfidReadRecord(_id); + const _id = row?.id || ids.value[0]; + // 获取查询日期(用于分表定位) + const queryDate = row?.recordTime?.substring(0, 10) || selectedRecordTimes.value[0]; + const res = await getRfidReadRecord(_id, queryDate); Object.assign(form.value, res.data); dialog.visible = true; dialog.title = "修改读取记录"; @@ -350,10 +396,17 @@ const submitForm = () => { /** 删除按钮操作 */ const handleDelete = async (row?: RfidReadRecordVO) => { const _ids = row?.id || ids.value; - await proxy?.$modal.confirm('是否确认删除读取记录编号为"' + _ids + '"的数据项?').finally(() => loading.value = false); - await delRfidReadRecord(_ids); - proxy?.$modal.msgSuccess("删除成功"); - await getList(); + // 获取查询日期(用于分表定位) + const queryDate = row?.recordTime?.substring(0, 10) || selectedRecordTimes.value[0]; + try { + await proxy?.$modal.confirm('是否确认删除读取记录编号为"' + _ids + '"的数据项?'); + loading.value = true; + await delRfidReadRecord(_ids, queryDate); + proxy?.$modal.msgSuccess("删除成功"); + await getList(); + } finally { + loading.value = false; + } } /** 导出按钮操作 */ @@ -364,6 +417,8 @@ const handleExport = () => { } onMounted(() => { + // 初始化默认时间范围(当天),确保分表查询有依据 + initDefaultDateRange(); getList(); loadDeviceOptions(); });