@ -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,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: '设备巡检任务' }) |
||||
|
} |
||||
|
} |
||||
@ -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,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: '设备保养任务' }) |
||||
|
} |
||||
|
} |
||||
@ -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,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, |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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,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 |
||||
|
} |
||||
|
} |
||||
@ -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,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: '设备盘点任务' }) |
||||
|
} |
||||
|
} |
||||
@ -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,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() |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
@ -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); |
||||
|
} |
||||
|
} |
||||
@ -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, |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
@ -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,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 |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -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> |
||||
|
|||||
@ -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> |
||||
|
|||||
@ -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> |
||||
|
|||||
@ -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; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
} |
||||
@ -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' }, |
||||
|
], |
||||
|
}, |
||||
|
], |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
} |
} |
||||
|
|||||
@ -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"; |
||||
|
} |
||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 368 KiB |
@ -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"; |
||||
|
} |
||||
|
After Width: | Height: | Size: 8.7 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 237 B |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 368 KiB |