合并自建站代码到话费修改

pull/1/head
袁野 3 years ago
parent f2b4af9894
commit cdcaaf07e1
  1. 14
      src/app/admin/api-merchants/api-merchants-routing.module.ts
  2. 29
      src/app/admin/api-merchants/api-merchants.module.ts
  3. 297
      src/app/admin/api-merchants/list/list.component.html
  4. 0
      src/app/admin/api-merchants/list/list.component.scss
  5. 465
      src/app/admin/api-merchants/list/list.component.ts
  6. 14
      src/app/admin/api-product/api-product-routing.module.ts
  7. 29
      src/app/admin/api-product/api-product.module.ts
  8. 195
      src/app/admin/api-product/list/list.component.html
  9. 0
      src/app/admin/api-product/list/list.component.scss
  10. 233
      src/app/admin/api-product/list/list.component.ts
  11. 18
      src/app/pipes/apiMerchant/source-type.pipe.ts
  12. 170
      src/app/services/api-merchants.service.ts
  13. 97
      src/app/services/api-product.service.ts

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {ListComponent} from './list/list.component';
const routes: Routes = [
{ path: 'list', component: ListComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ApiMerchantsRoutingModule { }

@ -0,0 +1,29 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ApiMerchantsRoutingModule} from './api-merchants-routing.module';
import {ListComponent} from './list/list.component';
import {NgZorroAntdModule} from '_ng-zorro-antd@9.3.0@ng-zorro-antd';
import {SeparateModule} from '../../common/separate/separate.module';
import {FormsModule, ReactiveFormsModule} from '_@angular_forms@9.0.7@@angular/forms';
import {BreadcrumbModule} from '../../common/breadcrumb/breadcrumb.module';
import {RegionSelectorModule} from '../../common/region-selector/region-selector.module';
import {AppCommonModule} from '../../app-common.module';
@NgModule({
declarations: [ListComponent],
imports: [
CommonModule,
ApiMerchantsRoutingModule,
NgZorroAntdModule,
SeparateModule,
ReactiveFormsModule,
FormsModule,
BreadcrumbModule,
RegionSelectorModule,
AppCommonModule
]
})
export class ApiMerchantsModule {
}

@ -0,0 +1,297 @@
<!-- start 面包屑 -->
<app-breadcrumb></app-breadcrumb>
<!-- end 面包屑 -->
<!--条件搜索-->
<div class="inner-content">
<form nz-form [formGroup]="searchForm" (ngSubmit)="getRequest(true , searchForm.value)">
<div nz-row>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">商户名称</nz-form-label>
<nz-form-control [nzSpan]="16">
<input nz-input formControlName="merchantName"/>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">商户号</nz-form-label>
<nz-form-control [nzSpan]="16">
<input nz-input formControlName="mchId"/>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">商户电话</nz-form-label>
<nz-form-control [nzSpan]="16">
<input nz-input formControlName="phone"/>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">状态</nz-form-label>
<nz-form-control [nzSpan]="16">
<nz-select nzShowSearch nzAllowClear formControlName="status" nzPlaceHolder="请选择状态">
<nz-option nzLabel="正常" [nzValue]="100"></nz-option>
<nz-option nzLabel="禁用" [nzValue]="101"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
</div>
<div nz-row>
<div nz-col nzSpan="24" class="search-button">
<button nz-button nzType="primary"><i nz-icon nzType="search" nzTheme="outline"></i>搜索</button>
<button nz-button nzType="default" (click)="resetForm()"><i nz-icon nzType="reload"
nzTheme="outline"></i>重置
</button>
</div>
</div>
</form>
</div>
<div class="inner-content">
<span>共计 {{total}} 条数据</span>
<div class="operating-button">
<button nz-button nzType="primary" class="right-btn" (click)="getEdit()"><i nz-icon nzType="plus" nzTheme="outline"></i>添加</button>
<button nz-button nzType="primary" class="right-btn" (click)="showRechargeModal()" ><i nz-icon nzType="plus" nzTheme="outline"></i>账户充值</button>
</div>
<nz-table
class="table"
#ajaxTable
nzShowSizeChanger
[nzFrontPagination]="false"
[nzData]="requestData"
[nzLoading]="loading"
[nzTotal]="total"
[(nzPageIndex)]="pageNum"
[(nzPageSize)]="pageSize"
[nzScroll]="{ x: '1200px' }"
(nzPageIndexChange)="getRequest(false , searchForm.value)"
(nzPageSizeChange)="getRequest(false , searchForm.value)">
<thead nzSingleSort>
<tr>
<th nzWidth="60px">编号</th>
<th nzWidth="90px">商户名称</th>
<th nzWidth="80px">联系人</th>
<th nzWidth="80px">电话</th>
<th nzWidth="90px">商户号</th>
<th nzWidth="160px">商户KEY</th>
<th nzWidth="80px">操作人员</th>
<th nzWidth="120px">创建时间</th>
<th nzRight nzWidth="100px">帐户余额</th>
<th nzRight nzWidth="60px">状态</th>
<th nzRight nzWidth="200px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of ajaxTable.data; let i = index">
<td>{{i + 1}}</td>
<td>{{data.merchantName}}</td>
<td>{{data.userName }}</td>
<td>{{data.phone }}</td>
<td>{{data.mchId }}</td>
<td>{{data.apiKey }}</td>
<td>{{data.operatorName }}</td>
<td>{{data.createTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td nzRight="">¥{{data.amounts}}</td>
<td nzRight="">{{data.status== 100? '正常':'禁用'}}</td>
<td nzRight class="table-td-operation">
<a *ngIf="data.status != 101" (click)="getEdit(data.id)">编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a nz-dropdown [nzDropdownMenu]="menu">更多操作</a>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li class="li-a" nz-menu-item><a (click)="configProduct(data.id)">配置产品</a></li>
<li class="li-a" nz-menu-item><a (click)="showConfigProduct(data.id)">产品详情</a></li>
<li class="li-a" nz-menu-item><a (click)="getMerchRechargeData(data.id)">金额记录</a></li>
<li class="li-a" nz-menu-item><a (click)='getForbiddenUser(data.id , data.status)'>{{data.status === 101 ? '上线': '禁用'}}</a></li>
</ul>
</nz-dropdown-menu>
</td>
</tbody>
</nz-table>
</div>
<nz-modal [(nzVisible)]="isVisible" nzTitle="商户编辑" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<form nz-form [formGroup]="validateForm" class="login-form">
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>商户名称</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入商户名称!">
<input nz-input formControlName="merchantName" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>商户联系人</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入商户联系人!">
<input nz-input formControlName="userName" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>商户电话</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入商户电话!">
<input nz-input formControlName="phone" />
</nz-form-control>
</nz-form-item>
</form>
</nz-modal>
<nz-modal [(nzVisible)]="rechargeModal" nzTitle="账户充值" (nzOnCancel)="rechargeModal = false" nzWidth="500px" [nzFooter]="null">
<form nz-form [formGroup]="rechargeForm">
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired >部门</nz-form-label>
<nz-form-control [nzSpan]="14">
<nz-select nzShowSearch nzAllowClear nzPlaceHolder="请选择商户" formControlName="merchantId">
<nz-option *ngFor="let data of merchantArray" nzLabel="{{data.merchantName}}" nzValue="{{data.id}}"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired >充值金额¥</nz-form-label>
<nz-form-control [nzSpan]="14">
<nz-input-number style="width: 100%" formControlName="amount" [nzMin]="0" [nzMax]="9999999" [nzStep]="1" [nzPrecision]="2"></nz-input-number>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired >验证码</nz-form-label>
<nz-form-control [nzSpan]="14">
<nz-input-group nzSearch [nzAddOnAfter]="suffixButton">
<input nz-input formControlName="smsCode" />
</nz-input-group>
<ng-template #suffixButton>
<button nz-button nzType="primary" [disabled]="smsCodeBtnLoading" (click)="sendSmsCode()">{{smsCodeBtnName}}</button>
</ng-template>
<span *ngIf="smsCodeBtnLoading" nz-typography nzType="warning">验证码已发送!5分钟内有效</span>
</nz-form-control>
</nz-form-item>
<div style="text-align: center;">
<button nz-button nzType="primary" style="width: 150px;" [nzLoading]="submitRechargeBtnLoading" (click)="submitRecharge()">确定</button>
</div>
</form>
</nz-modal>
<nz-modal [(nzVisible)]="visible" nzTitle="商户充值记录" (nzOnCancel)="visible = false" nzWidth="1000px" [nzFooter]="null">
<nz-table #errorStudentTable [nzScroll]="{ x: '1200px' }" [nzData]="merchRechargeData">
<thead>
<tr>
<th nzWidth="100px">类型</th>
<th nzWidth="100px">交易金额</th>
<th nzWidth="120px">变更前金额</th>
<th nzWidth="120px">变更后金额</th>
<th nzWidth="100px">来源类型</th>
<th nzWidth="300px">来源内容</th>
<th nzWidth="130px">操作人</th>
<th nzWidth="200px">记录时间</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of errorStudentTable.data;let i = index">
<td>{{data.amountType | companyAmountRecordType}}</td>
<td>¥{{data.amount}}</td>
<td>¥{{data.beforeAmount}}</td>
<td>¥{{data.afterAmount}}</td>
<td>{{data.sourceType | sourceType}}</td>
<td>{{data.sourceContent}}</td>
<td>{{data.operatorName}}</td>
<td>{{data.createTime | date : 'yyyy-MM-dd HH:mm:ss'}}</td>
</tr>
</tbody>
</nz-table>
</nz-modal>
<nz-modal [(nzVisible)]="configVisible" nzTitle="产品配置" (nzOnCancel)="configVisible = false" (nzOnOk)="handleOkConfig()">
<form nz-form [formGroup]="configValidateForm" class="login-form">
<nz-form-item *ngIf="!edit">
<nz-form-label [nzSpan]="6" nzRequired>选择产品</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请选择选择产品!">
<nz-select
nzMode="multiple"
formControlName="productIds"
nzPlaceHolder="请选择充值金额"
>
<nz-option *ngFor="let item of productArray" nzValue="{{item.id}}" nzLabel="{{item.operatorType | rechargePrice}}|{{item.phoneRechargeType== 1? '快充':'慢充'}}|{{item.rechargePrice}}元"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>折扣比例</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入折扣比例!">
<nz-input-number
style="width: 200px"
formControlName="discount"
[nzMin]="1"
[nzMax]="100"
[nzStep]="1"
[nzPrecision]="2"
nzPrecisionMode="cut"
nzPlaceHolder="请输入折扣比例!"
[nzFormatter]="formatterPercent"
[nzParser]="parserPercent"
></nz-input-number>
</nz-form-control>
</nz-form-item>
</form>
</nz-modal>
<nz-modal [(nzVisible)]="visibleConfigProduct" nzTitle="商户产品" (nzOnCancel)="visibleConfigProduct = false" nzWidth="1000px" [nzFooter]="null">
<nz-table #productConfigTable [nzScroll]="{ x: '1200px' }" [nzData]="productConfigArray">
<thead>
<tr>
<th nzWidth="60px">编号</th>
<th nzWidth="90px">充值金额</th>
<th nzWidth="90px">折扣金额</th>
<th nzWidth="120px">运营商</th>
<th nzWidth="90px">充值类型</th>
<th nzWidth="120px">创建时间</th>
<th nzRight nzWidth="80px">状态</th>
<th nzRight nzWidth="160px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of productConfigTable.data;let i = index">
<td>{{i + 1}}</td>
<td>{{data.rechargePrice}}</td>
<td>{{data.discountPrice}}</td>
<td>{{data.operatorType | rechargePrice}}</td>
<td>{{data.phoneRechargeType== 1? '快充':'慢充'}}</td>
<td>{{data.createTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td nzRight>{{data.configStatus== 101? '正常':'下架中'}}</td>
<td nzRight class="table-td-operation">
<a *ngIf="data.configStatus != 101" (click)="getEditConfigProduct(data.apiConfigProductId)">编辑</a>
<a style="margin-left: 20px;" (click)='getEditConfigProductStatus(data.apiConfigProductId , data.configStatus)'>{{data.configStatus === 101 ? '下线': '上线'}}</a>
</td>
</tr>
</tbody>
</nz-table>
</nz-modal>
<nz-modal [(nzVisible)]="editConfigVisible" nzTitle="编辑产品折扣" (nzOnCancel)="editConfigVisible = false" (nzOnOk)="handleOkConfigEdit()">
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>折扣比例</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入折扣比例!">
<nz-input-number
style="width: 200px"
[(ngModel)]="discount"
[nzMin]="1"
[nzMax]="100"
[nzStep]="1"
[nzPrecision]="2"
nzPrecisionMode="cut"
nzPlaceHolder="请输入折扣比例!"
[nzFormatter]="formatterPercent"
[nzParser]="parserPercent"
></nz-input-number>
</nz-form-control>
</nz-form-item>
</nz-modal>

@ -0,0 +1,465 @@
import {Component, OnInit} from '@angular/core';
import {environment} from '../../../../environments/environment';
import {FormBuilder, FormGroup, Validators} from '_@angular_forms@9.0.7@@angular/forms';
import {CouponService} from '../../../services/coupon.service';
import {IconService} from '../../../services/icon.service';
import {NzMessageService} from '_ng-zorro-antd@9.3.0@ng-zorro-antd';
import {Router} from '_@angular_router@9.0.7@@angular/router';
import {CommonsService} from '../../../services/commons.service';
import {ApiMerchantsService} from '../../../services/api-merchants.service';
import {ApiProductService} from '../../../services/api-product.service';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {
WEB_SERVE_URL = environment.imageUrl;
searchForm: FormGroup; // 搜索框
validateForm: FormGroup; // 添加框
requestData = []; // 列表数据
priceArray = []; // 价格数据
platformArray = []; // 充值平台数据
total: number; // 页码
pageNum = 1; // 页码
pageSize = 10; // 条码
loading = true;
isVisible = false;
visible = false;
configVisible = false;
editConfigVisible = false;
visibleConfigProduct = false;
id: number;
edit = false;
payPrice;
isVisibleGoods = false;
realPrice = 0;
rechargeModal = false;
rechargeForm: FormGroup;
configValidateForm: FormGroup;
rechargeParam: any = {};
smsCodeBtnName = '获取验证码';
smsCodeBtnLoading = false;
submitRechargeBtnLoading = false;
countdownTime;
merchantArray = [];
merchRechargeData = [];
merchantId: number;
configProductId: number;
productArray = [];
productConfigArray = [];
discount;
formatterPercent = (value: number): string => `${value == null ? 0 : value} %`;
parserPercent = (value: string): string => value.replace(' %', '');
constructor(
private form: FormBuilder,
private apiMerchants: ApiMerchantsService,
private apiProduct: ApiProductService,
private coupon: CouponService,
private iconService: IconService,
private message: NzMessageService,
private router: Router,
private common: CommonsService
) {
}
ngOnInit(): void {
this.init();
this.getInitData();
}
// 获取初始化数据
private getInitData(): void {
// 获取价格数据
this.common.getDictionary('RECHARGE_AMOUNT ', data => {
this.priceArray = data['return_data'];
});
// 获取充值平台
this.common.getDictionary('RECHARGE_PLATFORM ', data => {
this.platformArray = data['return_data'];
});
}
public init(): void {
this.rechargeForm = this.form.group({
merchantId: [null, [Validators.required]],
amount: [null, [Validators.required]],
smsCode: [null, [Validators.required]],
});
this.searchForm = this.form.group({
merchantName: [null],
mchId: [null],
phone: [null],
status: [null],
});
this.validateForm = this.form.group({
merchantName: [null, [Validators.required]],
userName: [null, [Validators.required]],
phone: [null, [Validators.required]],
});
this.configValidateForm = this.form.group({
productIds: [null, [Validators.required]],
discount: [null, [Validators.required]],
});
this.getRequest(true, this.searchForm.value);
this.apiMerchants.getListApiMerchantByAll(data => {
if (data['return_code'] === '000000') {
this.merchantArray = data['return_data'];
} else {
this.message.error(data['return_msg']);
}
});
this.apiProduct.getAvailableApiProductByList(data => {
if (data['return_code'] === '000000') {
this.productArray = data['return_data'];
} else {
this.message.error(data['return_msg']);
}
});
}
// 查询列表
public getRequest(reset: boolean = false, whereObject: object) {
this.loading = false;
if (reset) {
this.pageNum = 1;
}
whereObject['pageNum'] = this.pageNum;
whereObject['pageSize'] = this.pageSize;
this.apiMerchants.getListApiMerchant(whereObject, data => {
if (data['return_code'] === '000000') {
this.requestData = data['return_data'].list;
this.total = data['return_data'].total;
} else {
this.message.error(data['return_msg']);
}
});
}
// 重置
public resetForm(): void {
this.searchForm.reset();
}
// 修改
public getEdit(id?: number): void {
if (id != null) {
this.id = id;
this.edit = true;
this.apiMerchants.findById(id, data => {
this.validateForm.patchValue(data['return_data']);
this.isVisible = true;
});
} else {
this.validateForm.reset();
this.isVisible = true;
this.edit = false;
}
}
handleOk(): void {
// tslint:disable-next-line:forin
for (const i in this.validateForm.controls) {
this.validateForm.controls[i].markAsDirty();
this.validateForm.controls[i].updateValueAndValidity();
if (this.validateForm.controls[i].errors != null) {
this.message.error('必填项不能为空');
return;
}
}
const wait = this.message.loading('提交中..', {nzDuration: 0}).messageId;
if (this.edit) {
this.validateForm.value['id'] = this.id;
this.apiMerchants.updateMerchant(this.validateForm.value, data => {
this.message.remove(wait);
if (data['return_code'] === '000000') {
this.message.success('修改成功');
this.getRequest(false, this.searchForm.value);
this.validateForm.reset();
this.isVisible = false;
} else {
this.message.error(data['return_msg']);
}
});
} else {
this.apiMerchants.insertMerchant(this.validateForm.value, data => {
this.message.remove(wait);
if (data['return_code'] === '000000') {
this.message.success('添加成功');
this.getRequest(false, this.searchForm.value);
this.isVisible = false;
this.validateForm.reset();
} else {
this.message.error(data['return_msg']);
}
});
}
}
handleCancel(): void {
this.validateForm.reset();
this.isVisible = false;
}
public getForbiddenUser(idPost, status: any): void {
let statusPost;
let message;
switch (status) {
case 101:
statusPost = 100;
message = '是否上线';
break;
case 100:
statusPost = 101;
message = '是否禁用';
break;
}
this.common.showConfirm(message, item => {
if (item) {
this.apiMerchants.editMerchantsStatus({
id: idPost,
status: statusPost
}, data => {
if (data['return_code'] === '000000') {
this.message.success('提交成功');
this.getRequest(false, this.searchForm.value);
} else {
this.message.error(data['return_msg']);
}
});
}
});
}
public getEditConfigProductStatus(idPost, status: any): void {
let statusPost;
let message;
switch (status) {
case 102:
statusPost = 101;
message = '是否上线';
break;
case 101:
statusPost = 102;
message = '是否禁用';
break;
}
this.common.showConfirm(message, item => {
if (item) {
this.apiMerchants.editConfigApiProductByStatus({
id: idPost,
status: statusPost
}, data => {
if (data['return_code'] === '000000') {
this.message.success('提交成功');
this.getListMerchProduct(this.merchantId);
} else {
this.message.error(data['return_msg']);
}
});
}
});
}
/**
*
*/
showRechargeModal() {
this.rechargeModal = true;
}
/**
*
*/
sendSmsCode() {
this.rechargeForm.controls.merchantId.enable();
this.rechargeForm.controls.amount.enable();
if (this.rechargeForm.value.merchantId == null) {
this.message.error('请选择充值商户');
return;
}
this.rechargeParam['merchantId'] = this.rechargeForm.value['merchantId'];
this.rechargeParam['amount'] = this.rechargeForm.value['amount'];
this.rechargeForm.controls.merchantId.disable();
this.rechargeForm.controls.amount.disable();
this.common.sendRechargeSmsCode('18090580471', this.rechargeParam['merchantId'], this.rechargeParam['amount'], data => {
if (data['return_code'] === '000000') {
this.getVerifyCodeCountdown();
} else {
this.message.error(data['return_msg']);
this.rechargeForm.controls.merchantId.enable();
this.rechargeForm.controls.amount.enable();
}
});
}
/**
*
*/
getVerifyCodeCountdown() {
let countdownSecond = 60; // 倒计时60秒
this.countdownTime = setInterval(() => {
if (countdownSecond <= 1) {
this.smsCodeBtnName = '获取验证码';
this.smsCodeBtnLoading = false;
clearInterval(this.countdownTime);
} else {
countdownSecond--;
this.smsCodeBtnName = String(countdownSecond);
this.smsCodeBtnLoading = true;
}
}, 1000);
}
/**
*
*/
submitRecharge() {
this.submitRechargeBtnLoading = true;
// tslint:disable-next-line:forin
for (const i in this.rechargeForm.controls) {
this.rechargeForm.controls[i].markAsDirty();
this.rechargeForm.controls[i].updateValueAndValidity();
}
this.rechargeParam.smsCode = this.rechargeForm.value['smsCode'];
if (this.rechargeForm.status == null || this.rechargeForm.status !== 'VALID') {
this.message.error('请填写所有必填项');
this.submitRechargeBtnLoading = false;
return;
}
this.apiMerchants.recharge(this.rechargeParam, data => {
if (data['return_code'] === '000000') {
this.message.success('充值成功');
this.rechargeForm.controls.merchantId.enable();
this.rechargeForm.controls.amount.enable();
this.rechargeForm = this.form.group({
merchantId: [null, [Validators.required]],
amount: [null, [Validators.required]],
smsCode: [null, [Validators.required]],
});
this.rechargeModal = false;
this.smsCodeBtnLoading = false;
this.getRequest(false, this.searchForm.value);
} else {
this.message.error(data['return_msg']);
}
this.submitRechargeBtnLoading = false;
});
}
// 请求商户充值记录
public getMerchRechargeData(id: number): void {
this.visible = true;
this.apiMerchants.getMerchRechargeData(id, data => {
if (data['return_code'] === '000000') {
this.merchRechargeData = data['return_data'];
} else {
this.message.error(data['return_msg']);
}
});
}
// 配置产品
public configProduct(id: number): void {
this.merchantId = id;
this.configVisible = true;
}
// 提交配置产品
handleOkConfig(): void {
// tslint:disable-next-line:forin
for (const i in this.configValidateForm.controls) {
this.configValidateForm.controls[i].markAsDirty();
this.configValidateForm.controls[i].updateValueAndValidity();
console.log(this.configValidateForm.controls[i]);
if (this.configValidateForm.controls[i].errors != null) {
this.message.error('必填项不能为空');
return;
}
}
this.configValidateForm.value['merchantsId'] = this.merchantId;
this.apiMerchants.configApiProduct(this.configValidateForm.value , data => {
if (data['return_code'] === '000000') {
this.configVisible = false;
this.message.success('提交成功');
} else {
this.message.error(data['return_msg']);
}
});
}
public showConfigProduct(id: number) {
this.visibleConfigProduct = true;
this.merchantId = id;
this.getListMerchProduct(id);
}
public getListMerchProduct(id: number): void {
this.apiProduct.getListMerchProduct(id , data => {
if (data['return_code'] === '000000') {
this.productConfigArray = data['return_data'];
} else {
this.message.error(data['return_msg']);
}
});
}
// 修改商品
public getEditConfigProduct(id: number) {
this.configProductId = id;
this.apiMerchants.findConfigProduct(id , data => {
if (data['return_code'] === '000000') {
this.discount = data['return_data']['discount'];
} else {
this.message.error(data['return_msg']);
}
});
this.editConfigVisible = true;
}
handleOkConfigEdit() {
if (this.discount == null) {
this.message.error('请输入折扣比例');
}
const prams = {
discount: this.discount,
id: this.configProductId
};
this.apiMerchants.updateConfigApiProduct(prams , data => {
if (data['return_code'] === '000000') {
this.editConfigVisible = false;
this.getListMerchProduct(this.merchantId);
} else {
this.message.error(data['return_msg']);
}
});
}
}

@ -0,0 +1,14 @@
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import {ListComponent} from './list/list.component';
const routes: Routes = [
{ path: 'list', component: ListComponent },
];
@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule]
})
export class ApiProductRoutingModule { }

@ -0,0 +1,29 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {ApiProductRoutingModule} from './api-product-routing.module';
import {ListComponent} from './list/list.component';
import {NgZorroAntdModule} from '_ng-zorro-antd@9.3.0@ng-zorro-antd';
import {SeparateModule} from '../../common/separate/separate.module';
import {FormsModule, ReactiveFormsModule} from '_@angular_forms@9.0.7@@angular/forms';
import {BreadcrumbModule} from '../../common/breadcrumb/breadcrumb.module';
import {RegionSelectorModule} from '../../common/region-selector/region-selector.module';
import {AppCommonModule} from '../../app-common.module';
@NgModule({
declarations: [ListComponent],
imports: [
CommonModule,
ApiProductRoutingModule,
NgZorroAntdModule,
SeparateModule,
ReactiveFormsModule,
FormsModule,
BreadcrumbModule,
RegionSelectorModule,
AppCommonModule
]
})
export class ApiProductModule {
}

@ -0,0 +1,195 @@
<!-- start 面包屑 -->
<app-breadcrumb></app-breadcrumb>
<!-- end 面包屑 -->
<!--条件搜索-->
<div class="inner-content">
<form nz-form [formGroup]="searchForm" (ngSubmit)="getRequest(true , searchForm.value)">
<div nz-row>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">运营商</nz-form-label>
<nz-form-control [nzSpan]="16">
<nz-select nzShowSearch nzAllowClear formControlName="operatorType" nzPlaceHolder="请选择运营商">
<nz-option nzLabel="电信运营商" nzValue="1"></nz-option>
<nz-option nzLabel="移动运营商" nzValue="2"></nz-option>
<nz-option nzLabel="联通运营商" nzValue="3"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">充值平台</nz-form-label>
<nz-form-control [nzSpan]="16">
<nz-select nzShowSearch nzAllowClear formControlName="rechargePlatform" nzPlaceHolder="请选择充值平台">
<nz-option nzLabel="尖椒" [nzValue]="1"></nz-option>
<nz-option nzLabel="龙阅" [nzValue]="2"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">话费充值类型</nz-form-label>
<nz-form-control [nzSpan]="16">
<nz-select nzShowSearch nzAllowClear formControlName="phoneRechargeType" nzPlaceHolder="请选择充值类型">
<nz-option nzLabel="快充" [nzValue]="1"></nz-option>
<nz-option nzLabel="慢充" [nzValue]="2"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="8">
<nz-form-item>
<nz-form-label [nzSpan]="6">状态</nz-form-label>
<nz-form-control [nzSpan]="16">
<nz-select nzShowSearch nzAllowClear formControlName="status" nzPlaceHolder="请选择状态">
<nz-option nzLabel="上线" [nzValue]="101"></nz-option>
<nz-option nzLabel="编辑中" [nzValue]="102"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
</div>
</div>
<div nz-row>
<div nz-col nzSpan="24" class="search-button">
<button nz-button nzType="primary"><i nz-icon nzType="search" nzTheme="outline"></i>搜索</button>
<button nz-button nzType="default" (click)="resetForm()"><i nz-icon nzType="reload"
nzTheme="outline"></i>重置
</button>
</div>
</div>
</form>
</div>
<div class="inner-content">
<span>共计 {{total}} 条数据</span>
<div class="operating-button">
<button nz-button nzType="primary" class="right-btn" (click)="getEdit()"><i nz-icon nzType="plus" nzTheme="outline"></i>添加</button>
</div>
<nz-table
class="table"
#ajaxTable
nzShowSizeChanger
[nzFrontPagination]="false"
[nzData]="requestData"
[nzLoading]="loading"
[nzTotal]="total"
[(nzPageIndex)]="pageNum"
[(nzPageSize)]="pageSize"
[nzScroll]="{ x: '1200px' }"
(nzPageIndexChange)="getRequest(false , searchForm.value)"
(nzPageSizeChange)="getRequest(false , searchForm.value)">
<thead nzSingleSort>
<tr>
<th nzWidth="60px">编号</th>
<th nzWidth="90px">充值金额</th>
<th nzWidth="120px">运营商</th>
<th nzWidth="90px">充值类型</th>
<th nzWidth="90px">充值平台</th>
<th nzWidth="80px">商品ID</th>
<th nzWidth="120px">创建时间</th>
<th nzRight nzWidth="80px">状态</th>
<th nzRight nzWidth="160px">操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of ajaxTable.data; let i = index">
<td>{{i + 1}}</td>
<td>{{data.rechargePrice}}</td>
<td>{{data.operatorType | rechargePrice}}</td>
<td>{{data.phoneRechargeType== 1? '快充':'慢充'}}</td>
<td>{{data.rechargePlatform | rechargePlatform}}</td>
<td>{{data.goodsId == null ? '暂未配置' : data.goodsId}}</td>
<td>{{data.createTime | date: 'yyyy-MM-dd HH:mm'}}</td>
<td nzRight="">{{data.status== 101? '上线':'编辑中'}}</td>
<td nzRight class="table-td-operation">
<a *ngIf="data.status != 101" (click)="getEdit(data.id)">编辑</a>
<a style="margin-left: 20px;" (click)='getForbiddenUser(data.id , data.status)'>{{data.status === 101 ? '下线': '上线'}}</a>
<a *ngIf="data.status != 101" style="margin-left: 20px;" (click)="getForbiddenUserD(data.id)">删除</a>
</td>
</tbody>
</nz-table>
</div>
<nz-modal [(nzVisible)]="isVisible" nzTitle="金额编辑" (nzOnCancel)="handleCancel()" (nzOnOk)="handleOk()">
<form nz-form [formGroup]="validateForm" class="login-form">
<nz-form-item *ngIf="!edit">
<nz-form-label [nzSpan]="6" nzRequired>充值金额</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请选择充值金额!">
<nz-select
nzMode="multiple"
formControlName="prices"
nzPlaceHolder="请选择充值金额"
>
<nz-option *ngFor="let item of priceArray" nzValue="{{item.codeValue}}" nzLabel="{{item.codeName}}"></nz-option>
</nz-select>
<input *ngIf="edit" readonly nz-input formControlName="rechargePrice" />
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="edit">
<nz-form-label [nzSpan]="6" nzRequired>充值金额</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入充值金额!">
<input readonly nz-input formControlName="rechargePrice" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>运营商</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请选择运营商!">
<nz-select nzShowSearch nzAllowClear formControlName="operatorType" nzPlaceHolder="请选择运营商!">
<nz-option nzLabel="电信运营商" [nzValue]="1"></nz-option>
<nz-option nzLabel="移动运营商" [nzValue]="2"></nz-option>
<nz-option nzLabel="联通运营商" [nzValue]="3"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>充值平台</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请选择充值平台!">
<nz-select nzShowSearch nzAllowClear nzMode="multiple" formControlName="rechargePlatform" nzPlaceHolder="请选择充值平台">
<nz-option *ngFor="let item of platformArray" [nzValue]="item.codeValue" [nzLabel]="item.codeName"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label [nzSpan]="6" nzRequired>充值类型</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请选择充值类型!">
<nz-select nzShowSearch nzAllowClear formControlName="phoneRechargeType" nzPlaceHolder="请选择充值类型">
<nz-option nzLabel="快充" [nzValue] ="1"></nz-option>
<nz-option nzLabel="慢充" [nzValue]="2"></nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="edit">
<nz-form-label [nzSpan]="6" >商品编号</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入商品编号!">
<nz-input-number
style="width: 200px"
formControlName="productId"
[nzMin]="1"
[nzMax]="10000"
[nzStep]="1"
nzPlaceHolder="请输入商品编号!"
></nz-input-number>
</nz-form-control>
</nz-form-item>
<nz-form-item *ngIf="edit">
<nz-form-label [nzSpan]="6" >排序</nz-form-label>
<nz-form-control [nzSpan]="16" nzErrorTip="请输入排序!">
<nz-input-number
style="width: 200px"
formControlName="sort"
[nzMin]="1"
[nzMax]="100"
[nzStep]="1"
nzPlaceHolder="请输入排序!"
></nz-input-number>
</nz-form-control>
</nz-form-item>
</form>
</nz-modal>

@ -0,0 +1,233 @@
import { Component, OnInit } from '@angular/core';
import {environment} from '../../../../environments/environment';
import {FormBuilder, FormGroup, Validators} from '_@angular_forms@9.0.7@@angular/forms';
import {RechargeService} from '../../../services/recharge.service';
import {DiscountService} from '../../../services/discount.service';
import {CouponService} from '../../../services/coupon.service';
import {IconService} from '../../../services/icon.service';
import {NzMessageService} from '_ng-zorro-antd@9.3.0@ng-zorro-antd';
import {Router} from '_@angular_router@9.0.7@@angular/router';
import {CommonsService} from '../../../services/commons.service';
import {ApiProductService} from '../../../services/api-product.service';
@Component({
selector: 'app-list',
templateUrl: './list.component.html',
styleUrls: ['./list.component.scss']
})
export class ListComponent implements OnInit {
WEB_SERVE_URL = environment.imageUrl;
searchForm: FormGroup; // 搜索框
validateForm: FormGroup; // 添加框
requestData = []; // 列表数据
priceArray = []; // 价格数据
platformArray = []; // 充值平台数据
total: number; // 页码
pageNum = 1; // 页码
pageSize = 10; // 条码
loading = true;
isVisible = false;
visible = false;
id: number;
edit = false;
payPrice;
isVisibleGoods = false;
realPrice = 0;
constructor(
private form: FormBuilder,
private recharge: RechargeService,
private discount: DiscountService,
private apiProduct: ApiProductService,
private coupon: CouponService,
private iconService: IconService,
private message: NzMessageService,
private router: Router,
private common: CommonsService
) {
}
ngOnInit(): void {
this.init();
this.getInitData();
}
// 获取初始化数据
private getInitData(): void {
// 获取价格数据
this.common.getDictionary('RECHARGE_AMOUNT ', data => {
this.priceArray = data['return_data'];
});
// 获取充值平台
this.common.getDictionary('RECHARGE_PLATFORM ', data => {
this.platformArray = data['return_data'];
});
}
public init(): void {
this.searchForm = this.form.group({
operatorType : [null],
rechargePlatform: [null],
phoneRechargeType: [null],
status: [null],
});
this.validateForm = this.form.group({
operatorType: [null, [Validators.required]],
prices: [null, [Validators.required]],
phoneRechargeType: [null, [Validators.required]],
rechargePlatform: [null, [Validators.required]],
rechargePrice: [null],
sort: [null],
productId: [null],
});
this.getRequest(true, this.searchForm.value);
}
// 查询列表
public getRequest(reset: boolean = false, whereObject: object) {
this.loading = false;
if (reset) {
this.pageNum = 1;
}
whereObject['pageNum'] = this.pageNum;
whereObject['pageSize'] = this.pageSize;
this.apiProduct.getListApiProduct(whereObject, data => {
if (data['return_code'] === '000000') {
this.requestData = data['return_data'].list;
this.total = data['return_data'].total;
} else {
this.message.error(data['return_msg']);
}
});
}
// 重置
public resetForm(): void {
this.searchForm.reset();
}
// 修改
public getEdit(id?: number): void {
if (id != null) {
this.id = id;
this.edit = true;
this.apiProduct.findById(id, data => {
data['return_data']['rechargePlatform'] = data['return_data']['rechargePlatform'].substring(1 , data['return_data']['rechargePlatform'].length - 1).split('-');
data['return_data']['prices'] = ['1'];
this.validateForm.patchValue(data['return_data']);
this.isVisible = true;
});
} else {
this.validateForm.reset();
this.isVisible = true;
this.edit = false;
}
}
handleOk(): void {
console.log(this.validateForm.value);
const wait = this.message.loading('提交中..', { nzDuration: 0 }).messageId;
// tslint:disable-next-line:forin
for (const i in this.validateForm.controls) {
this.validateForm.controls[i].markAsDirty();
this.validateForm.controls[i].updateValueAndValidity();
if (this.validateForm.controls[i].errors != null) {
this.message.error('必填项不能为空');
return;
}
}
if (this.edit) {
this.validateForm.value['id'] = this.id;
this.apiProduct.updateProduct(this.validateForm.value, data => {
this.message.remove(wait);
if (data['return_code'] === '000000') {
this.message.success('修改成功');
this.getRequest(false, this.searchForm.value);
this.validateForm.reset();
this.isVisible = false;
} else {
this.message.error(data['return_msg']);
}
});
} else {
this.apiProduct.insertProduct(this.validateForm.value, data => {
this.message.remove(wait);
if (data['return_code'] === '000000') {
this.message.success('添加成功');
this.getRequest(false, this.searchForm.value);
this.isVisible = false;
this.validateForm.reset();
} else {
this.message.error(data['return_msg']);
}
});
}
}
handleCancel(): void {
this.validateForm.reset();
this.isVisible = false;
}
public getForbiddenUser(idPost, status: any): void {
let statusPost;
let message;
switch (status) {
case 101:
statusPost = 102;
message = '是否下线';
break;
case 102:
statusPost = 101;
message = '是否上线';
break;
}
this.common.showConfirm(message, item => {
if (item) {
this.apiProduct.editProductStatus( {
id: idPost,
status: statusPost
}, data => {
if (data['return_code'] === '000000') {
this.message.success('提交成功');
this.getRequest(false, this.searchForm.value);
} else {
this.message.error(data['return_msg']);
}
});
}
});
}
public getForbiddenUserD(idPost): void {
this.common.showConfirm('是否删除', item => {
if (item) {
this.recharge.editPriceStatus({
id: idPost,
status: 0
}, data => {
if (data['return_code'] === '000000') {
this.message.success('删除成功');
this.getRequest(false, this.searchForm.value);
} else {
this.message.error(data['return_msg']);
}
});
}
});
}
}

@ -0,0 +1,18 @@
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'sourceType'
})
export class SourceTypePipe implements PipeTransform {
transform(value: number): string {
switch (value) {
case 1:
return '金额充值';
case 2:
return '订单退款';
case 3:
return '话费消费';
}
}
}

@ -0,0 +1,170 @@
import { Injectable } from '@angular/core';
import {HttpClient} from "_@angular_common@9.0.7@@angular/common/http";
import {CommonsService} from './commons.service';
import {environment} from "../../environments/environment";
@Injectable({
providedIn: 'root'
})
export class ApiMerchantsService {
constructor(
private http: HttpClient,
private common: CommonsService
) { }
/**
*
*
* @param paramsObject
* @param callBack
*/
public getListApiMerchant(paramsObject: object, callBack) {
this.http.get(environment.baseUrl + 'apiMerchants/getListApiMerchant?' + this.common.getWhereCondition(paramsObject)).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public updateMerchant(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiMerchants/updateMerchant', params).subscribe(data => {
callBack(data);
});
}
/**
*
*
* @param params
* @param callBack
* @return data
*/
public insertMerchant(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiMerchants/insertMerchant', params).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public editMerchantsStatus(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiMerchants/editMerchantsStatus', params).subscribe(data => {
callBack(data);
});
}
/**
* id查询详情
*
* @param id id
* @param callBack
*/
public findById(id: number, callBack) {
this.http.get(environment.baseUrl + 'apiMerchants/findById?id=' + id).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName getListApiMerchantByAll
* @Description // 查询所有正常商户
* @Date 15:03 2021/8/12
* @Param mod
**/
public getListApiMerchantByAll(callBack) {
this.http.get(environment.baseUrl + 'apiMerchants/getListApiMerchantByAll').subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName
* @Description // 商户充值
* @Date 14:28 2022/6/8
* @Param
*/
public recharge(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiMerchants/recharge', params).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName getMerchRechargeData
* @Description // 查询商户充值数据
* @Date 15:19 2022/6/8
* @Param id
*/
public getMerchRechargeData(id: number, callBack) {
this.http.get(environment.baseUrl + 'apiMerchants/getMerchRechargeData?merchantId=' + id).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName configApiProduct
* @Description // 提交配置产品
* @Date 18:00 2022/6/8
* @Param
*/
public configApiProduct(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/configApiProduct', params).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public editConfigApiProductByStatus(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/editConfigApiProductByStatus', params).subscribe(data => {
callBack(data);
});
}
/**
* id查询详情
*
* @param id id
* @param callBack
*/
public findConfigProduct(id: number, callBack) {
this.http.get(environment.baseUrl + 'apiProduct/findConfigProduct?id=' + id).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public updateConfigApiProduct(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/updateConfigApiProduct', params).subscribe(data => {
callBack(data);
});
}
}

@ -0,0 +1,97 @@
import { Injectable } from '@angular/core';
import {HttpClient} from '_@angular_common@9.0.7@@angular/common/http';
import {CommonsService} from './commons.service';
import {environment} from "../../environments/environment";
@Injectable({
providedIn: 'root'
})
export class ApiProductService {
constructor(
private http: HttpClient,
private common: CommonsService
) { }
/**
*
*
* @param paramsObject
* @param callBack
*/
public getListApiProduct(paramsObject: object, callBack) {
this.http.get(environment.baseUrl + 'apiProduct/getListApiProduct?' + this.common.getWhereCondition(paramsObject)).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public updateProduct(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/updateProduct', params).subscribe(data => {
callBack(data);
});
}
/**
*
*
* @param params
* @param callBack
* @return data
*/
public insertProduct(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/insertProduct', params).subscribe(data => {
callBack(data);
});
}
/**
* @Author Sum1Dream
* @methodName editPriceStatus
* @Description // 修改价格状态
* @Date 15:03 2021/8/12
* @Param mod
**/
public editProductStatus(params: object, callBack) {
this.http.post(environment.baseUrl + 'apiProduct/editProductStatus', params).subscribe(data => {
callBack(data);
});
}
/**
* id查询详情
*
* @param id id
* @param callBack
*/
public findById(id: number, callBack) {
this.http.get(environment.baseUrl + 'apiProduct/findById?id=' + id).subscribe(data => {
callBack(data);
});
}
/**
* id查询详情
*
* @param callBack
*/
public getAvailableApiProductByList(callBack) {
this.http.get(environment.baseUrl + 'apiProduct/getAvailableApiProductByList').subscribe(data => {
callBack(data);
});
}
public getListMerchProduct(id: number, callBack) {
this.http.get(environment.baseUrl + 'apiMerchants/getListMerchProduct?merchantId=' + id).subscribe(data => {
callBack(data);
});
}
}
Loading…
Cancel
Save