From df2dfebf9f5525fdf6b162cb0ed2f0a5c6fa6146 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期二, 20 十月 2026 14:00:55 +0800
Subject: [PATCH] feat(mes): 添加数采监控功能并完善设备管理
---
src/api/mes/dv/machinery/index.ts | 2
src/api/mes/collect/index.ts | 220 ++++++++++++
src/views/mes/dv/machinery/index.vue | 7
src/views/mes/dv/machinery/data.ts | 48 ++
src/views/mes/collect/monitor/index.vue | 719 ++++++++++++++++++++++++++++++++++++++++++
5 files changed, 995 insertions(+), 1 deletions(-)
diff --git a/src/api/mes/collect/index.ts b/src/api/mes/collect/index.ts
new file mode 100644
index 0000000..f058c69
--- /dev/null
+++ b/src/api/mes/collect/index.ts
@@ -0,0 +1,220 @@
+import type { PageParam, PageResult } from '@vben/request';
+
+import { requestClient } from '#/api/request';
+
+export namespace MesCollectApi {
+ /** 鏁伴噰璁惧鐘舵�� */
+ export interface Device {
+ machineryId?: number; // 璁惧鍙拌处缂栧彿
+ machineryCode?: string; // 璁惧缂栫爜
+ machineryName?: string; // 璁惧鍚嶇О
+ workstationId?: number; // 宸ヤ綔绔欑紪鍙�
+ workstationName?: string; // 宸ヤ綔绔欏悕绉�
+ collectDeviceCode?: string; // 鏁伴噰璁惧缂栧彿
+ collectEnabled?: boolean; // 鏄惁鍚敤鏁伴噰
+ lastCollectTime?: string; // 鏈�杩戦噰闆嗘椂闂�
+ online?: boolean; // 鏄惁鍦ㄧ嚎
+ sts?: number; // 杩愯鐘舵��
+ stsName?: string; // 杩愯鐘舵�佸悕绉�
+ opm?: number; // 杩愯妯″紡
+ opmName?: string; // 杩愯妯″紡鍚嶇О
+ ot?: number; // 娌规俯
+ temps?: string; // 鍚勬娓╁害 JSON
+ taskId?: number; // 鐢熶骇浠诲姟缂栧彿
+ taskCode?: string; // 鐢熶骇浠诲姟缂栫爜
+ taskName?: string; // 鐢熶骇浠诲姟鍚嶇О
+ workOrderId?: number; // 鐢熶骇宸ュ崟缂栧彿
+ workOrderCode?: string; // 鐢熶骇宸ュ崟缂栫爜
+ activeAlarmCount?: number; // 鏈秷闄ゆ姤璀︽暟閲�
+ }
+
+ /** 瀹炴椂鐘舵�� */
+ export interface Realtime {
+ id?: number;
+ machineryId?: number;
+ deviceCode?: string;
+ taskId?: number;
+ workOrderId?: number;
+ collectTime?: string;
+ sts?: number;
+ stsName?: string;
+ opm?: number;
+ opmName?: string;
+ asts?: number;
+ ot?: number;
+ temps?: string;
+ dataJson?: string;
+ }
+
+ /** 鍛ㄦ湡杩囩▼鏁版嵁 */
+ export interface Cycle {
+ id?: number;
+ machineryId?: number;
+ deviceCode?: string;
+ taskId?: number;
+ workOrderId?: number;
+ cycleNo?: number;
+ collectTime?: string;
+ ecyct?: number;
+ eiss?: number;
+ eivm?: number;
+ eipm?: number;
+ esipt?: number;
+ esipp?: number;
+ esips?: number;
+ eipt?: number;
+ eipse?: number;
+ eplst?: number;
+ eplsse?: number;
+ eplspm?: number;
+ etTemps?: string;
+ dataJson?: string;
+ }
+
+ /** 宸ヨ壓鍙傛暟蹇収 */
+ export interface Tech {
+ id?: number;
+ machineryId?: number;
+ deviceCode?: string;
+ taskId?: number;
+ workOrderId?: number;
+ collectTime?: string;
+ dataJson?: string;
+ }
+
+ /** 鍙傛暟鍙樻洿鏃ュ織 */
+ export interface OperateLog {
+ id?: number;
+ machineryId?: number;
+ deviceCode?: string;
+ taskId?: number;
+ workOrderId?: number;
+ varId?: string;
+ oldValue?: string;
+ newValue?: string;
+ modifyTime?: string;
+ collectTime?: string;
+ dataJson?: string;
+ }
+
+ /** 鎶ヨ璁板綍 */
+ export interface Alarm {
+ id?: number;
+ machineryId?: number;
+ deviceCode?: string;
+ taskId?: number;
+ workOrderId?: number;
+ wmId?: number;
+ wmMsg?: string;
+ wmTime?: string;
+ status?: number;
+ statusName?: string;
+ clearTime?: string;
+ collectTime?: string;
+ dataJson?: string;
+ }
+
+ /** 瀹炴椂鍘嗗彶鏌ヨ鍙傛暟 */
+ export interface RealtimeHistoryPageParam extends PageParam {
+ deviceCode?: string;
+ machineryId?: number;
+ taskId?: number;
+ workOrderId?: number;
+ collectTime?: string[];
+ }
+
+ /** 鍛ㄦ湡杩囩▼鏌ヨ鍙傛暟 */
+ export interface CyclePageParam extends PageParam {
+ deviceCode?: string;
+ machineryId?: number;
+ taskId?: number;
+ workOrderId?: number;
+ collectTime?: string[];
+ }
+
+ /** 鍙傛暟鍙樻洿鏌ヨ鍙傛暟 */
+ export interface OperateLogPageParam extends PageParam {
+ deviceCode?: string;
+ machineryId?: number;
+ workOrderId?: number;
+ varId?: string;
+ modifyTime?: string[];
+ }
+
+ /** 鎶ヨ鏌ヨ鍙傛暟 */
+ export interface AlarmPageParam extends PageParam {
+ deviceCode?: string;
+ machineryId?: number;
+ workOrderId?: number;
+ status?: number;
+ wmTime?: string[];
+ }
+}
+
+/** 鏌ヨ鏁伴噰璁惧鐘舵�佸垪琛� */
+export function getCollectDeviceList() {
+ return requestClient.get<MesCollectApi.Device[]>('/mes/collect/device/list');
+}
+
+/** 鏌ヨ璁惧鏈�鏂板疄鏃剁姸鎬� */
+export function getCollectRealtime(deviceCode: string) {
+ return requestClient.get<MesCollectApi.Realtime | null>(
+ '/mes/collect/realtime/get',
+ { params: { deviceCode } },
+ );
+}
+
+/** 鏌ヨ瀹炴椂鐘舵�佸巻鍙插垎椤� */
+export function getCollectRealtimeHistoryPage(
+ params: MesCollectApi.RealtimeHistoryPageParam,
+) {
+ return requestClient.get<PageResult<MesCollectApi.Realtime>>(
+ '/mes/collect/realtime/history-page',
+ { params },
+ );
+}
+
+/** 鏌ヨ鍛ㄦ湡杩囩▼鏁版嵁鍒嗛〉 */
+export function getCollectCyclePage(params: MesCollectApi.CyclePageParam) {
+ return requestClient.get<PageResult<MesCollectApi.Cycle>>(
+ '/mes/collect/cycle/page',
+ { params },
+ );
+}
+
+/** 鏌ヨ鍛ㄦ湡杩囩▼鏁版嵁鏄庣粏 */
+export function getCollectCycle(id: number) {
+ return requestClient.get<MesCollectApi.Cycle>('/mes/collect/cycle/get', {
+ params: { id },
+ });
+}
+
+/** 鏌ヨ璁惧鏈�鏂板伐鑹哄弬鏁板揩鐓� */
+export function getCollectTech(deviceCode: string) {
+ return requestClient.get<MesCollectApi.Tech | null>('/mes/collect/tech/get', {
+ params: { deviceCode },
+ });
+}
+
+/** 鏌ヨ鍙傛暟鍙樻洿鏃ュ織鍒嗛〉 */
+export function getCollectOperateLogPage(
+ params: MesCollectApi.OperateLogPageParam,
+) {
+ return requestClient.get<PageResult<MesCollectApi.OperateLog>>(
+ '/mes/collect/operate-log/page',
+ { params },
+ );
+}
+
+/** 鏌ヨ鎶ヨ璁板綍鍒嗛〉 */
+export function getCollectAlarmPage(params: MesCollectApi.AlarmPageParam) {
+ return requestClient.get<PageResult<MesCollectApi.Alarm>>(
+ '/mes/collect/alarm/page',
+ { params },
+ );
+}
+
+/** 浜哄伐纭娑堥櫎鎶ヨ */
+export function ackCollectAlarm(id: number) {
+ return requestClient.put<boolean>(`/mes/collect/alarm/ack?id=${id}`);
+}
diff --git a/src/api/mes/dv/machinery/index.ts b/src/api/mes/dv/machinery/index.ts
index c46b39f..9d42c8a 100644
--- a/src/api/mes/dv/machinery/index.ts
+++ b/src/api/mes/dv/machinery/index.ts
@@ -21,6 +21,8 @@
conveyorBelt?: number; // 浼犻�佸饱甯︽暟閲�
workstationId?: number; // 宸ヤ綔绔欑紪鍙�
workstationName?: string; // 宸ヤ綔绔欏悕绉�
+ collectDeviceCode?: string; // 鏁伴噰璁惧缂栧彿
+ collectEnabled?: boolean; // 鏄惁鍚敤鏁伴噰
remark?: string; // 澶囨敞
createTime?: Date; // 鍒涘缓鏃堕棿
}
diff --git a/src/views/mes/collect/monitor/index.vue b/src/views/mes/collect/monitor/index.vue
new file mode 100644
index 0000000..226cb25
--- /dev/null
+++ b/src/views/mes/collect/monitor/index.vue
@@ -0,0 +1,719 @@
+<script lang="ts" setup>
+import type { Ref } from 'vue';
+
+import type { MesCollectApi } from '#/api/mes/collect';
+
+import { computed, onMounted, onUnmounted, ref } from 'vue';
+
+import { Page } from '@vben/common-ui';
+import { formatDateTime } from '@vben/utils';
+
+import {
+ Button,
+ Descriptions,
+ Drawer,
+ Empty,
+ message,
+ Modal,
+ Popconfirm,
+ Space,
+ Table,
+ Tabs,
+ Tag,
+} from 'ant-design-vue';
+
+import {
+ ackCollectAlarm,
+ getCollectAlarmPage,
+ getCollectCyclePage,
+ getCollectDeviceList,
+ getCollectOperateLogPage,
+ getCollectRealtime,
+ getCollectRealtimeHistoryPage,
+ getCollectTech,
+} from '#/api/mes/collect';
+
+defineOptions({ name: 'MesCollectMonitor' });
+
+interface PageData<T> {
+ list: T[];
+ total: number;
+}
+
+interface PageQuery {
+ current?: number;
+ pageSize?: number;
+}
+
+const deviceLoading = ref(false);
+const devices = ref<MesCollectApi.Device[]>([]);
+const lastRefreshAt = ref('');
+let refreshTimer: ReturnType<typeof setInterval> | undefined;
+
+const drawerOpen = ref(false);
+const currentDevice = ref<MesCollectApi.Device>();
+const activeTab = ref('realtime');
+
+const realtime = ref<MesCollectApi.Realtime | null>(null);
+const realtimeLoading = ref(false);
+const tech = ref<MesCollectApi.Tech | null>(null);
+const techLoading = ref(false);
+
+const rawJsonOpen = ref(false);
+const rawJsonTitle = ref('');
+const rawJsonContent = ref('');
+
+const drawerTitle = computed(() => {
+ const device = currentDevice.value;
+ if (!device) {
+ return '鏁伴噰璁惧璇︽儏';
+ }
+ return `${device.machineryName ?? ''}锛�${device.collectDeviceCode ?? ''}锛塦;
+});
+
+const onlineCount = computed(
+ () => devices.value.filter((item) => item.online).length,
+);
+
+const alarmCount = computed(() =>
+ devices.value.reduce(
+ (total, item) => total + Number(item.activeAlarmCount ?? 0),
+ 0,
+ ),
+);
+
+function usePagedTable<T>(
+ label: string,
+ loader: (pageNo: number, pageSize: number) => Promise<PageData<T>>,
+) {
+ const loading = ref(false);
+ const rows = ref([]) as Ref<T[]>;
+ const total = ref(0);
+ const pageNo = ref(1);
+ const pageSize = ref(10);
+
+ async function load() {
+ loading.value = true;
+ try {
+ const data = await loader(pageNo.value, pageSize.value);
+ rows.value = data.list ?? [];
+ total.value = data.total ?? 0;
+ } catch {
+ rows.value = [];
+ total.value = 0;
+ message.error(`${label}鍔犺浇澶辫触锛岃绋嶅悗閲嶈瘯`);
+ } finally {
+ loading.value = false;
+ }
+ }
+
+ function changePage(page: PageQuery) {
+ pageNo.value = page.current ?? 1;
+ pageSize.value = page.pageSize ?? 10;
+ void load();
+ }
+
+ function reset() {
+ pageNo.value = 1;
+ pageSize.value = 10;
+ rows.value = [];
+ total.value = 0;
+ }
+
+ return { changePage, load, loading, pageNo, pageSize, reset, rows, total };
+}
+
+const realtimeHistory = usePagedTable<MesCollectApi.Realtime>(
+ '瀹炴椂鐘舵�佸巻鍙�',
+ async (pageNo, pageSize) =>
+ await getCollectRealtimeHistoryPage({
+ pageNo,
+ pageSize,
+ deviceCode: currentDevice.value?.collectDeviceCode,
+ }),
+);
+const cyclePage = usePagedTable<MesCollectApi.Cycle>(
+ '鍛ㄦ湡杩囩▼鏁版嵁',
+ async (pageNo, pageSize) =>
+ await getCollectCyclePage({
+ pageNo,
+ pageSize,
+ deviceCode: currentDevice.value?.collectDeviceCode,
+ }),
+);
+const operateLogPage = usePagedTable<MesCollectApi.OperateLog>(
+ '鍙傛暟鍙樻洿鏃ュ織',
+ async (pageNo, pageSize) =>
+ await getCollectOperateLogPage({
+ pageNo,
+ pageSize,
+ deviceCode: currentDevice.value?.collectDeviceCode,
+ }),
+);
+const alarmPage = usePagedTable<MesCollectApi.Alarm>(
+ '鎶ヨ璁板綍',
+ async (pageNo, pageSize) =>
+ await getCollectAlarmPage({
+ pageNo,
+ pageSize,
+ deviceCode: currentDevice.value?.collectDeviceCode,
+ }),
+);
+
+const deviceColumns = [
+ { title: '璁惧', key: 'device', width: 220, fixed: 'left' as const },
+ { title: '鏁伴噰缂栫爜', dataIndex: 'collectDeviceCode', width: 110 },
+ { title: '鍦ㄧ嚎', key: 'online', width: 90 },
+ { title: '杩愯鐘舵��', key: 'sts', width: 100 },
+ { title: '杩愯妯″紡', key: 'opm', width: 110 },
+ { title: '娌规俯(鈩�)', dataIndex: 'ot', width: 100 },
+ { title: '褰撳墠浠诲姟', key: 'task', width: 220 },
+ { title: '鐢熶骇宸ュ崟', dataIndex: 'workOrderCode', width: 160 },
+ { title: '鏈秷闄ゆ姤璀�', key: 'alarm', width: 110 },
+ {
+ title: '鏈�杩戦噰闆嗘椂闂�',
+ dataIndex: 'lastCollectTime',
+ key: 'lastCollectTime',
+ width: 180,
+ },
+ { title: '鎿嶄綔', key: 'action', width: 110, fixed: 'right' as const },
+];
+
+const realtimeHistoryColumns = [
+ {
+ title: '閲囬泦鏃堕棿',
+ dataIndex: 'collectTime',
+ key: 'collectTime',
+ width: 180,
+ },
+ { title: '杩愯鐘舵��', dataIndex: 'stsName', width: 100 },
+ { title: '杩愯妯″紡', dataIndex: 'opmName', width: 110 },
+ { title: '娌规俯(鈩�)', dataIndex: 'ot', width: 100 },
+ { title: '闄勫姞鐘舵��', dataIndex: 'asts', width: 100 },
+ { title: '娓╁害', key: 'temps', minWidth: 220 },
+ { title: '鍘熷鏁版嵁', key: 'raw', width: 100 },
+];
+
+const cycleColumns = [
+ { title: '鍛ㄦ湡搴忓彿', dataIndex: 'cycleNo', width: 110 },
+ {
+ title: '閲囬泦鏃堕棿',
+ dataIndex: 'collectTime',
+ key: 'collectTime',
+ width: 180,
+ },
+ { title: '鍛ㄦ湡鏃堕棿', dataIndex: 'ecyct', width: 100 },
+ { title: '灏勫嚭鏃堕棿', dataIndex: 'eipt', width: 100 },
+ { title: '鏈�澶у皠閫�', dataIndex: 'eivm', width: 100 },
+ { title: '鏈�澶у皠鍘�', dataIndex: 'eipm', width: 100 },
+ { title: '杞繚鍘嬫椂闂�', dataIndex: 'esipt', width: 110 },
+ { title: '杞繚鍘嬪帇鍔�', dataIndex: 'esipp', width: 110 },
+ { title: '鍌ㄦ枡鏃堕棿', dataIndex: 'eplst', width: 100 },
+ { title: '娓╁害', key: 'etTemps', minWidth: 220 },
+ { title: '鍘熷鏁版嵁', key: 'raw', width: 100 },
+];
+
+const operateLogColumns = [
+ { title: '鍙橀噺缂栫爜', dataIndex: 'varId', width: 120 },
+ { title: '淇敼鍓�', dataIndex: 'oldValue', width: 150 },
+ { title: '淇敼鍚�', dataIndex: 'newValue', width: 150 },
+ { title: '淇敼鏃堕棿', dataIndex: 'modifyTime', key: 'modifyTime', width: 180 },
+ { title: '閲囬泦鏃堕棿', dataIndex: 'collectTime', key: 'collectTime', width: 180 },
+ { title: '鍘熷鏁版嵁', key: 'raw', width: 100 },
+];
+
+const alarmColumns = [
+ { title: '鎶ヨ鏃堕棿', dataIndex: 'wmTime', key: 'wmTime', width: 180 },
+ { title: '鎶ヨ鍐呭', dataIndex: 'wmMsg', minWidth: 220 },
+ { title: '鐘舵��', key: 'status', width: 100 },
+ { title: '娑堥櫎鏃堕棿', dataIndex: 'clearTime', key: 'clearTime', width: 180 },
+ { title: '閲囬泦鏃堕棿', dataIndex: 'collectTime', key: 'collectTime', width: 180 },
+ { title: '鎿嶄綔', key: 'action', width: 120, fixed: 'right' as const },
+];
+
+async function loadDevices() {
+ deviceLoading.value = true;
+ try {
+ devices.value = await getCollectDeviceList();
+ lastRefreshAt.value = formatDateTime(new Date());
+ } catch {
+ message.error('鏁伴噰璁惧鐘舵�佸姞杞藉け璐ワ紝璇风偣鍑烩�滃埛鏂扳�濋噸璇�');
+ } finally {
+ deviceLoading.value = false;
+ }
+}
+
+function resetDetailData() {
+ realtime.value = null;
+ tech.value = null;
+ realtimeHistory.reset();
+ cyclePage.reset();
+ operateLogPage.reset();
+ alarmPage.reset();
+}
+
+async function loadRealtime() {
+ const deviceCode = currentDevice.value?.collectDeviceCode;
+ if (!deviceCode) {
+ return;
+ }
+ realtimeLoading.value = true;
+ try {
+ realtime.value = await getCollectRealtime(deviceCode);
+ } catch {
+ realtime.value = null;
+ message.error('璁惧瀹炴椂鐘舵�佸姞杞藉け璐ワ紝璇峰叧闂鎯呭悗閲嶆柊鎵撳紑');
+ } finally {
+ realtimeLoading.value = false;
+ }
+ void realtimeHistory.load();
+}
+
+async function loadTech() {
+ const deviceCode = currentDevice.value?.collectDeviceCode;
+ if (!deviceCode) {
+ return;
+ }
+ techLoading.value = true;
+ try {
+ tech.value = await getCollectTech(deviceCode);
+ } catch {
+ tech.value = null;
+ message.error('宸ヨ壓鍙傛暟鍔犺浇澶辫触锛岃鍒囨崲鍒板叾瀹冮〉绛惧悗閲嶈瘯');
+ } finally {
+ techLoading.value = false;
+ }
+}
+
+function openDetail(device: MesCollectApi.Device) {
+ currentDevice.value = device;
+ activeTab.value = 'realtime';
+ resetDetailData();
+ drawerOpen.value = true;
+ void loadRealtime();
+}
+
+function handleTabChange(key: number | string) {
+ const tab = String(key);
+ if (tab === 'realtime') {
+ void loadRealtime();
+ } else if (tab === 'cycle') {
+ void cyclePage.load();
+ } else if (tab === 'tech') {
+ void loadTech();
+ } else if (tab === 'oplog') {
+ void operateLogPage.load();
+ } else if (tab === 'alarm') {
+ void alarmPage.load();
+ }
+}
+
+async function handleAck(row: MesCollectApi.Alarm) {
+ if (!row.id) {
+ return;
+ }
+ try {
+ await ackCollectAlarm(row.id);
+ message.success('鎶ヨ宸茬‘璁ゆ秷闄�');
+ void alarmPage.load();
+ void loadDevices();
+ } catch {
+ message.error('鎶ヨ纭澶辫触锛岃褰曞彲鑳藉凡琚澶囪嚜鍔ㄦ秷闄わ紝璇峰埛鏂板悗閲嶈瘯');
+ void alarmPage.load();
+ }
+}
+
+function safeParseObject(text?: string): null | Record<string, unknown> {
+ if (!text) {
+ return null;
+ }
+ try {
+ const value = JSON.parse(text) as unknown;
+ return value && typeof value === 'object'
+ ? (value as Record<string, unknown>)
+ : null;
+ } catch {
+ return null;
+ }
+}
+
+function formatNumberedFields(text?: string) {
+ const value = safeParseObject(text);
+ if (!value) {
+ return '';
+ }
+ return Object.entries(value)
+ .map(([key, item]) => `${key}: ${String(item ?? '')}`)
+ .join('锛�');
+}
+
+function showRaw(title: string, json?: string) {
+ rawJsonTitle.value = title;
+ rawJsonContent.value = prettyJson(json);
+ rawJsonOpen.value = true;
+}
+
+function prettyJson(json?: string) {
+ if (!json) {
+ return '鏆傛棤鍘熷鏁版嵁';
+ }
+ try {
+ return JSON.stringify(JSON.parse(json), null, 2);
+ } catch {
+ return json;
+ }
+}
+
+onMounted(() => {
+ void loadDevices();
+ refreshTimer = setInterval(() => {
+ if (!drawerOpen.value) {
+ void loadDevices();
+ }
+ }, 30_000);
+});
+
+onUnmounted(() => {
+ if (refreshTimer) {
+ clearInterval(refreshTimer);
+ }
+});
+</script>
+
+<template>
+ <Page auto-content-height>
+ <div
+ class="mb-4 flex flex-wrap items-center justify-between gap-3"
+ >
+ <div class="flex flex-wrap items-center gap-4">
+ <h2 class="text-lg font-semibold">鏁伴噰鐩戞帶</h2>
+ <span class="text-sm text-gray-500">
+ 鍦ㄧ嚎 {{ onlineCount }} / {{ devices.length }} 鍙�
+ </span>
+ <span class="text-sm text-gray-500">
+ 鏈秷闄ゆ姤璀� {{ alarmCount }} 鏉�
+ </span>
+ <span v-if="lastRefreshAt" class="text-sm text-gray-400">
+ 鍒锋柊浜� {{ lastRefreshAt }}
+ </span>
+ </div>
+ <Button type="primary" :loading="deviceLoading" @click="loadDevices">
+ 鍒锋柊
+ </Button>
+ </div>
+
+ <Table
+ :columns="deviceColumns"
+ :data-source="devices"
+ :loading="deviceLoading"
+ :pagination="false"
+ :scroll="{ x: 1600 }"
+ row-key="machineryId"
+ size="middle"
+ >
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'device'">
+ <div class="font-medium">
+ {{ (record as MesCollectApi.Device).machineryName }}
+ </div>
+ <div class="text-xs text-gray-500">
+ {{ (record as MesCollectApi.Device).machineryCode }}
+ <span v-if="(record as MesCollectApi.Device).workstationName">
+ 路 {{ (record as MesCollectApi.Device).workstationName }}
+ </span>
+ </div>
+ </template>
+ <template v-else-if="column.key === 'online'">
+ <Tag
+ :color="(record as MesCollectApi.Device).online ? 'success' : 'default'"
+ >
+ {{ (record as MesCollectApi.Device).online ? '鍦ㄧ嚎' : '绂荤嚎' }}
+ </Tag>
+ </template>
+ <template v-else-if="column.key === 'sts'">
+ <Tag
+ :color="
+ (record as MesCollectApi.Device).sts === 2 ? 'processing' : 'default'
+ "
+ >
+ {{ (record as MesCollectApi.Device).stsName || '-' }}
+ </Tag>
+ </template>
+ <template v-else-if="column.key === 'opm'">
+ {{ (record as MesCollectApi.Device).opmName || '-' }}
+ </template>
+ <template v-else-if="column.key === 'task'">
+ <div v-if="(record as MesCollectApi.Device).taskId">
+ <div>{{ (record as MesCollectApi.Device).taskName }}</div>
+ <div class="text-xs text-gray-500">
+ {{ (record as MesCollectApi.Device).taskCode }}
+ </div>
+ </div>
+ <span v-else class="text-gray-400">鏃犺繍琛屼换鍔�</span>
+ </template>
+ <template v-else-if="column.key === 'alarm'">
+ <Tag
+ :color="
+ Number((record as MesCollectApi.Device).activeAlarmCount) > 0
+ ? 'error'
+ : 'default'
+ "
+ >
+ {{ (record as MesCollectApi.Device).activeAlarmCount ?? 0 }}
+ </Tag>
+ </template>
+ <template v-else-if="column.key === 'lastCollectTime'">
+ {{ formatDateTime((record as MesCollectApi.Device).lastCollectTime) || '-' }}
+ </template>
+ <template v-else-if="column.key === 'action'">
+ <Button type="link" @click="openDetail(record as MesCollectApi.Device)">
+ 鏌ョ湅璇︽儏
+ </Button>
+ </template>
+ </template>
+ </Table>
+
+ <Drawer
+ v-model:open="drawerOpen"
+ :title="drawerTitle"
+ destroy-on-close
+ width="90%"
+ >
+ <Tabs v-model:active-key="activeTab" @change="handleTabChange">
+ <Tabs.TabPane key="realtime" tab="瀹炴椂鐘舵��">
+ <div v-loading="realtimeLoading" class="mb-4">
+ <Descriptions v-if="realtime" bordered size="small" :column="4">
+ <Descriptions.Item label="閲囬泦鏃堕棿">
+ {{ formatDateTime(realtime.collectTime) || '-' }}
+ </Descriptions.Item>
+ <Descriptions.Item label="杩愯鐘舵��">
+ {{ realtime.stsName || '-' }}
+ </Descriptions.Item>
+ <Descriptions.Item label="杩愯妯″紡">
+ {{ realtime.opmName || '-' }}
+ </Descriptions.Item>
+ <Descriptions.Item label="娌规俯">
+ {{ realtime.ot ?? '-' }}
+ </Descriptions.Item>
+ <Descriptions.Item label="鍚勬娓╁害" :span="4">
+ <div class="max-h-24 overflow-auto">
+ {{ formatNumberedFields(realtime.temps) || '-' }}
+ </div>
+ </Descriptions.Item>
+ </Descriptions>
+ <Empty v-else description="鏆傛棤瀹炴椂鏁版嵁" />
+ </div>
+ <Table
+ :columns="realtimeHistoryColumns"
+ :data-source="realtimeHistory.rows.value"
+ :loading="realtimeHistory.loading.value"
+ :pagination="{
+ current: realtimeHistory.pageNo.value,
+ pageSize: realtimeHistory.pageSize.value,
+ total: realtimeHistory.total.value,
+ showSizeChanger: true,
+ showTotal: (total: number) => `鍏� ${total} 鏉,
+ }"
+ :scroll="{ x: 1200 }"
+ row-key="id"
+ size="small"
+ @change="realtimeHistory.changePage"
+ >
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'temps'">
+ <div class="max-h-20 overflow-auto text-xs">
+ {{ formatNumberedFields((record as MesCollectApi.Realtime).temps) || '-' }}
+ </div>
+ </template>
+ <template v-else-if="column.key === 'raw'">
+ <Button
+ type="link"
+ size="small"
+ @click="
+ showRaw(
+ '瀹炴椂鐘舵�佸師濮嬫暟鎹�',
+ (record as MesCollectApi.Realtime).dataJson,
+ )
+ "
+ >
+ 鏌ョ湅
+ </Button>
+ </template>
+ <template v-else-if="column.key === 'collectTime'">
+ {{ formatDateTime((record as MesCollectApi.Realtime).collectTime) || '-' }}
+ </template>
+ </template>
+ </Table>
+ </Tabs.TabPane>
+
+ <Tabs.TabPane key="cycle" tab="鍛ㄦ湡杩囩▼">
+ <Table
+ :columns="cycleColumns"
+ :data-source="cyclePage.rows.value"
+ :loading="cyclePage.loading.value"
+ :pagination="{
+ current: cyclePage.pageNo.value,
+ pageSize: cyclePage.pageSize.value,
+ total: cyclePage.total.value,
+ showSizeChanger: true,
+ showTotal: (total: number) => `鍏� ${total} 鏉,
+ }"
+ :scroll="{ x: 1500 }"
+ row-key="id"
+ size="small"
+ @change="cyclePage.changePage"
+ >
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'etTemps'">
+ <div class="max-h-20 overflow-auto text-xs">
+ {{ formatNumberedFields((record as MesCollectApi.Cycle).etTemps) || '-' }}
+ </div>
+ </template>
+ <template v-else-if="column.key === 'raw'">
+ <Button
+ type="link"
+ size="small"
+ @click="
+ showRaw(
+ '鍛ㄦ湡杩囩▼鍘熷鏁版嵁',
+ (record as MesCollectApi.Cycle).dataJson,
+ )
+ "
+ >
+ 鏌ョ湅
+ </Button>
+ </template>
+ <template v-else-if="column.key === 'collectTime'">
+ {{ formatDateTime((record as MesCollectApi.Cycle).collectTime) || '-' }}
+ </template>
+ </template>
+ </Table>
+ </Tabs.TabPane>
+
+ <Tabs.TabPane key="tech" tab="宸ヨ壓鍙傛暟">
+ <div v-loading="techLoading">
+ <div v-if="tech" class="mb-3 text-sm text-gray-500">
+ 閲囬泦鏃堕棿锛歿{ formatDateTime(tech.collectTime) || '-' }}
+ </div>
+ <pre
+ v-if="tech"
+ class="max-h-[65vh] overflow-auto rounded bg-gray-50 p-4 text-xs"
+ >{{ prettyJson(tech.dataJson) }}</pre>
+ <Empty v-else description="鏆傛棤宸ヨ壓鍙傛暟" />
+ </div>
+ </Tabs.TabPane>
+
+ <Tabs.TabPane key="oplog" tab="鍙傛暟鍙樻洿">
+ <Table
+ :columns="operateLogColumns"
+ :data-source="operateLogPage.rows.value"
+ :loading="operateLogPage.loading.value"
+ :pagination="{
+ current: operateLogPage.pageNo.value,
+ pageSize: operateLogPage.pageSize.value,
+ total: operateLogPage.total.value,
+ showSizeChanger: true,
+ showTotal: (total: number) => `鍏� ${total} 鏉,
+ }"
+ :scroll="{ x: 1000 }"
+ row-key="id"
+ size="small"
+ @change="operateLogPage.changePage"
+ >
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'raw'">
+ <Button
+ type="link"
+ size="small"
+ @click="
+ showRaw(
+ '鍙傛暟鍙樻洿鍘熷鏁版嵁',
+ (record as MesCollectApi.OperateLog).dataJson,
+ )
+ "
+ >
+ 鏌ョ湅
+ </Button>
+ </template>
+ <template v-else-if="column.key === 'modifyTime'">
+ {{ formatDateTime((record as MesCollectApi.OperateLog).modifyTime) || '-' }}
+ </template>
+ <template v-else-if="column.key === 'collectTime'">
+ {{ formatDateTime((record as MesCollectApi.OperateLog).collectTime) || '-' }}
+ </template>
+ </template>
+ </Table>
+ </Tabs.TabPane>
+
+ <Tabs.TabPane key="alarm" tab="鎶ヨ璁板綍">
+ <Table
+ :columns="alarmColumns"
+ :data-source="alarmPage.rows.value"
+ :loading="alarmPage.loading.value"
+ :pagination="{
+ current: alarmPage.pageNo.value,
+ pageSize: alarmPage.pageSize.value,
+ total: alarmPage.total.value,
+ showSizeChanger: true,
+ showTotal: (total: number) => `鍏� ${total} 鏉,
+ }"
+ :scroll="{ x: 1000 }"
+ row-key="id"
+ size="small"
+ @change="alarmPage.changePage"
+ >
+ <template #bodyCell="{ column, record }">
+ <template v-if="column.key === 'status'">
+ <Tag
+ :color="
+ (record as MesCollectApi.Alarm).status === 0
+ ? 'error'
+ : 'success'
+ "
+ >
+ {{ (record as MesCollectApi.Alarm).statusName || '-' }}
+ </Tag>
+ </template>
+ <template v-else-if="column.key === 'action'">
+ <Popconfirm
+ v-if="(record as MesCollectApi.Alarm).status === 0"
+ title="纭宸插鐞嗗苟娑堥櫎璇ユ姤璀︼紵"
+ @confirm="handleAck(record as MesCollectApi.Alarm)"
+ >
+ <Button
+ v-access:code="['mes:collect-alarm:ack']"
+ type="link"
+ size="small"
+ >
+ 纭娑堥櫎
+ </Button>
+ </Popconfirm>
+ <span v-else class="text-gray-400">宸叉秷闄�</span>
+ </template>
+ <template v-else-if="column.key === 'wmTime'">
+ {{ formatDateTime((record as MesCollectApi.Alarm).wmTime) || '-' }}
+ </template>
+ <template v-else-if="column.key === 'clearTime'">
+ {{ formatDateTime((record as MesCollectApi.Alarm).clearTime) || '-' }}
+ </template>
+ <template v-else-if="column.key === 'collectTime'">
+ {{ formatDateTime((record as MesCollectApi.Alarm).collectTime) || '-' }}
+ </template>
+ </template>
+ </Table>
+ </Tabs.TabPane>
+ </Tabs>
+ </Drawer>
+
+ <Modal
+ v-model:open="rawJsonOpen"
+ :title="rawJsonTitle"
+ :footer="null"
+ width="800px"
+ >
+ <pre class="max-h-[70vh] overflow-auto rounded bg-gray-50 p-4 text-xs">{{
+ rawJsonContent
+ }}</pre>
+ </Modal>
+ </Page>
+</template>
diff --git a/src/views/mes/dv/machinery/data.ts b/src/views/mes/dv/machinery/data.ts
index ad0c421..056497d 100644
--- a/src/views/mes/dv/machinery/data.ts
+++ b/src/views/mes/dv/machinery/data.ts
@@ -113,6 +113,25 @@
rules: z.number().default(MesDvMachineryStatusEnum.STOP),
},
{
+ fieldName: 'collectDeviceCode',
+ label: '鏁伴噰璁惧缂栧彿',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ユ暟閲囪澶囩紪鍙凤紝濡� C01',
+ },
+ help: '涓庢暟閲囩綉鍏充笂鎶ョ殑 devId 涓�鑷达紝鍏ㄥ眬鍞竴锛涚暀绌鸿〃绀烘湭鎺ュ叆鏁伴噰',
+ },
+ {
+ fieldName: 'collectEnabled',
+ label: '鍚敤鏁伴噰',
+ component: 'Switch',
+ componentProps: {
+ checkedChildren: '鏄�',
+ unCheckedChildren: '鍚�',
+ },
+ },
+ {
fieldName: 'productionEfficiency',
label: '鐢熶骇鏁堢巼',
component: 'InputNumber',
@@ -219,6 +238,28 @@
},
},
{
+ fieldName: 'collectDeviceCode',
+ label: '鏁伴噰璁惧缂栧彿',
+ component: 'Input',
+ componentProps: {
+ allowClear: true,
+ placeholder: '璇疯緭鍏ユ暟閲囪澶囩紪鍙�',
+ },
+ },
+ {
+ fieldName: 'collectEnabled',
+ label: '鍚敤鏁伴噰',
+ component: 'Select',
+ componentProps: {
+ allowClear: true,
+ options: [
+ { label: '鏄�', value: true },
+ { label: '鍚�', value: false },
+ ],
+ placeholder: '璇烽�夋嫨鏄惁鍚敤鏁伴噰',
+ },
+ },
+ {
fieldName: 'status',
label: '璁惧鐘舵��',
component: 'Select',
@@ -247,6 +288,13 @@
{ field: 'specification', title: '瑙勬牸鍨嬪彿', minWidth: 140 },
{ field: 'machineryTypeName', title: '璁惧绫诲瀷', minWidth: 140 },
{ field: 'workshopName', title: '鎵�灞炶溅闂�', minWidth: 140 },
+ { field: 'collectDeviceCode', title: '鏁伴噰璁惧缂栧彿', minWidth: 130 },
+ {
+ field: 'collectEnabled',
+ title: '鍚敤鏁伴噰',
+ width: 100,
+ slots: { default: 'collectEnabled' },
+ },
{
field: 'status',
title: '璁惧鐘舵��',
diff --git a/src/views/mes/dv/machinery/index.vue b/src/views/mes/dv/machinery/index.vue
index b2e4ee5..92c037c 100644
--- a/src/views/mes/dv/machinery/index.vue
+++ b/src/views/mes/dv/machinery/index.vue
@@ -8,7 +8,7 @@
import { Page, useVbenModal } from '@vben/common-ui';
import { downloadFileFromBlobPart } from '@vben/utils';
-import { Button, message } from 'ant-design-vue';
+import { Button, message, Tag } from 'ant-design-vue';
import { ACTION_ICON, TableAction, useVbenVxeGrid } from '#/adapter/vxe-table';
import {
@@ -160,6 +160,11 @@
{{ row.code }}
</Button>
</template>
+ <template #collectEnabled="{ row }">
+ <Tag :color="row.collectEnabled ? 'green' : 'default'">
+ {{ row.collectEnabled ? '鏄�' : '鍚�' }}
+ </Tag>
+ </template>
<template #actions="{ row }">
<TableAction
:actions="[
--
Gitblit v1.9.3