@ -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> |
|||
<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 class="flex-1 overflow-hidden bg-white px-4"> |
|||
<nz-tabset> |
|||
<nz-tab [nzTitle]="'资产管理(' + assetTotal + ')'"> |
|||
<app-flow-list-by-type page="apply" type="asset" (onLoad)="onLoad($event, 'assetTotal')" /> |
|||
</nz-tab> |
|||
<nz-tab [nzTitle]="'设备任务(' + deviceTotal + ')'"> |
|||
<app-flow-list-by-type page="apply" type="device" (onLoad)="onLoad($event, 'deviceTotal')" /> |
|||
</nz-tab> |
|||
</nz-tabset> |
|||
</div> |
|||
</app-page> |
|||
|
|||
@ -1,34 +1,12 @@ |
|||
<app-page> |
|||
<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 class="flex-1 overflow-hidden bg-white px-4"> |
|||
<nz-tabset> |
|||
<nz-tab [nzTitle]="'资产管理(' + assetTotal + ')'"> |
|||
<app-flow-list-by-type page="finished" type="asset" (onLoad)="onLoad($event, 'assetTotal')" /> |
|||
</nz-tab> |
|||
<nz-tab [nzTitle]="'设备任务(' + deviceTotal + ')'"> |
|||
<app-flow-list-by-type page="finished" type="device" (onLoad)="onLoad($event, 'deviceTotal')" /> |
|||
</nz-tab> |
|||
</nz-tabset> |
|||
</div> |
|||
</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({ |
|||
selector: 'app-flow-report', |
|||
standalone: true, |
|||
imports: [], |
|||
imports: [SharedModule], |
|||
templateUrl: './flow-report.component.html', |
|||
styleUrl: './flow-report.component.less' |
|||
styleUrl: './flow-report.component.less', |
|||
}) |
|||
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 |