陈鹏飞 3 years ago
parent
commit
2fd3c3b448
  1. 2
      src/app/babylon/controller/mode-manager.ts
  2. 12
      src/app/pages/criminal-records-admin/criminal-records-admin.component.html
  3. 51
      src/app/pages/criminal-records-admin/criminal-records-admin.component.ts
  4. 4
      src/app/pages/criminal-records/criminal-records.component.html
  5. 48
      src/app/pages/criminal-records/criminal-records.component.ts
  6. 11
      src/app/pages/init3D.guard.ts
  7. 3
      src/app/pages/oil-unloading-process-list/oil-unloading-process-list.component.ts
  8. 1
      src/app/pages/plan-admin/plan-admin.component.ts
  9. 2
      src/app/pages/plan/plan.component.ts
  10. 6
      src/app/system-management/analysis-of-the-host/analysis-of-the-host.component.html
  11. 57
      src/app/system-management/analysis-of-the-host/analysis-of-the-host.component.ts
  12. 19
      src/app/system-management/analysis-of-the-host/editcamera/editcamera.component.html
  13. 15
      src/app/system-management/analysis-of-the-host/editcamera/editcamera.component.ts

2
src/app/babylon/controller/mode-manager.ts

@ -28,7 +28,7 @@ export class ModeManager {
/** /**
* *
*/ */
public static isDebug = true; public static isDebug = false;

12
src/app/pages/criminal-records-admin/criminal-records-admin.component.html

@ -115,7 +115,7 @@
<div nz-col nzSpan="2"> <div nz-col nzSpan="2">
预警级别 预警级别
</div> </div>
<div nz-col nzSpan="3"> <div nz-col nzSpan="2">
预警类型 预警类型
</div> </div>
<div nz-col nzSpan="3"> <div nz-col nzSpan="3">
@ -139,7 +139,7 @@
<div nz-col nzSpan="2"> <div nz-col nzSpan="2">
状态 状态
</div> </div>
<div nz-col nzSpan="1"> <div nz-col nzSpan="2">
操作 操作
</div> </div>
</div> </div>
@ -161,7 +161,7 @@
<span *ngIf="item.violation.level == 3">Ⅲ级</span> <span *ngIf="item.violation.level == 3">Ⅲ级</span>
<span *ngIf="item.violation.level == 4">Ⅳ级</span> <span *ngIf="item.violation.level == 4">Ⅳ级</span>
</div> </div>
<div nz-col nzSpan="3"> <div nz-col nzSpan="2">
{{item.violation.violationType}} {{item.violation.violationType}}
</div> </div>
<div nz-col nzSpan="3"> <div nz-col nzSpan="3">
@ -186,8 +186,10 @@
<span *ngIf="item.handleTime">已处置</span> <span *ngIf="item.handleTime">已处置</span>
<span *ngIf="!item.handleTime" style="color: #FF4B65;">未处置</span> <span *ngIf="!item.handleTime" style="color: #FF4B65;">未处置</span>
</div> </div>
<div nz-col nzSpan="1"> <div nz-col nzSpan="2">
<span class="look" (click)="look(item)">查看</span> <span class="look" (click)="look(item)" style="margin-right: 12px;">查看</span>
<span *ngIf="!item.handleTime" style="color: #36A2FF;cursor: pointer;" (click)="dispose(item)">处置</span>
<span *ngIf="item.handleTime" style="color: rgba(145, 204, 255, 0.24);;">处置</span>
</div> </div>
</div> </div>
</div> </div>

51
src/app/pages/criminal-records-admin/criminal-records-admin.component.ts

@ -11,6 +11,8 @@ import * as moment from 'moment';
import { NzModalService } from 'ng-zorro-antd/modal'; import { NzModalService } from 'ng-zorro-antd/modal';
import { GetOutOfLineDetailsComponent } from '../today-warning/get-out-of-line-details/get-out-of-line-details.component'; import { GetOutOfLineDetailsComponent } from '../today-warning/get-out-of-line-details/get-out-of-line-details.component';
import { OilUnloadingProcessComponent } from '../oil-unloading-process/oil-unloading-process.component'; import { OilUnloadingProcessComponent } from '../oil-unloading-process/oil-unloading-process.component';
import { DispositionComponent } from '../disposition/disposition.component';
import { NzMessageService } from 'ng-zorro-antd/message';
@Component({ @Component({
selector: 'app-criminal-records-admin', selector: 'app-criminal-records-admin',
templateUrl: './criminal-records-admin.component.html', templateUrl: './criminal-records-admin.component.html',
@ -19,7 +21,7 @@ import { OilUnloadingProcessComponent } from '../oil-unloading-process/oil-unloa
export class CriminalRecordsAdminComponent implements OnInit { export class CriminalRecordsAdminComponent implements OnInit {
validateForm!: FormGroup; validateForm!: FormGroup;
constructor(private element: ElementRef, private http: HttpClient, private fb: FormBuilder, private router: Router, private modal: NzModalService, private viewContainerRef: ViewContainerRef) { } constructor(private element: ElementRef, private http: HttpClient, private fb: FormBuilder, private router: Router, private modal: NzModalService, private viewContainerRef: ViewContainerRef, private message: NzMessageService) { }
//饼图 //饼图
myChart myChart
option = { option = {
@ -33,7 +35,7 @@ export class CriminalRecordsAdminComponent implements OnInit {
type: 'pie', type: 'pie',
radius: [88, 116],//内半径外,外半径 radius: [88, 116],//内半径外,外半径
left: '0', left: '0',
top:'8%', top: '8%',
avoidLabelOverlap: false,//防止标签重叠策略 avoidLabelOverlap: false,//防止标签重叠策略
label: {//每一个标签外网延伸的引导说明 label: {//每一个标签外网延伸的引导说明
show: false, show: false,
@ -427,6 +429,7 @@ export class CriminalRecordsAdminComponent implements OnInit {
this.totalCount = data.result.totalCount this.totalCount = data.result.totalCount
console.log('违规记录列表', data) console.log('违规记录列表', data)
this.getAggregations() this.getAggregations()
console.log(this.list.length)
}) })
} }
@ -435,10 +438,12 @@ export class CriminalRecordsAdminComponent implements OnInit {
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件 fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件
if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) { if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) {
if(this.totalCount > this.list.length){
console.log('需要加载数据了', event) console.log('需要加载数据了', event)
this.SkipCount = String(Number(this.SkipCount) + 50) this.SkipCount = String(Number(this.SkipCount) + 50)
this.getViolateRecordList() this.getViolateRecordList()
} }
}
}); });
} }
getThirtyDays() { getThirtyDays() {
@ -579,7 +584,47 @@ export class CriminalRecordsAdminComponent implements OnInit {
//跳转卸油统计页面 //跳转卸油统计页面
goOilList(){ goOilList() {
this.router.navigate(['/records/oliunloadinglist']) this.router.navigate(['/records/oliunloadinglist'])
} }
dispose(item) {
console.log(item)
const modal = this.modal.create({
nzContent: DispositionComponent,
nzViewContainerRef: this.viewContainerRef,
nzWidth: 380,
nzBodyStyle: {
'border': '1px solid #91CCFF',
'border-radius': '0px',
'padding': '7px',
'box-shadow': '0 0 8px 0 #fff',
'background-image': 'linear-gradient(#003665, #000f25)'
},
nzComponentParams: {},
nzFooter: null,
nzClosable: false,
nzOnOk: async () => {
if (instance.validateForm.valid) {
await new Promise(resolve => {
let body = {
id: item.id,
handleRecord: instance.validateForm.value.content
}
this.http.post('/api/services/app/ViolateRecord/HandleViolateRecord', body).subscribe(data => {
resolve(data)
this.message.create('success', '处置成功!');
item.handleTime = new Date()
return true
})
})
} else {
this.message.create('warning', '请填写完整!');
return false
}
},
});
const instance = modal.getContentComponent();
}
} }

4
src/app/pages/criminal-records/criminal-records.component.html

@ -168,7 +168,9 @@
<span *ngIf="!item.handleTime" style="color: #FF4B65;">未处置</span> <span *ngIf="!item.handleTime" style="color: #FF4B65;">未处置</span>
</div> </div>
<div nz-col nzSpan="2"> <div nz-col nzSpan="2">
<span class="look" (click)="look(item)">查看</span> <span class="look" (click)="look(item)" style="margin-right: 12px;">查看</span>
<span *ngIf="!item.handleTime" style="color: #36A2FF;cursor: pointer;" (click)="dispose(item)">处置</span>
<span *ngIf="item.handleTime" style="color: rgba(145, 204, 255, 0.24);;">处置</span>
</div> </div>
</div> </div>
</div> </div>

48
src/app/pages/criminal-records/criminal-records.component.ts

@ -8,6 +8,8 @@ import * as moment from 'moment';
import { fromEvent } from 'rxjs'; import { fromEvent } from 'rxjs';
import { debounceTime } from 'rxjs/operators'; import { debounceTime } from 'rxjs/operators';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { DispositionComponent } from '../disposition/disposition.component';
import { NzMessageService } from 'ng-zorro-antd/message';
@Component({ @Component({
selector: 'app-criminal-records', selector: 'app-criminal-records',
templateUrl: './criminal-records.component.html', templateUrl: './criminal-records.component.html',
@ -15,7 +17,7 @@ import { Router } from '@angular/router';
}) })
export class CriminalRecordsComponent implements OnInit { export class CriminalRecordsComponent implements OnInit {
validateForm!: FormGroup; validateForm!: FormGroup;
constructor(private http: HttpClient, private fb: FormBuilder, private router: Router, private modal: NzModalService, private viewContainerRef: ViewContainerRef, private element: ElementRef) { } constructor(private http: HttpClient, private fb: FormBuilder, private router: Router, private modal: NzModalService, private viewContainerRef: ViewContainerRef, private element: ElementRef, private message: NzMessageService) { }
myChart: any //左侧饼图 myChart: any //左侧饼图
option = { option = {
@ -29,7 +31,7 @@ export class CriminalRecordsComponent implements OnInit {
type: 'pie', type: 'pie',
radius: [88, 116],//内半径外,外半径 radius: [88, 116],//内半径外,外半径
left: '0', left: '0',
top:'8%', top: '8%',
avoidLabelOverlap: false,//防止标签重叠策略 avoidLabelOverlap: false,//防止标签重叠策略
label: {//每一个标签外网延伸的引导说明 label: {//每一个标签外网延伸的引导说明
show: false, show: false,
@ -440,10 +442,12 @@ export class CriminalRecordsComponent implements OnInit {
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件 fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件
if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) { if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) {
if(this.totalCount > this.list.length){
console.log('需要加载数据了', event) console.log('需要加载数据了', event)
this.SkipCount = String(Number(this.SkipCount) + 50) this.SkipCount = String(Number(this.SkipCount) + 50)
this.getViolateRecordList() this.getViolateRecordList()
} }
}
}); });
} }
@ -528,7 +532,45 @@ export class CriminalRecordsComponent implements OnInit {
} }
goOilList(){ goOilList() {
this.router.navigate(['/records/petrolStation/oliunloadinglist']) this.router.navigate(['/records/petrolStation/oliunloadinglist'])
} }
dispose(item) {
console.log(item)
const modal = this.modal.create({
nzContent: DispositionComponent,
nzViewContainerRef: this.viewContainerRef,
nzWidth: 380,
nzBodyStyle: {
'border': '1px solid #91CCFF',
'border-radius': '0px',
'padding': '7px',
'box-shadow': '0 0 8px 0 #fff',
'background-image': 'linear-gradient(#003665, #000f25)'
},
nzComponentParams: {},
nzFooter: null,
nzClosable: false,
nzOnOk: async () => {
if (instance.validateForm.valid) {
await new Promise(resolve => {
let body = {
id: item.id,
handleRecord: instance.validateForm.value.content
}
this.http.post('/api/services/app/ViolateRecord/HandleViolateRecord', body).subscribe(data => {
resolve(data)
this.message.create('success', '处置成功!');
item.handleTime = new Date()
return true
})
})
} else {
this.message.create('warning', '请填写完整!');
return false
}
},
});
const instance = modal.getContentComponent();
}
} }

11
src/app/pages/init3D.guard.ts

@ -16,6 +16,15 @@ export class init3DGuard implements CanActivate {
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot,): Promise<boolean> { canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot,): Promise<boolean> {
return this.getGasStationBaseInfo().then((res: any) => { return this.getGasStationBaseInfo().then((res: any) => {
sessionStorage.setItem("3dSceneData", JSON.stringify(res)) sessionStorage.setItem("3dSceneData", JSON.stringify(res))
if (true) //是superadmin,是内部制作账号
{
ModeManager.s_isMakeMode = true;
}
else {
ModeManager.s_isMakeMode = true;
}
let isMakeMode = ModeManager.s_isMakeMode let isMakeMode = ModeManager.s_isMakeMode
if (isMakeMode || res.hasBuildingInfo) { if (isMakeMode || res.hasBuildingInfo) {
return true return true
@ -30,7 +39,7 @@ export class init3DGuard implements CanActivate {
getGasStationBaseInfo() { getGasStationBaseInfo() {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let params = { organizationUnitId: (JSON.parse(sessionStorage.getItem('userdataOfgasstation'))).organization.id } let params = { organizationUnitId: (JSON.parse(sessionStorage.getItem('userdataOfgasstation'))).organization.id }
this.http.get('/api/services/app/GasStation/Get', { params: params }).subscribe((data: any)=>{ this.http.get('/api/services/app/GasStation/Get', { params: params }).subscribe((data: any) => {
resolve(data.result) resolve(data.result)
}) })
}) })

3
src/app/pages/oil-unloading-process-list/oil-unloading-process-list.component.ts

@ -574,10 +574,13 @@ export class OilUnloadingProcessListComponent implements OnInit {
ngAfterViewInit(): void { ngAfterViewInit(): void {
fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件 fromEvent(this.element.nativeElement.querySelector(`#tbody`) as HTMLCanvasElement, 'scroll').pipe(debounceTime(100)).subscribe((event: any) => { //监听 DOM 滚动事件
if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) { if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) {
if (this.totalCount > this.list.length) {
console.log('需要加载数据了', event) console.log('需要加载数据了', event)
this.SkipCount = String(Number(this.SkipCount) + 50) this.SkipCount = String(Number(this.SkipCount) + 50)
this.getViolateRecordList() this.getViolateRecordList()
} }
}
}); });
} }
look(item) { look(item) {

1
src/app/pages/plan-admin/plan-admin.component.ts

@ -33,7 +33,6 @@ export class PlanAdminComponent implements OnInit {
if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) { if (event.target.scrollHeight - (event.target.scrollTop + event.target.clientHeight) <= 10) {
console.log('需要加载数据了', event) console.log('需要加载数据了', event)
this.SkipCount = String(Number(this.SkipCount) + 50) this.SkipCount = String(Number(this.SkipCount) + 50)
// await this.getGasStation()
let params = { let params = {
StationName: this.validateForm.value.name, StationName: this.validateForm.value.name,
OrganizationUnitId: String(this.OrganizationUnitId), OrganizationUnitId: String(this.OrganizationUnitId),

2
src/app/pages/plan/plan.component.ts

@ -63,6 +63,7 @@ export class PlanComponent implements OnInit {
ServeManager.Init(this.buildingBISrv, this.objectsSrv); ServeManager.Init(this.buildingBISrv, this.objectsSrv);
this.canvas = this.element.nativeElement.querySelector('#center') as HTMLCanvasElement; this.canvas = this.element.nativeElement.querySelector('#center') as HTMLCanvasElement;
this.game.init(this.canvas); this.game.init(this.canvas);
this.companyData = (JSON.parse(sessionStorage.getItem('userdataOfgasstation'))).organization || {} this.companyData = (JSON.parse(sessionStorage.getItem('userdataOfgasstation'))).organization || {}
let editMode = sessionStorage.getItem('isGasStation') let editMode = sessionStorage.getItem('isGasStation')
if (editMode == 'false') { if (editMode == 'false') {
@ -72,6 +73,7 @@ export class PlanComponent implements OnInit {
this.editMode = false this.editMode = false
ModeManager.currentMode = ModeType.Look ModeManager.currentMode = ModeType.Look
} }
//监听 delete键盘事件 //监听 delete键盘事件
this.eventManager.addGlobalEventListener('window', 'keydown', (event: any) => { this.eventManager.addGlobalEventListener('window', 'keydown', (event: any) => {
if (event.keyCode == 46) { //delete if (event.keyCode == 46) { //delete

6
src/app/system-management/analysis-of-the-host/analysis-of-the-host.component.html

@ -66,7 +66,7 @@
</tbody> </tbody>
</nz-table> </nz-table>
</div> </div>
<!-- <div class="topbox"> <div class="topbox" style="margin-top: 20px;">
<div class="lefttop"> <div class="lefttop">
<span>{{selectedOilStation ? selectedOilStation.displayName : '加油站'}} 摄像头列表 <span>{{selectedOilStation ? selectedOilStation.displayName : '加油站'}} 摄像头列表
</span> </span>
@ -90,12 +90,12 @@
<td>{{data.name}}</td> <td>{{data.name}}</td>
<td>{{data.code}}</td> <td>{{data.code}}</td>
<td class="operation"> <td class="operation">
<a (click)="editCamera(data)">编辑</a> <a (click)="editCamera(data)" style="margin-right: 12px;">编辑</a>
<a (click)="deleteCamera(data)">删除</a> <a (click)="deleteCamera(data)">删除</a>
</td> </td>
</tr> </tr>
</tbody> </tbody>
</nz-table> </nz-table>
</div> --> </div>
</div> </div>
</div> </div>

57
src/app/system-management/analysis-of-the-host/analysis-of-the-host.component.ts

@ -8,6 +8,7 @@ import { NzMessageService } from 'ng-zorro-antd/message';
import { AddhostComponent } from './addhost/addhost.component'; import { AddhostComponent } from './addhost/addhost.component';
import { AddcameraComponent } from './addcamera/addcamera.component'; import { AddcameraComponent } from './addcamera/addcamera.component';
import { EdithostComponent } from './edithost/edithost.component'; import { EdithostComponent } from './edithost/edithost.component';
import { EditcameraComponent } from './editcamera/editcamera.component';
@Component({ @Component({
selector: 'app-analysis-of-the-host', selector: 'app-analysis-of-the-host',
templateUrl: './analysis-of-the-host.component.html', templateUrl: './analysis-of-the-host.component.html',
@ -60,6 +61,7 @@ export class AnalysisOfTheHostComponent implements OnInit {
this.nzSelectedKeys = [...this.nzSelectedKeys] this.nzSelectedKeys = [...this.nzSelectedKeys]
this.selectedOilStation = event.node.origin this.selectedOilStation = event.node.origin
this.getHost() this.getHost()
this.getCamera()
} else { } else {
this.message.info('只有加油站才可以增加主机'); this.message.info('只有加油站才可以增加主机');
} }
@ -88,7 +90,7 @@ export class AnalysisOfTheHostComponent implements OnInit {
} }
}).subscribe((data: any) => { }).subscribe((data: any) => {
console.log('摄像头列表', data) console.log('摄像头列表', data)
this.listOfDataCamera = data this.listOfDataCamera = data.result.items
}) })
} }
@ -197,7 +199,7 @@ export class AnalysisOfTheHostComponent implements OnInit {
organizationUnitId: this.selectedOilStation.id, organizationUnitId: this.selectedOilStation.id,
code: instance.validateForm.value.code, code: instance.validateForm.value.code,
name: instance.validateForm.value.name, name: instance.validateForm.value.name,
description: "", // description: "",
} }
this.http.post('/api/services/app/Camera/Create', body).subscribe(data => { this.http.post('/api/services/app/Camera/Create', body).subscribe(data => {
resolve(data) resolve(data)
@ -218,8 +220,55 @@ export class AnalysisOfTheHostComponent implements OnInit {
} }
editCamera(data) { editCamera(data) {
console.log(data)
const modal = this.modal.create({
nzTitle: '编辑加油站摄像头',
nzContent: EditcameraComponent,
nzViewContainerRef: this.viewContainerRef,
nzWidth: 288,
nzComponentParams: {
data: data
},
nzOnOk: async () => {
if (instance.validateForm.valid) {
await new Promise(resolve => {
console.log('表单信息', instance.validateForm)
data.name = instance.validateForm.value.name
data.code = instance.validateForm.value.code
this.http.put('/api/services/app/Camera/Update', data).subscribe(data => {
resolve(data)
this.message.create('success', '编辑成功!');
this.getCamera()
return true
}, err => {
return false
})
})
} else {
this.message.create('warning', '请填写完整!');
return false
} }
deleteCamera(data) { }
});
const instance = modal.getContentComponent();
}
deleteCamera(item) {
console.log(item)
this.modal.confirm({
nzTitle: `确定要删除${item.name}这个摄像头吗?`,
nzOkText: '确定',
nzOkType: 'danger',
nzOnOk: () => {
this.http.delete('/api/services/app/Camera/Delete', {
params: {
Id: item.id
}
}).subscribe(data => {
this.message.create('success', '删除成功!');
this.getCamera()
})
},
nzCancelText: '取消'
});
} }
} }

19
src/app/system-management/analysis-of-the-host/editcamera/editcamera.component.html

@ -1 +1,18 @@
<p>editcamera works!</p> <div class="box">
<form nz-form [formGroup]="validateForm">
<nz-form-item>
<nz-form-control>
<nz-input-group>
<input nz-input type="text" formControlName="name" placeholder="请输入名称" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<nz-input-group>
<input nz-input type="text" formControlName="code" placeholder="请输入编号" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
</form>
</div>

15
src/app/system-management/analysis-of-the-host/editcamera/editcamera.component.ts

@ -1,4 +1,7 @@
import { Component, OnInit } from '@angular/core'; import { Component, OnInit, Input } from '@angular/core';
import { NzModalRef } from 'ng-zorro-antd/modal';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
@Component({ @Component({
selector: 'app-editcamera', selector: 'app-editcamera',
@ -6,10 +9,16 @@ import { Component, OnInit } from '@angular/core';
styleUrls: ['./editcamera.component.scss'] styleUrls: ['./editcamera.component.scss']
}) })
export class EditcameraComponent implements OnInit { export class EditcameraComponent implements OnInit {
@Input() data: any
constructor() { } validateForm!: FormGroup;
constructor(private modal: NzModalRef, private fb: FormBuilder, private http: HttpClient) { }
ngOnInit(): void { ngOnInit(): void {
let datacopy = JSON.parse(JSON.stringify(this.data))
this.validateForm = this.fb.group({
name: [datacopy.name, [Validators.required]],
code: [datacopy.code, [Validators.required]]
});
} }
} }

Loading…
Cancel
Save