kely 1 year ago
parent
commit
e6f013ef65
  1. 43
      web-admin-app/README.md
  2. 4
      web-admin-app/proxy/devserver.js
  3. 7
      web-admin-app/src/app/app.routes.ts
  4. 8
      web-admin-app/src/app/components/asset-form/asset-form.component.html
  5. 8
      web-admin-app/src/app/components/asset-form/asset-form.component.ts
  6. 52
      web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.html
  7. 0
      web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.less
  8. 63
      web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.ts
  9. 52
      web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.html
  10. 0
      web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.less
  11. 63
      web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.ts
  12. 83
      web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.html
  13. 0
      web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.less
  14. 153
      web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.ts
  15. 2
      web-admin-app/src/app/components/asset-repair-records/asset-repair-records.component.ts
  16. 157
      web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.html
  17. 0
      web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.less
  18. 222
      web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.ts
  19. 52
      web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.html
  20. 0
      web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.less
  21. 63
      web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.ts
  22. 32
      web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.html
  23. 0
      web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.less
  24. 135
      web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.ts
  25. 75
      web-admin-app/src/app/components/plan-task/handle-task-asset-item/handle-task-asset-item.component.html
  26. 39
      web-admin-app/src/app/components/plan-task/handle-task-asset-item/handle-task-asset-item.component.ts
  27. 2
      web-admin-app/src/app/components/plan-task/handle-task/handle-task.component.html
  28. 23
      web-admin-app/src/app/components/plan-task/handle-task/handle-task.component.ts
  29. 7
      web-admin-app/src/app/components/plan-task/task-form/task-form.component.html
  30. 2
      web-admin-app/src/app/components/plan-task/task-form/task-form.component.ts
  31. 30
      web-admin-app/src/app/components/plan-task/task-list/task-list.component.ts
  32. 113
      web-admin-app/src/app/pages/data-vis/data-vis.component.html
  33. 761
      web-admin-app/src/app/pages/data-vis/data-vis.component.less
  34. 271
      web-admin-app/src/app/pages/data-vis/data-vis.component.ts
  35. 23
      web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form-manage/basic-flow-form-manage.component.html
  36. 43
      web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form-manage/basic-flow-form-manage.component.ts
  37. 15
      web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form/basic-flow-form.component.html
  38. 35
      web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form/basic-flow-form.component.ts
  39. 14
      web-admin-app/src/app/pages/fixed-asset/fixed-asset.component.html
  40. 25
      web-admin-app/src/app/pages/fixed-asset/maintain/maintain-on-off/maintain-on-off.component.ts
  41. 12
      web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.html
  42. 0
      web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.less
  43. 27
      web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.ts
  44. 21
      web-admin-app/src/app/pages/fixed-asset/registration/registration.component.html
  45. 16
      web-admin-app/src/app/pages/fixed-asset/registration/registration.component.ts
  46. 40
      web-admin-app/src/app/pages/flow/flow-my-apply/flow-my-apply.component.html
  47. 67
      web-admin-app/src/app/pages/flow/flow-my-apply/flow-my-apply.component.ts
  48. 40
      web-admin-app/src/app/pages/flow/flow-my-finished/flow-my-finished.component.html
  49. 45
      web-admin-app/src/app/pages/flow/flow-my-finished/flow-my-finished.component.ts
  50. 17
      web-admin-app/src/app/pages/flow/flow-my-todo/flow-my-todo.component.html
  51. 11
      web-admin-app/src/app/pages/flow/flow-my-todo/flow-my-todo.component.ts
  52. 136
      web-admin-app/src/app/pages/flow/flow-report/flow-report.component.html
  53. 127
      web-admin-app/src/app/pages/flow/flow-report/flow-report.component.less
  54. 211
      web-admin-app/src/app/pages/flow/flow-report/flow-report.component.ts
  55. 8
      web-admin-app/src/app/pages/msg/flow-msg/flow-msg.component.ts
  56. 56
      web-admin-app/src/app/services/api.service.ts
  57. 2
      web-admin-app/src/app/shared/components/header/header.component.html
  58. 24
      web-admin-app/src/app/shared/components/server-paginated-table/server-paginated-table.component.html
  59. 1
      web-admin-app/src/app/utils/index.ts
  60. 62
      web-admin-app/src/assets/data-vis/fonts/icomoon.css
  61. BIN
      web-admin-app/src/assets/data-vis/fonts/icomoon.eot
  62. 22
      web-admin-app/src/assets/data-vis/fonts/icomoon.svg
  63. BIN
      web-admin-app/src/assets/data-vis/fonts/icomoon.ttf
  64. BIN
      web-admin-app/src/assets/data-vis/fonts/icomoon.woff
  65. BIN
      web-admin-app/src/assets/data-vis/images/border.png
  66. BIN
      web-admin-app/src/assets/data-vis/images/line.png
  67. BIN
      web-admin-app/src/assets/data-vis/images/logo.png
  68. BIN
      web-admin-app/src/assets/data-vis/images/rect.png
  69. BIN
      web-admin-app/src/assets/data-vis/images/更多内容关注公众号.gif
  70. 62
      web-admin-app/src/assets/images/data-vis/fonts/icomoon.css
  71. BIN
      web-admin-app/src/assets/images/data-vis/fonts/icomoon.eot
  72. 22
      web-admin-app/src/assets/images/data-vis/fonts/icomoon.svg
  73. BIN
      web-admin-app/src/assets/images/data-vis/fonts/icomoon.ttf
  74. BIN
      web-admin-app/src/assets/images/data-vis/fonts/icomoon.woff
  75. BIN
      web-admin-app/src/assets/images/data-vis/images/border.png
  76. BIN
      web-admin-app/src/assets/images/data-vis/images/line.png
  77. BIN
      web-admin-app/src/assets/images/data-vis/images/logo.png
  78. BIN
      web-admin-app/src/assets/images/data-vis/images/rect.png
  79. BIN
      web-admin-app/src/assets/images/data-vis/images/更多内容关注公众号.gif

43
web-admin-app/README.md

@ -125,3 +125,46 @@
执行接口 /api/v2/flowable/device/update/{id} 执行接口 /api/v2/flowable/device/update/{id}
<!-- defectStatus 巡检状态:0-待检,1-正常(完成),2-异常,3-取消。 盘点状态:0-待盘点,1-正常(完成),2-盘赢,3-盘亏 --> <!-- defectStatus 巡检状态:0-待检,1-正常(完成),2-异常,3-取消。 盘点状态:0-待盘点,1-正常(完成),2-盘赢,3-盘亏 -->
# 10.30
1、资产查看-维修、巡检、保养、盘点记录 procInsId
/api/eamAssetLog/list/device
operationType:可选项:[设备报修维修, 设备巡检任务, 设备保养任务, 设备盘点任务]
详情:/api/flowable/task/flowRecord 参数只用填procInsId
2、运行统计 /api/eamAsset/operationalStatistics
6、开关机记录
/api/v2/device/record
7、计划- cron表达式有误(TODO)
8、任务
处置信息已经更新 但是页面需要刷新
~~9、任务报表 /api/v2/flowable/device/export/{jobId} 直接下载文档~~
10、流程表单
Excel导入 /api/v2/sysFormTemp/exportExcel
Excel导出 /api/v2/sysFormTemp/importByExcel
11、流程表单新建
步骤一、导入模板
选择模板 上传字段模板 接口不变
点击下载字段模板
步骤二、检查明细
检查步骤一返回的字段内容
步骤三、模板确认
上传模板接口返回word和excel模板的zip /api/v2/sysFormTemp/formTemplate
上传表单(接口/api/v2/sysFormTemp/upload),暂存templatePath,调用模板确认预览 /api/v2/sysFormTemp/confirm
formKey(必填项), formName,formValue,templatePath;
确认上传添加保存数据
12、维修 详情
13、工作报表 api/flFormItem/workReport
富文本 + html转word api/flFormItem/h52word 或者api/flFormItem/h5str2word

4
web-admin-app/proxy/devserver.js

@ -11,6 +11,10 @@ const devProxy = (remote) => {
target, target,
secure: false, secure: false,
}, },
'/file/upload': {
target,
secure: false,
},
} }
} }

7
web-admin-app/src/app/app.routes.ts

@ -85,6 +85,7 @@ import { SystemLogComponent } from './pages/msg/system-log/system-log.component'
import { MsgLayoutComponent } from './pages/msg/msg-layout/msg-layout.component' import { MsgLayoutComponent } from './pages/msg/msg-layout/msg-layout.component'
import { FlowManagementComponent } from './pages/flow/flow-management/flow-management.component' import { FlowManagementComponent } from './pages/flow/flow-management/flow-management.component'
import { FlowReportComponent } from './pages/flow/flow-report/flow-report.component' import { FlowReportComponent } from './pages/flow/flow-report/flow-report.component'
import { DataVisComponent } from './pages/data-vis/data-vis.component'
export const routes: Routes = [ export const routes: Routes = [
{ {
@ -96,6 +97,12 @@ export const routes: Routes = [
component: LicensePageComponent, component: LicensePageComponent,
}, },
{
path: 'data-vis',
canActivate: [authGuard],
component: DataVisComponent,
},
{ {
path: '', path: '',
component: LayoutComponent, component: LayoutComponent,

8
web-admin-app/src/app/components/asset-form/asset-form.component.html

@ -502,16 +502,16 @@
<app-asset-repair-records [assetId]="data?.value?.assetId" /> <app-asset-repair-records [assetId]="data?.value?.assetId" />
} }
@case (5) { @case (5) {
5 <app-asset-inspection-records [assetId]="data?.value?.assetId" />
} }
@case (6) { @case (6) {
6 <app-asset-maintenance-records [assetId]="data?.value?.assetId" />
} }
@case (7) { @case (7) {
<app-asset-operation-records /> <app-asset-stocktaking-records [assetId]="data?.value?.assetId" />
} }
@case (8) { @case (8) {
<app-asset-operation-records /> <app-asset-operational-statistics [assetId]="data?.value?.assetId" />
} }
} }
} }

8
web-admin-app/src/app/components/asset-form/asset-form.component.ts

@ -15,7 +15,11 @@ import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NZ_MODAL_DATA } from 'ng-zorro-antd/modal' import { NZ_MODAL_DATA } from 'ng-zorro-antd/modal'
import { AssetOperationRecordsComponent } from '../asset-operation-records/asset-operation-records.component' import { AssetOperationRecordsComponent } from '../asset-operation-records/asset-operation-records.component'
import { AssetRepairRecordsComponent } from '../asset-repair-records/asset-repair-records.component' import { AssetRepairRecordsComponent } from '../asset-repair-records/asset-repair-records.component'
import { AssetInspectionRecordComponent } from '../asset-inspection-record/asset-inspection-record.component'
import { AssetMaintenanceRecordComponent } from '../asset-maintenance-record/asset-maintenance-record.component'
import { AssetStocktakingRecordComponent } from '../asset-stocktaking-record/asset-stocktaking-record.component'
import { UploadComponent } from '../../shared/components/upload/upload.component' import { UploadComponent } from '../../shared/components/upload/upload.component'
import { AssetOperationalStatisticsComponent } from '../asset-operational-statistics/asset-operational-statistics.component'
@Component({ @Component({
selector: 'app-asset-form', selector: 'app-asset-form',
@ -30,6 +34,10 @@ import { UploadComponent } from '../../shared/components/upload/upload.component
MaintenanceSelectComponent, MaintenanceSelectComponent,
AssetOperationRecordsComponent, AssetOperationRecordsComponent,
AssetRepairRecordsComponent, AssetRepairRecordsComponent,
AssetInspectionRecordComponent,
AssetStocktakingRecordComponent,
AssetMaintenanceRecordComponent,
AssetOperationalStatisticsComponent,
UploadComponent, UploadComponent,
], ],
templateUrl: './asset-form.component.html', templateUrl: './asset-form.component.html',

52
web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.html

@ -0,0 +1,52 @@
<div class="overflow-hidden flex-1">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm">
<ng-template #renderColumnTpl let-data let-key="key" let-row="row">
@switch (key) {
@case ('status') {
<!-- <nz-badge
[nzText]="data === 0 ? '在职' : '离职'"
[nzStatus]="data === 0 ? 'processing' : 'error'"
/> -->
}
@case ('_repairType') {
{{ data.name }}
}
@default {
{{ data }}
}
}
</ng-template>
<!-- <ng-container *appTableForm>
<div class="mr-3">
<div nz-row nzGutter="24">
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>业务编号</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="businessId" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作内容</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationNotes" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作类型</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationType" />
</nz-form-control>
</nz-form-item>
</div>
</div>
</div>
</ng-container> -->
</app-server-paginated-table>
</div>

0
web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.less

63
web-admin-app/src/app/components/asset-inspection-record/asset-inspection-record.component.ts

@ -0,0 +1,63 @@
import { Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { SharedModule } from 'app/shared/shared.module'
import { ApiService } from 'app/services'
import { FormBuilder, FormGroup } from '@angular/forms'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzModalService } from 'ng-zorro-antd/modal'
import { NzMessageService } from 'ng-zorro-antd/message'
import { NzDrawerRef, NzDrawerService } from 'ng-zorro-antd/drawer'
import { lastValueFrom } from 'rxjs'
import { FormValidators } from 'app/utils'
@Component({
selector: 'app-asset-inspection-records',
standalone: true,
imports: [SharedModule],
templateUrl: './asset-inspection-record.component.html',
styleUrl: './asset-inspection-record.component.less',
})
export class AssetInspectionRecordComponent {
constructor(
private modal: NzModalService,
private msg: NzMessageService,
private drawer: NzDrawerService,
private api: ApiService,
private fb: FormBuilder,
) {}
@Input() assetId!: number
table = new TableOption(this.fetchData.bind(this))
queryForm!: FormGroup
initQueryForm() {
this.queryForm = this.fb.group({
businessId: [],
operationNotes: [],
operationType: [],
})
}
ngOnInit(): void {
this.table
.setConfig({
cacheKey: 'asset-repair-records.component',
})
.setColumn([
{ key: 'id', title: '主键', width: '100px' },
{ key: 'businessId', title: '业务编号', width: '170px' },
{ key: 'name', title: '名称' },
{ key: '_repairType', title: '维修类型' },
{ key: 'plannedDate', title: '计划完成时间' },
// { key: 'createTime', title: '创建时间' },
])
this.initQueryForm()
}
fetchData(p: {}, q: AnyObject) {
return this.api.deviceLog({ ...p, ...q, assetId: this.assetId, operationType: '设备巡检任务' })
}
}

52
web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.html

@ -0,0 +1,52 @@
<div class="overflow-hidden flex-1">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm">
<ng-template #renderColumnTpl let-data let-key="key" let-row="row">
@switch (key) {
@case ('status') {
<!-- <nz-badge
[nzText]="data === 0 ? '在职' : '离职'"
[nzStatus]="data === 0 ? 'processing' : 'error'"
/> -->
}
@case ('_repairType') {
{{ data.name }}
}
@default {
{{ data }}
}
}
</ng-template>
<!-- <ng-container *appTableForm>
<div class="mr-3">
<div nz-row nzGutter="24">
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>业务编号</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="businessId" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作内容</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationNotes" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作类型</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationType" />
</nz-form-control>
</nz-form-item>
</div>
</div>
</div>
</ng-container> -->
</app-server-paginated-table>
</div>

0
web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.less

63
web-admin-app/src/app/components/asset-maintenance-record/asset-maintenance-record.component.ts

@ -0,0 +1,63 @@
import { Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { SharedModule } from 'app/shared/shared.module'
import { ApiService } from 'app/services'
import { FormBuilder, FormGroup } from '@angular/forms'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzModalService } from 'ng-zorro-antd/modal'
import { NzMessageService } from 'ng-zorro-antd/message'
import { NzDrawerRef, NzDrawerService } from 'ng-zorro-antd/drawer'
import { lastValueFrom } from 'rxjs'
import { FormValidators } from 'app/utils'
@Component({
selector: 'app-asset-maintenance-records',
standalone: true,
imports: [SharedModule],
templateUrl: './asset-maintenance-record.component.html',
styleUrl: './asset-maintenance-record.component.less',
})
export class AssetMaintenanceRecordComponent {
constructor(
private modal: NzModalService,
private msg: NzMessageService,
private drawer: NzDrawerService,
private api: ApiService,
private fb: FormBuilder,
) {}
@Input() assetId!: number
table = new TableOption(this.fetchData.bind(this))
queryForm!: FormGroup
initQueryForm() {
this.queryForm = this.fb.group({
businessId: [],
operationNotes: [],
operationType: [],
})
}
ngOnInit(): void {
this.table
.setConfig({
cacheKey: 'asset-repair-records.component',
})
.setColumn([
{ key: 'id', title: '主键', width: '100px' },
{ key: 'businessId', title: '业务编号', width: '170px' },
{ key: 'name', title: '名称' },
{ key: '_repairType', title: '维修类型' },
{ key: 'plannedDate', title: '计划完成时间' },
// { key: 'createTime', title: '创建时间' },
])
this.initQueryForm()
}
fetchData(p: {}, q: AnyObject) {
return this.api.deviceLog({ ...p, ...q, assetId: this.assetId, operationType: '设备保养任务' })
}
}

83
web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.html

@ -0,0 +1,83 @@
<nz-spin [nzSpinning]="loading">
<div nz-row [nzGutter]="[12, 12]">
<div nz-col [nzSpan]="24">
<h2 class="flex items-center justify-between">
<div>
@if (data?.deviceStatus === 'ON') {
<nz-badge nzStatus="success" nzText="运行正常" />
} @else {
<nz-badge nzStatus="error" nzText="关机" />
}
</div>
<div>
<nz-radio-group [(ngModel)]="type" (ngModelChange)="onChange()">
<!-- 0-近一周 1-近一月 2-近一年 3-全部 -->
<label nz-radio [nzValue]="0">近一周</label>
<label nz-radio [nzValue]="1">近一月</label>
<label nz-radio [nzValue]="2">近一年</label>
<label nz-radio [nzValue]="3">全部</label>
</nz-radio-group>
</div>
</h2>
</div>
<div nz-col nzSpan="12">
<nz-card nzTitle="设备图片">
@if (data?.img) {
<img [src]="data?.img" class="w-full h-96 object-fill" />
} @else {
<nz-empty />
}
</nz-card>
</div>
<div nz-col nzSpan="12">
<nz-card nzTitle="开机时长分析">
<div class="w-full h-96 object-fill" #openTimeChart></div>
</nz-card>
</div>
<div nz-col nzSpan="8">
<nz-card nzTitle="运行状态分析">
<div class="w-full h-96 object-fill" #runningStatusChart></div>
</nz-card>
</div>
<div nz-col nzSpan="8">
<nz-card nzTitle="运维投入分析">
<div class="w-full object-fill">
<div nz-row [nzGutter]="[12, 12]">
<div nz-col nzSpan="12">
<nz-statistic nzTitle="累计巡检次数" [nzValue]="data?.inspectionCount ?? 0" nzSuffix="次" />
</div>
<div nz-col nzSpan="12">
<nz-statistic
nzTitle="累计保养次数"
[nzValue]="data?.maintenanceCount ?? 0"
nzSuffix="次"
/>
</div>
<div nz-col nzSpan="12">
<nz-statistic
nzTitle="累计盘点次数"
[nzValue]="data?.stocktakingCount ?? 0"
nzSuffix="次"
/>
</div>
<div nz-col nzSpan="12">
<nz-statistic nzTitle="累计报修次数" [nzValue]="data?.repairCount ?? 0" nzSuffix="次" />
</div>
</div>
</div>
</nz-card>
</div>
<div nz-col nzSpan="8">
<nz-card nzTitle="生命周期分析">
<div>
<label class="mr-4 text-black opacity-40">折旧损耗</label>
<b class="text-2xl"> {{ data?.depreciation ?? '-' }}% </b>
</div>
<div class="mt-4">
<label class="mr-4 text-black opacity-40"> 维保剩余天数 </label>
<b class="text-2xl">{{ data?.maintenanceDays ?? '-' }}天 </b>
</div>
</nz-card>
</div>
</div>
</nz-spin>

0
web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.less

153
web-admin-app/src/app/components/asset-operational-statistics/asset-operational-statistics.component.ts

@ -0,0 +1,153 @@
import { Component, ElementRef, Input, ViewChild } from '@angular/core'
import { ApiService } from 'app/services'
import { SharedModule } from 'app/shared/shared.module'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { finalize } from 'rxjs'
import { init, EChartsType } from 'echarts'
@Component({
selector: 'app-asset-operational-statistics',
standalone: true,
imports: [SharedModule],
templateUrl: './asset-operational-statistics.component.html',
styleUrl: './asset-operational-statistics.component.less',
})
export class AssetOperationalStatisticsComponent {
constructor(private api: ApiService) {}
@ViewChild('openTimeChart') openTimeChart?: ElementRef<HTMLDivElement>
@ViewChild('runningStatusChart') runningStatusChart?: ElementRef<HTMLDivElement>
@Input() assetId!: number
data: NzSafeAny
type = 1
loading = false
ngOnInit() {
this.onChange()
}
onChange() {
this.loading = true
this.api
.operationalStatistics({ id: this.assetId, type: this.type })
.pipe(
finalize(() => {
this.loading = false
}),
)
.subscribe((res) => {
// console.log('res', res)
this.data = res.body
if (this.data) {
setTimeout(() => {
this.renderBar()
this.renderPie()
})
}
})
}
barRef?: EChartsType
renderBar() {
if (this.openTimeChart?.nativeElement) {
if (!this.barRef) {
this.barRef = init(this.openTimeChart?.nativeElement)
}
const name: string[] = []
const value: number[] = []
this.data?.openLogs.forEach((i: NzSafeAny) => {
name.push(i.name)
value.push(i.value)
})
this.barRef.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
},
},
xAxis: {
type: 'category',
data: name,
},
yAxis: {
type: 'value',
},
series: [
{
data: value,
type: 'bar',
},
],
})
}
}
pieRef?: EChartsType
renderPie() {
if (this.runningStatusChart?.nativeElement) {
if (!this.pieRef) {
this.pieRef = init(this.runningStatusChart?.nativeElement)
let data: NzSafeAny[] = []
const odata: NzSafeAny[] = this.data?.runLogs ?? []
const total = odata.reduce((a: number, b: any) => a + Math.abs(b.value), 0)
data = odata.map((i: NzSafeAny) => {
const value = Math.abs(i.value)
const percent = ((value / total) * 100).toFixed(2)
return {
...i,
percent,
value,
}
})
this.pieRef.setOption({
tooltip: {
trigger: 'item',
},
legend: {
orient: 'scroll',
formatter: function (name: string, d: any) {
const item = data.find((i: NzSafeAny) => i.name === name)
return name + ' | ' + item?.percent + '%' + ' ' + item?.value + '天'
},
},
series: [
{
name: '运行状态',
type: 'pie',
radius: ['40%', '70%'],
// center: ['40%', '50%'],
avoidLabelOverlap: false,
itemStyle: {
borderRadius: 10,
borderColor: '#fff',
borderWidth: 2,
},
label: {
show: false,
position: 'center',
},
emphasis: {
label: {
show: true,
fontSize: 30,
fontWeight: 'bold',
},
},
labelLine: {
show: false,
},
data: data,
},
],
})
}
}
}
}

2
web-admin-app/src/app/components/asset-repair-records/asset-repair-records.component.ts

@ -58,6 +58,6 @@ export class AssetRepairRecordsComponent {
} }
fetchData(p: {}, q: AnyObject) { fetchData(p: {}, q: AnyObject) {
return this.api.getAssetRepairLog({ ...p, ...q }, this.assetId) return this.api.deviceLog({ ...p, ...q, assetId: this.assetId, operationType: '设备报修维修' })
} }
} }

157
web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.html

@ -0,0 +1,157 @@
<nz-card nzType="inner" [nzBodyStyle]="{ padding: 0 }" [nzTitle]="disabled ? undefined : nzTitleTpl">
<ng-template #nzTitleTpl>
<nz-space>
<button nz-button *nzSpaceItem type="button" nzType="primary" (click)="onTrigger(modalTpl)">
选择备件
</button>
<button
nz-button
*nzSpaceItem
type="button"
nzDanger
(click)="onRemove()"
[disabled]="setOfCheckedId.size === 0"
>
删除
</button>
</nz-space>
</ng-template>
<ng-template #modalTpl>
<app-server-paginated-table [options]="table" [formGroup]="queryForm" [renderColumn]="renderColumnTpl">
<ng-template #renderColumnTpl let-data let-key="key" let-row="row">
@switch (key) {
@case ('status') {
<nz-tag>
{{ ASSET_STATUS_MAP[data] }}
</nz-tag>
}
@case ('_useUser') {
{{ data?.userName ?? '-' }}
}
@case ('_warehouse') {
{{ data?.name ?? '-' }}
}
@case ('_position') {
{{ data?.name ?? '-' }}
}
@case ('_head') {
{{ data?.userName ?? '-' }}
}
@case ('_ownCompany') {
{{ data?.organizationName ?? '-' }}
}
@case ('_useOrganization') {
{{ data?.organizationName ?? '-' }}
}
@case ('_category') {
{{ data?.categoryName ?? '-' }}
}
@default {
{{ data }}
}
}
</ng-template>
<!-- <ng-container *appTableAction>
<nz-space>
<button *nzSpaceItem nz-button nzType="link">资产确认</button>
<button *nzSpaceItem nz-button nzType="link" nzDanger="">删除</button>
</nz-space>
</ng-container> -->
<ng-container *appTableForm>
<app-query-item label="资产名称">
<input nz-input placeholder="请输入" formControlName="name" />
</app-query-item>
<app-query-item label="资产编号">
<input nz-input placeholder="请输入" formControlName="assetCode" />
</app-query-item>
<app-query-item label="规格型号">
<input nz-input placeholder="请输入" formControlName="model" />
</app-query-item>
<app-query-item label="序列号">
<input nz-input placeholder="请输入" formControlName="serialNumber" />
</app-query-item>
<app-query-item label="资产状态">
<nz-select
nzPlacement="bottomRight"
class="!w-24"
[nzDropdownMatchSelectWidth]="false"
formControlName="status"
>
@for (item of ASSET_STATUS_MAP | keyvalue; track $index) {
<nz-option [nzLabel]="item.value" [nzValue]="item.key"></nz-option>
}
</nz-select>
</app-query-item>
<app-query-item label="资产来源">
<nz-select
nzPlacement="bottomRight"
class="!w-24"
[nzDropdownMatchSelectWidth]="false"
formControlName="sourceId"
>
@for (item of ASSET_SOURCE_MAP | keyvalue; track $index) {
<nz-option [nzLabel]="item.value" [nzValue]="item.key"></nz-option>
}
</nz-select>
</app-query-item>
<app-query-item label="位置">
<app-position-select formControlName="positionId" />
</app-query-item>
<app-query-item label="生产厂商">
<app-manufacturer-select class="block w-36" formControlName="manufacturersVendorId" />
</app-query-item>
</ng-container>
</app-server-paginated-table>
</ng-template>
<nz-table [nzSize]="'small'" [nzBordered]="true" [nzData]="selectedDataList" #basicTable>
<thead>
<tr>
<th
[nzChecked]="checkedAll"
[nzIndeterminate]="indeterminate"
nzLabel="Select all"
(nzCheckedChange)="onAllChecked($event)"
></th>
<th>资产编号</th>
<th>资产状态</th>
<th>资产名称</th>
<th>数量</th>
<th>规格型号</th>
<th>资产序列号</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of basicTable.data">
<td
[nzChecked]="setOfCheckedId.has(data.key)"
[nzDisabled]="data.disabled"
[nzLabel]="data.name"
(nzCheckedChange)="onItemChecked(data.key, $event)"
></td>
<td>{{ data.assetCode }}</td>
<td>
<nz-tag>
{{ ASSET_STATUS_MAP[data.status] }}
</nz-tag>
</td>
<td>{{ data.name }}</td>
<td>
<nz-input-number
[nzMin]="0"
[nzMax]="storage ? undefined : data.max"
[nzDisabled]="disabled"
[(ngModel)]="data.count"
(ngModelChange)="onCountChange()"
/>
</td>
<td>{{ data.model }}</td>
<td>{{ data.serialNumber }}</td>
</tr>
</tbody>
</nz-table>
</nz-card>

0
web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.less

222
web-admin-app/src/app/components/asset-select-beijian/asset-select-beijian.component.ts

@ -0,0 +1,222 @@
import {
ChangeDetectorRef,
Component,
EventEmitter,
Input,
OnInit,
Output,
TemplateRef,
ViewChild,
forwardRef,
} from '@angular/core'
import { ControlValueAccessor, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzModalService } from 'ng-zorro-antd/modal'
import { ApiService } from 'app/services'
import { NzMessageService } from 'ng-zorro-antd/message'
import { SharedModule } from 'app/shared/shared.module'
import { Utils } from 'app/utils'
import { NzFormatEmitEvent, NzTreeNode } from 'ng-zorro-antd/tree'
import { NzTreeSelectComponent } from 'ng-zorro-antd/tree-select'
import { ASSET_SOURCE_MAP, ASSET_STATUS_MAP, MAX_PAGE_SIZE } from 'app/constants'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { PositionSelectComponent } from '../position-select/position-select.component'
import { ManufacturerSelectComponent } from '../manufacturer-select/manufacturer-select.component'
import { map, tap } from 'rxjs'
@Component({
selector: 'asset-select-beijian',
standalone: true,
imports: [SharedModule, PositionSelectComponent, ManufacturerSelectComponent],
templateUrl: './asset-select-beijian.component.html',
styleUrl: './asset-select-beijian.component.less',
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => AssetSelectBeijianComponent),
},
],
})
export class AssetSelectBeijianComponent {
constructor(
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService,
) {}
@Input() radio: boolean = false
@Input() storage: boolean = false
@Input() buy: boolean = false
@Output() onSelected = new EventEmitter<NzSafeAny>()
allGetedDataMap = new Map<number, NzSafeAny>()
originData: NzSafeAny[] = []
value?: string
disabled = false
selectedDataList: NzSafeAny[] = []
checkedAll = false
loading = false
indeterminate = false
setOfCheckedId = new Set<number>()
ASSET_SOURCE_MAP = ASSET_SOURCE_MAP
ASSET_STATUS_MAP = ASSET_STATUS_MAP()
table = new TableOption(this.fetchData.bind(this))
queryForm = new FormGroup({
name: new FormControl(),
model: new FormControl(),
status: new FormControl(),
assetCode: new FormControl(),
serialNumber: new FormControl(),
sourceId: new FormControl(),
positionId: new FormControl(),
manufacturersVendorId: new FormControl(),
})
ngOnInit(): void {
this.api.getBasicSupplierVendorPage({ pageSize: MAX_PAGE_SIZE, pageNum: 1 }).subscribe((res) => {
this.originData = res.body.rows
})
this.table
.setConfig({
selectable: true,
rowKey: 'key',
radio: this.radio,
noneCache: true,
})
.setColumn([
{ key: 'assetCode', title: '资产编号', visible: true },
{ key: 'name', title: '资产名称', visible: true },
{ key: 'status', title: '资产状态', visible: true },
{ key: 'count', title: '库存数量', visible: true },
])
}
fetchData(p: {}, q: AnyObject) {
const fn = this.storage ? 'getAeamBusinessStorageList' : 'getAssetStroagePage'
return this.api[fn]({ ...p, ...q }).pipe(
map((res) => {
res.body.rows.forEach((item: NzSafeAny) => {
item['key'] = item.assetId + '_' + item.warehouseId
this.allGetedDataMap.set(item.key, item)
})
return res
}),
)
}
onTrigger(nzContent: TemplateRef<{}>) {
this.modal.create({
nzTitle: '选择资产',
nzContent,
nzWidth: '80vw',
nzWrapClassName: 'modal-lg',
nzOnOk: () => {
if (this.radio) {
this.selectedDataList = []
}
this.allGetedDataMap.forEach((i: NzSafeAny) => {
if (this.table.ref.selected.has(String(i.key))) {
if (!this.selectedDataList.some((s) => s.key === i.key)) {
this.selectedDataList.push({ ...i, count: i.count === 0 ? 0 : 1, max: i.count })
}
}
})
this.selectedDataList = this.selectedDataList.slice()
this.refreshCheckedStatus()
this.onChange(this.selectedDataList)
},
})
}
onRemove() {
this.selectedDataList = this.selectedDataList.filter((i) => !this.setOfCheckedId.has(i.key))
this.refreshCheckedStatus()
this.onChange(this.selectedDataList)
}
onCountChange() {
this.onChange(this.selectedDataList)
}
updateCheckedSet(id: number, checked: boolean): void {
if (checked) {
this.setOfCheckedId.add(id)
} else {
this.setOfCheckedId.delete(id)
}
}
refreshCheckedStatus(): void {
const listOfEnabledData = this.selectedDataList.filter(({ disabled }) => !disabled)
this.checkedAll = listOfEnabledData.every(({ key }) => this.setOfCheckedId.has(key))
this.indeterminate = listOfEnabledData.some(({ key }) => this.setOfCheckedId.has(key)) && !this.checkedAll
}
onItemChecked(id: number, checked: boolean): void {
this.updateCheckedSet(id, checked)
this.refreshCheckedStatus()
}
onAllChecked(checked: boolean): void {
this.selectedDataList
.filter(({ disabled }) => !disabled)
.forEach(({ key }) => this.updateCheckedSet(key, checked))
this.refreshCheckedStatus()
}
onTouched = () => {}
onChange(v: NzSafeAny[]) {}
writeValue(v: NzSafeAny): void {
let vals = v
if (typeof v === 'string') {
try {
vals = JSON.parse(v) ?? []
} catch (error) {}
}
if (Array.isArray(vals) && vals.length > 0) {
// const ids = vals.map((i: NzSafeAny) => i.assetId)
this.api.getAssetStorageListByIds(vals).subscribe((res) => {
this.selectedDataList = res.body.rows.map((item: NzSafeAny) => {
item['key'] = item.assetId + '_' + item.warehouseId
this.allGetedDataMap.set(item.assetId, item)
return item
})
})
}
}
registerOnChange(fn: any): void {
this.onChange = fn
}
registerOnTouched(fn: any): void {
this.onTouched = fn
}
setDisabledState?(isDisabled: boolean): void {
this.disabled = isDisabled
}
}

52
web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.html

@ -0,0 +1,52 @@
<div class="overflow-hidden flex-1">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm">
<ng-template #renderColumnTpl let-data let-key="key" let-row="row">
@switch (key) {
@case ('status') {
<!-- <nz-badge
[nzText]="data === 0 ? '在职' : '离职'"
[nzStatus]="data === 0 ? 'processing' : 'error'"
/> -->
}
@case ('_repairType') {
{{ data.name }}
}
@default {
{{ data }}
}
}
</ng-template>
<!-- <ng-container *appTableForm>
<div class="mr-3">
<div nz-row nzGutter="24">
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>业务编号</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="businessId" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作内容</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationNotes" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label>操作类型</nz-form-label>
<nz-form-control>
<input nz-input placeholder="请输入" formControlName="operationType" />
</nz-form-control>
</nz-form-item>
</div>
</div>
</div>
</ng-container> -->
</app-server-paginated-table>
</div>

0
web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.less

63
web-admin-app/src/app/components/asset-stocktaking-record/asset-stocktaking-record.component.ts

@ -0,0 +1,63 @@
import { Component, Input, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { SharedModule } from 'app/shared/shared.module'
import { ApiService } from 'app/services'
import { FormBuilder, FormGroup } from '@angular/forms'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzModalService } from 'ng-zorro-antd/modal'
import { NzMessageService } from 'ng-zorro-antd/message'
import { NzDrawerRef, NzDrawerService } from 'ng-zorro-antd/drawer'
import { lastValueFrom } from 'rxjs'
import { FormValidators } from 'app/utils'
@Component({
selector: 'app-asset-stocktaking-records',
standalone: true,
imports: [SharedModule],
templateUrl: './asset-stocktaking-record.component.html',
styleUrl: './asset-stocktaking-record.component.less',
})
export class AssetStocktakingRecordComponent {
constructor(
private modal: NzModalService,
private msg: NzMessageService,
private drawer: NzDrawerService,
private api: ApiService,
private fb: FormBuilder,
) {}
@Input() assetId!: number
table = new TableOption(this.fetchData.bind(this))
queryForm!: FormGroup
initQueryForm() {
this.queryForm = this.fb.group({
businessId: [],
operationNotes: [],
operationType: [],
})
}
ngOnInit(): void {
this.table
.setConfig({
cacheKey: 'asset-repair-records.component',
})
.setColumn([
{ key: 'id', title: '主键', width: '100px' },
{ key: 'businessId', title: '业务编号', width: '170px' },
{ key: 'name', title: '名称' },
{ key: '_repairType', title: '维修类型' },
{ key: 'plannedDate', title: '计划完成时间' },
// { key: 'createTime', title: '创建时间' },
])
this.initQueryForm()
}
fetchData(p: {}, q: AnyObject) {
return this.api.deviceLog({ ...p, ...q, assetId: this.assetId, operationType: '设备盘点任务' })
}
}

32
web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.html

@ -0,0 +1,32 @@
<div class="flex-1 overflow-hidden">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm">
<ng-container *appTableForm>
<app-query-item label="名称" class="w-60">
<input nz-input placeholder="请输入" formControlName="name" />
</app-query-item>
</ng-container>
<ng-template #renderColumnTpl let-data let-key="key" let-row="row">
@switch (key) {
@case ('_assignee') {
<nz-tag> {{ data.userName }} </nz-tag>
}
@case ('procVars') {
{{ data?.title ?? data?.name ?? '-' }}
}
@case ('status') {
<nz-tag nzColor="blue">
{{ FLOW_STATUS.get(data) }}
</nz-tag>
}
@case ('urgency') {
<nz-tag [nzColor]="row.procVars.urgency === 2 ? 'error' : ''">
{{ row.procVars.urgency === 2 ? '紧急' : '普通' }}
</nz-tag>
}
@default {
{{ data }}
}
}
</ng-template>
</app-server-paginated-table>
</div>

0
web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.less

135
web-admin-app/src/app/components/flow-list-by-type/flow-list-by-type.component.ts

@ -0,0 +1,135 @@
import { Component, EventEmitter, Input, Output, TemplateRef, ViewChild } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { ApiService } from 'app/services'
import { SharedModule } from 'app/shared/shared.module'
import { last, lastValueFrom, map, of, tap } from 'rxjs'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzModalService } from 'ng-zorro-antd/modal'
import { NzMessageService } from 'ng-zorro-antd/message'
import { FormValidators } from 'app/utils'
import { AssetEmployeeApplyComponent } from 'app/components'
import { FLOW_STATUS, flowIntStatus } from 'app/constants'
import { comsMap } from 'app/pages/flow/flow-main/flow-main.component'
@Component({
selector: 'app-flow-list-by-type',
standalone: true,
imports: [SharedModule],
templateUrl: './flow-list-by-type.component.html',
styleUrl: './flow-list-by-type.component.less',
})
export class FlowListByTypeComponent {
constructor(
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService,
) {}
@ViewChild('createFormTpl') createFormTpl!: TemplateRef<{}>
@Input() type: 'asset' | 'device' = 'asset'
@Input() page: string = 'apply'
@Output() onLoad = new EventEmitter()
createForm = new FormGroup({
formId: new FormControl(''),
userId: new FormControl<NzSafeAny[]>([], [FormValidators.required('请选择')]),
})
table = new TableOption(this.fetchData.bind(this))
assetTotal = 0
deviceTotal = 0
queryForm = new FormGroup({
name: new FormControl(''),
})
FLOW_STATUS = flowIntStatus
ngOnInit(): void {
this.table
.setConfig({
cacheKey: this.page + '_' + this.type,
})
.setColumn([
{ key: 'procVars', title: '名称', visible: true },
{ key: 'status', title: '流程状态', visible: true },
{ key: 'urgency', title: '紧急程度', visible: true },
{ key: 'procDefName', title: '流程类型', visible: true },
{ key: 'assigneeName', title: '审批人', visible: true },
{ key: 'createTime', title: '提交时间', visible: true },
// { key: 'createTime', title: '作废时间', visible: true },
// { key: 'createTime', title: '完成时间', visible: true },
])
.setRowOperate([
{ title: '查看', onClick: this.onDetail.bind(this) },
{
title: '作废',
onClick: this.cancleFlow.bind(this),
visible: (v) => {
return this.page === 'apply'
},
},
])
}
fetchData(p: {}, q: AnyObject) {
if (this.page === 'apply') {
return this.api.getMyApplyAssetFlow({ ...p, ...q, type: this.type }).pipe(
tap((res) => {
this.onLoad.emit(res.body.total)
}),
)
} else if (this.page === 'finished') {
return this.api.getMyFinishedAssetFlow({ ...p, ...q, type: this.type }).pipe(
tap((res) => {
this.onLoad.emit(res.body.total)
}),
)
} else {
return this.api.getMyTodoAssetFlow({ ...p, ...q, type: this.type }).pipe(
tap((res) => {
this.onLoad.emit(res.body.total)
}),
)
}
}
onDetail(model: NzSafeAny) {
console.log('model', model, comsMap[model.category])
this.modal.create({
nzTitle: '查看',
nzContent: comsMap[model.category],
nzWrapClassName: 'modal-lg',
nzWidth: '80vw',
nzFooter: null,
nzData: {
value: model,
preview: true,
},
})
}
cancleFlow(d: NzSafeAny) {
this.modal.confirm({
nzTitle: '作废',
nzContent: '是否要作废该申请?',
nzOnOk: async () => {
const res = await lastValueFrom(
this.api.cancelFlow({
instanceId: d.procInsId,
taskId: d.taskId,
}),
)
this.msg.success('作废成功')
this.table.ref.reload()
},
})
}
}

75
web-admin-app/src/app/components/plan-task/handle-task-asset-item/handle-task-asset-item.component.html

@ -38,15 +38,28 @@
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</div> </div>
@if (type != 'stocktaking' && formGroup.get('defectStatus')?.value === 2) {
<div nz-col [nzSpan]="8"> <div nz-col [nzSpan]="8">
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired>自动发起报修流程</nz-form-label> <nz-form-label nzRequired>自动发起报修流程</nz-form-label>
<nz-form-control [nzErrorTip]="errorTpl"> <nz-form-control [nzErrorTip]="errorTpl">
<nz-switch /> <nz-switch formControlName="autoRepair" />
</nz-form-control>
</nz-form-item>
</div>
}
@if (type === 'maintenance') {
<div nz-col [nzSpan]="8">
<nz-form-item>
<nz-form-label nzRequired>保养成本</nz-form-label>
<nz-form-control [nzErrorTip]="errorTpl">
<nz-input-group nzAddOnAfter="元">
<nz-input-number class="!w-full" nzPlaceHolder="请输入" formControlName="cost" />
</nz-input-group>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</div> </div>
}
<div nz-col [nzSpan]="24"> <div nz-col [nzSpan]="24">
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired>{{ data.title }}总结</nz-form-label> <nz-form-label nzRequired>{{ data.title }}总结</nz-form-label>
@ -128,6 +141,64 @@
} }
</div> </div>
@if (type === 'maintenance') {
<div class="text-lg mb-6">备件损耗</div>
<asset-select-beijian formControlName="sparePartsList" />
<!-- <div formArrayName="sparePartsList" class="hidden">
<div nz-row [nzGutter]="24">
<nz-table nzTemplateMode class="w-full" [nzBordered]="true" nzSize="small">
<thead>
<tr>
<th>编号</th>
<th>备件名称</th>
<th>备件型号</th>
<th>数量</th>
<th>备注</th>
<th>操作</th>
</tr>
</thead>
<tbody>
@for (item of sparePartsList.controls; track $index) {
<tr nz-col nzSpan="8" [formGroupName]="$index">
<td>
<input nz-input formControlName="assetId" />
</td>
<td>
<input nz-input formControlName="name" />
</td>
<td>
<input nz-input formControlName="model" />
</td>
<td>
<input nz-input formControlName="model" />
</td>
<td>
<input nz-input formControlName="count" />
</td>
<td>
<input nz-input formControlName="remark" />
</td>
<td>
<button nz-button nzDanger nzType="link" (click)="removeSpareParts($index)">
<i nz-icon nzType="delete"></i>
</button>
</td>
</tr>
}
<tr>
<td [colSpan]="7">
<button nz-button nzType="dashed" nzBlock (click)="addSpareParts()">
<i nz-icon nzType="plus"></i>
新增备件
</button>
</td>
</tr>
</tbody>
</nz-table>
</div>
</div> -->
}
<ng-template #errorTpl let-control> <ng-template #errorTpl let-control>
<form-error-tips [control]="control"></form-error-tips> <form-error-tips [control]="control"></form-error-tips>
</ng-template> </ng-template>

39
web-admin-app/src/app/components/plan-task/handle-task-asset-item/handle-task-asset-item.component.ts

@ -12,10 +12,11 @@ import { ASSET_SOURCE_MAP, ASSET_STATUS, MAINTENANCE_STATUS, MAINTENANCE_TYPE, M
// import { PositionSelectComponent } from '../position-select/position-select.component' // import { PositionSelectComponent } from '../position-select/position-select.component'
// import { MaintenanceSelectComponent } from '../maintenance-select/maintenance-select.component' // import { MaintenanceSelectComponent } from '../maintenance-select/maintenance-select.component'
import { NzSafeAny } from 'ng-zorro-antd/core/types' import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NZ_MODAL_DATA } from 'ng-zorro-antd/modal' import { NZ_MODAL_DATA, NzModalService } from 'ng-zorro-antd/modal'
import { UploadComponent } from 'app/shared/components/upload/upload.component' import { UploadComponent } from 'app/shared/components/upload/upload.component'
import { AssetCategorySelectComponent } from 'app/components/asset-category-select/asset-category-select.component' import { AssetCategorySelectComponent } from 'app/components/asset-category-select/asset-category-select.component'
import { PlanTaskType } from 'app/types' import { PlanTaskType } from 'app/types'
import { AssetSelectBeijianComponent } from 'app/components/asset-select-beijian/asset-select-beijian.component'
// import { AssetOperationRecordsComponent } from '../asset-operation-records/asset-operation-records.component' // import { AssetOperationRecordsComponent } from '../asset-operation-records/asset-operation-records.component'
// import { AssetRepairRecordsComponent } from '../asset-repair-records/asset-repair-records.component' // import { AssetRepairRecordsComponent } from '../asset-repair-records/asset-repair-records.component'
// import { UploadComponent } from '../../shared/components/upload/upload.component' // import { UploadComponent } from '../../shared/components/upload/upload.component'
@ -39,6 +40,7 @@ export const defectStatusText: Record<PlanTaskType, string[]> = {
// MaintenanceSelectComponent, // MaintenanceSelectComponent,
// AssetOperationRecordsComponent, // AssetOperationRecordsComponent,
// AssetRepairRecordsComponent, // AssetRepairRecordsComponent,
AssetSelectBeijianComponent,
UploadComponent, UploadComponent,
], ],
templateUrl: './handle-task-asset-item.component.html', templateUrl: './handle-task-asset-item.component.html',
@ -86,8 +88,10 @@ export class HandleTaskAssetItemComponent {
img: this.fb.control(null, []), img: this.fb.control(null, []),
attachment: this.fb.control(null, []), attachment: this.fb.control(null, []),
notes: this.fb.control(null, []), notes: this.fb.control(null, []),
autoRepair: this.fb.control(false, []),
cost: this.fb.control(0, []),
_extInfo: this.fb.array([]), _extInfo: this.fb.array([]),
sparePartsList: this.fb.control([], []),
}) })
this.patchValues() this.patchValues()
@ -100,6 +104,9 @@ export class HandleTaskAssetItemComponent {
get extInfo(): FormArray { get extInfo(): FormArray {
return this.formGroup.get('_extInfo') as FormArray return this.formGroup.get('_extInfo') as FormArray
} }
// get sparePartsList(): FormArray {
// return this.formGroup.get('sparePartsList') as FormArray
// }
onExtraChange(v: any) { onExtraChange(v: any) {
let extraFields: NzSafeAny[] = [] let extraFields: NzSafeAny[] = []
@ -110,9 +117,36 @@ export class HandleTaskAssetItemComponent {
} catch (error) {} } catch (error) {}
this.setExtraFields(extraFields) this.setExtraFields(extraFields)
// this.setBeijian(extraFields)
} }
ext = null ext = null
setBeijian(beijian: NzSafeAny[]) {
// this.sparePartsList.clear()
// beijian.forEach((i) => {
// this.sparePartsList.push(
// this.fb.group({
// assetId: this.fb.control(i.assetId),
// categoryId: this.fb.control(i.categoryId),
// categoryName: this.fb.control(i.categoryName),
// count: this.fb.control(i.count),
// model: this.fb.control(i.model),
// name: this.fb.control(i.name),
// remark: this.fb.control(i.remark),
// }),
// )
// })
}
modal = inject(NzModalService)
addSpareParts() {
this.modal.create({
nzTitle: '选择备件',
})
}
removeSpareParts(idx: number) {
// this.sparePartsList.removeAt(idx)
}
setExtraFields(fields: NzSafeAny[]) { setExtraFields(fields: NzSafeAny[]) {
this.extInfo.clear() this.extInfo.clear()
this.extraFields = fields this.extraFields = fields
@ -136,6 +170,7 @@ export class HandleTaskAssetItemComponent {
fields: this.fb.control(i.type === 'RADIO' ? i.value?.fields : []), fields: this.fb.control(i.type === 'RADIO' ? i.value?.fields : []),
}), }),
) )
return i return i
}) })
} }

2
web-admin-app/src/app/components/plan-task/handle-task/handle-task.component.html

@ -172,7 +172,7 @@
</td> </td>
<td> <td>
<nz-space> <nz-space>
@if (item.defectStatus == 0) { @if (item.defectStatus == 0 && !data.preview) {
<a <a
*nzSpaceItem *nzSpaceItem
nz-button nz-button

23
web-admin-app/src/app/components/plan-task/handle-task/handle-task.component.ts

@ -100,10 +100,14 @@ export class HandleTaskComponent {
2: 'warning', 2: 'warning',
3: 'error', 3: 'error',
} as any } as any
handlePreview(asset: NzSafeAny) {} handlePreview(asset: NzSafeAny) {
this.handleItem(asset, true)
}
modal = inject(NzModalService) modal = inject(NzModalService)
handleItem(asset: NzSafeAny) {
tempData: NzSafeAny = null
handleItem(asset: NzSafeAny, preview?: boolean) {
this.modal.create({ this.modal.create({
nzTitle: this.title + '执行详情', nzTitle: this.title + '执行详情',
nzContent: HandleTaskAssetItemComponent, nzContent: HandleTaskAssetItemComponent,
@ -114,13 +118,13 @@ export class HandleTaskComponent {
task: this.data.value, task: this.data.value,
title: this.title, title: this.title,
type: this.data.type, type: this.data.type,
preview,
}, },
nzFooter: preview ? null : void 0,
nzOnOk: async (e) => { nzOnOk: async (e) => {
const vals = e.getValues() const vals = e.getValues()
if (vals) { if (vals) {
const res = await lastValueFrom( const val = {
this.api.handleTaskAssetItem(
{
...this.data.value, ...this.data.value,
defects: this.data.value.defects.map((i: NzSafeAny) => { defects: this.data.value.defects.map((i: NzSafeAny) => {
if (i.assetId === asset.assetId) { if (i.assetId === asset.assetId) {
@ -132,10 +136,11 @@ export class HandleTaskComponent {
} }
return i return i
}), }),
}, }
this.data.value.id, console.log('val', val)
),
) const res = await lastValueFrom(this.api.handleTaskAssetItem(val, this.data.value.id))
this.data.value = JSON.parse(JSON.stringify(val))
this.msg.success(res.desc) this.msg.success(res.desc)
// this.table.ref.reload() // this.table.ref.reload()
return true return true

7
web-admin-app/src/app/components/plan-task/task-form/task-form.component.html

@ -45,7 +45,12 @@
<nz-form-label nzRequired>任务时长</nz-form-label> <nz-form-label nzRequired>任务时长</nz-form-label>
<nz-form-control [nzErrorTip]="errorTpl"> <nz-form-control [nzErrorTip]="errorTpl">
<nz-input-group nzAddOnAfter="日"> <nz-input-group nzAddOnAfter="日">
<nz-input-number class="!w-full" [nzMin]="0" formControlName="duration" /> <nz-input-number
nzPlaceHolder="请输入"
class="!w-full"
[nzMin]="0"
formControlName="duration"
/>
</nz-input-group> </nz-input-group>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>

2
web-admin-app/src/app/components/plan-task/task-form/task-form.component.ts

@ -81,7 +81,7 @@ export class TaskFormComponent {
businessId: this.fb.control(null, []), businessId: this.fb.control(null, []),
attachment: this.fb.control(null, []), attachment: this.fb.control(null, []),
duration: this.fb.control(null, []), duration: this.fb.control(null, [FormValidators.required('请输入')]),
order: this.fb.control(false, []), order: this.fb.control(false, []),
limit: this.fb.control(false, []), limit: this.fb.control(false, []),
img: this.fb.control(null, []), img: this.fb.control(null, []),

30
web-admin-app/src/app/components/plan-task/task-list/task-list.component.ts

@ -17,6 +17,7 @@ import { StatusTagComponent } from 'app/components/status-tag/status-tag.compone
import { PlanFormComponent } from '../plan-form/plan-form.component' import { PlanFormComponent } from '../plan-form/plan-form.component'
import { TaskFormComponent } from '../task-form/task-form.component' import { TaskFormComponent } from '../task-form/task-form.component'
import { HandleTaskComponent } from '../handle-task/handle-task.component' import { HandleTaskComponent } from '../handle-task/handle-task.component'
import { Utils } from 'app/utils'
@Component({ @Component({
selector: 'app-task-list', selector: 'app-task-list',
@ -99,13 +100,12 @@ export class TaskListComponent {
title: '审批', title: '审批',
onClick: this.onHandle.bind(this, false), onClick: this.onHandle.bind(this, false),
visible(v) { visible(v) {
return true
return v.status === 'APPROVAL' return v.status === 'APPROVAL'
}, },
}, },
{ {
title: '报表', title: '报表',
onClick: this.deleteItem.bind(this), onClick: this.onReport.bind(this),
visible(v) { visible(v) {
return v.status === 'COMPLETED' return v.status === 'COMPLETED'
}, },
@ -171,6 +171,7 @@ export class TaskListComponent {
} }
this.table.ref.reload() this.table.ref.reload()
this.modalRef?.close()
return true return true
}, },
}) })
@ -245,8 +246,22 @@ export class TaskListComponent {
// }, // },
}) })
} }
modalRef?: NzModalRef
onReport(i: NzSafeAny) {
this.msg.loading('下载中...')
this.api.downloadTaskReport(i.id).subscribe((res) => {
Utils.downLoadFile(
res,
'application/vnd.openxmlformats-officedocument.wordprocessingml.document;charset=utf-8',
)
this.msg.remove()
this.msg.success('下载成功')
})
}
onHandle(chuzhi: boolean, data: NzSafeAny) { onHandle(chuzhi: boolean, data: NzSafeAny) {
console.log('chuzhi', chuzhi, data) // console.log('chuzhi', chuzhi, data)
let nzTitle = chuzhi ? '处置任务' : '任务审批' let nzTitle = chuzhi ? '处置任务' : '任务审批'
const footer: NzSafeAny[] = chuzhi const footer: NzSafeAny[] = chuzhi
? [ ? [
@ -268,7 +283,7 @@ export class TaskListComponent {
) )
this.msg.success(res.desc) this.msg.success(res.desc)
this.table.ref.reload() this.table.ref.reload()
ref.close() this.modalRef?.close()
} }
}, },
}, },
@ -293,7 +308,7 @@ export class TaskListComponent {
) )
this.msg.success(res.desc) this.msg.success(res.desc)
this.table.ref.reload() this.table.ref.reload()
ref.close() this.modalRef?.close()
} }
}, },
}, },
@ -317,7 +332,7 @@ export class TaskListComponent {
}, },
}, },
] ]
const ref = this.modal.create({ this.modalRef = this.modal.create({
nzTitle, nzTitle,
nzContent: HandleTaskComponent, nzContent: HandleTaskComponent,
nzWidth: '80vw', nzWidth: '80vw',
@ -326,11 +341,12 @@ export class TaskListComponent {
value: data, value: data,
chuzhi, chuzhi,
type: this.type, type: this.type,
preview: !chuzhi,
}, },
nzFooter: footer.concat({ nzFooter: footer.concat({
label: '取消', label: '取消',
onClick: () => { onClick: () => {
ref.close() this.modalRef?.close()
}, },
}), }),
}) })

113
web-admin-app/src/app/pages/data-vis/data-vis.component.html

@ -0,0 +1,113 @@
<div *ngIf="data">
<div>
<div class="vis-hd-left">
{{ time | date: 'yyyy-MM-dd HH:mm:ss' }}
</div>
<div class="vis-title">EAM数据可视化</div>
<div class="vis-hd-right">
{{ data?.slogan }}
</div>
</div>
<div class="viewport">
<div class="column">
<div class="overview panel">
<div class="inner">
@for (item of data.assetCostStatistics; track $index) {
<div class="item">
<h4>{{ item.content }}</h4>
<span>
<!-- <i class="icon-dot" style="color: #006cff"></i> -->
{{ item.name }}
</span>
</div>
}
</div>
</div>
<!--监控-->
<div class="point panel">
<div class="inner">
<h3>资产分类排名</h3>
<div class="chart" #cateTpl></div>
</div>
</div>
<div class="quarter panel">
<div class="inner">
<h3>设备状态分析</h3>
<div class="chart" #statusTpl></div>
</div>
</div>
<!--点位-->
</div>
<div class="column">
<!-- 地图 -->
<div class="map">
<h3>
<span class="icon-cube"></span>
资产变动分析
</h3>
<div class="chart" #changeTpl></div>
</div>
<!-- 用户 -->
<div class="users panel">
<div class="inner">
<h3>资产存放分布</h3>
<div class="chart" #posTpl></div>
</div>
</div>
</div>
<div class="column">
<div class="order panel">
<div class="inner">
<div class="filter">
<a href="javascript:;" data-key="day365" class="active">流程工单统计</a>
</div>
<div class="data">
@for (item of data.flowCountStatistics; track $index) {
<div class="item">
<h4>{{ item.content }}</h4>
<span>
<i class="icon-dot" style="color: #ed3f35"></i>
{{ item.name }}
</span>
</div>
}
</div>
</div>
</div>
<!-- 销售额 -->
<div class="sales panel">
<div class="inner">
<div class="caption">
<h3>流程类型统计</h3>
</div>
<div class="chart" #flowTpl></div>
</div>
</div>
<div class="monitor panel">
<div class="inner">
<div class="tabs">
<a href="javascript:;" data-index="0" class="active">消息中心</a>
</div>
<div class="content" style="display: block">
<div class="head">
<span class="col">消息名称</span>
<span class="col">消息内容</span>
</div>
<div class="marquee-view">
<div class="marquee">
@for (item of data.messages; track $index) {
<div class="row">
<span class="col">{{ item.name }}</span>
<span class="col">{{ item.content }}</span>
</div>
}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>

761
web-admin-app/src/app/pages/data-vis/data-vis.component.less

@ -0,0 +1,761 @@
::ng-deep {
body {
line-height: 1.15;
font-size: 0.5rem;
margin: 0;
padding: 0;
background-repeat: no-repeat;
background-position: 0 0 / cover;
background-color: #101129;
}
html {
font-size: 21.1375px !important;
}
* {
margin: 0;
padding: 0;
font-weight: normal;
}
ul {
list-style: none;
}
a {
text-decoration: none;
}
}
.vis-hd-left {
position: absolute;
left: 40px;
top: 40px;
color: #fff;
font-size: 20px;
}
.vis-hd-right {
position: absolute;
right: 40px;
top: 40px;
color: #fff;
font-size: 20px;
}
.vis-title {
font-size: 28px;
position: absolute;
top: 40px;
left: 50%;
transform: translateX(-50%);
color: #fff;
}
.viewport {
/* 限定大小 */
min-width: 1024px;
max-width: 1920px;
min-height: 780px;
margin: 0 auto;
background: url(/assets/data-vis/images/logo.png) no-repeat 0 0 / contain;
display: flex;
padding: 3.667rem 0.833rem 0;
position: relative;
}
.column {
flex: 3;
position: relative;
}
.column:nth-child(2) {
flex: 4;
margin: 1.333rem 0.833rem 0;
}
.panel {
/* 边框 */
box-sizing: border-box;
border: 2px solid red;
border-image: url(/assets/data-vis/images/border.png) 51 38 21 132;
border-width: 2.125rem 1.583rem 0.875rem 5.5rem;
position: relative;
&:not(:first-child) {
margin-top: 0.833rem;
}
}
.panel .inner {
/* 装内容 */
/* height: 60px; */
position: absolute;
top: -2.125rem;
right: -1.583rem;
bottom: -0.875rem;
left: -5.5rem;
padding: 1rem 1.5rem;
}
.panel h3 {
font-size: 0.833rem;
color: #fff;
}
/* 概览区域 */
.overview {
height: 4.583rem;
}
.overview .inner {
display: flex;
justify-content: space-between;
}
.overview h4 {
font-size: 1.167rem;
padding-left: 0.2rem;
color: #fff;
margin-bottom: 0.333rem
}
.overview span {
font-size: 0.667rem;
color: #4c9bfd;
}
/* 监控 */
.monitor {
height: 14rem;
}
.monitor .inner {
padding: 1rem 0;
display: flex;
flex-direction: column;
}
.monitor .tabs {
padding: 0 1.5rem;
margin-bottom: 0.75rem;
}
.monitor .tabs a {
color: #1950c4;
font-size: 0.75rem;
padding: 0 1.125rem;
}
// .monitor .tabs a:first-child {
// border-right: 0.083rem solid #00f2f1;
// padding-left: 0;
// }
.monitor .tabs a.active {
color: #fff;
}
.monitor .content {
flex: 1;
display: none;
position: relative;
}
.monitor .head {
background: rgba(255, 255, 255, 0.1);
font-size: 0.583rem;
padding: 0.5rem 1.5rem;
color: #68d8fe;
display: flex;
justify-content: space-between;
line-height: 1.05;
}
.monitor .col:nth-child(1) {
width: 3.2rem;
}
.monitor .col:nth-child(2) {
width: 8.4rem;
/* 不换行 一行省略*/
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.monitor .col:nth-child(3) {
width: 3.2rem;
}
.monitor .marquee-view {
position: absolute;
top: 1.6rem;
bottom: 0;
width: 100%;
overflow: hidden;
}
.monitor .row {
line-height: 1.05;
padding: 0.5rem 1.5rem;
color: #61a8ff;
font-size: 0.5rem;
position: relative;
display: flex;
justify-content: space-between;
}
.monitor .row:hover {
color: #68d8ff;
background: rgba(255, 255, 255, 0.1);
}
.monitor .row:hover .icon-dot {
opacity: 1;
}
.monitor .icon-dot {
position: absolute;
left: 0.64rem;
opacity: 0;
}
.monitor .marquee-view {
position: absolute;
top: 1.6rem;
bottom: 0;
width: 100%;
overflow: hidden;
}
.monitor .row {
line-height: 1.05;
padding: 0.5rem 1.5rem;
color: #61a8ff;
font-size: 0.5rem;
position: relative;
display: flex;
justify-content: space-between;
}
.monitor .row:hover {
color: #68d8ff;
background: rgba(255, 255, 255, 0.1);
}
.monitor .row:hover .icon-dot {
opacity: 1;
}
.monitor .icon-dot {
position: absolute;
left: 0.64rem;
opacity: 0;
}
/* ------------------------------------------------------------动画 */
@keyframes row {
0% {}
100% {
transform: translateY(-50%);
}
}
/* 调用动画 */
.monitor .marquee {
/* //infinite永久调用动画 */
animation: row 10s linear infinite;
}
/*鼠标划入 停止动画 */
.monitor .marquee:hover {
animation-play-state: paused;
}
/* 点位 */
.point {
height: 20rem;
}
.point .chart {
display: flex;
margin-top: 1rem;
justify-content: space-between;
}
.point .pie {
width: 13rem;
height: 10rem;
margin-left: -0.4rem;
}
.point .data {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 7rem;
padding: 1.5rem 1.25rem;
box-sizing: border-box;
background-image: url(/assets/data-vis/images/rect.png);
background-size: cover;
}
.point h4 {
margin-bottom: 0.5rem;
font-size: 1.167rem;
color: #fff;
}
.point span {
display: block;
color: #4c9bfd;
font-size: 0.667rem;
}
/* 地图 */
.map {
height: 24.1rem;
margin-bottom: 0.833rem;
display: flex;
flex-direction: column;
}
.map h3 {
line-height: 1;
padding: 0.667rem 0;
margin: 0;
font-size: 0.833rem;
color: #fff;
}
.map .icon-cube {
color: #68d8fe;
}
.map .chart {
flex: 1;
background-color: rgba(255, 255, 255, 0.05);
}
.map .geo {
width: 100%;
height: 100%;
}
/* 用户模块 */
.users {
height: 14.167rem;
display: flex;
}
.users .inner {
display: flex;
flex-direction: column;
}
.users .chart {
flex: 1;
display: flex;
margin-top: 1rem;
}
.users .bar {
width: 24.5rem;
height: 10rem;
}
.users .data {
display: flex;
flex-direction: column;
justify-content: space-between;
width: 7rem;
padding: 1.5rem 1.25rem;
box-sizing: border-box;
background-image: url(/assets/data-vis/images/rect.png);
background-size: cover;
}
.users h4 {
margin-bottom: 0.5rem;
font-size: 1.167rem;
color: #fff;
}
.users span {
display: block;
color: #4c9bfd;
font-size: 0.667rem;
}
/* 订单 */
.order {
height: 6.167rem;
}
.order .filter {
display: flex;
}
.order .filter a {
display: block;
height: 0.75rem;
line-height: 1;
padding: 0 0.75rem;
color: #1950c4;
font-size: 0.75rem;
border-right: 0.083rem solid #00f2f1;
}
.order .filter a:first-child {
padding-left: 0;
}
.order .filter a:last-child {
border-right: none;
}
.order .filter a.active {
color: #fff;
font-size: 0.833rem;
}
.order .data {
display: flex;
margin-top: 0.833rem;
}
.order .item {
width: 50%;
}
.order h4 {
font-size: 1.167rem;
color: #fff;
margin-bottom: 0.417rem;
}
.order span {
display: block;
color: #4c9bfd;
font-size: 0.667rem;
}
/* 销售区域 */
.sales {
height: 18.333rem;
}
.sales .caption {
display: flex;
line-height: 1;
}
.sales h3 {
height: 0.75rem;
padding-right: 0.75rem;
}
.sales a {
padding: 0.167rem;
font-size: 0.667rem;
margin: -0.125rem 0 0 0.875rem;
border-radius: 0.125rem;
color: #0bace6;
}
.sales a.active {
background-color: #4c9bfd;
color: #fff;
}
.sales .inner {
display: flex;
flex-direction: column;
}
.sales .chart {
flex: 1;
padding-top: 0.6rem;
position: relative;
}
.sales .label {
position: absolute;
left: 1.75rem;
top: 0.75rem;
color: #4996f5;
font-size: 0.583rem;
}
.sales .line {
width: 100%;
height: 100%;
}
/* 渠道区块 */
.wrap {
display: flex;
}
.channel,
.quarter {
flex: 1;
height: 13.5rem;
}
.channel {
margin-right: 0.833rem;
}
.channel .data {
overflow: hidden;
}
.channel .item {
margin-top: 0.85rem;
}
.channel .item:first-child {
float: left;
}
.channel .item:last-child {
float: right;
}
.channel h4 {
color: #fff;
font-size: 1.333rem;
margin-bottom: 0.2rem;
}
.channel small {
font-size: 50%;
}
.channel span {
display: block;
color: #4c9bfd;
font-size: 0.583rem;
}
/* 季度区块 */
.quarter .inner {
display: flex;
flex-direction: column;
margin: 0 -0.25rem;
}
.quarter .chart {
flex: 1;
padding-top: 0.75rem;
}
.quarter .box {
position: relative;
}
.quarter .label {
transform: translate(-50%, -30%);
color: #fff;
font-size: 1.25rem;
position: absolute;
left: 50%;
top: 50%;
}
.quarter .label small {
font-size: 50%;
}
.quarter .gauge {
height: 3.5rem;
}
.quarter .data {
display: flex;
justify-content: space-between;
}
.quarter .item {
width: 50%;
}
.quarter h4 {
color: #fff;
font-size: 1rem;
margin-bottom: 0.4rem;
}
.quarter span {
display: block;
width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
color: #4c9bfd;
font-size: 0.583rem;
}
.point .inner {
display: flex;
flex-direction: column;
.chart {
flex: 1;
}
}
/* 排行榜 */
.top {
height: 11.8rem;
}
.top .inner {
display: flex;
}
.top .all {
display: flex;
flex-direction: column;
width: 7rem;
color: #4c9bfd;
font-size: 0.6rem;
vertical-align: middle;
}
.top .all ul {
padding-left: 0.5rem;
margin-top: 0.5rem;
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-around;
}
.top .all li {
overflow: hidden;
}
.top .all [class^="icon-"] {
font-size: 1.5rem;
vertical-align: middle;
margin-right: 0.5rem;
}
.top .province {
flex: 1;
display: flex;
flex-direction: column;
color: #fff;
}
.top .province i {
padding: 0 0.5rem;
margin-top: 0.208rem;
float: right;
font-style: normal;
font-size: 0.583rem;
color: #0bace6;
}
.top .province s {
display: inline-block;
transform: scale(0.8);
text-decoration: none;
}
.top .province .icon-up {
color: #dc3c33;
}
.top .province .icon-down {
color: #36be90;
}
.top .province .data {
flex: 1;
display: flex;
margin-top: 0.6rem;
}
.top .province ul {
flex: 1;
line-height: 1;
margin-bottom: 0.25rem;
}
.top .province ul li {
display: flex;
justify-content: space-between;
}
.top .province ul span {
display: block;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.top .province ul.sup {
font-size: 0.583rem;
}
.top .province ul.sup li {
color: #4995f4;
padding: 0.5rem;
}
.top .province ul.sup li.active {
color: #a3c6f2;
background-color: rgba(10, 67, 188, 0.2);
}
.top .province ul.sub {
display: flex;
flex-direction: column;
justify-content: space-around;
font-size: 0.5rem;
background-color: rgba(10, 67, 188, 0.2);
}
.top .province ul.sub li {
color: #52ffff;
padding: 0.417rem 0.6rem;
}
.clock {
position: absolute;
top: -1.5rem;
right: 1.667rem;
font-size: 0.833rem;
color: #0bace6;
}
.clock i {
margin-right: 5px;
font-size: 0.833rem;
}
@media screen and (max-width: 1600px) {
.top span {
transform: scale(0.9);
}
.top .province ul.sup li {
padding: 0.4rem 0.5rem;
}
.top .province ul.sub li {
padding: 0.23rem 0.5rem;
}
.quarter span {
transform: scale(0.9);
}
}

271
web-admin-app/src/app/pages/data-vis/data-vis.component.ts

@ -0,0 +1,271 @@
import { Component, ElementRef, ViewChild } from '@angular/core'
import { ApiService } from 'app/services'
import { SharedModule } from 'app/shared/shared.module'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { interval } from 'rxjs'
import { init, EChartsType } from 'echarts'
@Component({
selector: 'app-data-vis',
standalone: true,
imports: [SharedModule],
templateUrl: './data-vis.component.html',
styleUrl: './data-vis.component.less',
})
export class DataVisComponent {
constructor(private api: ApiService) {}
@ViewChild('cateTpl') cateTpl?: ElementRef<HTMLDivElement>
@ViewChild('statusTpl') statusTpl?: ElementRef<HTMLDivElement>
@ViewChild('changeTpl') changeTpl?: ElementRef<HTMLDivElement>
@ViewChild('posTpl') posTpl?: ElementRef<HTMLDivElement>
@ViewChild('flowTpl') flowTpl?: ElementRef<HTMLDivElement>
cateRef?: EChartsType
statusRef?: EChartsType
changeRef?: EChartsType
posRef?: EChartsType
flowRef?: EChartsType
data: NzSafeAny
time = new Date()
ngOnInit(): void {
this.api.bigScreen().subscribe((res) => {
this.data = res.body
setTimeout(() => {
this.renderCate()
this.renderStatus()
this.renderChange()
this.renderPos()
this.renderFlow()
}, 10)
})
interval(1000).subscribe(() => {
this.time = new Date()
})
}
renderFlow() {
if (!this.flowTpl?.nativeElement) {
return
}
if (!this.flowRef) {
this.flowRef = init(this.flowTpl.nativeElement)
}
const name: string[] = []
const val: number[] = []
this.data.assetPositionChart.forEach((i: any) => {
name.push(i.name)
val.push(i.total)
})
this.flowRef.setOption({
legend: {
bottom: 'bottom',
left: 'center',
type: 'scroll',
textStyle: {
color: '#ffffff',
},
},
series: [
{
type: 'pie',
radius: [10, 100],
label: {
formatter: '{b}-{c}',
// position: 'inside',
},
itemStyle: {
borderRadius: 8,
},
data: this.data.flowTypeChart.map((i: any) => {
return {
value: i.total,
name: i.name,
}
}),
},
],
})
}
renderPos() {
if (!this.posTpl?.nativeElement) {
return
}
if (!this.posRef) {
this.posRef = init(this.posTpl.nativeElement)
}
const name: string[] = []
const val: number[] = []
this.data.assetPositionChart.forEach((i: any) => {
name.push(i.name)
val.push(i.total)
})
this.posRef.setOption({
grid: {
top: '5%',
left: '10%',
right: '5%',
bottom: '10%',
},
xAxis: {
type: 'category',
boundaryGap: false,
data: name,
splitLine: {
show: false,
},
axisLabel: {
show: true,
color: '#ffffff',
},
},
yAxis: {
type: 'value',
axisLabel: {
show: true,
color: '#ffffff',
},
},
series: [
{
data: val,
type: 'bar',
areaStyle: {},
smooth: true,
},
],
})
}
renderChange() {
if (!this.changeTpl?.nativeElement) {
return
}
if (!this.changeRef) {
this.changeRef = init(this.changeTpl.nativeElement)
}
const name: string[] = []
const val: number[] = []
this.data.assetChangeChart.forEach((i: any) => {
name.push(i.name)
val.push(i.total)
})
this.changeRef.setOption({
grid: {
top: '5%',
left: '10%',
right: '5%',
bottom: '6%',
},
xAxis: {
type: 'category',
boundaryGap: false,
data: name,
splitLine: {
show: false,
},
axisLabel: {
show: true,
color: '#ffffff',
},
},
yAxis: {
type: 'value',
axisLabel: {
show: true,
color: '#ffffff',
},
},
series: [
{
data: val,
type: 'line',
areaStyle: {},
smooth: true,
},
],
})
}
renderStatus() {
if (!this.statusTpl?.nativeElement) {
return
}
if (!this.statusRef) {
this.statusRef = init(this.statusTpl.nativeElement)
}
this.statusRef.setOption({
legend: {
bottom: 'bottom',
left: 'center',
type: 'scroll',
textStyle: {
color: '#ffffff',
},
},
series: [
{
type: 'pie',
radius: [10, 100],
roseType: 'area',
itemStyle: {
borderRadius: 8,
},
data: this.data.assetStatusChart.map((i: any) => {
return {
value: i.total,
name: i.name,
}
}),
},
],
})
}
renderCate() {
if (!this.cateTpl?.nativeElement) {
return
}
if (!this.cateRef) {
this.cateRef = init(this.cateTpl.nativeElement)
}
const name: string[] = []
const val: number[] = []
this.data.assetCategoryChart.forEach((i: any) => {
name.push(i.name)
val.push(i.total)
})
this.cateRef.setOption({
grid: {
top: 0,
left: '10%',
right: '0%',
bottom: '0%',
},
xAxis: {
type: 'value',
splitLine: {
show: false,
},
},
yAxis: {
type: 'category',
data: name,
axisLabel: {
show: true,
color: '#ffffff',
},
},
series: [
{
type: 'bar',
data: val,
},
],
})
}
}

23
web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form-manage/basic-flow-form-manage.component.html

@ -23,7 +23,11 @@
<nz-form-control nzSpan="14" nzOffset="5"> <nz-form-control nzSpan="14" nzOffset="5">
<nz-space> <nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="nextStep(this.step)"> <button *nzSpaceItem nz-button nzType="primary" (click)="nextStep(this.step)">
@if (step < 2) {
下一步 下一步
} @else {
确认上传
}
</button> </button>
<button *nzSpaceItem nz-button nzType="primary" nzDanger (click)="cancel(this.step)"> <button *nzSpaceItem nz-button nzType="primary" nzDanger (click)="cancel(this.step)">
取消 取消
@ -50,7 +54,6 @@
<nz-form-control nzSpan="14" [nzExtra]="tempalteTpl" [nzErrorTip]="formErrorTpl"> <nz-form-control nzSpan="14" [nzExtra]="tempalteTpl" [nzErrorTip]="formErrorTpl">
<app-upload <app-upload
[drag]="true" [drag]="true"
formControlName="templatePath"
[uploadFn]="api.uploadFlowFormTpl" [uploadFn]="api.uploadFlowFormTpl"
(onUpload)="onTplupload($event)" (onUpload)="onTplupload($event)"
/> />
@ -206,7 +209,23 @@
</div> </div>
</div> </div>
</ng-template> </ng-template>
<ng-template #step3> 33 </ng-template> <ng-template #step3>
<nz-form-item>
<nz-form-label nzSpan="5" nzRequired>模板示例</nz-form-label>
<nz-form-control nzSpan="14" [nzErrorTip]="formErrorTpl">
<a nz-button nzType="link" (click)="downloadTemplate1()">
<i nz-icon nzType="cloud-download" class="mb-2"></i>
点击下载模板示例
</a>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="5" [nzRequired]="true">上传模板</nz-form-label>
<nz-form-control nzSpan="14" [nzErrorTip]="formErrorTpl">
<app-upload [drag]="true" [uploadFn]="api.uploadFormTpl" formControlName="templatePath" />
</nz-form-control>
</nz-form-item>
</ng-template>
<ng-template #radioTpl let-fields> </ng-template> <ng-template #radioTpl let-fields> </ng-template>
</form> </form>

43
web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form-manage/basic-flow-form-manage.component.ts

@ -21,7 +21,7 @@ import { lastValueFrom } from 'rxjs'
export class BasicFlowFormManageComponent { export class BasicFlowFormManageComponent {
constructor(public api: ApiService) {} constructor(public api: ApiService) {}
step = 0 step = 2
formJsonData = [] formJsonData = []
@ -73,6 +73,15 @@ export class BasicFlowFormManageComponent {
}) })
} }
downloadTemplate1() {
this.msg.loading('模板下载中...')
this.api.downloadTpl().subscribe((res) => {
Utils.downLoadFile(res, 'application/zip;charset=utf-8')
this.msg.remove()
this.msg.success('模板下载成功')
})
}
fb = inject(FormBuilder) fb = inject(FormBuilder)
onTplupload(e: NzSafeAny[]) { onTplupload(e: NzSafeAny[]) {
this.patchFormValue(e) this.patchFormValue(e)
@ -114,9 +123,12 @@ export class BasicFlowFormManageComponent {
return return
} }
} }
if (currentStep === 2) { if (currentStep === 1) {
this.handleSubmit() this.handleSubmit()
} }
if (currentStep === 2) {
this.handleConfirm()
}
this.step = currentStep + 1 this.step = currentStep + 1
} }
@ -138,6 +150,33 @@ export class BasicFlowFormManageComponent {
: this.api.addFlowForm(vals), : this.api.addFlowForm(vals),
) )
this.msg.success(res.desc) this.msg.success(res.desc)
// this.router.navigate(['/fixed-asset/basic/flow-form/list'])
this.step = 2
},
})
}
}
handleConfirm() {
if (FormValidators.validateFormGroup(this.formGroup)) {
if (!this.formValue.value?.templatePath) {
this.msg.error('请先上传模板')
return
}
this.modal.confirm({
nzTitle: '确认上传',
nzContent: '确认上传模板?',
nzOnOk: async () => {
const vals = this.formGroup.value
const res = await lastValueFrom(
this.id
? this.api.confirmFlowForm({
...vals,
id: this.id,
formTempId: this.id,
})
: this.api.addFlowForm(vals),
)
this.msg.success(res.desc)
this.router.navigate(['/fixed-asset/basic/flow-form/list']) this.router.navigate(['/fixed-asset/basic/flow-form/list'])
}, },
}) })

15
web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form/basic-flow-form.component.html

@ -9,8 +9,9 @@
> >
新建 新建
</button> </button>
<button *nzSpaceItem nzType="primary" nz-button [routerLink]="['/fixed-asset/basic/flow-form/import']"> <!-- <button *nzSpaceItem nz-button (click)="importData(importTpl)">导入</button> -->
导入 <button [disabled]="table.ref.selected.size === 0" *nzSpaceItem nz-button (click)="exportData()">
导出
</button> </button>
</nz-space> </nz-space>
</ng-template> </ng-template>
@ -69,3 +70,13 @@
<ng-template #errorTpl let-control> <ng-template #errorTpl let-control>
<form-error-tips [control]="control"></form-error-tips> <form-error-tips [control]="control"></form-error-tips>
</ng-template> </ng-template>
<ng-template #importTpl>
<div nz-form>
<nz-form-item>
<nz-form-label> 文件 </nz-form-label>
<nz-form-control>
<app-upload [drag]="true" [uploadFn]="api.importFlowFormByExcel" />
</nz-form-control>
</nz-form-item>
</div>
</ng-template>

35
web-admin-app/src/app/pages/fixed-asset/basic/basic-flow-form/basic-flow-form.component.ts

@ -1,11 +1,12 @@
import { Component, inject, TemplateRef, ViewChild } from '@angular/core' import { Component, ElementRef, inject, TemplateRef, ViewChild } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms' import { FormControl, FormGroup } from '@angular/forms'
import { Router } from '@angular/router' import { Router } from '@angular/router'
import { StatusTagComponent } from 'app/components' import { StatusTagComponent } from 'app/components'
import { ApiService } from 'app/services' import { ApiService } from 'app/services'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table' import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
import { UploadComponent } from 'app/shared/components/upload/upload.component'
import { SharedModule } from 'app/shared/shared.module' import { SharedModule } from 'app/shared/shared.module'
import { FormValidators } from 'app/utils' import { FormValidators, Utils } from 'app/utils'
import { NzSafeAny } from 'ng-zorro-antd/core/types' import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NzMessageService } from 'ng-zorro-antd/message' import { NzMessageService } from 'ng-zorro-antd/message'
import { NzModalService } from 'ng-zorro-antd/modal' import { NzModalService } from 'ng-zorro-antd/modal'
@ -14,13 +15,13 @@ import { lastValueFrom, map } from 'rxjs'
@Component({ @Component({
selector: 'app-basic-flow-form', selector: 'app-basic-flow-form',
standalone: true, standalone: true,
imports: [SharedModule, StatusTagComponent], imports: [SharedModule, StatusTagComponent, UploadComponent],
templateUrl: './basic-flow-form.component.html', templateUrl: './basic-flow-form.component.html',
styleUrl: './basic-flow-form.component.less', styleUrl: './basic-flow-form.component.less',
}) })
export class BasicFlowFormComponent { export class BasicFlowFormComponent {
constructor( constructor(
private api: ApiService, public api: ApiService,
private modal: NzModalService, private modal: NzModalService,
private msg: NzMessageService, private msg: NzMessageService,
) {} ) {}
@ -38,12 +39,28 @@ export class BasicFlowFormComponent {
table = new TableOption(this.fetchData.bind(this)) table = new TableOption(this.fetchData.bind(this))
importData(nzContent: TemplateRef<{}>) {
this.modal.create({
nzTitle: '导入',
nzContent,
nzFooter: null,
})
}
exportData() {
this.msg.loading('正在下载...', { nzDuration: 0 })
this.api.exportFlowForm(Array.from(this.table.ref.selected)).subscribe((res) => {
Utils.downLoadFile(res, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8')
this.msg.remove()
this.msg.success('下载成功')
})
}
ngOnInit(): void { ngOnInit(): void {
this.table this.table
// .setConfig({ .setConfig({
// selectable: true, selectable: true,
// rowKey: 'id', rowKey: 'formTempId',
// }) })
.setColumn([ .setColumn([
{ key: 'formKey', title: '表单编号', visible: true }, { key: 'formKey', title: '表单编号', visible: true },
{ key: 'formName', title: '表单名称', visible: true }, { key: 'formName', title: '表单名称', visible: true },
@ -64,7 +81,7 @@ export class BasicFlowFormComponent {
router = inject(Router) router = inject(Router)
onEdit(v: NzSafeAny) { onEdit(v: NzSafeAny) {
this.router.navigate(['/fixed-asset/basic/flow-form/manage/', v.formKey]) this.router.navigate(['/fixed-asset/basic/flow-form/manage/', v.formTempId])
} }
onDelete(data: NzSafeAny) { onDelete(data: NzSafeAny) {

14
web-admin-app/src/app/pages/fixed-asset/fixed-asset.component.html

@ -307,7 +307,7 @@
> >
供应商 供应商
</li> </li>
<li <!-- <li
*appPermission="['basic-maintainer:view']" *appPermission="['basic-maintainer:view']"
nz-menu-item nz-menu-item
[nzPaddingLeft]="24" [nzPaddingLeft]="24"
@ -315,8 +315,8 @@
nzMatchRouter nzMatchRouter
> >
维保商 维保商
</li> </li> -->
<li <!-- <li
*appPermission="['basic-save-position:view']" *appPermission="['basic-save-position:view']"
nz-menu-item nz-menu-item
[nzPaddingLeft]="24" [nzPaddingLeft]="24"
@ -324,8 +324,8 @@
nzMatchRouter nzMatchRouter
> >
存放位置 存放位置
</li> </li> -->
<li <!-- <li
*appPermission="['basic-warehouse:view']" *appPermission="['basic-warehouse:view']"
nz-menu-item nz-menu-item
[nzPaddingLeft]="24" [nzPaddingLeft]="24"
@ -333,7 +333,7 @@
nzMatchRouter nzMatchRouter
> >
存放仓库 存放仓库
</li> </li> -->
<li <li
*appPermission="['fixed-asset-repair-type:view']" *appPermission="['fixed-asset-repair-type:view']"
nz-menu-item nz-menu-item
@ -341,7 +341,7 @@
[routerLink]="['/fixed-asset/basic/repair-type']" [routerLink]="['/fixed-asset/basic/repair-type']"
nzMatchRouter nzMatchRouter
> >
类型 类型
</li> </li>
<li <li

25
web-admin-app/src/app/pages/fixed-asset/maintain/maintain-on-off/maintain-on-off.component.ts

@ -1,4 +1,4 @@
import { Component, TemplateRef, ViewChild } from '@angular/core' import { Component, inject, TemplateRef, ViewChild } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms' import { FormControl, FormGroup } from '@angular/forms'
import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table' import { AnyObject, TableOption } from 'app/shared/components/server-paginated-table'
@ -11,6 +11,8 @@ import { NzModalService } from 'ng-zorro-antd/modal'
import { NzMessageService } from 'ng-zorro-antd/message' import { NzMessageService } from 'ng-zorro-antd/message'
import { ASSET_STATUS_MAP, ASSET_TYPE } from 'app/constants' import { ASSET_STATUS_MAP, ASSET_TYPE } from 'app/constants'
import { PositionSelectComponent } from 'app/components' import { PositionSelectComponent } from 'app/components'
import { Router } from '@angular/router'
import { OnOffRecordsComponent } from '../on-off-records/on-off-records.component'
@Component({ @Component({
selector: 'app-maintain-on-off', selector: 'app-maintain-on-off',
@ -39,6 +41,8 @@ export class MaintainOnOffComponent {
ASSET_STATUS_MAP = ASSET_STATUS_MAP() ASSET_STATUS_MAP = ASSET_STATUS_MAP()
ASSET_TYPE = ASSET_TYPE ASSET_TYPE = ASSET_TYPE
router = inject(Router)
ngOnInit(): void { ngOnInit(): void {
this.table this.table
.setConfig({ .setConfig({
@ -49,12 +53,12 @@ export class MaintainOnOffComponent {
{ key: 'serialNumber', title: '序号', visible: true }, { key: 'serialNumber', title: '序号', visible: true },
{ key: 'assetCode', title: '资产编号', visible: true }, { key: 'assetCode', title: '资产编号', visible: true },
{ key: 'name', title: '资产名称', visible: true }, { key: 'name', title: '资产名称', visible: true },
{ key: 'deviceStatus', title: '当前状态', visible: true },
{ key: '_category', title: '资产分类', visible: true }, { key: '_category', title: '资产分类', visible: true },
{ key: 'status', title: '资产状态', visible: true }, { key: 'status', title: '资产状态', visible: true },
{ key: '_ownCompany', title: '所属公司', visible: true }, { key: '_ownCompany', title: '所属公司', visible: true },
{ key: '_useOrganization', title: '使用组织', visible: true }, { key: '_useOrganization', title: '使用组织', visible: true },
{ key: '_position', title: '位置信息', visible: true }, { key: '_position', title: '位置信息', visible: true },
{ key: 'deviceStatus', title: '当前状态', visible: true },
]) ])
.setRowOperate([ .setRowOperate([
// { title: '修改', onClick: this.onCreate.bind(this) }, // { title: '修改', onClick: this.onCreate.bind(this) },
@ -77,7 +81,12 @@ export class MaintainOnOffComponent {
this.onOff([v.assetId], 0) this.onOff([v.assetId], 0)
}, },
}, },
{ title: '记录', onClick: this.onOff.bind(this, [0]) }, {
title: '记录',
onClick: (v) => {
this.onRecords(v)
},
},
]) ])
} }
@ -102,4 +111,14 @@ export class MaintainOnOffComponent {
batchOnOff(type: number) { batchOnOff(type: number) {
this.onOff(Array.from(this.table.ref.selected), type) this.onOff(Array.from(this.table.ref.selected), type)
} }
onRecords(v: NzSafeAny) {
this.modal.create({
nzTitle: '开关机记录',
nzContent: OnOffRecordsComponent,
nzData: v,
nzFooter: null,
})
}
} }

12
web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.html

@ -0,0 +1,12 @@
<div class="max-h-[60vh] mt-6">
<nz-timeline>
@for (item of records; track $index) {
<nz-timeline-item
[nzLabel]="item.createTime"
[nzColor]="item.operationType.includes('开机') ? 'green' : 'red'"
>
{{ item.operationType }}
</nz-timeline-item>
}
</nz-timeline>
</div>

0
web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.less

27
web-admin-app/src/app/pages/fixed-asset/maintain/on-off-records/on-off-records.component.ts

@ -0,0 +1,27 @@
import { Component, inject, OnInit } from '@angular/core'
import { ApiService } from 'app/services'
import { SharedModule } from 'app/shared/shared.module'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import { NZ_MODAL_DATA } from 'ng-zorro-antd/modal'
@Component({
selector: 'app-on-off-records',
standalone: true,
imports: [SharedModule],
templateUrl: './on-off-records.component.html',
styleUrl: './on-off-records.component.less',
})
export class OnOffRecordsComponent implements OnInit {
constructor(private api: ApiService) {}
data = inject(NZ_MODAL_DATA)
records: NzSafeAny[] = []
ngOnInit(): void {
if (this.data) {
this.api.onOffRecord(this.data.assetId).subscribe((res) => {
this.records = res.body
})
}
}
}

21
web-admin-app/src/app/pages/fixed-asset/registration/registration.component.html

@ -244,6 +244,27 @@
<ng-template #importExcelTpl> <ng-template #importExcelTpl>
<div nz-form> <div nz-form>
<div class="mb-3"> <div class="mb-3">
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired> 模版文件类型 </nz-form-label>
<nz-form-control [nzSpan]="12">
<nz-radio-group [(ngModel)]="templateType" (ngModelChange)="extendFormId = undefined">
<label nz-radio nzValue="1">标准资产表单</label>
<label nz-radio nzValue="2">扩展资产表单</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
@if (templateType === '2') {
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired> 扩展资产表单 </nz-form-label>
<nz-form-control [nzSpan]="12">
<nz-select nzPlaceHolder="请选择扩展资产表单" [(ngModel)]="extendFormId">
@for (item of flowForms; track $index) {
<nz-option [nzLabel]="item.formName" [nzValue]="item.formTempId" />
}
</nz-select>
</nz-form-control>
</nz-form-item>
}
<nz-form-item> <nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired> 模版文件 </nz-form-label> <nz-form-label [nzSpan]="6" nzRequired> 模版文件 </nz-form-label>
<nz-form-control [nzSpan]="12"> <nz-form-control [nzSpan]="12">

16
web-admin-app/src/app/pages/fixed-asset/registration/registration.component.ts

@ -48,6 +48,12 @@ export class RegistrationComponent {
copyNum = 1 copyNum = 1
templateType = '1'
extendFormId?: string
flowForms: NzSafeAny[] = []
ngOnInit(): void { ngOnInit(): void {
this.table this.table
.setConfig({ .setConfig({
@ -75,6 +81,10 @@ export class RegistrationComponent {
{ title: '二维码', onClick: this.qrcode.bind(this) }, { title: '二维码', onClick: this.qrcode.bind(this) },
{ title: '删除', onClick: this.deleteItem.bind(this) }, { title: '删除', onClick: this.deleteItem.bind(this) },
]) ])
this.api.getFlowFormList().subscribe((res) => {
this.flowForms = res.body
})
} }
fetchData(p: {}, q: AnyObject) { fetchData(p: {}, q: AnyObject) {
@ -163,8 +173,12 @@ export class RegistrationComponent {
} }
downloadTemplate() { downloadTemplate() {
if (this.templateType === '2' && !this.extendFormId) {
this.msg.error('请选择表单')
return
}
this.msg.loading('模板下载中...') this.msg.loading('模板下载中...')
this.api.downloadAssetTemplate().subscribe((res) => { this.api.downloadAssetTemplate(this.extendFormId).subscribe((res) => {
Utils.downLoadFile(res, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8') Utils.downLoadFile(res, 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8')
this.msg.remove() this.msg.remove()
this.msg.success('模板下载成功') this.msg.success('模板下载成功')

40
web-admin-app/src/app/pages/flow/flow-my-apply/flow-my-apply.component.html

@ -1,34 +1,12 @@
<app-page> <app-page>
<div class="flex-1 overflow-hidden"> <div class="flex-1 overflow-hidden bg-white px-4">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm"> <nz-tabset>
<ng-container *appTableForm> <nz-tab [nzTitle]="'资产管理' + assetTotal + ')'">
<app-query-item label="名称" class="w-60"> <app-flow-list-by-type page="apply" type="asset" (onLoad)="onLoad($event, 'assetTotal')" />
<input nz-input placeholder="请输入" formControlName="name" /> </nz-tab>
</app-query-item> <nz-tab [nzTitle]="'设备任务' + deviceTotal + ')'">
</ng-container> <app-flow-list-by-type page="apply" type="device" (onLoad)="onLoad($event, 'deviceTotal')" />
<ng-template #renderColumnTpl let-data let-key="key" let-row="row"> </nz-tab>
@switch (key) { </nz-tabset>
@case ('_assignee') {
<nz-tag> {{ data.userName }} </nz-tag>
}
@case ('procVars') {
{{ data?.title ?? data?.name ?? '-' }}
}
@case ('status') {
<nz-tag nzColor="blue">
{{ FLOW_STATUS.get(data) }}
</nz-tag>
}
@case ('urgency') {
<nz-tag [nzColor]="row.procVars.urgency === 2 ? 'error' : ''">
{{ row.procVars.urgency === 2 ? '紧急' : '普通' }}
</nz-tag>
}
@default {
{{ data }}
}
}
</ng-template>
</app-server-paginated-table>
</div> </div>
</app-page> </app-page>

67
web-admin-app/src/app/pages/flow/flow-my-apply/flow-my-apply.component.ts

@ -13,11 +13,12 @@ import { FormValidators } from 'app/utils'
import { AssetEmployeeApplyComponent } from 'app/components' import { AssetEmployeeApplyComponent } from 'app/components'
import { comsMap } from '../flow-main/flow-main.component' import { comsMap } from '../flow-main/flow-main.component'
import { FLOW_STATUS, flowIntStatus } from 'app/constants' import { FLOW_STATUS, flowIntStatus } from 'app/constants'
import { FlowListByTypeComponent } from 'app/components/flow-list-by-type/flow-list-by-type.component'
@Component({ @Component({
selector: 'app-flow-my-apply', selector: 'app-flow-my-apply',
standalone: true, standalone: true,
imports: [SharedModule], imports: [SharedModule, FlowListByTypeComponent],
templateUrl: './flow-my-apply.component.html', templateUrl: './flow-my-apply.component.html',
styleUrl: './flow-my-apply.component.less', styleUrl: './flow-my-apply.component.less',
}) })
@ -35,67 +36,13 @@ export class FlowMyApplyComponent {
userId: new FormControl<NzSafeAny[]>([], [FormValidators.required('请选择')]), userId: new FormControl<NzSafeAny[]>([], [FormValidators.required('请选择')]),
}) })
table = new TableOption(this.fetchData.bind(this)) assetTotal = 0
queryForm = new FormGroup({ deviceTotal = 0
name: new FormControl(''),
})
FLOW_STATUS = flowIntStatus
ngOnInit(): void {
this.table
// .setConfig({
// selectable: true,
// rowKey: 'id',
// })
.setColumn([
{ key: 'procVars', title: '名称', visible: true },
{ key: 'status', title: '流程状态', visible: true },
{ key: 'urgency', title: '紧急程度', visible: true },
{ key: 'procDefName', title: '流程类型', visible: true },
{ key: 'assigneeName', title: '审批人', visible: true },
{ key: 'createTime', title: '提交时间', visible: true },
// { key: 'createTime', title: '作废时间', visible: true },
// { key: 'createTime', title: '完成时间', visible: true },
])
.setRowOperate([
{ title: '查看', onClick: this.onDetail.bind(this) },
{ title: '作废', onClick: this.cancleFlow.bind(this) },
])
}
fetchData(p: {}, q: AnyObject) {
return this.api.getMyApplyAssetFlow({ ...p, ...q })
}
onDetail(model: NzSafeAny) { onLoad(total: number, type: 'assetTotal' | 'deviceTotal') {
this.modal.create({ this[type] = total
nzTitle: '查看任务',
nzContent: comsMap[model.category],
nzWrapClassName: 'modal-lg',
nzWidth: '80vw',
nzFooter: null,
nzData: {
value: model,
preview: true,
},
})
} }
cancleFlow(d: NzSafeAny) { ngOnInit(): void {}
this.modal.confirm({
nzTitle: '作废',
nzContent: '是否要作废该申请?',
nzOnOk: async () => {
const res = await lastValueFrom(
this.api.cancelFlow({
instanceId: d.procInsId,
taskId: d.taskId,
}),
)
this.msg.success('作废成功')
this.table.ref.reload()
},
})
}
} }

40
web-admin-app/src/app/pages/flow/flow-my-finished/flow-my-finished.component.html

@ -1,34 +1,12 @@
<app-page> <app-page>
<div class="flex-1 overflow-hidden"> <div class="flex-1 overflow-hidden bg-white px-4">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm"> <nz-tabset>
<ng-container *appTableForm> <nz-tab [nzTitle]="'资产管理' + assetTotal + ')'">
<app-query-item label="名称" class="w-60"> <app-flow-list-by-type page="finished" type="asset" (onLoad)="onLoad($event, 'assetTotal')" />
<input nz-input placeholder="请输入" formControlName="name" /> </nz-tab>
</app-query-item> <nz-tab [nzTitle]="'设备任务' + deviceTotal + ')'">
</ng-container> <app-flow-list-by-type page="finished" type="device" (onLoad)="onLoad($event, 'deviceTotal')" />
<ng-template #renderColumnTpl let-data let-key="key" let-row="row"> </nz-tab>
@switch (key) { </nz-tabset>
@case ('_assignee') {
<nz-tag> {{ data.userName }} </nz-tag>
}
@case ('procVars') {
{{ data?.title ?? data?.name ?? '-' }}
}
@case ('status') {
<nz-tag nzColor="blue">
{{ FLOW_STATUS.get(data) }}
</nz-tag>
}
@case ('urgency') {
<nz-tag [nzColor]="row.procVars.urgency === 2 ? 'error' : ''">
{{ row.procVars.urgency === 2 ? '紧急' : '普通' }}
</nz-tag>
}
@default {
{{ data }}
}
}
</ng-template>
</app-server-paginated-table>
</div> </div>
</app-page> </app-page>

45
web-admin-app/src/app/pages/flow/flow-my-finished/flow-my-finished.component.ts

@ -12,11 +12,12 @@ import { NzMessageService } from 'ng-zorro-antd/message'
import { FormValidators } from 'app/utils' import { FormValidators } from 'app/utils'
import { comsMap } from '../flow-main/flow-main.component' import { comsMap } from '../flow-main/flow-main.component'
import { FLOW_STATUS, flowIntStatus } from 'app/constants' import { FLOW_STATUS, flowIntStatus } from 'app/constants'
import { FlowListByTypeComponent } from 'app/components/flow-list-by-type/flow-list-by-type.component'
@Component({ @Component({
selector: 'app-flow-my-finished', selector: 'app-flow-my-finished',
standalone: true, standalone: true,
imports: [SharedModule], imports: [SharedModule, FlowListByTypeComponent],
templateUrl: './flow-my-finished.component.html', templateUrl: './flow-my-finished.component.html',
styleUrl: './flow-my-finished.component.less', styleUrl: './flow-my-finished.component.less',
}) })
@ -34,6 +35,14 @@ export class FlowMyFinishedComponent {
userId: new FormControl<NzSafeAny[]>([], [FormValidators.required('请选择')]), userId: new FormControl<NzSafeAny[]>([], [FormValidators.required('请选择')]),
}) })
assetTotal = 0
deviceTotal = 0
onLoad(total: number, type: 'assetTotal' | 'deviceTotal') {
this[type] = total
}
table = new TableOption(this.fetchData.bind(this)) table = new TableOption(this.fetchData.bind(this))
queryForm = new FormGroup({ queryForm = new FormGroup({
@ -43,23 +52,23 @@ export class FlowMyFinishedComponent {
FLOW_STATUS = flowIntStatus FLOW_STATUS = flowIntStatus
ngOnInit(): void { ngOnInit(): void {
this.table // this.table
// .setConfig({ // // .setConfig({
// selectable: true, // // selectable: true,
// rowKey: 'id', // // rowKey: 'id',
// }) // // })
.setColumn([ // .setColumn([
{ key: 'procVars', title: '名称', visible: true }, // { key: 'procVars', title: '名称', visible: true },
// { key: 'status', title: '审批状态', visible: true }, // // { key: 'status', title: '审批状态', visible: true },
{ key: 'status', title: '流程状态', visible: true }, // { key: 'status', title: '流程状态', visible: true },
{ key: 'urgency', title: '紧急程度', visible: true }, // { key: 'urgency', title: '紧急程度', visible: true },
{ key: 'procDefName', title: '流程类型', visible: true }, // { key: 'procDefName', title: '流程类型', visible: true },
{ key: 'assigneeName', title: '审批人', visible: true }, // { key: 'assigneeName', title: '审批人', visible: true },
{ key: 'createTime', title: '提交时间', visible: true }, // { key: 'createTime', title: '提交时间', visible: true },
// { key: 'createTime', title: '作废时间', visible: true }, // // { key: 'createTime', title: '作废时间', visible: true },
// { key: 'createTime', title: '完成时间', visible: true }, // // { key: 'createTime', title: '完成时间', visible: true },
]) // ])
.setRowOperate([{ title: '查看', onClick: this.onDetail.bind(this) }]) // .setRowOperate([{ title: '查看', onClick: this.onDetail.bind(this) }])
} }
fetchData(p: {}, q: AnyObject) { fetchData(p: {}, q: AnyObject) {

17
web-admin-app/src/app/pages/flow/flow-my-todo/flow-my-todo.component.html

@ -1,4 +1,4 @@
<app-page> <!-- <app-page>
<div class="flex-1 overflow-hidden"> <div class="flex-1 overflow-hidden">
<app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm"> <app-server-paginated-table [options]="table" [renderColumn]="renderColumnTpl" [formGroup]="queryForm">
<ng-container *appTableForm> <ng-container *appTableForm>
@ -42,4 +42,17 @@
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</div> </div>
</ng-template> </ng-template> -->
<app-page>
<div class="flex-1 overflow-hidden bg-white px-4">
<nz-tabset>
<nz-tab [nzTitle]="'资产管理' + assetTotal + ')'">
<app-flow-list-by-type page="todo" type="asset" (onLoad)="onLoad($event, 'assetTotal')" />
</nz-tab>
<nz-tab [nzTitle]="'设备任务' + deviceTotal + ')'">
<app-flow-list-by-type page="todo" type="device" (onLoad)="onLoad($event, 'deviceTotal')" />
</nz-tab>
</nz-tabset>
</div>
</app-page>

11
web-admin-app/src/app/pages/flow/flow-my-todo/flow-my-todo.component.ts

@ -12,11 +12,12 @@ import { NzMessageService } from 'ng-zorro-antd/message'
import { FormValidators } from 'app/utils' import { FormValidators } from 'app/utils'
import { comsMap } from '../flow-main/flow-main.component' import { comsMap } from '../flow-main/flow-main.component'
import { FLOW_STATUS, flowIntStatus } from 'app/constants' import { FLOW_STATUS, flowIntStatus } from 'app/constants'
import { FlowListByTypeComponent } from 'app/components/flow-list-by-type/flow-list-by-type.component'
@Component({ @Component({
selector: 'app-flow-my-todo', selector: 'app-flow-my-todo',
standalone: true, standalone: true,
imports: [SharedModule], imports: [SharedModule, FlowListByTypeComponent],
templateUrl: './flow-my-todo.component.html', templateUrl: './flow-my-todo.component.html',
styleUrl: './flow-my-todo.component.less', styleUrl: './flow-my-todo.component.less',
}) })
@ -38,6 +39,14 @@ export class FlowMyTodoComponent {
comment = '' comment = ''
assetTotal = 0
deviceTotal = 0
onLoad(total: number, type: 'assetTotal' | 'deviceTotal') {
this[type] = total
}
table = new TableOption(this.fetchData.bind(this)) table = new TableOption(this.fetchData.bind(this))
queryForm = new FormGroup({ queryForm = new FormGroup({

136
web-admin-app/src/app/pages/flow/flow-report/flow-report.component.html

@ -1 +1,135 @@
<p>flow-report works!</p> <app-page>
<div nz-form nz-row [nzGutter]="12">
<div nz-col nzFlex="400px">
<nz-form-item>
<nz-form-label> 任务类型 </nz-form-label>
<nz-form-control>
<nz-select
nzPlaceHolder="请选择类型"
class="!w-full"
(ngModelChange)="onChange()"
[(ngModel)]="formKeys"
>
<nz-option nzLabel="维保" nzValue="1"></nz-option>
<nz-option nzLabel="巡检" nzValue="2"></nz-option>
<nz-option nzLabel="保养" nzValue="3"></nz-option>
<nz-option nzLabel="盘点" nzValue="4"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzFlex="300px">
<nz-form-item>
<nz-form-label> 类型 </nz-form-label>
<nz-form-control>
<nz-radio-group
nzButtonStyle="solid"
(ngModelChange)="onTypeChange($event)"
[(ngModel)]="reportType"
>
<label nz-radio-button [nzValue]="0">日报</label>
<label nz-radio-button [nzValue]="1">周报</label>
<label nz-radio-button [nzValue]="2">月报</label>
<label nz-radio-button [nzValue]="3">年报</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzFlex="400px">
<nz-form-item>
<nz-form-label> 自定义时段 </nz-form-label>
<nz-form-control>
<nz-range-picker [(ngModel)]="range" (ngModelChange)="onChange()" />
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzFlex="auto">
<div class="flex justify-end">
<nz-space>
<button *nzSpaceItem nz-button>编辑</button>
<button *nzSpaceItem nz-button>复制</button>
<button *nzSpaceItem nz-button>打印</button>
<button *nzSpaceItem nz-button>导出</button>
</nz-space>
</div>
</div>
</div>
<div class="mt-2">
<nz-spin [nzSpinning]="loading">
<div class="report" *ngIf="data">
<div class="report-title">
<div class="report-title-left">
<span>工作报表</span>
</div>
<div class="report-title-right">
日期:{{ data.startDate | date: 'yyyy-MM-dd' }} ~ {{ data.endDate | date: 'yyyy-MM-dd' }}
</div>
</div>
<div class="report-content">
<div class="report-content-row">
<h2>{{ reportTypeText.get(reportType) }}工作概述</h2>
<div class="report-content-row-content">
<div class="report-content-row-left">
<textarea [(ngModel)]="gaishu"></textarea>
</div>
<div class="report-content-row-right">
<ul class="counter">
<li class="info">
<p>{{ data.taskCount ?? 0 }}</p>
<p>累计处理任务数</p>
</li>
<li class="success">
<p>{{ data.taskCount ?? 0 }}</p>
<p>已完成任务数</p>
</li>
<li class="error">
<p>{{ data.taskCount ?? 0 }}</p>
<p>未完成任务数</p>
</li>
</ul>
</div>
</div>
</div>
@for (item of data.taskList; track $index) {
<div class="report-content-row" #itemElement>
<h2>任务执行情况</h2>
<div class="report-content-row-content">
<div class="report-content-row-left">
<p>任务执行数据分析</p>
<div class="pie echart"></div>
<div class="line echart"></div>
</div>
<div class="report-content-row-right">
<textarea></textarea>
</div>
</div>
</div>
}
<div class="report-content-row">
<h2>工作计划</h2>
<div class="report-content-row-content">
<div class="report-content-row-left">
<textarea [(ngModel)]="plan"></textarea>
</div>
</div>
</div>
<div class="report-content-row">
<h2>总结</h2>
<div class="report-content-row-content">
<div class="report-content-row-left">
<textarea [(ngModel)]="zongjie"></textarea>
</div>
</div>
</div>
</div>
<div class="report-footer">
<div>
{{ data.organizationName }}
</div>
<div>{{ today }}</div>
</div>
</div>
</nz-spin>
</div>
</app-page>

127
web-admin-app/src/app/pages/flow/flow-report/flow-report.component.less

@ -0,0 +1,127 @@
.report {
padding: 24px;
background-color: #fff;
h1 {
font-size: 22px;
}
h2 {
font-size: 18x;
}
.report-title {
position: relative;
}
.echart {
height: 205px;
}
.pie {
width: 200px;
}
.line {
flex: 1;
}
.report-title-left {
font-size: 24px;
text-align: center;
}
.report-title-right {
position: absolute;
bottom: 5px;
right: 0;
}
.report-footer {
text-align: right;
&>div {
margin-top: 12px;
}
}
.report-content-row {
min-height: 220px;
textarea {
width: 100%;
min-height: 150px;
border: 1px solid #ccc;
&:focus,
&:active,
&:hover {
outline: none;
border: 1px solid #ccc;
}
}
}
.report-content-row-content {
display: flex;
.report-content-row-left {
display: flex;
flex-basis: 60%;
flex-shrink: 0;
}
.report-content-row-right {
padding-left: 24px;
flex: 1;
}
}
.counter {
display: flex;
justify-content: center;
li {
margin: 0 12px;
padding: 24px 0;
flex: 1;
text-align: center;
border: 1px dashed #027db4;
&.error {
border-color: #d9001b;
p {
color: #d9001b;
}
}
&.success {
border-color: #70b603;
p {
color: #70b603;
}
}
p {
margin-bottom: 6px;
color: #027db4;
&:first-child {
font-size: 20px;
font-weight: bold;
}
}
}
}
}

211
web-admin-app/src/app/pages/flow/flow-report/flow-report.component.ts

@ -1,12 +1,217 @@
import { Component } from '@angular/core'; import { Component, ElementRef, QueryList, ViewChildren } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { ApiService } from 'app/services'
import { SharedModule } from 'app/shared/shared.module'
import { NzSafeAny } from 'ng-zorro-antd/core/types'
import {
startOfDay,
endOfDay,
startOfWeek,
endOfWeek,
startOfMonth,
endOfMonth,
startOfYear,
endOfYear,
format,
} from 'date-fns'
import { finalize } from 'rxjs'
import { init, EChartsType } from 'echarts'
function getDateRange(n: number): { startDate: string; endDate: string } {
const today = new Date()
let startDate: Date
let endDate: Date
switch (n) {
case 0: // Today
startDate = startOfDay(today)
endDate = endOfDay(today)
break
case 1: // This Week
startDate = startOfWeek(today, { weekStartsOn: 1 })
endDate = endOfWeek(today, { weekStartsOn: 1 })
break
case 2: // This Month
startDate = startOfMonth(today)
endDate = endOfMonth(today)
break
case 3: // This Year
startDate = startOfYear(today)
endDate = endOfYear(today)
break
default:
throw new Error('Invalid parameter. Please provide a number between 0 and 3.')
}
return {
startDate: format(startDate, 'yyyy-MM-dd'),
endDate: format(endDate, 'yyyy-MM-dd'),
}
}
@Component({ @Component({
selector: 'app-flow-report', selector: 'app-flow-report',
standalone: true, standalone: true,
imports: [], imports: [SharedModule],
templateUrl: './flow-report.component.html', templateUrl: './flow-report.component.html',
styleUrl: './flow-report.component.less' styleUrl: './flow-report.component.less',
}) })
export class FlowReportComponent { export class FlowReportComponent {
constructor(private api: ApiService) {}
formKeys: NzSafeAny[] = []
reportType: number = 0
range: Date[] = []
data: NzSafeAny
loading = false
today = format(new Date(), 'yyyy年MM月dd日')
reportTypeText = new Map([
[0, '今日'],
[1, '本周'],
[2, '本月'],
[3, '本年'],
])
gaishu = `
1
2
3`
plan = `
1{001}
2
3
`
zongjie = `
001
`
@ViewChildren('itemElement') itemElements!: QueryList<ElementRef>
ngOnInit() {
this.onTypeChange(this.reportType)
}
ngAfterViewInit() {}
onTypeChange(n: number) {
const { startDate, endDate } = getDateRange(n)
this.range = [new Date(startDate), new Date(endDate)]
this.onChange()
}
onChange() {
let startDate = ''
let endDate = ''
this.loading = true
if (this.range.length === 2) {
startDate = format(this.range[0], 'yyyy-MM-dd')
endDate = format(this.range[1], 'yyyy-MM-dd')
}
this.api
.getReportData({
formKeys: this.formKeys,
reportType: this.reportType,
startDate,
endDate,
})
.pipe(
finalize(() => {
this.loading = false
}),
)
.subscribe((res) => {
this.data = res.body
setTimeout(() => {
console.log('this.data', this.itemElements)
this.itemElements.forEach((itemElement, index) => {
console.log(`Element ${index}: `, itemElement.nativeElement)
this.renderPie(itemElement.nativeElement, [])
this.renderLine(itemElement.nativeElement, [])
})
}, 0)
})
}
renderLine(el: HTMLElement, data: NzSafeAny[]) {
el = el.querySelector('.line') as HTMLDivElement
if (el) {
const lineRef = init(el)
const name: string[] = []
const value: number[] = []
data?.forEach((i: NzSafeAny) => {
name.push(i.name)
value.push(i.value)
})
lineRef.setOption({
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
},
yAxis: {
type: 'value',
},
series: [
{
data: [820, 932, 901, 934, 1290, 1330, 1320],
type: 'line',
smooth: true,
},
],
})
}
}
renderPie(el: HTMLElement, data: NzSafeAny[]) {
el = el.querySelector('.pie') as HTMLDivElement
if (el) {
const pieRef = init(el)
let data: NzSafeAny[] = []
const odata: NzSafeAny[] = data ?? []
const total = odata.reduce((a: number, b: any) => a + Math.abs(b.value), 0)
data = odata.map((i: NzSafeAny) => {
const value = Math.abs(i.value)
const percent = ((value / total) * 100).toFixed(2)
return {
...i,
percent,
value,
}
})
pieRef.setOption({
title: {
text: '',
},
series: [
{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
label: {
show: false,
position: 'center',
},
data: [
{ value: 1048, name: 'Search Engine' },
{ value: 735, name: 'Direct' },
],
},
],
})
}
}
} }

8
web-admin-app/src/app/pages/msg/flow-msg/flow-msg.component.ts

@ -43,11 +43,11 @@ export class FlowMsgComponent {
// rowKey: 'id', // rowKey: 'id',
// }) // })
.setColumn([ .setColumn([
{ key: 'logId', title: '序号', visible: true }, // { key: 'logId', title: '序号', visible: true },
{ key: 'createTime', title: '时间', visible: true }, { key: 'createTime', title: '时间', visible: true },
{ key: 'logType', title: '类型', visible: true }, // { key: 'logType', title: '类型', visible: true },
{ key: 'logContent', title: '内容', visible: true }, { key: 'content', title: '内容', visible: true },
{ key: 'logContent', title: '待我处理', visible: true }, // { key: 'logContent', title: '待我处理', visible: true },
]) ])
} }

56
web-admin-app/src/app/services/api.service.ts

@ -55,6 +55,13 @@ export class ApiService {
uploadLicenseInfo(formData: FormData) { uploadLicenseInfo(formData: FormData) {
return this.http.post<JwResponse>('/api/license/update', formData) return this.http.post<JwResponse>('/api/license/update', formData)
} }
importFlowFormByExcel(formData: FormData) {
return this.http.post<JwResponse>('/api/v2/sysFormTemp/importByExcel', formData)
}
uploadFormTpl(formData: FormData) {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/upload`, formData)
}
login(data: {}) { login(data: {}) {
return this.http.post<JwResponse>('/api/oauth/login', data).pipe( return this.http.post<JwResponse>('/api/oauth/login', data).pipe(
@ -194,8 +201,9 @@ export class ApiService {
responseType: 'blob' as 'json', responseType: 'blob' as 'json',
}) })
} }
downloadAssetTemplate() { downloadAssetTemplate(categoryId?: string) {
return this.http.post<JwResponse>(`/api/eamAsset/importTemplate`, null, { return this.http.post<JwResponse>(`/api/eamAsset/importTemplate`, null, {
params: categoryId ? { categoryId } : {},
observe: 'response', observe: 'response',
responseType: 'blob' as 'json', responseType: 'blob' as 'json',
}) })
@ -617,6 +625,12 @@ export class ApiService {
responseType: 'blob' as 'json', responseType: 'blob' as 'json',
}) })
} }
downloadTpl() {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/formTemplate`, null, {
observe: 'response',
responseType: 'blob' as 'json',
})
}
addFlowForm(data: NzSafeAny) { addFlowForm(data: NzSafeAny) {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/add`, data) return this.http.post<JwResponse>(`/api/v2/sysFormTemp/add`, data)
@ -624,6 +638,9 @@ export class ApiService {
updateFlowForm(data: NzSafeAny) { updateFlowForm(data: NzSafeAny) {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/update`, data) return this.http.post<JwResponse>(`/api/v2/sysFormTemp/update`, data)
} }
confirmFlowForm(data: NzSafeAny) {
return this.http.post<JwResponse>(` /api/v2/sysFormTemp/confirm`, data)
}
deleteFlowForm(idList: NzSafeAny) { deleteFlowForm(idList: NzSafeAny) {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/delete`, idList) return this.http.post<JwResponse>(`/api/v2/sysFormTemp/delete`, idList)
@ -709,4 +726,41 @@ export class ApiService {
flowFormUpdate(d: {}) { flowFormUpdate(d: {}) {
return this.http.post<JwResponse>('/api/flForm/update', d) return this.http.post<JwResponse>('/api/flForm/update', d)
} }
deviceLog(d: {}) {
return this.http.post<JwResponse>('/api/eamAssetLog/list/device', d)
}
assetFlowRecord(d: {}) {
return this.http.post<JwResponse>('/api/flowable/task/flowRecord', d)
}
operationalStatistics(d: {}) {
return this.http.get<JwResponse>('/api/eamAsset/operationalStatistics', {
params: d,
})
}
downloadTaskReport(id: string) {
return this.http.post<JwResponse>(`/api/v2/flowable/device/export/${id}`, null, {
observe: 'response',
responseType: 'blob' as 'json',
})
}
exportFlowForm(idList: string[]) {
return this.http.post<JwResponse>(`/api/v2/sysFormTemp/exportExcel`, idList, {
observe: 'response',
responseType: 'blob' as 'json',
})
}
onOffRecord(id: string) {
return this.http.post<JwResponse>(`/api/v2/device/record`, null, {
params: { id },
})
}
getReportData(data: {}) {
return this.http.post<JwResponse>(`/api/flFormItem/workReport`, data)
}
saveReport(content: string) {
return this.http.post<JwResponse>(`api/flFormItem/h5str2word`, null, { params: { content } })
}
bigScreen() {
return this.http.post<JwResponse>(`/api/home/bigScreen`, null)
}
} }

2
web-admin-app/src/app/shared/components/header/header.component.html

@ -94,7 +94,7 @@
<span nz-icon nzType="poweroff" nzTheme="outline"></span> <span nz-icon nzType="poweroff" nzTheme="outline"></span>
</a> </a>
<a nz-tooltip="大屏" nz-button nzType="text" routerLink="/dashboard" *nzSpaceItem> <a nz-tooltip="大屏" nz-button nzType="text" routerLink="/data-vis" target="_blank" *nzSpaceItem>
<span nz-icon nzType="fund"></span> <span nz-icon nzType="fund"></span>
</a> </a>
</nz-space> </nz-space>

24
web-admin-app/src/app/shared/components/server-paginated-table/server-paginated-table.component.html

@ -90,7 +90,7 @@
} }
} }
@if (options.operate.length > 0) { @if (options.operate.length > 0) {
<th nzWidth="70px" nzRight>操作</th> <th [nzWidth]="'150px'" nzRight>操作</th>
} }
@if (options.columns.length > 3) { @if (options.columns.length > 3) {
<th nzWidth="60px" nzRight> <th nzWidth="60px" nzRight>
@ -142,26 +142,24 @@
@if (options.operate.length > 0) { @if (options.operate.length > 0) {
<td nzRight> <td nzRight>
<button nz-button nzType="text" nz-dropdown [nzDropdownMenu]="menu"> <nz-space class="flex-wrap">
<span nz-icon nzType="more" nzTheme="outline" class="more-icon"></span> <ng-template #spaceSplit>
</button> <nz-divider nzType="vertical"></nz-divider>
<nz-dropdown-menu #menu="nzDropdownMenu"> </ng-template>
<ul nz-menu class="operate-list">
@for (operate of options.operate; track operate.title) { @for (operate of options.operate; track operate.title) {
@if (operate?.visible(data)) { @if (operate?.visible(data)) {
<li <a
nz-menu-item class="whitespace-nowrap"
*nzSpaceItem
(click)="onOperateClick(operate, data)" (click)="onOperateClick(operate, data)"
[routerLink]="operate.link" [routerLink]="operate.link"
[nzDanger]="operate.danger"
[nzDisabled]="operate.disabled"
> >
{{ operate.title }} {{ operate.title }}
</li> </a>
} }
} }
</ul> </nz-space>
</nz-dropdown-menu>
</td> </td>
} }
@if (options.columns.length > 3) { @if (options.columns.length > 3) {

1
web-admin-app/src/app/utils/index.ts

@ -98,6 +98,7 @@ export class Utils {
} }
static downLoadFile(response: HttpResponse<Object>, type: string, ext: string = 'pdf') { static downLoadFile(response: HttpResponse<Object>, type: string, ext: string = 'pdf') {
const fileNameFromHeader = response.headers.get('Content-Disposition') const fileNameFromHeader = response.headers.get('Content-Disposition')
console.log('fileNameFromHeader', fileNameFromHeader)
let fileName = '' let fileName = ''
if (fileNameFromHeader) { if (fileNameFromHeader) {
fileName = fileNameFromHeader.trim().split('=')[1].replace(/"/g, '') fileName = fileNameFromHeader.trim().split('=')[1].replace(/"/g, '')

62
web-admin-app/src/assets/data-vis/fonts/icomoon.css

@ -0,0 +1,62 @@
@font-face {
font-family: 'icomoon';
src: url('icomoon.eot');
src: url('icomoon.eot#iefix') format('embedded-opentype'),
url('icomoon.ttf') format('truetype'),
url('icomoon.woff') format('woff'),
url('icomoon.svg#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-dot:before {
content: "\e900";
}
.icon-cup1:before {
content: "\e901";
}
.icon-cup2:before {
content: "\e902";
}
.icon-cup3:before {
content: "\e903";
}
.icon-clock:before {
content: "\e904";
}
.icon-down:before {
content: "\e905";
}
.icon-cube:before {
content: "\e906";
}
.icon-plane:before {
content: "\e907";
}
.icon-train:before {
content: "\e908";
}
.icon-bus:before {
content: "\e909";
}
.icon-bag:before {
content: "\e90a";
}
.icon-up:before {
content: "\e90b";
}

BIN
web-admin-app/src/assets/data-vis/fonts/icomoon.eot

Binary file not shown.

22
web-admin-app/src/assets/data-vis/fonts/icomoon.svg

@ -0,0 +1,22 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="857.6" descent="-166.4" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="dot" d="M510-44.4v0c-109.2 0-198.8 89.2-198.8 198.8v395.2c0 109.2 89.2 198.8 198.8 198.8v0c109.2 0 198.8-89.2 198.8-198.8v-395.6c0-109.2-89.6-198.4-198.8-198.4z" />
<glyph unicode="&#xe901;" glyph-name="cup1" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM199.2 424c-68.8 0-125.2 56-125.2 124.8s1.6 62.4 124.8 62.4l0.4-187.2zM578.8 296h-94.4v202.4c0 29.2-0.8 46.8-2 52.8-1.6 6-5.2 10.4-11.6 13.6s-20.4 4.4-42 4.4h-9.2v44c45.6 10 80 30 103.6 60.4h55.6v-377.6zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe902;" glyph-name="cup2" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM74 548.8c0 68.8 1.6 62.4 124.8 62.4l0.4-187.2c-68.8 0-125.2 56-125.2 124.8zM618 359.2v-63.6h-205.2v53.2c60.8 99.6 96.8 161.2 108.4 184.8s17.2 42 17.2 55.2c0 10-1.6 17.6-5.2 22.8s-8.8 7.6-16 7.6-12.4-2.8-16-8.4c-3.6-5.6-5.2-16.4-5.2-33.2v-35.6h-83.6v14c0 20.8 1.2 37.6 3.2 49.6s7.6 24 16 35.6 19.6 20.4 33.2 26.4 29.6 9.2 48.8 9.2c37.2 0 65.2-9.2 84-27.6 19.2-18.4 28.4-41.6 28.4-70 0-21.6-5.2-44-16-68s-42.4-74.4-94.4-152h102.4zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe903;" glyph-name="cup3" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM74 548.8c0 68.8 1.6 62.4 124.8 62.4l0.4-187.2c-68.8 0-125.2 56-125.2 124.8zM620 337.2c-6.8-16.4-18.4-28.8-35.2-37.6-16.8-8.4-38-12.8-64-12.8-29.6 0-52.8 4.8-69.6 14.8s-28 22-33.2 36.4-8 39.2-8 74.8v29.6h93.2v-60.8c0-16 0.8-26.4 2.8-30.8s6-6.4 12.8-6.4c7.2 0 12 2.8 14.4 8.4s3.6 20 3.6 43.6v26c0 14.4-1.6 24.8-4.8 31.6s-8 10.8-14.4 13.2c-6.4 2-18.4 3.2-36.8 3.6v53.6c22 0 36 0.8 41.2 2.4s9.2 5.2 11.2 11.2c2.4 5.6 3.6 14.8 3.6 26.8v20.8c0 13.2-1.2 21.6-4 26-2.8 4-6.8 6.4-12.4 6.4-6.4 0-10.8-2-13.2-6.4s-3.6-13.6-3.6-28v-30.8h-93.2v32c0 35.6 8 60 24.4 72.4s42.4 18.8 78 18.8c44.4 0 74.8-8.8 90.4-26 16-17.2 23.6-41.6 23.6-72.4 0-20.8-2.8-36-8.4-45.2s-15.6-18-30-25.6c14-4.8 24.8-12.4 31.6-23.6 6.8-10.8 10.4-36.4 10.4-76.4-0.4-30.4-3.6-53.2-10.4-69.6zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe904;" glyph-name="clock" d="M502.898 788.907c-245.76 0-443.733-197.973-443.733-443.733s200.249-443.733 443.733-443.733c245.76 0 443.733 197.973 443.733 443.733s-200.249 443.733-443.733 443.733zM791.893 210.916c-6.827-13.653-18.204-20.48-31.858-20.48-4.551 0-9.102 0-13.653 2.276l-261.689 118.329c-11.378 4.551-20.48 18.204-20.48 31.858v245.76c0 18.204 15.929 34.133 34.133 34.133s34.133-15.929 34.133-34.133v-225.28l241.209-109.227c18.204-6.827 25.031-25.031 18.204-43.236z" />
<glyph unicode="&#xe905;" glyph-name="down" d="M174.56 163.976l288.192-242.864c26.864-22.384 70.512-22.384 97.368 0l288.192 242.864c43.088 36.936 12.312 99.608-48.128 99.608h-110.24v448.8c0 49.248-40.288 89.536-89.536 89.536h-179.072c-49.248 0-89.536-40.288-89.536-89.536v-448.8h-109.12c-61-0.56-91.216-63.232-48.128-99.608z" />
<glyph unicode="&#xe906;" glyph-name="cube" d="M930.8 632.4c-2 1.2-4.4 2.4-6.4 3.2l-378 219.6c-21.2 12-47.2 12-68.4 0l-384.8-222.8c-21.2-12.4-34.4-34.8-34-59.2l1.2-442.4c0-24.4 12.8-47.2 34-59.2l378.4-219.6c1.6-1.2 3.2-2 4.8-3.2 3.6-2 7.6-3.6 11.6-5.2 0 0 0.4 0 0.4 0 0.4 0 1.2-0.4 1.6-0.4 6.4-2 12.8-3.2 19.6-3.2 0 0 0 0 0 0 0.4 0 0.8 0 0.8 0v0c12 0 23.6 2.8 34 8.8l385.2 222.4c21.2 12.4 34.4 34.8 34 59.2v442.8c0.4 24.4-12.8 47.2-34 59.2zM512.4 716.4l246.4-143.2-248.4-143.2-61.6 36-184.8 107.2 248.4 143.2zM829.2 169.6l-249.2-143.2v285.2l249.2 143.2v-285.2z" />
<glyph unicode="&#xe907;" glyph-name="plane" d="M937.2 777.6c-22 18.4-53.2 20.8-93.6 7.6s-76.8-36-108.8-68.4l-101.2-101.2-421.6 100.8c-7.2 1.6-13.2 0.8-19.2-5.2l-80.8-81.2c-4.4-4.4-6.4-10-5.6-17.2 1.2-6.8 5.2-12 10.8-15.2l321.6-176.8-164-164-122.8 33.6c-0.8 0.8-2.4 0.8-5.2 0.8-5.6 0-10.8-2-14.4-5.6l-61.2-61.6c-4.8-4.8-6.4-10-5.6-16 0.8-6.8 4-11.2 8.4-14.4l159.6-119.6 119.6-159.6c3.6-4.8 8.8-7.6 15.2-8.4h1.2c5.6 0 10.8 2 14.4 5.6l60.8 60.8c5.6 5.6 6.8 12.8 5.2 19.6l-33.6 122.8 164 164 176.8-321.6c3.2-5.6 6.8-8.8 13.2-10 1.2-0.8 2.4-0.8 4.4-0.8 5.2 0 8.8 1.2 12 4l81.2 60.8c7.2 5.6 9.6 12.8 7.6 20.8l-101.2 440.8 102 102c32.4 32.4 55.2 68.4 68.4 108.8 13.2 40.8 10.8 72-7.6 94z" />
<glyph unicode="&#xe908;" glyph-name="train" d="M576 237.2v-256h277.2c35.2 0 64-28.8 64-64s-28.8-64-64-64h-682.4c-35.2 0-64 28.8-64 64s28.8 64 64 64h277.2v256h-64c-35.2 0-64 28.8-64 64s28.8 64 64 64h256c35.2 0 64-28.8 64-64s-28.8-64-64-64h-64zM427.6 784c7.2 46.4 50.8 78.4 97.6 71.2 36.8-5.6 65.2-34.4 71.2-71.2 88-16.8 169.2-59.6 232.4-123.2 174.8-174.8 174.8-458.4 0-633.6-24.8-24.8-65.6-24.8-90.4 0s-24.8 65.6 0 90.4v0c124.8 124.8 124.8 327.6 0 452.4s-327.6 124.8-452.4 0-124.8-327.6 0-452.4c0 0 0 0 0 0 24.8-24.8 24.8-65.6 0-90.4s-65.6-24.8-90.4 0c-175.2 174.8-175.2 458.4-0.4 633.6 63.2 63.2 144.4 106.4 232.4 123.2v0z" />
<glyph unicode="&#xe909;" glyph-name="bus" d="M203.902 20.316c-76.37 13.852-131.4 80.862-131.026 158.354l3.744 479.182c0.748 87.6 72.252 158.354 159.852 158.354h551.058c87.6 0 159.104-70.754 159.852-158.354l3.744-479.182c0.374-69.631-43.8-131.4-110.062-153.114l31.072-53.534c16.846-28.452 7.112-65.138-21.338-81.984s-65.138-7.112-81.984 21.338c0 0.374-0.374 0.374-0.374 0.748l-59.898 103.698c-0.374 0.748-0.748 1.124-1.124 1.872h-366.874c-0.374-0.748-0.748-1.498-1.124-1.872l-59.898-103.698c-16.472-28.826-52.784-38.934-81.61-22.462s-38.934 52.784-22.462 81.61c0 0.374 0.374 0.374 0.374 0.748l28.077 48.292zM452.102 656.354h-279.648c-10.856 0-19.842-8.984-19.842-19.842v0-239.59c0-10.856 8.984-19.842 19.842-19.842v0h279.648c10.856 0 19.842 8.984 19.842 19.842v0 239.59c0 10.856-8.984 19.842-19.842 19.842 0 0 0 0 0 0zM851.544 656.354h-279.648c-10.856 0-19.842-8.984-19.842-19.842v0-239.59c0-10.856 8.984-19.842 19.842-19.842 0 0 0 0 0 0h279.648c10.856 0 19.842 8.984 19.842 19.842v0 239.59c0 10.856-8.984 19.842-19.842 19.842 0 0 0 0 0 0zM731.75 296.97c-32.944 0-59.898-26.954-59.898-59.898s26.954-59.898 59.898-59.898 59.898 26.954 59.898 59.898-26.954 59.898-59.898 59.898zM292.25 296.97c-32.944 0-59.898-26.954-59.898-59.898s26.954-59.898 59.898-59.898 59.898 26.954 59.898 59.898-26.58 59.898-59.898 59.898z" />
<glyph unicode="&#xe90a;" glyph-name="bag" d="M816.928 591.86h-87.137c0 120.346-97.563 218.028-217.791 218.028s-217.791-97.681-217.791-218.028h-87.137c-47.902 0-86.659-39.26-86.659-87.231l-0.478-523.315c0.109-48.101 39.043-87.067 87.119-87.231h609.844c48.090 0.163 87.028 39.13 87.137 87.222v523.299c-0.109 48.101-39.043 87.067-87.119 87.231h-0.017zM512 722.682c0.034 0 0.079 0 0.119 0 72.12 0 130.585-58.465 130.585-130.585 0-0.084 0-0.168 0-0.252v0.011h-261.406c0 0.069 0 0.153 0 0.237 0 72.12 58.465 130.585 130.585 130.585 0.040 0 0.084 0 0.124 0h-0.006zM512 286.577c-120.228 0-217.791 97.681-217.791 218.077h87.112c0-0.079 0-0.17 0-0.261 0-72.12 58.465-130.585 130.585-130.585 0.040 0 0.084 0 0.124 0h-0.006c0.022 0 0.048 0 0.074 0 72.12 0 130.585 58.465 130.585 130.585 0 0.084 0 0.168 0 0.252v-0.011h87.137c0-120.371-97.588-218.052-217.816-218.052z" />
<glyph unicode="&#xe90b;" glyph-name="up" d="M848.728 541.856l-288.368 242.696c-26.864 22.392-70.752 22.392-97.616 0l-288.368-242.696c-42.984-36.72-12.536-99.408 48.36-99.408h110.152c0 0 0-0.896 0-0.896v-447.776c0-49.256 40.296-89.552 89.552-89.552h179.112c49.256 0 89.552 40.296 89.552 89.552v447.776c0 0 0 0.896 0 0.896h109.256c60.896 0 91.344 62.688 48.36 99.408z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
web-admin-app/src/assets/data-vis/fonts/icomoon.ttf

Binary file not shown.

BIN
web-admin-app/src/assets/data-vis/fonts/icomoon.woff

Binary file not shown.

BIN
web-admin-app/src/assets/data-vis/images/border.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
web-admin-app/src/assets/data-vis/images/line.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

BIN
web-admin-app/src/assets/data-vis/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
web-admin-app/src/assets/data-vis/images/rect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web-admin-app/src/assets/data-vis/images/更多内容关注公众号.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

62
web-admin-app/src/assets/images/data-vis/fonts/icomoon.css

@ -0,0 +1,62 @@
@font-face {
font-family: 'icomoon';
src: url('icomoon.eot');
src: url('icomoon.eot#iefix') format('embedded-opentype'),
url('icomoon.ttf') format('truetype'),
url('icomoon.woff') format('woff'),
url('icomoon.svg#icomoon') format('svg');
font-weight: normal;
font-style: normal;
}
[class^="icon-"], [class*=" icon-"] {
/* use !important to prevent issues with browser extensions that change fonts */
font-family: 'icomoon' !important;
speak: none;
font-style: normal;
font-weight: normal;
font-variant: normal;
text-transform: none;
line-height: 1;
/* Better Font Rendering =========== */
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-dot:before {
content: "\e900";
}
.icon-cup1:before {
content: "\e901";
}
.icon-cup2:before {
content: "\e902";
}
.icon-cup3:before {
content: "\e903";
}
.icon-clock:before {
content: "\e904";
}
.icon-down:before {
content: "\e905";
}
.icon-cube:before {
content: "\e906";
}
.icon-plane:before {
content: "\e907";
}
.icon-train:before {
content: "\e908";
}
.icon-bus:before {
content: "\e909";
}
.icon-bag:before {
content: "\e90a";
}
.icon-up:before {
content: "\e90b";
}

BIN
web-admin-app/src/assets/images/data-vis/fonts/icomoon.eot

Binary file not shown.

22
web-admin-app/src/assets/images/data-vis/fonts/icomoon.svg

@ -0,0 +1,22 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg">
<metadata>Generated by IcoMoon</metadata>
<defs>
<font id="icomoon" horiz-adv-x="1024">
<font-face units-per-em="1024" ascent="857.6" descent="-166.4" />
<missing-glyph horiz-adv-x="1024" />
<glyph unicode="&#x20;" horiz-adv-x="512" d="" />
<glyph unicode="&#xe900;" glyph-name="dot" d="M510-44.4v0c-109.2 0-198.8 89.2-198.8 198.8v395.2c0 109.2 89.2 198.8 198.8 198.8v0c109.2 0 198.8-89.2 198.8-198.8v-395.6c0-109.2-89.6-198.4-198.8-198.4z" />
<glyph unicode="&#xe901;" glyph-name="cup1" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM199.2 424c-68.8 0-125.2 56-125.2 124.8s1.6 62.4 124.8 62.4l0.4-187.2zM578.8 296h-94.4v202.4c0 29.2-0.8 46.8-2 52.8-1.6 6-5.2 10.4-11.6 13.6s-20.4 4.4-42 4.4h-9.2v44c45.6 10 80 30 103.6 60.4h55.6v-377.6zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe902;" glyph-name="cup2" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM74 548.8c0 68.8 1.6 62.4 124.8 62.4l0.4-187.2c-68.8 0-125.2 56-125.2 124.8zM618 359.2v-63.6h-205.2v53.2c60.8 99.6 96.8 161.2 108.4 184.8s17.2 42 17.2 55.2c0 10-1.6 17.6-5.2 22.8s-8.8 7.6-16 7.6-12.4-2.8-16-8.4c-3.6-5.6-5.2-16.4-5.2-33.2v-35.6h-83.6v14c0 20.8 1.2 37.6 3.2 49.6s7.6 24 16 35.6 19.6 20.4 33.2 26.4 29.6 9.2 48.8 9.2c37.2 0 65.2-9.2 84-27.6 19.2-18.4 28.4-41.6 28.4-70 0-21.6-5.2-44-16-68s-42.4-74.4-94.4-152h102.4zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe903;" glyph-name="cup3" d="M824.4 674c0 68.8-56.4 124.8-124.8 124.8h-375.6c-68.8 0-124.8-56.4-124.8-124.8-214 0-187.6 0-187.6-124.8 0-103.2 84.4-187.6 187.6-187.6 8 0 14 1.6 22 1.6 42-122 140.8-232.8 259.2-250v-145.2h-93.6c-17.2 0-31.2-14-31.2-31.2s14-31.2 31.2-31.2h250c17.2 0 31.2 14 31.2 31.2s-14 31.2-31.2 31.2h-93.6v145.2c118.8 17.2 217.2 126.4 259.2 250 6.4 0 14-1.6 22-1.6 103.2 0 187.6 84.4 187.6 187.6-2 124.8 24.8 124.8-187.6 124.8zM74 548.8c0 68.8 1.6 62.4 124.8 62.4l0.4-187.2c-68.8 0-125.2 56-125.2 124.8zM620 337.2c-6.8-16.4-18.4-28.8-35.2-37.6-16.8-8.4-38-12.8-64-12.8-29.6 0-52.8 4.8-69.6 14.8s-28 22-33.2 36.4-8 39.2-8 74.8v29.6h93.2v-60.8c0-16 0.8-26.4 2.8-30.8s6-6.4 12.8-6.4c7.2 0 12 2.8 14.4 8.4s3.6 20 3.6 43.6v26c0 14.4-1.6 24.8-4.8 31.6s-8 10.8-14.4 13.2c-6.4 2-18.4 3.2-36.8 3.6v53.6c22 0 36 0.8 41.2 2.4s9.2 5.2 11.2 11.2c2.4 5.6 3.6 14.8 3.6 26.8v20.8c0 13.2-1.2 21.6-4 26-2.8 4-6.8 6.4-12.4 6.4-6.4 0-10.8-2-13.2-6.4s-3.6-13.6-3.6-28v-30.8h-93.2v32c0 35.6 8 60 24.4 72.4s42.4 18.8 78 18.8c44.4 0 74.8-8.8 90.4-26 16-17.2 23.6-41.6 23.6-72.4 0-20.8-2.8-36-8.4-45.2s-15.6-18-30-25.6c14-4.8 24.8-12.4 31.6-23.6 6.8-10.8 10.4-36.4 10.4-76.4-0.4-30.4-3.6-53.2-10.4-69.6zM824.4 424v187.2c123.6 0 124.8 6.4 124.8-62.4-1.6-68.8-56.4-124.8-124.8-124.8z" />
<glyph unicode="&#xe904;" glyph-name="clock" d="M502.898 788.907c-245.76 0-443.733-197.973-443.733-443.733s200.249-443.733 443.733-443.733c245.76 0 443.733 197.973 443.733 443.733s-200.249 443.733-443.733 443.733zM791.893 210.916c-6.827-13.653-18.204-20.48-31.858-20.48-4.551 0-9.102 0-13.653 2.276l-261.689 118.329c-11.378 4.551-20.48 18.204-20.48 31.858v245.76c0 18.204 15.929 34.133 34.133 34.133s34.133-15.929 34.133-34.133v-225.28l241.209-109.227c18.204-6.827 25.031-25.031 18.204-43.236z" />
<glyph unicode="&#xe905;" glyph-name="down" d="M174.56 163.976l288.192-242.864c26.864-22.384 70.512-22.384 97.368 0l288.192 242.864c43.088 36.936 12.312 99.608-48.128 99.608h-110.24v448.8c0 49.248-40.288 89.536-89.536 89.536h-179.072c-49.248 0-89.536-40.288-89.536-89.536v-448.8h-109.12c-61-0.56-91.216-63.232-48.128-99.608z" />
<glyph unicode="&#xe906;" glyph-name="cube" d="M930.8 632.4c-2 1.2-4.4 2.4-6.4 3.2l-378 219.6c-21.2 12-47.2 12-68.4 0l-384.8-222.8c-21.2-12.4-34.4-34.8-34-59.2l1.2-442.4c0-24.4 12.8-47.2 34-59.2l378.4-219.6c1.6-1.2 3.2-2 4.8-3.2 3.6-2 7.6-3.6 11.6-5.2 0 0 0.4 0 0.4 0 0.4 0 1.2-0.4 1.6-0.4 6.4-2 12.8-3.2 19.6-3.2 0 0 0 0 0 0 0.4 0 0.8 0 0.8 0v0c12 0 23.6 2.8 34 8.8l385.2 222.4c21.2 12.4 34.4 34.8 34 59.2v442.8c0.4 24.4-12.8 47.2-34 59.2zM512.4 716.4l246.4-143.2-248.4-143.2-61.6 36-184.8 107.2 248.4 143.2zM829.2 169.6l-249.2-143.2v285.2l249.2 143.2v-285.2z" />
<glyph unicode="&#xe907;" glyph-name="plane" d="M937.2 777.6c-22 18.4-53.2 20.8-93.6 7.6s-76.8-36-108.8-68.4l-101.2-101.2-421.6 100.8c-7.2 1.6-13.2 0.8-19.2-5.2l-80.8-81.2c-4.4-4.4-6.4-10-5.6-17.2 1.2-6.8 5.2-12 10.8-15.2l321.6-176.8-164-164-122.8 33.6c-0.8 0.8-2.4 0.8-5.2 0.8-5.6 0-10.8-2-14.4-5.6l-61.2-61.6c-4.8-4.8-6.4-10-5.6-16 0.8-6.8 4-11.2 8.4-14.4l159.6-119.6 119.6-159.6c3.6-4.8 8.8-7.6 15.2-8.4h1.2c5.6 0 10.8 2 14.4 5.6l60.8 60.8c5.6 5.6 6.8 12.8 5.2 19.6l-33.6 122.8 164 164 176.8-321.6c3.2-5.6 6.8-8.8 13.2-10 1.2-0.8 2.4-0.8 4.4-0.8 5.2 0 8.8 1.2 12 4l81.2 60.8c7.2 5.6 9.6 12.8 7.6 20.8l-101.2 440.8 102 102c32.4 32.4 55.2 68.4 68.4 108.8 13.2 40.8 10.8 72-7.6 94z" />
<glyph unicode="&#xe908;" glyph-name="train" d="M576 237.2v-256h277.2c35.2 0 64-28.8 64-64s-28.8-64-64-64h-682.4c-35.2 0-64 28.8-64 64s28.8 64 64 64h277.2v256h-64c-35.2 0-64 28.8-64 64s28.8 64 64 64h256c35.2 0 64-28.8 64-64s-28.8-64-64-64h-64zM427.6 784c7.2 46.4 50.8 78.4 97.6 71.2 36.8-5.6 65.2-34.4 71.2-71.2 88-16.8 169.2-59.6 232.4-123.2 174.8-174.8 174.8-458.4 0-633.6-24.8-24.8-65.6-24.8-90.4 0s-24.8 65.6 0 90.4v0c124.8 124.8 124.8 327.6 0 452.4s-327.6 124.8-452.4 0-124.8-327.6 0-452.4c0 0 0 0 0 0 24.8-24.8 24.8-65.6 0-90.4s-65.6-24.8-90.4 0c-175.2 174.8-175.2 458.4-0.4 633.6 63.2 63.2 144.4 106.4 232.4 123.2v0z" />
<glyph unicode="&#xe909;" glyph-name="bus" d="M203.902 20.316c-76.37 13.852-131.4 80.862-131.026 158.354l3.744 479.182c0.748 87.6 72.252 158.354 159.852 158.354h551.058c87.6 0 159.104-70.754 159.852-158.354l3.744-479.182c0.374-69.631-43.8-131.4-110.062-153.114l31.072-53.534c16.846-28.452 7.112-65.138-21.338-81.984s-65.138-7.112-81.984 21.338c0 0.374-0.374 0.374-0.374 0.748l-59.898 103.698c-0.374 0.748-0.748 1.124-1.124 1.872h-366.874c-0.374-0.748-0.748-1.498-1.124-1.872l-59.898-103.698c-16.472-28.826-52.784-38.934-81.61-22.462s-38.934 52.784-22.462 81.61c0 0.374 0.374 0.374 0.374 0.748l28.077 48.292zM452.102 656.354h-279.648c-10.856 0-19.842-8.984-19.842-19.842v0-239.59c0-10.856 8.984-19.842 19.842-19.842v0h279.648c10.856 0 19.842 8.984 19.842 19.842v0 239.59c0 10.856-8.984 19.842-19.842 19.842 0 0 0 0 0 0zM851.544 656.354h-279.648c-10.856 0-19.842-8.984-19.842-19.842v0-239.59c0-10.856 8.984-19.842 19.842-19.842 0 0 0 0 0 0h279.648c10.856 0 19.842 8.984 19.842 19.842v0 239.59c0 10.856-8.984 19.842-19.842 19.842 0 0 0 0 0 0zM731.75 296.97c-32.944 0-59.898-26.954-59.898-59.898s26.954-59.898 59.898-59.898 59.898 26.954 59.898 59.898-26.954 59.898-59.898 59.898zM292.25 296.97c-32.944 0-59.898-26.954-59.898-59.898s26.954-59.898 59.898-59.898 59.898 26.954 59.898 59.898-26.58 59.898-59.898 59.898z" />
<glyph unicode="&#xe90a;" glyph-name="bag" d="M816.928 591.86h-87.137c0 120.346-97.563 218.028-217.791 218.028s-217.791-97.681-217.791-218.028h-87.137c-47.902 0-86.659-39.26-86.659-87.231l-0.478-523.315c0.109-48.101 39.043-87.067 87.119-87.231h609.844c48.090 0.163 87.028 39.13 87.137 87.222v523.299c-0.109 48.101-39.043 87.067-87.119 87.231h-0.017zM512 722.682c0.034 0 0.079 0 0.119 0 72.12 0 130.585-58.465 130.585-130.585 0-0.084 0-0.168 0-0.252v0.011h-261.406c0 0.069 0 0.153 0 0.237 0 72.12 58.465 130.585 130.585 130.585 0.040 0 0.084 0 0.124 0h-0.006zM512 286.577c-120.228 0-217.791 97.681-217.791 218.077h87.112c0-0.079 0-0.17 0-0.261 0-72.12 58.465-130.585 130.585-130.585 0.040 0 0.084 0 0.124 0h-0.006c0.022 0 0.048 0 0.074 0 72.12 0 130.585 58.465 130.585 130.585 0 0.084 0 0.168 0 0.252v-0.011h87.137c0-120.371-97.588-218.052-217.816-218.052z" />
<glyph unicode="&#xe90b;" glyph-name="up" d="M848.728 541.856l-288.368 242.696c-26.864 22.392-70.752 22.392-97.616 0l-288.368-242.696c-42.984-36.72-12.536-99.408 48.36-99.408h110.152c0 0 0-0.896 0-0.896v-447.776c0-49.256 40.296-89.552 89.552-89.552h179.112c49.256 0 89.552 40.296 89.552 89.552v447.776c0 0 0 0.896 0 0.896h109.256c60.896 0 91.344 62.688 48.36 99.408z" />
</font></defs></svg>

After

Width:  |  Height:  |  Size: 8.7 KiB

BIN
web-admin-app/src/assets/images/data-vis/fonts/icomoon.ttf

Binary file not shown.

BIN
web-admin-app/src/assets/images/data-vis/fonts/icomoon.woff

Binary file not shown.

BIN
web-admin-app/src/assets/images/data-vis/images/border.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

BIN
web-admin-app/src/assets/images/data-vis/images/line.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 B

BIN
web-admin-app/src/assets/images/data-vis/images/logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

BIN
web-admin-app/src/assets/images/data-vis/images/rect.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
web-admin-app/src/assets/images/data-vis/images/更多内容关注公众号.gif

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 KiB

Loading…
Cancel
Save