2 Commitit a16683245e ... 55d1b675e1

Tekijä SHA1 Viesti Päivämäärä
  未来全栈 55d1b675e1 Merge branch 'master' of http://git.fmode.cn:3000/0235699/industry-monitor 1 viikko sitten
  未来全栈 b49ccbc6ef feat: device manage with list 1 viikko sitten

+ 11 - 11
industry-monitor-web/src/app/pages/device-management/comp-device-item/comp-device-item.html

@@ -1,26 +1,26 @@
+<!-- comp-device-item.html 更新 -->
 @if(type=="list"){
     <tr>
         <td>{{device.did}}</td>
         <td>{{device.name}}</td>
         <td>{{device.type}}</td>
         <td>{{device.location}}</td>
-        <td><span class="status-badge running">{{device.status}}</span></td>
-        <td><div class="health-meter"><div class="health-fill" style="width: {{device.health}}%;"></div><span>{{device.health}}%</span></div></td>
+        <td><span class="status-badge {{getStatusClass(device.status)}}">{{device.status}}</span></td>
+        <td><div class="health-meter"><div class="health-fill" [style.width]="device.health + '%'"></div><span>{{device.health}}%</span></div></td>
         <td>{{device.lastMaintenance | date:'yyyy-MM-dd'}}</td>
         <td>
-            <button class="icon-btn"><i class="fa fa-eye" routerLink="/device"></i></button>
-            <button class="icon-btn"><i class="fa fa-edit"></i></button>
-            <button class="icon-btn danger"><i class="fa fa-trash"></i></button>
+            <button class="icon-btn"><i class="fa fa-eye" routerLink="/device/{{device.id}}"></i></button>
+            <button class="icon-btn" (click)="edit.emit(device)"><i class="fa fa-edit"></i></button>
+            <button class="icon-btn danger" (click)="delete.emit(device)"><i class="fa fa-trash"></i></button>
         </td>
     </tr>
 }
 
 @if(type=="card"){
-
   <div class="device-card">
     <div class="card-header">
       <h2>{{device.did}}</h2>
-      <span class="status-badge running">{{device.status}}</span>
+      <span class="status-badge {{getStatusClass(device.status)}}">{{device.status}}</span>
     </div>
     <div class="card-body">
       <div class="card-row">
@@ -45,9 +45,9 @@
       </div>
     </div>
     <div class="card-actions">
-      <button class="icon-btn"><i class="fa fa-eye" routerLink="/device"></i></button>
-      <button class="icon-btn"><i class="fa fa-edit"></i></button>
-      <button class="icon-btn danger"><i class="fa fa-trash"></i></button>
+      <button class="icon-btn"><i class="fa fa-eye" routerLink="/device/{{device.id}}"></i></button>
+      <button class="icon-btn" (click)="edit.emit(device)"><i class="fa fa-edit"></i></button>
+      <button class="icon-btn danger" (click)="delete.emit(device)"><i class="fa fa-trash"></i></button>
     </div>
   </div>
-}
+}

+ 6 - 5
industry-monitor-web/src/app/pages/device-management/comp-device-item/comp-device-item.scss

@@ -1,9 +1,10 @@
 
-.device-container {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
-  gap: 20px;
-  padding: 20px;
+:host{
+  display:contents;
+  // tr{
+  //   display:table-row;
+
+  // }
 }
 
 .device-card {

+ 19 - 7
industry-monitor-web/src/app/pages/device-management/comp-device-item/comp-device-item.ts

@@ -1,16 +1,28 @@
+// comp-device-item.ts 更新
 import { CommonModule, DatePipe } from '@angular/common';
-import { Component, Input } from '@angular/core';
+import { Component, Input, Output, EventEmitter } from '@angular/core';
 import { RouterModule } from '@angular/router';
 
 @Component({
+  standalone: true,
   selector: 'app-comp-device-item',
-  imports: [CommonModule,RouterModule],
+  imports: [CommonModule, RouterModule, DatePipe],
   templateUrl: './comp-device-item.html',
   styleUrl: './comp-device-item.scss'
 })
 export class CompDeviceItem {
-  @Input()
-  device:any
-  @Input()
-  type:string = "list" // list card
-}
+  @Input() device: any;
+  @Input() type: string = "list";
+  @Output() edit = new EventEmitter<any>();
+  @Output() delete = new EventEmitter<any>();
+
+  getStatusClass(status: string) {
+    switch (status) {
+      case '运行中': return 'running';
+      case '待机': return 'idle';
+      case '维护中': return 'maintenance';
+      case '已停止': return 'stopped';
+      default: return '';
+    }
+  }
+}

+ 95 - 24
industry-monitor-web/src/app/pages/device-management/device-management.component.html

@@ -1,10 +1,9 @@
-
 <!-- device-management.component.html -->
 <div class="management-container">
   <div class="management-header">
     <h2><i class="fa fa-toolbox"></i> 设备管理</h2>
     <div class="controls">
-      <button class="btn primary"><i class="fa fa-plus"></i> 添加设备</button>
+      <button class="btn primary" (click)="showAddModal()"><i class="fa fa-plus"></i> 添加设备</button>
     </div>
   </div>
 
@@ -13,7 +12,7 @@
     <div class="filter-controls">
       <div class="filter-group">
         <label>设备类型:</label>
-        <select>
+        <select [(ngModel)]="filter.type">
           <option>全部</option>
           <option>CNC加工中心</option>
           <option>注塑机</option>
@@ -23,7 +22,7 @@
       </div>
       <div class="filter-group">
         <label>设备状态:</label>
-        <select>
+        <select [(ngModel)]="filter.status">
           <option>全部</option>
           <option>运行中</option>
           <option>待机</option>
@@ -33,37 +32,109 @@
       </div>
       <div class="filter-group">
         <label>所在位置:</label>
-        <select>
+        <select [(ngModel)]="filter.location">
           <option>全部车间</option>
           <option>车间A</option>
           <option>车间B</option>
           <option>车间C</option>
         </select>
       </div>
-      <button class="btn"><i class="fa fa-search"></i> 搜索</button>
+      <button class="btn" (click)="applyFilter()"><i class="fa fa-search"></i> 搜索</button>
+      <button class="btn" (click)="resetFilter()"><i class="fa fa-refresh"></i> 重置</button>
     </div>
   </div>
 
   <div class="device-table-card">
-    <h3><i class="fa fa-list"></i> 设备列表 <button (click)="listType='list'">列表</button> <button (click)="listType='card'">卡片</button></h3>
-    <table class="device-table">
-      <thead>
-        <tr>
-          <th>设备ID</th>
-          <th>设备名称</th>
-          <th>类型</th>
-          <th>位置</th>
-          <th>状态</th>
-          <th>健康指数</th>
-          <th>最后维护</th>
-          <th>操作</th>
-        </tr>
-      </thead>
-      <tbody>
-        @for(device of deviceList;track device){
+    <h3><i class="fa fa-list"></i> 设备列表 
+      <button (click)="listType='list'" [class.active]="listType === 'list'">列表</button> 
+      <button (click)="listType='card'" [class.active]="listType === 'card'">卡片</button>
+    </h3>
+    
+    @if (listType === 'list') {
+      <table class="device-table">
+        <thead>
+          <tr>
+            <th>设备ID</th>
+            <th>设备名称</th>
+            <th>类型</th>
+            <th>位置</th>
+            <th>状态</th>
+            <th>健康指数</th>
+            <th>最后维护</th>
+            <th>操作</th>
+          </tr>
+        </thead>
+        <tbody>
+          @for (device of deviceList; track device.id) {
+            <app-comp-device-item [type]="listType" [device]="device"></app-comp-device-item>
+          }
+        </tbody>
+      </table>
+    }
+    
+    @if (listType === 'card') {
+      <div class="device-grid">
+        @for (device of deviceList; track device.id) {
           <app-comp-device-item [type]="listType" [device]="device"></app-comp-device-item>
         }
-      </tbody>
-    </table>
+      </div>
+    }
   </div>
+
+  <!-- 添加/编辑设备模态框 -->
+  @if (isAddModalVisible) {
+    <div class="modal-overlay">
+      <div class="modal">
+        <div class="modal-header">
+          <h3>{{ editingDevice ? '编辑设备' : '添加新设备' }}</h3>
+          <button class="close-btn" (click)="isAddModalVisible = false">&times;</button>
+        </div>
+        <div class="modal-body">
+          <div class="form-group">
+            <label>设备名称</label>
+            <input type="text" [(ngModel)]="newDevice.name" placeholder="请输入设备名称">
+          </div>
+          <div class="form-group">
+            <label>设备类型</label>
+            <select [(ngModel)]="newDevice.type">
+              <option>CNC加工中心</option>
+              <option>注塑机</option>
+              <option>装配机器人</option>
+              <option>检测设备</option>
+            </select>
+          </div>
+          <div class="form-group">
+            <label>所在位置</label>
+            <select [(ngModel)]="newDevice.location">
+              <option>车间A/产线1</option>
+              <option>车间A/产线2</option>
+              <option>车间A/产线3</option>
+              <option>车间B/产线1</option>
+              <option>车间B/产线2</option>
+              <option>车间C/产线1</option>
+              <option>车间C/产线2</option>
+              <option>车间C/产线3</option>
+            </select>
+          </div>
+          <div class="form-group">
+            <label>设备状态</label>
+            <select [(ngModel)]="newDevice.status">
+              <option>运行中</option>
+              <option>待机</option>
+              <option>维护中</option>
+              <option>已停止</option>
+            </select>
+          </div>
+          <div class="form-group">
+            <label>健康指数</label>
+            <input type="number" [(ngModel)]="newDevice.health" min="0" max="100" placeholder="0-100">
+          </div>
+        </div>
+        <div class="modal-footer">
+          <button class="btn" (click)="isAddModalVisible = false">取消</button>
+          <button class="btn primary" (click)="saveDevice()">保存</button>
+        </div>
+      </div>
+    </div>
+  }
 </div>

+ 136 - 55
industry-monitor-web/src/app/pages/device-management/device-management.component.ts

@@ -1,7 +1,10 @@
-import { Component, ViewEncapsulation } from '@angular/core';
+// device-management.component.ts
+import { Component, ViewEncapsulation, OnInit } from '@angular/core';
 import { DatePipe } from '@angular/common';
 import { RouterModule } from '@angular/router';
 import { CompDeviceItem } from './comp-device-item/comp-device-item';
+import { FormsModule } from '@angular/forms';
+import { CloudObject, CloudQuery } from '../../../lib/ncloud';
 
 @Component({
   standalone: true,
@@ -10,66 +13,144 @@ import { CompDeviceItem } from './comp-device-item/comp-device-item';
   imports: [
     DatePipe,
     RouterModule,
-    CompDeviceItem
+    CompDeviceItem,
+    FormsModule
   ],
   templateUrl: './device-management.component.html',
   encapsulation: ViewEncapsulation.None
 })
-export class DeviceManagementComponent {
-  listType:string = "list"
-  deviceList:Array<any> = [
-  {
-    "did": "DEV-2023-001",
-    "name": "CNC 铣床 #01",
-    "type": "CNC加工中心",
-    "location": "车间A/产线3",
-    "status": "运行中",
-    "health": 89,
-    "lastMaintenance": new Date()
-    },
-  {
-    "did": "DEV-2023-002",
-    "name": "注塑机 #05",
-    "type": "注塑机",
-    "location": "车间B/产线1",
-    "status": "待机",
-    "health": 76,
-    "lastMaintenance": new Date()
-    },
-  {
-    "did": "DEV-2023-003",
-    "name": "装配机器人 #03",
-    "type": "装配机器人",
-    "location": "车间C/产线2",
-    "status": "维护中",
-    "health": 65,
-    "lastMaintenance": new Date()
-    },
-  {
-    "did": "DEV-2023-004",
-    "name": "质检仪 #02",
-    "type": "检测设备",
-    "location": "车间A/产线1",
-    "status": "运行中",
-    "health": 92,
-    "lastMaintenance": new Date()
-    },
-  {
-    "did": "DEV-2023-005",
-    "name": "包装机 #07",
-    "type": "包装设备",
-    "location": "车间C/产线3",
-    "status": "已停止",
-    "health": 58,
-    "lastMaintenance": new Date()
-    }
-]
-currentTime: Date = new Date();
-   ngOnInit(): void {
+export class DeviceManagementComponent implements OnInit {
+  listType: string = "list";
+  deviceList: Array<any> = [];
+  currentTime: Date = new Date();
+  
+  // 筛选条件
+  filter = {
+    type: '全部',
+    status: '全部',
+    location: '全部车间'
+  };
+  
+  // 添加/编辑设备
+  editingDevice: any = null;
+  isAddModalVisible = false;
+  newDevice = {
+    name: '',
+    type: 'CNC加工中心',
+    location: '车间A/产线1',
+    status: '运行中',
+    health: 90
+  };
+
+  constructor() {}
+
+  async ngOnInit() {
     // 更新时间,每秒更新一次
     setInterval(() => {
       this.currentTime = new Date();
     }, 1000);
 
-   }
-}
+    await this.loadDevices();
+  }
+
+  async loadDevices() {
+    const query = new CloudQuery("Device");
+    
+    // 添加筛选条件
+    if (this.filter.type !== '全部') {
+      query.equalTo("type", this.filter.type);
+    }
+    if (this.filter.status !== '全部') {
+      query.equalTo("status", this.filter.status);
+    }
+    if (this.filter.location !== '全部车间') {
+      query.equalTo("location", this.filter.location);
+    }
+    
+    const devices = await query.find();
+    this.deviceList = devices.map((device:any) => ({
+      id: device.id,
+      did: device.get("did"),
+      name: device.get("name"),
+      type: device.get("type"),
+      location: device.get("location"),
+      status: device.get("status"),
+      health: device.get("health"),
+      lastMaintenance: new Date(device.get("lastMaintenance")?.iso || new Date())
+    }));
+  }
+
+  async applyFilter() {
+    await this.loadDevices();
+  }
+
+  async resetFilter() {
+    this.filter = {
+      type: '全部',
+      status: '全部',
+      location: '全部车间'
+    };
+    await this.loadDevices();
+  }
+
+  showAddModal() {
+    this.isAddModalVisible = true;
+    this.editingDevice = null;
+    this.newDevice = {
+      name: '',
+      type: 'CNC加工中心',
+      location: '车间A/产线1',
+      status: '运行中',
+      health: 90
+    };
+  }
+
+  showEditModal(device: any) {
+    this.isAddModalVisible = true;
+    this.editingDevice = device;
+    this.newDevice = {
+      name: device.name,
+      type: device.type,
+      location: device.location,
+      status: device.status,
+      health: device.health
+    };
+  }
+
+  async saveDevice() {
+    const deviceObj = this.editingDevice 
+      ? new CloudObject("Device") 
+      : new CloudObject("Device");
+    
+    if (this.editingDevice) {
+      deviceObj.id = this.editingDevice.id;
+    } else {
+      // 为新设备生成ID
+      const now = new Date();
+      const did = `DEV-${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}${now.getDate().toString().padStart(2, '0')}-${Math.floor(1000 + Math.random() * 9000)}`;
+      deviceObj.set({ did });
+    }
+    
+    deviceObj.set({
+      name: this.newDevice.name,
+      type: this.newDevice.type,
+      location: this.newDevice.location,
+      status: this.newDevice.status,
+      health: this.newDevice.health,
+      lastMaintenance: new Date().toISOString()
+    });
+    
+    await deviceObj.save();
+    this.isAddModalVisible = false;
+    await this.loadDevices();
+  }
+
+  async deleteDevice(device: any) {
+    if (confirm(`确定要删除设备 ${device.name} 吗?`)) {
+      const deviceObj = new CloudObject("Device");
+      deviceObj.id = device.id;
+      await deviceObj.destroy();
+      await this.loadDevices();
+    }
+  }
+}

+ 148 - 1
industry-monitor-web/src/app/pages/device-management/device-management.css

@@ -179,4 +179,151 @@
 .icon-btn.danger:hover {
   background: #e74c3c;
   color: white;
-}
+}
+
+
+.device-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: 20px;
+}
+
+button.active {
+  background-color: #3498db;
+  color: white;
+}
+
+/* 设备状态标签样式 */
+.status-badge {
+  display: inline-block;
+  padding: 4px 10px;
+  border-radius: 4px;
+  font-size: 13px;
+  font-weight: 500;
+}
+
+.status-badge.running {
+  background: #e8f6f0;
+  color: #27ae60;
+  border: 1px solid #27ae60;
+}
+
+.status-badge.idle {
+  background: #fef5e7;
+  color: #f39c12;
+  border: 1px solid #f39c12;
+}
+
+.status-badge.maintenance {
+  background: #ebf5fb;
+  color: #3498db;
+  border: 1px solid #3498db;
+}
+
+.status-badge.stopped {
+  background: #f5f7fa;
+  color: #7f8c8d;
+  border: 1px solid #bdc3c7;
+}
+/* device-management.component.css 新增部分 */
+:host {
+  display: block;
+  width: 100%;
+  padding: 15px;
+}
+
+.management-container {
+  max-width: 1400px;
+  margin: 0 auto;
+  padding: 20px;
+  background: white;
+  border-radius: 8px;
+  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
+  position: relative;
+}
+
+.device-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+  gap: 20px;
+  margin-top: 20px;
+}
+
+button.active {
+  background-color: #3498db;
+  color: white;
+}
+
+/* 模态框样式 */
+.modal-overlay {
+  position: fixed;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background: rgba(0, 0, 0, 0.5);
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  z-index: 1000;
+}
+
+.modal {
+  background: white;
+  border-radius: 8px;
+  width: 500px;
+  max-width: 90%;
+  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
+}
+
+.modal-header {
+  padding: 15px 20px;
+  border-bottom: 1px solid #eee;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+}
+
+.modal-header h3 {
+  margin: 0;
+}
+
+.close-btn {
+  background: none;
+  border: none;
+  font-size: 24px;
+  cursor: pointer;
+  color: #7f8c8d;
+}
+
+.modal-body {
+  padding: 20px;
+}
+
+.form-group {
+  margin-bottom: 15px;
+}
+
+.form-group label {
+  display: block;
+  margin-bottom: 5px;
+  font-weight: 500;
+}
+
+.form-group input,
+.form-group select {
+  width: 100%;
+  padding: 8px 12px;
+  border: 1px solid #ddd;
+  border-radius: 4px;
+}
+
+.modal-footer {
+  padding: 15px 20px;
+  border-top: 1px solid #eee;
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+/* 其他原有样式保持不变... */

+ 63 - 0
industry-monitor-web/src/app/pages/device-management/device.model.ts

@@ -0,0 +1,63 @@
+import { CloudObject } from "../../../lib/ncloud";
+
+export class Device extends CloudObject {
+  constructor() {
+    super('Device');
+  }
+
+  get did(): string {
+    return this.get('did');
+  }
+
+  set did(value: string) {
+    this.set({ did: value }); // 修改为使用对象方式
+  }
+
+  get name(): string {
+    return this.get('name');
+  }
+
+  set name(value: string) {
+    this.set({ name: value }); // 修改为使用对象方式
+  }
+
+  get type(): string {
+    return this.get('type');
+  }
+
+  set type(value: string) {
+    this.set({ type: value }); // 修改为使用对象方式
+  }
+
+  get location(): string {
+    return this.get('location');
+  }
+
+  set location(value: string) {
+    this.set({ location: value }); // 修改为使用对象方式
+  }
+
+  get status(): string {
+    return this.get('status');
+  }
+
+  set status(value: string) {
+    this.set({ status: value }); // 修改为使用对象方式
+  }
+
+  get health(): number {
+    return this.get('health');
+  }
+
+  set health(value: number) {
+    this.set({ health: value }); // 修改为使用对象方式
+  }
+
+  get lastMaintenance(): Date {
+    return new Date(this.get('lastMaintenance'));
+  }
+
+  set lastMaintenance(value: Date) {
+    this.set({ lastMaintenance: value.toISOString() }); // 修改为使用对象方式
+  }
+}