|
@@ -15,9 +15,11 @@
|
|
|
(input)="searchTerm.set($event.target.value)"
|
|
|
class="search-input"
|
|
|
>
|
|
|
- <button mat-icon-button *ngIf="searchTerm()" (click)="searchTerm.set('')" class="clear-search">
|
|
|
- <mat-icon>close</mat-icon>
|
|
|
- </button>
|
|
|
+ @if (searchTerm()) {
|
|
|
+ <button mat-icon-button (click)="searchTerm.set('')" class="clear-search">
|
|
|
+ <mat-icon>close</mat-icon>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
</div>
|
|
|
|
|
|
<div class="filter-controls">
|
|
@@ -25,7 +27,9 @@
|
|
|
<label>资产类型:</label>
|
|
|
<select [value]="typeFilter()" (change)="typeFilter.set($any($event.target).value)" class="filter-select">
|
|
|
<option value="">全部类型</option>
|
|
|
- <option *ngFor="let type of assetTypes()" [value]="type">{{ type }}</option>
|
|
|
+ @for (type of assetTypes(); track type) {
|
|
|
+ <option [value]="type">{{ type }}</option>
|
|
|
+ }
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
@@ -44,7 +48,9 @@
|
|
|
<label>所属部门:</label>
|
|
|
<select [value]="departmentFilter()" (change)="departmentFilter.set($any($event.target).value)" class="filter-select">
|
|
|
<option value="">全部部门</option>
|
|
|
- <option *ngFor="let dept of departments()" [value]="dept">{{ dept }}</option>
|
|
|
+ @for (dept of departments(); track dept) {
|
|
|
+ <option [value]="dept">{{ dept }}</option>
|
|
|
+ }
|
|
|
</select>
|
|
|
</div>
|
|
|
|
|
@@ -151,147 +157,66 @@
|
|
|
<!-- 资产列表/网格视图 -->
|
|
|
<div class="assets-view">
|
|
|
<!-- 网格视图 -->
|
|
|
- <div *ngIf="selectedView() === 'grid'" class="assets-grid">
|
|
|
- <div
|
|
|
- *ngFor="let asset of filteredAssets()"
|
|
|
- class="asset-card"
|
|
|
- [class.faulty]="asset.status === '故障' || asset.status === '报修中'"
|
|
|
- >
|
|
|
- <div class="asset-header">
|
|
|
- <div class="asset-type-icon" [class]="asset.type">
|
|
|
- <mat-icon>{{ getTypeIcon(asset.type) }}</mat-icon>
|
|
|
- </div>
|
|
|
- <div class="asset-status" [class]="getStatusClass(asset.status)">
|
|
|
- {{ asset.status }}
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="asset-content">
|
|
|
- <h3 class="asset-name">{{ asset.name }}</h3>
|
|
|
- <div class="asset-info">
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">序列号:</span>
|
|
|
- <span class="info-value">{{ asset.serialNumber }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">购买日期:</span>
|
|
|
- <span class="info-value">{{ formatDate(asset.purchaseDate) }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">价值:</span>
|
|
|
- <span class="info-value">{{ formatCurrency(asset.value) }}</span>
|
|
|
- </div>
|
|
|
- <div class="info-item">
|
|
|
- <span class="info-label">部门:</span>
|
|
|
- <span class="info-value">{{ asset.department }}</span>
|
|
|
- </div>
|
|
|
- <div *ngIf="asset.assignedToName" class="info-item">
|
|
|
- <span class="info-label">使用人:</span>
|
|
|
- <span class="info-value">{{ asset.assignedToName }}</span>
|
|
|
- </div>
|
|
|
- <div *ngIf="asset.status === '故障' || asset.status === '报修中'" class="info-item faulty-notice">
|
|
|
- <span class="info-label">故障描述:</span>
|
|
|
- <span class="info-value">需要维修</span>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- <div class="asset-actions">
|
|
|
- <button mat-icon-button matTooltip="查看详情" class="action-btn">
|
|
|
- <mat-icon>visibility</mat-icon>
|
|
|
- </button>
|
|
|
- <button
|
|
|
- mat-icon-button
|
|
|
- matTooltip="编辑信息"
|
|
|
- class="action-btn"
|
|
|
+ @if (selectedView() === 'grid') {
|
|
|
+ <div class="assets-grid">
|
|
|
+ @for (asset of filteredAssets(); track asset.id) {
|
|
|
+ <div
|
|
|
+ class="asset-card"
|
|
|
+ [class.faulty]="asset.status === '故障' || asset.status === '报修中'"
|
|
|
>
|
|
|
- <mat-icon>edit</mat-icon>
|
|
|
- </button>
|
|
|
- <ng-container *ngIf="asset.status === '空闲'">
|
|
|
- <button
|
|
|
- mat-icon-button
|
|
|
- matTooltip="分配资产"
|
|
|
- class="action-btn primary"
|
|
|
- (click)="openAssignmentDialog(asset)"
|
|
|
- >
|
|
|
- <mat-icon>assignment_ind</mat-icon>
|
|
|
- </button>
|
|
|
- </ng-container>
|
|
|
- <ng-container *ngIf="asset.status === '占用'">
|
|
|
- <button
|
|
|
- mat-icon-button
|
|
|
- matTooltip="归还资产"
|
|
|
- class="action-btn primary"
|
|
|
- (click)="openAssignmentDialog(asset, true)"
|
|
|
- >
|
|
|
- <mat-icon>undo</mat-icon>
|
|
|
- </button>
|
|
|
- </ng-container>
|
|
|
- <ng-container *ngIf="asset.status === '故障'">
|
|
|
- <button
|
|
|
- mat-icon-button
|
|
|
- matTooltip="申请报修"
|
|
|
- class="action-btn warning"
|
|
|
- (click)="openRepairDialog(asset)"
|
|
|
- >
|
|
|
- <mat-icon>build</mat-icon>
|
|
|
- </button>
|
|
|
- </ng-container>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 列表视图 -->
|
|
|
- <div *ngIf="selectedView() === 'list'" class="assets-list">
|
|
|
- <table mat-table [dataSource]="filteredAssets()" class="assets-table">
|
|
|
- <ng-container matColumnDef="name">
|
|
|
- <th mat-header-cell *matHeaderCellDef>资产名称</th>
|
|
|
- <td mat-cell *matCellDef="let asset">
|
|
|
- <div class="asset-name-list">
|
|
|
- <div class="asset-type-icon-small" [class]="asset.type">
|
|
|
+ <div class="asset-header">
|
|
|
+ <div class="asset-type-icon" [class]="asset.type">
|
|
|
<mat-icon>{{ getTypeIcon(asset.type) }}</mat-icon>
|
|
|
</div>
|
|
|
- <span>{{ asset.name }}</span>
|
|
|
+ <div class="asset-status" [class]="getStatusClass(asset.status)">
|
|
|
+ {{ asset.status }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="asset-content">
|
|
|
+ <h3 class="asset-name">{{ asset.name }}</h3>
|
|
|
+ <div class="asset-info">
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">序列号:</span>
|
|
|
+ <span class="info-value">{{ asset.serialNumber }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">购买日期:</span>
|
|
|
+ <span class="info-value">{{ formatDate(asset.purchaseDate) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">价值:</span>
|
|
|
+ <span class="info-value">{{ formatCurrency(asset.value) }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">部门:</span>
|
|
|
+ <span class="info-value">{{ asset.department }}</span>
|
|
|
+ </div>
|
|
|
+ @if (asset.assignedToName) {
|
|
|
+ <div class="info-item">
|
|
|
+ <span class="info-label">使用人:</span>
|
|
|
+ <span class="info-value">{{ asset.assignedToName }}</span>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ @if (asset.status === '故障' || asset.status === '报修中') {
|
|
|
+ <div class="info-item faulty-notice">
|
|
|
+ <span class="info-label">故障描述:</span>
|
|
|
+ <span class="info-value">需要维修</span>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="type">
|
|
|
- <th mat-header-cell *matHeaderCellDef>类型</th>
|
|
|
- <td mat-cell *matCellDef="let asset">{{ asset.type }}</td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="status">
|
|
|
- <th mat-header-cell *matHeaderCellDef>状态</th>
|
|
|
- <td mat-cell *matCellDef="let asset">
|
|
|
- <span class="status-badge" [class]="getStatusClass(asset.status)">
|
|
|
- {{ asset.status }}
|
|
|
- </span>
|
|
|
- </td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="department">
|
|
|
- <th mat-header-cell *matHeaderCellDef>部门</th>
|
|
|
- <td mat-cell *matCellDef="let asset">{{ asset.department }}</td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="assignedTo">
|
|
|
- <th mat-header-cell *matHeaderCellDef>使用人</th>
|
|
|
- <td mat-cell *matCellDef="let asset">{{ asset.assignedToName || '-' }}</td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="purchaseDate">
|
|
|
- <th mat-header-cell *matHeaderCellDef>购买日期</th>
|
|
|
- <td mat-cell *matCellDef="let asset">{{ formatDate(asset.purchaseDate) }}</td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="value">
|
|
|
- <th mat-header-cell *matHeaderCellDef>价值</th>
|
|
|
- <td mat-cell *matCellDef="let asset">{{ formatCurrency(asset.value) }}</td>
|
|
|
- </ng-container>
|
|
|
- <ng-container matColumnDef="actions">
|
|
|
- <th mat-header-cell *matHeaderCellDef>操作</th>
|
|
|
- <td mat-cell *matCellDef="let asset" class="actions-column">
|
|
|
- <div class="action-buttons-list">
|
|
|
+ <div class="asset-actions">
|
|
|
<button mat-icon-button matTooltip="查看详情" class="action-btn">
|
|
|
<mat-icon>visibility</mat-icon>
|
|
|
</button>
|
|
|
- <button mat-icon-button matTooltip="编辑信息" class="action-btn">
|
|
|
+ <button
|
|
|
+ mat-icon-button
|
|
|
+ matTooltip="编辑信息"
|
|
|
+ class="action-btn"
|
|
|
+ >
|
|
|
<mat-icon>edit</mat-icon>
|
|
|
</button>
|
|
|
- <ng-container *ngIf="asset.status === '空闲'">
|
|
|
+ @if (asset.status === '空闲') {
|
|
|
<button
|
|
|
mat-icon-button
|
|
|
matTooltip="分配资产"
|
|
@@ -300,8 +225,8 @@
|
|
|
>
|
|
|
<mat-icon>assignment_ind</mat-icon>
|
|
|
</button>
|
|
|
- </ng-container>
|
|
|
- <ng-container *ngIf="asset.status === '占用'">
|
|
|
+ }
|
|
|
+ @if (asset.status === '占用') {
|
|
|
<button
|
|
|
mat-icon-button
|
|
|
matTooltip="归还资产"
|
|
@@ -310,8 +235,8 @@
|
|
|
>
|
|
|
<mat-icon>undo</mat-icon>
|
|
|
</button>
|
|
|
- </ng-container>
|
|
|
- <ng-container *ngIf="asset.status === '故障'">
|
|
|
+ }
|
|
|
+ @if (asset.status === '故障') {
|
|
|
<button
|
|
|
mat-icon-button
|
|
|
matTooltip="申请报修"
|
|
@@ -320,24 +245,114 @@
|
|
|
>
|
|
|
<mat-icon>build</mat-icon>
|
|
|
</button>
|
|
|
- </ng-container>
|
|
|
- </div>
|
|
|
- </td>
|
|
|
- </ng-container>
|
|
|
-
|
|
|
- <tr mat-header-row *matHeaderRowDef="['name', 'type', 'status', 'department', 'assignedTo', 'purchaseDate', 'value', 'actions']"></tr>
|
|
|
- <tr mat-row *matRowDef="let row; columns: ['name', 'type', 'status', 'department', 'assignedTo', 'purchaseDate', 'value', 'actions']"></tr>
|
|
|
-
|
|
|
- <tr class="mat-row" *matNoDataRow>
|
|
|
- <td class="mat-cell" colspan="8" class="no-data">
|
|
|
- <div class="empty-state">
|
|
|
- <mat-icon>search_off</mat-icon>
|
|
|
- <p>没有找到符合条件的资产</p>
|
|
|
+ }
|
|
|
</div>
|
|
|
- </td>
|
|
|
- </tr>
|
|
|
- </table>
|
|
|
- </div>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ }
|
|
|
+
|
|
|
+ <!-- 列表视图 -->
|
|
|
+ @if (selectedView() === 'list') {
|
|
|
+ <div class="assets-list">
|
|
|
+ <table mat-table [dataSource]="filteredAssets()" class="assets-table">
|
|
|
+ <ng-container matColumnDef="name">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>资产名称</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">
|
|
|
+ <div class="asset-name-list">
|
|
|
+ <div class="asset-type-icon-small" [class]="asset.type">
|
|
|
+ <mat-icon>{{ getTypeIcon(asset.type) }}</mat-icon>
|
|
|
+ </div>
|
|
|
+ <span>{{ asset.name }}</span>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="type">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>类型</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">{{ asset.type }}</td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="status">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>状态</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">
|
|
|
+ <span class="status-badge" [class]="getStatusClass(asset.status)">
|
|
|
+ {{ asset.status }}
|
|
|
+ </span>
|
|
|
+ </td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="department">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>部门</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">{{ asset.department }}</td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="assignedTo">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>使用人</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">{{ asset.assignedToName || '-' }}</td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="purchaseDate">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>购买日期</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">{{ formatDate(asset.purchaseDate) }}</td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="value">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>价值</th>
|
|
|
+ <td mat-cell *matCellDef="let asset">{{ formatCurrency(asset.value) }}</td>
|
|
|
+ </ng-container>
|
|
|
+ <ng-container matColumnDef="actions">
|
|
|
+ <th mat-header-cell *matHeaderCellDef>操作</th>
|
|
|
+ <td mat-cell *matCellDef="let asset" class="actions-column">
|
|
|
+ <div class="action-buttons-list">
|
|
|
+ <button mat-icon-button matTooltip="查看详情" class="action-btn">
|
|
|
+ <mat-icon>visibility</mat-icon>
|
|
|
+ </button>
|
|
|
+ <button mat-icon-button matTooltip="编辑信息" class="action-btn">
|
|
|
+ <mat-icon>edit</mat-icon>
|
|
|
+ </button>
|
|
|
+ @if (asset.status === '空闲') {
|
|
|
+ <button
|
|
|
+ mat-icon-button
|
|
|
+ matTooltip="分配资产"
|
|
|
+ class="action-btn primary"
|
|
|
+ (click)="openAssignmentDialog(asset)"
|
|
|
+ >
|
|
|
+ <mat-icon>assignment_ind</mat-icon>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
+ @if (asset.status === '占用') {
|
|
|
+ <button
|
|
|
+ mat-icon-button
|
|
|
+ matTooltip="归还资产"
|
|
|
+ class="action-btn primary"
|
|
|
+ (click)="openAssignmentDialog(asset, true)"
|
|
|
+ >
|
|
|
+ <mat-icon>undo</mat-icon>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
+ @if (asset.status === '故障') {
|
|
|
+ <button
|
|
|
+ mat-icon-button
|
|
|
+ matTooltip="申请报修"
|
|
|
+ class="action-btn warning"
|
|
|
+ (click)="openRepairDialog(asset)"
|
|
|
+ >
|
|
|
+ <mat-icon>build</mat-icon>
|
|
|
+ </button>
|
|
|
+ }
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </ng-container>
|
|
|
+
|
|
|
+ <tr mat-header-row *matHeaderRowDef="['name', 'type', 'status', 'department', 'assignedTo', 'purchaseDate', 'value', 'actions']"></tr>
|
|
|
+ <tr mat-row *matRowDef="let row; columns: ['name', 'type', 'status', 'department', 'assignedTo', 'purchaseDate', 'value', 'actions']"></tr>
|
|
|
+
|
|
|
+ <tr class="mat-row" *matNoDataRow>
|
|
|
+ <td class="mat-cell" colspan="8" class="no-data">
|
|
|
+ <div class="empty-state">
|
|
|
+ <mat-icon>search_off</mat-icon>
|
|
|
+ <p>没有找到符合条件的资产</p>
|
|
|
+ </div>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ }
|
|
|
</div>
|
|
|
|
|
|
<!-- 统计图表区 -->
|
|
@@ -349,16 +364,18 @@
|
|
|
</div>
|
|
|
<div class="chart-content">
|
|
|
<div class="type-stats">
|
|
|
- <div *ngFor="let typeStat of assetStats().typeStatsArray" class="type-stat-item">
|
|
|
- <div class="type-info">
|
|
|
- <span class="type-name">{{ typeStat.type }}</span>
|
|
|
- <span class="type-count">{{ typeStat.count }}台</span>
|
|
|
- </div>
|
|
|
- <div class="progress-bar">
|
|
|
- <div class="progress-fill" [style.width]="(typeStat.count / assetStats().total * 100) + '%'"></div>
|
|
|
+ @for (typeStat of assetStats().typeStatsArray; track typeStat.type) {
|
|
|
+ <div class="type-stat-item">
|
|
|
+ <div class="type-info">
|
|
|
+ <span class="type-name">{{ typeStat.type }}</span>
|
|
|
+ <span class="type-count">{{ typeStat.count }}台</span>
|
|
|
+ </div>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width]="(typeStat.count / assetStats().total * 100) + '%'"></div>
|
|
|
+ </div>
|
|
|
+ <div class="type-value">{{ formatCurrency(typeStat.value) }}</div>
|
|
|
</div>
|
|
|
- <div class="type-value">{{ formatCurrency(typeStat.value) }}</div>
|
|
|
- </div>
|
|
|
+ }
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -370,16 +387,18 @@
|
|
|
</div>
|
|
|
<div class="chart-content">
|
|
|
<div class="department-stats">
|
|
|
- <div *ngFor="let deptStat of assetStats().departmentStatsArray" class="department-stat-item">
|
|
|
- <div class="department-info">
|
|
|
- <span class="department-name">{{ deptStat.department }}</span>
|
|
|
- <span class="department-count">{{ deptStat.count }}台</span>
|
|
|
- </div>
|
|
|
- <div class="progress-bar">
|
|
|
- <div class="progress-fill" [style.width]="(deptStat.count / assetStats().total * 100) + '%'"></div>
|
|
|
+ @for (deptStat of assetStats().departmentStatsArray; track deptStat.department) {
|
|
|
+ <div class="department-stat-item">
|
|
|
+ <div class="department-info">
|
|
|
+ <span class="department-name">{{ deptStat.department }}</span>
|
|
|
+ <span class="department-count">{{ deptStat.count }}台</span>
|
|
|
+ </div>
|
|
|
+ <div class="progress-bar">
|
|
|
+ <div class="progress-fill" [style.width]="(deptStat.count / assetStats().total * 100) + '%'" ></div>
|
|
|
+ </div>
|
|
|
+ <div class="department-value">{{ formatCurrency(deptStat.value) }}</div>
|
|
|
</div>
|
|
|
- <div class="department-value">{{ formatCurrency(deptStat.value) }}</div>
|
|
|
- </div>
|
|
|
+ }
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
@@ -391,16 +410,18 @@
|
|
|
</div>
|
|
|
<div class="chart-content">
|
|
|
<div class="usage-stats">
|
|
|
- <div *ngFor="let usage of assetStats().usageStats" class="usage-stat-item">
|
|
|
- <div class="usage-info">
|
|
|
- <span class="usage-type">{{ usage.type }}</span>
|
|
|
- </div>
|
|
|
- <div class="bar-container">
|
|
|
- <div class="usage-bar" [style.height]="(usage.avgHours / 200 * 100) + '%'">
|
|
|
- <span class="usage-value">{{ usage.avgHours }}h</span>
|
|
|
+ @for (usage of assetStats().usageStats; track usage.type) {
|
|
|
+ <div class="usage-stat-item">
|
|
|
+ <div class="usage-info">
|
|
|
+ <span class="usage-type">{{ usage.type }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="bar-container">
|
|
|
+ <div class="usage-bar" [style.height]="(usage.avgHours / 200 * 100) + '%'">
|
|
|
+ <span class="usage-value">{{ usage.avgHours }}h</span>
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
+ }
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|