Browse Source

营养标准列表 & 菜品

main
kkerwin 2 years ago
parent
commit
d8eec9736e
  1. 15
      README.md
  2. 24
      projects/admin/src/app/components/dish-form/dish-form.component.html
  3. 59
      projects/admin/src/app/components/dish-form/dish-form.component.ts
  4. 3
      projects/admin/src/app/components/food-form/food-form.component.ts
  5. 33
      projects/admin/src/app/pages/dish/dish.component.html
  6. 58
      projects/admin/src/app/pages/dish/dish.component.ts
  7. 16
      projects/admin/src/app/pages/organization/organization-form/organization-form.component.html
  8. 7
      projects/admin/src/app/pages/organization/organization-form/organization-form.component.ts
  9. 104
      projects/admin/src/app/pages/organization/organization-list/organization-list.component.html
  10. 62
      projects/admin/src/app/pages/organization/organization-list/organization-list.component.ts
  11. 35
      projects/admin/src/app/pages/standard/standard-form/standard-form.component.html
  12. 40
      projects/admin/src/app/pages/standard/standard-form/standard-form.component.ts
  13. 183
      projects/admin/src/app/pages/standard/standard-setting/standard-setting.component.html
  14. 162
      projects/admin/src/app/pages/standard/standard-setting/standard-setting.component.ts
  15. 1
      projects/cdk/src/dtos/enum.dto.ts
  16. 53
      projects/cdk/src/services/api.service.ts
  17. 1
      projects/cdk/src/shared/components/index.ts
  18. 47
      projects/cdk/src/shared/components/search-and-select/search-and-select.component.html
  19. 17
      projects/cdk/src/shared/components/search-and-select/search-and-select.component.less
  20. 58
      projects/cdk/src/shared/components/search-and-select/search-and-select.component.ts
  21. 13
      projects/cdk/src/shared/shared.module.ts
  22. 1
      projects/cdk/src/table-list/table-list/table-list.component.ts
  23. 5
      projects/cdk/src/types/index.ts
  24. 12
      projects/cdk/src/utils/index.ts

15
README.md

@ -37,4 +37,17 @@ To get more help on the Angular CLI use `ng help` or go check out the [Angular C
5. 删除 食材 系统异常 5. 删除 食材 系统异常
6. 管理端 食材列表 没有返回 更新日期 6. 管理端 食材列表 没有返回 更新日期
7. 营养标准列表 列表 没有 更新时间 7. 营养标准列表 列表 没有 更新时间
8. 营养标准添加成功 需要 返回他的 id 8. 营养标准添加成功 需要 返回他的 id
------------------
# 09/17 下午
1. 获取企业列表 原型上 还有个 单位类型
2. 营养标准 需要 返回 单位名称和id vendors:[{label:"单位名称",id:vendorId}]
3. 根据关键字搜索单位 报错或者 返回空 list
----------------
1. 食材也需要一个 类似 /api/vender/select 的接口
2. 新增菜品 -> 食材名称 -> 食材标签 的枚举值 有哪些?
3. 单位修改 status 我调用的是编辑接口报错

24
projects/admin/src/app/components/dish-form/dish-form.component.html

@ -4,12 +4,23 @@
单位 单位
</nz-form-label> </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl"> <nz-form-control [nzErrorTip]="formControlErrorTpl">
<nz-select <!-- <nz-select
formControlName="unit" formControlName="unit"
[nzOptions]="[]" [nzOptions]="[]"
nzMode="multiple" nzMode="multiple"
nzPlaceHolder="请选择单位,多选时在多个单位均添加此菜品"> nzPlaceHolder="请选择单位,多选时在多个单位均添加此菜品">
</nz-select> -->
<nz-select
[nzMode]="'multiple'"
nzShowSearch
nzServerSearch
nzPlaceHolder="请选择单位,多选时在多个单位均添加此菜品"
[nzShowArrow]="false"
[nzFilterOption]="nzFilterOption"
(nzOnSearch)="search($event)">
<nz-option *ngFor="let o of listOfOption" [nzLabel]="o.text" [nzValue]="o.value"></nz-option>
</nz-select> </nz-select>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
@ -27,11 +38,10 @@
</nz-form-label> </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl"> <nz-form-control [nzErrorTip]="formControlErrorTpl">
<nz-select <nz-select
formControlName="tag" formControlName="mark"
[nzOptions]="[]"
nzPlaceHolder="请选择菜品标签"> nzPlaceHolder="请选择菜品标签">
<nz-option *ngFor="let item of globalEnum.mark" [nzValue]="item.value" [nzLabel]="item.key"></nz-option>
</nz-select> </nz-select>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
@ -64,10 +74,14 @@
菜品图片 菜品图片
</nz-form-label> </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl"> <nz-form-control [nzErrorTip]="formControlErrorTpl">
<input type="hidden" formControlName="icon" />
<div class="mb-2" *ngIf="icon">
<img [src]="icon" class="h-20 w-20" />
</div>
<button class="upload-btn " nz-button [nzLoading]="uploadLoading"> <button class="upload-btn " nz-button [nzLoading]="uploadLoading">
<i nz-icon nzType="upload"></i> <i nz-icon nzType="upload"></i>
上传图片 上传图片
<input type="file" formControlName="img" (change)="onFileChange($event)" /> <input type="file" (change)="onFileChange($event)" />
</button> </button>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>

59
projects/admin/src/app/components/dish-form/dish-form.component.ts

@ -1,5 +1,6 @@
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { FormArray, FormBuilder, FormGroup } from "@angular/forms"; import { FormArray, FormBuilder, FormGroup } from "@angular/forms";
import { ApiService } from "@cdk/services";
import { FormValidators } from "@cdk/validators"; import { FormValidators } from "@cdk/validators";
import { NzMessageService } from "ng-zorro-antd/message"; import { NzMessageService } from "ng-zorro-antd/message";
import { finalize } from "rxjs"; import { finalize } from "rxjs";
@ -10,10 +11,18 @@ import { finalize } from "rxjs";
styleUrls: ["./dish-form.component.less"], styleUrls: ["./dish-form.component.less"],
}) })
export class DishFormComponent { export class DishFormComponent {
constructor(private fb: FormBuilder, private msg: NzMessageService) {} constructor(private fb: FormBuilder, private msg: NzMessageService, private api: ApiService) {}
formGroup!: FormGroup; formGroup!: FormGroup;
selectedValue = null;
listOfOption: Array<{ value: string; text: string }> = [];
nzFilterOption = (): boolean => true;
globalEnum = this.api.globalEnum;
allMonth = [ allMonth = [
{ value: "1", label: "一月", checked: false }, { value: "1", label: "一月", checked: false },
{ value: "2", label: "二月", checked: false }, { value: "2", label: "二月", checked: false },
@ -41,18 +50,38 @@ export class DishFormComponent {
return this.formGroup.get("food") as FormArray; return this.formGroup.get("food") as FormArray;
} }
get icon() {
return this.formGroup.get("icon")?.value;
}
ngOnInit(): void { ngOnInit(): void {
this.formGroup = this.fb.group({ this.formGroup = this.fb.group({
id: this.fb.control("", [FormValidators.required()]), id: this.fb.control("", [FormValidators.required()]),
unit: this.fb.control([], [FormValidators.required()]), vendors: this.fb.control([], [FormValidators.required()]),
name: this.fb.control("", [FormValidators.required()]), name: this.fb.control("", [FormValidators.required()]),
img: this.fb.control("", []), icon: this.fb.control("", []),
tag: this.fb.control([], []), mark: this.fb.control([], []),
food: this.fb.array([], [FormValidators.required()]), food: this.fb.array([], [FormValidators.required()]),
month: this.fb.control([], []), month: this.fb.control([], []),
}); });
} }
search(value: string): void {
this.api
.getOrgList({ keyword: value })
.subscribe((data) => {
const listOfOption: Array<{ value: string; text: string }> = [];
data.body.forEach((item) => {
listOfOption.push({
value: item.id.toString(),
text: item.account,
});
});
this.listOfOption = listOfOption;
});
}
addFood() { addFood() {
this.food.push( this.food.push(
this.fb.group({ this.fb.group({
@ -98,25 +127,15 @@ export class DishFormComponent {
const target = e.target as HTMLInputElement; const target = e.target as HTMLInputElement;
const file = target.files![0]; const file = target.files![0];
target.value = ""; target.value = "";
const formData = new FormData(); if (file.size / 1024 / 1024 >= 5) {
this.msg.error("图片大小不能超过5M");
return;
}
const fileReader = new FileReader(); const fileReader = new FileReader();
fileReader.onload = () => { fileReader.onload = () => {
const base64 = fileReader.result as string; const base64 = fileReader.result as string;
this.formGroup.get("icon")?.setValue(base64);
const v = base64.split("base64,")[1];
}; };
formData.append("file", file); fileReader.readAsDataURL(file);
this.uploadLoading = true;
// this.api
// .uploadLogo(formData)
// .pipe(
// finalize(() => {
// this.uploadLoading = false;
// })
// )
// .subscribe((r) => {
// this.msg.success(r.desc);
// fileReader.readAsDataURL(file);
// });
} }
} }

3
projects/admin/src/app/components/food-form/food-form.component.ts

@ -33,6 +33,7 @@ export class FoodFormComponent implements OnInit {
}); });
if (this.food) { if (this.food) {
this.formGroup.patchValue(this.food); this.formGroup.patchValue(this.food);
this.formGroup.get("key")?.disable();
this.food.nutrientArr?.forEach((n: any) => { this.food.nutrientArr?.forEach((n: any) => {
this.createNutrition(n); this.createNutrition(n);
}); });
@ -78,7 +79,7 @@ export class FoodFormComponent implements OnInit {
public getValues() { public getValues() {
let values = null; let values = null;
if (Utils.validateFormGroup(this.formGroup)) { if (Utils.validateFormGroup(this.formGroup)) {
const { _nutrition, key, name, type } = this.formGroup.value; const { _nutrition, key, name, type } = this.formGroup.getRawValue();
let nutrient = Object.create(null); let nutrient = Object.create(null);
if (Array.isArray(_nutrition)) { if (Array.isArray(_nutrition)) {
for (const n of _nutrition) { for (const n of _nutrition) {

33
projects/admin/src/app/pages/dish/dish.component.html

@ -1,7 +1,7 @@
<app-page> <app-page>
<ng-template #pageExtraTpl> <ng-template #pageExtraTpl>
<nz-space> <nz-space>
<button *nzSpaceItem nz-button>批量删除</button> <button *nzSpaceItem nz-button [disabled]="!selectedIds.length" (click)="deleteItem()">批量删除</button>
<button *nzSpaceItem nz-button>批量打印营养标签</button> <button *nzSpaceItem nz-button>批量打印营养标签</button>
<button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()"> <button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()">
<i nz-icon nzType="plus"></i> <i nz-icon nzType="plus"></i>
@ -15,9 +15,7 @@
<table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" [formGroup]="queryForm" <table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" [formGroup]="queryForm"
[renderColumns]="renderColumnsTpl"> [renderColumns]="renderColumnsTpl">
<ng-template #actionTpl>
<button nz-button>批量删除</button>
</ng-template>
<ng-template #searchTpl> <ng-template #searchTpl>
<nz-form-item class="w-40"> <nz-form-item class="w-40">
<nz-form-control> <nz-form-control>
@ -26,7 +24,11 @@
</nz-form-item> </nz-form-item>
<nz-form-item class="w-40"> <nz-form-item class="w-40">
<nz-form-control> <nz-form-control>
<nz-select nzPlaceHolder="菜品标签" [nzOptions]="[]"></nz-select> <nz-select nzPlaceHolder="菜品标签" formControlName="mark" nzAllowClear>
<nz-option *ngFor="let item of globalEnum.mark" [nzLabel]="item.key"
[nzValue]="item.key">
</nz-option>
</nz-select>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>
@ -37,11 +39,28 @@
</ng-template> </ng-template>
<ng-template #renderColumnsTpl let-data let-key="key" let-row="row"> <ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<ng-container [ngSwitch]="key"> <ng-container [ngSwitch]="key">
<ng-container *ngSwitchCase="'img'"> <ng-container *ngSwitchCase="'icon'">
<div class="dish-img overflow-auto" <div class="dish-img overflow-auto"
[ngStyle]="{'background-image':'url(' + tempImg + ')'}"> [ngStyle]="{'background-image':'url(' + data + ')'}">
</div> </div>
</ng-container> </ng-container>
<ng-container *ngSwitchCase="'vender'">
{{tableOrg[data] || '-'}}
</ng-container>
<ng-container *ngSwitchCase="'foodArr'">
<a nz-popover
[nzPopoverContent]="popoverTpl"
nzPopoverTitle="食材及含量">
<b>{{data.length}}</b>中营养素
</a>
<ng-template #popoverTpl>
<div class=" max-w-sm max-h-60 overflow-auto">
<nz-tag *ngFor="let item of data" class="m-1">
{{item.label}}:{{item.value}}{{item.measurement}}
</nz-tag>
</div>
</ng-template>
</ng-container>
<ng-container *ngSwitchDefault> <ng-container *ngSwitchDefault>
{{data}} {{data}}

58
projects/admin/src/app/pages/dish/dish.component.ts

@ -4,7 +4,9 @@ import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api"; import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components"; import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@cdk/services"; import { ApiService } from "@cdk/services";
import { Subject } from "rxjs"; import { Subject, lastValueFrom, tap } from "rxjs";
import { NzModalService } from "ng-zorro-antd/modal";
import { NzMessageService } from "ng-zorro-antd/message";
@Component({ @Component({
selector: "app-dish", selector: "app-dish",
@ -12,7 +14,12 @@ import { Subject } from "rxjs";
styleUrls: ["./dish.component.less"], styleUrls: ["./dish.component.less"],
}) })
export class DishComponent { export class DishComponent {
constructor(private drawer: NzDrawerService, private api: ApiService) {} constructor(
private drawer: NzDrawerService,
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService
) {}
@ViewChild("formFooterTpl") formFooterTpl!: TemplateRef<{}>; @ViewChild("formFooterTpl") formFooterTpl!: TemplateRef<{}>;
@ -20,16 +27,22 @@ export class DishComponent {
private destroy$ = new Subject<void>(); private destroy$ = new Subject<void>();
tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg"; public globalEnum = this.api.globalEnum;
public tableList = new TableListOption(this.fetchData.bind(this), { public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true, selectable: true,
frontPagination: false,
}); });
public queryForm = new FormGroup({ public queryForm = new FormGroup({
name: new FormControl(""), name: new FormControl(""),
mark: new FormControl(""),
}); });
public selectedIds: string[] = [];
tableOrg: { [k: number]: string } = {};
ngOnInit(): void { ngOnInit(): void {
this.initTableList(); this.initTableList();
} }
@ -42,11 +55,11 @@ export class DishComponent {
initTableList() { initTableList() {
this.tableList.scroll = { x: null }; this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([ this.tableList = this.tableList.setColumns([
{ key: "img", title: "菜品图片", width: "66px" }, { key: "icon", title: "菜品图片", width: "66px" },
{ key: "name", title: "菜品名称" }, { key: "name", title: "菜品名称" },
{ key: "name", title: "菜品标签" }, { key: "marks", title: "菜品标签" },
{ key: "name", title: "食材及含量", width: "30%" }, { key: "foodArr", title: "食材及含量", width: "30%" },
{ key: "name", title: "单位" }, { key: "vender", title: "单位" },
]); ]);
this.tableList = this.tableList.setOptions([ this.tableList = this.tableList.setOptions([
@ -69,7 +82,22 @@ export class DishComponent {
} }
fetchData(query: AnyObject, pager: AnyObject) { fetchData(query: AnyObject, pager: AnyObject) {
return this.api.page(pager, query); return this.api.getDishPage(pager, query).pipe(
tap((res) => {
if (Array.isArray(res.body.content)) {
this.api.getOrgList({ vendors: res.body.content.map((i) => i.vender) }).subscribe((org) => {
if (Array.isArray(org.body)) {
this.tableOrg = org.body.reduce((a, c) => {
return {
...a,
[c.id]: c.account,
};
}, {} as AnyObject);
}
});
}
})
);
} }
showFoodForm(food?: any) { showFoodForm(food?: any) {
@ -85,5 +113,17 @@ export class DishComponent {
this.drawerRef?.close(); this.drawerRef?.close();
} }
deleteItem() {} deleteItem(v?: any) {
const ids = v ? [v.key] : this.selectedIds;
this.modal.confirm({
nzTitle: "警告",
nzContent: `是否要删除${ids.length}个菜品?`,
nzOkDanger: true,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteDish(ids));
this.msg.success(res.desc);
this.tableList.run();
},
});
}
} }

16
projects/admin/src/app/pages/organization/organization-form/organization-form.component.html

@ -10,6 +10,18 @@
<input nz-input placeholder="请输入单位名称" formControlName="name" /> <input nz-input placeholder="请输入单位名称" formControlName="name" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item>
<nz-form-label nzRequired>
单位类型
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl" nzSpan="12">
<nz-select nzPlaceHolder="单位类型" formControlName="category">
<nz-option *ngFor="let item of globalEnum.venderType" [nzValue]="item.value"
[nzLabel]="item.key">
</nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item> <nz-form-item>
<nz-form-label> <nz-form-label>
单位Logo 单位Logo
@ -71,7 +83,7 @@
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired> <nz-form-label [nzRequired]="!state">
初始密码 初始密码
</nz-form-label> </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl" nzSpan="12"> <nz-form-control [nzErrorTip]="formControlErrorTpl" nzSpan="12">
@ -96,7 +108,7 @@
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()"> <button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()">
确定 确定
</button> </button>
<button *nzSpaceItem nz-button> <button *nzSpaceItem nz-button [routerLink]="['/organization/list']">
取消 取消
</button> </button>
</nz-space> </nz-space>

7
projects/admin/src/app/pages/organization/organization-form/organization-form.component.ts

@ -39,6 +39,8 @@ export class OrganizationFormComponent {
uploadLoading = false; uploadLoading = false;
globalEnum = this.api.globalEnum;
get icon() { get icon() {
return this.formGroup.get("icon")?.value; return this.formGroup.get("icon")?.value;
} }
@ -47,8 +49,9 @@ export class OrganizationFormComponent {
this.formGroup = this.fb.group({ this.formGroup = this.fb.group({
id: this.fb.control(""), id: this.fb.control(""),
account: this.fb.control("", [FormValidators.required("账号不能为空")]), account: this.fb.control("", [FormValidators.required("账号不能为空")]),
password: this.fb.control("", [FormValidators.required("密码不能为空")]), password: this.fb.control("", this.state ? [] : [FormValidators.required("密码不能为空")]),
name: this.fb.control("", [FormValidators.required("单位名称不能为空")]), name: this.fb.control("", [FormValidators.required("单位名称不能为空")]),
category: this.fb.control("", [FormValidators.required("单位类型不能为空")]),
expire: this.fb.control("", [FormValidators.required("账号到期时间不能为空")]), expire: this.fb.control("", [FormValidators.required("账号到期时间不能为空")]),
icon: this.fb.control("", []), icon: this.fb.control("", []),
address: this.fb.control("", []), address: this.fb.control("", []),
@ -62,7 +65,7 @@ export class OrganizationFormComponent {
async onSubmit() { async onSubmit() {
if (Utils.validateFormGroup(this.formGroup)) { if (Utils.validateFormGroup(this.formGroup)) {
const org = { ...(this.state ?? {}), ...this.formGroup.value }; const org = { ...(this.state ?? {}), ...this.formGroup.value };
org["password"] = MD5(org.password!).toString().substring(16).toUpperCase(); org["password"] = org.password ? MD5(org.password!).toString().substring(16).toUpperCase() : void 0;
org["expire"] = format(org["expire"], "yyyy-MM-dd"); org["expire"] = format(org["expire"], "yyyy-MM-dd");
org["venderId"] = org.id; org["venderId"] = org.id;
// const account = await lastValueFrom(this.api.checkOrgAccount(org.account!)); // const account = await lastValueFrom(this.api.checkOrgAccount(org.account!));

104
projects/admin/src/app/pages/organization/organization-list/organization-list.component.html

@ -5,70 +5,62 @@
<nz-card [nzBordered]="false" nzTitle="单位列表"> <nz-card [nzBordered]="false" nzTitle="单位列表">
<ng-template #pageExtraTpl>
<nz-space>
<button *nzSpaceItem nz-button nzType="primary" [routerLink]="['/','organization','form','create']">
<i nz-icon nzType="plus"></i>
新增单位
</button>
</nz-space>
</ng-template>
<div class="flex items-center justify-between "> <table-list [props]="tableList"
<div nz-row [nzGutter]="12"> [search]="searchTpl"
<nz-form-item class="w-52 mr-2"> [action]="pageExtraTpl"
[formGroup]="queryForm"
[renderColumns]="renderColumnsTpl">
<ng-template #searchTpl>
<nz-form-item class="w-52 ">
<nz-form-control> <nz-form-control>
<nz-select nzPlaceHolder="单位类型" [nzOptions]="[]"></nz-select> <nz-select nzPlaceHolder="单位类型" formControlName="category">
<nz-option *ngFor="let item of globalEnum.venderType" [nzValue]="item.value"
[nzLabel]="item.key">
</nz-option>
</nz-select>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item class="w-60"> <nz-form-item class="w-60">
<nz-form-control> <nz-form-control>
<input nz-input placeholder="请输入单位名称" /> <input nz-input placeholder="请输入单位名称" formControlName="keyword" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</div> </ng-template>
<nz-space> <ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<button *nzSpaceItem nz-button nzType="primary" [routerLink]="['/','organization','form','create']"> <ng-container [ngSwitch]="key">
<i nz-icon nzType="plus"></i> <ng-container *ngSwitchCase="'expire'">
新增单位 {{ data|date:'yyyy-MM-dd HH:mm:ss'}}
</button> </ng-container>
</nz-space> <ng-container *ngSwitchCase="'status'">
</div> <div (click)="toggleStatus(row)">
<div class="pointer-events-none">
<nz-table #table [nzData]="orgList"> <nz-switch
<thead> nzCheckedChildren="开"
<tr> nzUnCheckedChildren="关"
<th>账号</th> [ngModel]="data"
<th>单位名称</th> [ngModelOptions]="{standalone: true}">
<th>单位类型</th> </nz-switch>
<th>地址</th> </div>
<th>联系人</th> </div>
<th>联系电话</th> </ng-container>
<th>账号使用状态</th> <ng-container *ngSwitchCase="'address'">
<th>账号到期时间</th> <td>{{row.province}}{{row.area}}{{data}}</td>
<th>操作</th> </ng-container>
</tr> <ng-container *ngSwitchDefault>
</thead> {{data}}
<tbody> </ng-container>
<tr *ngFor="let data of table.data"> </ng-container>
<td>{{data.account}}</td> </ng-template>
<td>{{data.name}}</td> </table-list>
<td>{{data.category}}</td>
<td>{{data.province}}{{data.area}}{{data.address}}</td>
<td>{{data.contacts}}</td>
<td>{{data.phone}}</td>
<td>
<nz-switch
nzCheckedChildren="开"
nzUnCheckedChildren="关"
[(ngModel)]="data.status">
</nz-switch>
</td>
<td>{{data.expire | date:'yyyy-MM-dd'}}</td>
<td>
<a (click)="toEdit(data)">编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="deleteItem(data.id)">删除</a>
</td>
</tr>
</tbody>
</nz-table>
</nz-card> </nz-card>
</div> </div>
</app-page> </app-page>

62
projects/admin/src/app/pages/organization/organization-list/organization-list.component.ts

@ -23,19 +23,51 @@ export class OrganizationListComponent {
private msg: NzMessageService private msg: NzMessageService
) {} ) {}
originalOrgList: OrgDTO[] = []; public tableList = new TableListOption(this.fetchData.bind(this), {
frontPagination: false,
});
orgList: OrgDTO[] = []; globalEnum = this.api.globalEnum;
public queryForm = new FormGroup({
category: new FormControl(""),
keyword: new FormControl(""),
});
ngOnInit(): void { ngOnInit(): void {
this.getList(); this.initTableList();
} }
getList() { initTableList() {
this.api.getOrgList().subscribe((res) => { this.tableList.scroll = { x: null };
this.originalOrgList = res.body ?? [];
this.orgList = this.originalOrgList; this.tableList = this.tableList.setColumns([
}); { key: "account", title: "账号" },
{ key: "name", title: "单位名称" },
{ key: "category", title: "单位类型" },
{ key: "address", title: "地址" },
{ key: "contacts", title: "联系人" },
{ key: "phone", title: "联系电话" },
{ key: "status", title: "账号使用状态" },
{ key: "expire", title: "账号到期时间" },
]);
this.tableList = this.tableList.setOptions([
{
title: "编辑",
premissions: [],
onClick: this.toEdit.bind(this),
},
{
title: "删除",
premissions: [],
onClick: this.deleteItem.bind(this),
},
]);
}
fetchData(p: {}, q: {}) {
return this.api.getOrgPage(p, q);
} }
toEdit(org: OrgDTO) { toEdit(org: OrgDTO) {
@ -44,17 +76,23 @@ export class OrganizationListComponent {
}); });
} }
deleteItem(id: number) { toggleStatus(v: any) {
this.api.saveOrg({ ...v, venderId: v.id, status: !v.status }).subscribe((res) => {
this.msg.success(res.desc);
this.tableList.run();
});
}
deleteItem(v: any) {
this.modal.confirm({ this.modal.confirm({
nzTitle: "警告", nzTitle: "警告",
nzContent: "是否要删除该单位?", nzContent: "是否要删除该单位?",
nzOkDanger: true, nzOkDanger: true,
nzOnOk: async () => { nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteOrg(id)); const res = await lastValueFrom(this.api.deleteOrg(v.id));
if (res.success) { if (res.success) {
this.msg.success(res.desc); this.msg.success(res.desc);
this.originalOrgList = this.originalOrgList.filter((f) => f.id !== id); this.tableList.run();
this.orgList = this.orgList.filter((f) => f.id !== id);
return true; return true;
} }
return false; return false;

35
projects/admin/src/app/pages/standard/standard-form/standard-form.component.html

@ -24,12 +24,37 @@
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired> <nz-form-label nzRequired>
适用单位 <div class="flex justify-between items-center w-full">
<span class="flex-1">适用单位</span>
<ng-template #searchAndSelectTpl>
<div class=" w-96">
<search-and-select [handleSearh]="searchOrg"
(onSelect)="onSelectOrg($event)">
</search-and-select>
</div>
</ng-template>
<button type="button" nz-button nzType="link"
nz-popover
[nzPopoverVisible]="nzPopoverVisible"
(click)="nzPopoverVisible = !nzPopoverVisible"
nzPopoverPlacement="right"
nzPopoverTrigger="click"
[nzPopoverContent]="searchAndSelectTpl">
<i nz-icon nzType="plus"></i>
添加单位
</button>
</div>
</nz-form-label> </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl" nzSpan="12"> <nz-form-control [nzErrorTip]="formControlErrorTpl" nzSpan="12">
<nz-select nzMode="multiple" nzPlaceHolder="请选择适用单位" formControlName="vendors"> <div>
<nz-option *ngFor="let org of orgList" [nzValue]="org.id" [nzLabel]="org.name"></nz-option> <nz-tag class="px-3 py-1" *ngFor="let v of vendors">
</nz-select> <span>{{v.label}}</span>
<a (click)="removeOrg(v.value)">
<i nz-icon nzType="close"></i>
</a>
</nz-tag>
</div>
<input type="hidden" formControlName="vendors" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>
@ -41,7 +66,7 @@
</button> </button>
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()" <button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()"
[nzLoading]="submitLoading"> [nzLoading]="submitLoading">
确定 保存
</button> </button>
<button *nzSpaceItem nz-button type="button" [routerLink]="['/standard/list']"> <button *nzSpaceItem nz-button type="button" [routerLink]="['/standard/list']">
取消 取消

40
projects/admin/src/app/pages/standard/standard-form/standard-form.component.ts

@ -3,10 +3,11 @@ import { FormBuilder, FormGroup } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { OrgDTO } from "@cdk/dtos"; import { OrgDTO } from "@cdk/dtos";
import { ApiService } from "@cdk/services"; import { ApiService } from "@cdk/services";
import { OptionItemInterface } from "@cdk/types";
import { Utils } from "@cdk/utils"; import { Utils } from "@cdk/utils";
import { FormValidators } from "@cdk/validators"; import { FormValidators } from "@cdk/validators";
import { NzMessageService } from "ng-zorro-antd/message"; import { NzMessageService } from "ng-zorro-antd/message";
import { finalize } from "rxjs"; import { finalize, map } from "rxjs";
@Component({ @Component({
selector: "app-standard-form", selector: "app-standard-form",
@ -40,10 +41,11 @@ export class StandardFormComponent {
state: any; state: any;
nzPopoverVisible = false;
vendors: OptionItemInterface[] = [];
ngOnInit(): void { ngOnInit(): void {
this.api.getOrgList().subscribe((res) => {
this.orgList = res.body;
});
this.formGroup = this.fb.group({ this.formGroup = this.fb.group({
id: this.fb.control("", []), id: this.fb.control("", []),
name: this.fb.control("", [FormValidators.required()]), name: this.fb.control("", [FormValidators.required()]),
@ -52,11 +54,39 @@ export class StandardFormComponent {
}); });
this.formGroup.patchValue(this.state); this.formGroup.patchValue(this.state);
} }
searchOrg = (k: string) => {
return this.api.getOrgList({ keyword: k }).pipe(
map((res) => {
return res.body.map((i) => ({
label: i.name,
value: i.id + "",
}));
})
);
};
onSelectOrg(v: OptionItemInterface[]) {
v.forEach((i) => {
if (!this.vendors.some((s) => s.value === i.value)) {
this.vendors.push(i);
}
});
this.formGroup.get("vendors")?.setValue(this.vendors.map((i) => i.value));
this.nzPopoverVisible = false;
}
removeOrg(v: string) {
this.vendors = this.vendors.filter((f) => f.value !== v);
this.formGroup.get("vendors")?.setValue(this.vendors.map((i) => i.value));
}
onSubmit(gotoSetting?: boolean) { onSubmit(gotoSetting?: boolean) {
if (Utils.validateFormGroup(this.formGroup)) { if (Utils.validateFormGroup(this.formGroup)) {
this.submitLoading = true; this.submitLoading = true;
const { foodCategoryDay, foodCategoryWeek, ingredient } = this.state;
this.api this.api
.saveStandard(this.formGroup.value) .saveStandard({ foodCategoryDay, foodCategoryWeek, ingredient, ...this.formGroup.value })
.pipe( .pipe(
finalize(() => { finalize(() => {
this.submitLoading = false; this.submitLoading = false;

183
projects/admin/src/app/pages/standard/standard-setting/standard-setting.component.html

@ -1,53 +1,198 @@
<app-page> <app-page>
<form nz-form [formGroup]="formGroup" nzLayout="vertical"> <div nz-form nzLayout="vertical">
<nz-card nzTitle="食物种类及数量标准设置"> <nz-card nzTitle="食物种类及数量标准设置">
<div nz-row [nzGutter]="12"> <div nz-row [nzGutter]="12">
<div nz-col nzSpan="12"> <div nz-col nzSpan="24">
<ng-template #dayBtnTpl> <ng-template #dayBtnTpl>
<button nz-button nzType="link"> <button nz-button nzType="link" (click)="addFoodType('day')">
添加食物种类 添加食物种类
</button> </button>
</ng-template> </ng-template>
<nz-card nzType="inner" nzTitle="食物种类及数量标准(日)" [nzExtra]="dayBtnTpl"> <nz-card nzType="inner" nzTitle="食物种类及数量标准(日)" [nzExtra]="dayBtnTpl">
<div class=" overflow-y-auto h-80"> <nz-empty *ngIf="foodCategoryDay.length === 0"></nz-empty>
add <div *ngIf="foodCategoryDay.length > 0">
<div class="">
<div nz-row [nzGutter]="[48,12]">
<div nz-col nzSpan="12" *ngFor="let d of foodCategoryDay">
<div class="flex items-center">
<div class="w-60">
<nz-select nzPlaceHolder="请选择" class="w-full" [(ngModel)]="d.type">
<nz-option *ngFor="let cate of globalEnum.category"
[nzLabel]="cate.key"
[nzValue]="cate.value">
</nz-option>
</nz-select>
</div>
<div class="flex-1 mx-3">
<nz-input-group nzAddOnBefore="大于等于" nzAddOnAfter="种">
<input type="number" min="0" nz-input [(ngModel)]="d.value" />
</nz-input-group>
</div>
<div>
<button nz-button nzType="link" (click)="removeFoodType('day',d.type)">
<i nz-icon nzType="delete"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</nz-card> </nz-card>
</div> </div>
<div nz-col nzSpan="12"> <div nz-col nzSpan="24" class="mt-4">
<ng-template #weekBtnTpl> <ng-template #weekBtnTpl>
<button nz-button nzType="link"> <button nz-button nzType="link" (click)="addFoodType('week')">
添加食物种类 添加食物种类
</button> </button>
</ng-template> </ng-template>
<nz-card nzType="inner" nzTitle="食物种类及数量标准(周)" [nzExtra]="weekBtnTpl"> <nz-card nzType="inner" nzTitle="食物种类及数量标准(周)" [nzExtra]="weekBtnTpl">
<div class=" overflow-y-auto h-80"> <nz-empty *ngIf="foodCategoryWeek.length === 0"></nz-empty>
<div *ngIf="foodCategoryWeek.length > 0">
<div class="">
<div nz-row [nzGutter]="[48,12]">
<div nz-col nzSpan="12" *ngFor="let w of foodCategoryWeek">
<div class="flex items-center">
<div class="w-60">
<nz-select nzPlaceHolder="请选择" class="w-full" [(ngModel)]="w.type">
<nz-option *ngFor="let cate of globalEnum.category"
[nzLabel]="cate.key"
[nzValue]="cate.value">
</nz-option>
</nz-select>
</div>
<div class="flex-1 mx-3">
<nz-input-group nzAddOnBefore="大于等于" nzAddOnAfter="种">
<input type="number" nz-input [(ngModel)]="w.value" />
</nz-input-group>
</div>
<div>
<button nz-button nzType="link" (click)="removeFoodType('week',w.type)">
<i nz-icon nzType="delete"></i>
</button>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</nz-card> </nz-card>
</div> </div>
</div> </div>
</nz-card> </nz-card>
<nz-card nzTitle="人群名称1" class="mt-4"></nz-card> <nz-card nzTitle="营养标准人群设置" class="mt-4 mb-20" [nzExtra]="peopleExtraTpl">
<nz-empty *ngIf="ingredient.length === 0"></nz-empty>
<ng-container *ngIf="ingredient.length > 0">
<nz-card
nzType="inner"
[nzTitle]="peopleGroupNamTpl"
[nzExtra]="peopleActionTpl"
*ngFor="let p of ingredient;let i = index"
class="mt-4">
<ng-template #peopleGroupNamTpl>
<div class="flex items-center">
<span class="mr-2">
人群名称:
</span>
<div class=" w-80">
<input nz-input placeholder="请输入人群名称" [(ngModel)]="p.name" />
</div>
</div>
</ng-template>
<ng-template #peopleActionTpl>
<nz-space>
<button *nzSpaceItem nz-button nzType="link" (click)="addNutrition(i)">
添加营养素
</button>
<button *nzSpaceItem nz-button nzType="link" (click)="removePeopleGroup(i)" nzDanger>
删除人群
</button>
</nz-space>
</ng-template>
<nz-empty *ngIf="p.nutritions.length === 0"></nz-empty>
<ng-container *ngIf="p.nutritions.length > 0">
<h3 class="mb-2">每人每天能量和营养素供给量标准</h3>
<div nz-row [nzGutter]="[48,12]">
<div nz-col nzSpan="12" *ngFor="let n of p.nutritions">
<div class="flex items-center">
<div class="w-40">
<nz-select nzPlaceHolder="请选择" class="w-full" [(ngModel)]="n.nutrition">
<nz-option *ngFor="let cate of globalEnum.nutrient "
[nzLabel]="cate.value + '(' + cate.measurement + ')'"
[nzValue]="cate.key">
</nz-option>
</nz-select>
</div>
<div class="flex-1 ml-2">
<nz-space nzAlign="center">
<input *nzSpaceItem nz-input placeholder="最小值" type="number"
[(ngModel)]="n.min" />
<div class="px-2" *nzSpaceItem>
~
</div>
<input *nzSpaceItem nz-input placeholder="最大值" type="number"
[(ngModel)]="n.max" />
</nz-space>
</div>
<div class="ml-2">
<nz-space nzAlign="center">
<div class="pr-3" *nzSpaceItem>
<label nz-checkbox [(ngModel)]="n.hasUl"
(ngModelChange)="ulChange(i,n.nutrition,$event)">
ul值
</label>
</div>
<div *nzSpaceItem class="w-20">
<ng-container>
<input [disabled]="!n.hasUl"
nz-input
placeholder=" ul值"
type="number"
[(ngModel)]="n.ul" />
</ng-container>
</div>
</nz-space>
</div>
<div class="ml-2">
<button nz-button nzType="link" (click)="removeNutrition(i,n.nutrition)">
<i nz-icon nzType="delete"></i>
</button>
</div>
</div>
</div>
</div>
</ng-container>
</nz-card>
</ng-container>
</nz-card>
<ng-template #peopleExtraTpl>
<button nz-button (click)="addPeopleGroup()">
添加人群
</button>
</ng-template>
</form> </div>
<ng-template #formControlErrorTpl let-control> <ng-template #formControlErrorTpl let-control>
<form-error-tips [control]="control"></form-error-tips> <form-error-tips [control]="control"></form-error-tips>
</ng-template> </ng-template>
<div class="fixed-footter left-[218px] fixed bottom-0 right-0 bg-white z-10 pl-8 py-2"> <div class="fixed-footter left-[218px] fixed bottom-0 right-0 bg-white z-10 py-2 ">
<nz-space> <div class="flex justify-center">
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()"> <nz-space>
确定 <button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()">
</button> 确定
<button *nzSpaceItem nz-button> </button>
取消 <button *nzSpaceItem nz-button>
</button> 取消
</nz-space> </button>
</nz-space>
</div>
</div> </div>
</app-page> </app-page>

162
projects/admin/src/app/pages/standard/standard-setting/standard-setting.component.ts

@ -2,9 +2,23 @@ import { Component } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms"; import { FormBuilder, FormGroup } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from "@angular/router";
import { ApiService } from "@cdk/services"; import { ApiService } from "@cdk/services";
import { AnyObject } from "@cdk/types";
import { FormValidators } from "@cdk/validators"; import { FormValidators } from "@cdk/validators";
import { NzMessageService } from "ng-zorro-antd/message"; import { NzMessageService } from "ng-zorro-antd/message";
export type StandardItemInterface = {
type: string;
value: number;
};
export type StandardPeopleInterface = {
ul?: number;
hasUl: boolean;
max: number;
min: number;
nutrition: string;
};
@Component({ @Component({
selector: "app-standard-setting", selector: "app-standard-setting",
templateUrl: "./standard-setting.component.html", templateUrl: "./standard-setting.component.html",
@ -29,15 +43,155 @@ export class StandardSettingComponent {
} }
} }
state: any; public globalEnum = this.api.globalEnum;
formGroup!: FormGroup; state: any;
uploadLoading = false; uploadLoading = false;
ngOnInit(): void {} foodCategoryDay: StandardItemInterface[] = [];
foodCategoryWeek: StandardItemInterface[] = [];
ingredient: { name: string; nutritions: StandardPeopleInterface[] }[] = [];
ngOnInit(): void {
this.foodCategoryDay = this.parseFoodCategory(this.state?.foodCategoryDay);
this.foodCategoryWeek = this.parseFoodCategory(this.state?.foodCategoryWeek);
this.ingredient = this.parseIngredient(this.state?.ingredient);
}
parseIngredient(data: any): { name: string; nutritions: StandardPeopleInterface[] }[] {
if (!data) {
return [];
}
return Object.entries(data).map(([k, v]) => {
return {
name: k,
nutritions: !v
? []
: Object.entries(v).map(([kn, vn]) => {
return {
ul: vn?.ul ?? 0,
hasUl: !!vn?.ul,
max: vn?.max ?? 0,
min: vn?.min ?? 0,
nutrition: kn,
};
}),
};
});
}
parseFoodCategory(data: any): StandardItemInterface[] {
if (!data) {
return [];
}
return Object.entries(data).map(([k, v]) => {
return {
type: k,
value: v as number,
};
});
}
onSubmit() { onSubmit() {
this.router.navigate(["/", "standard", "setting", "45"]); if (this.foodCategoryDay.some((s) => !s.type || !s.value)) {
this.msg.error("请设置正确的食物种类及数量标准(日)");
return;
}
if (this.foodCategoryWeek.some((s) => !s.type || !s.value)) {
this.msg.error("请设置正确的食物种类及数量标准(周)");
return;
}
if (this.ingredient.some((s) => !s.name || !s.nutritions.some((sn) => sn.nutrition))) {
this.msg.error("请设置正确的营养标准人群");
return;
}
const foodCategoryDay = this.foodCategoryDay.reduce((a, c) => {
return {
...a,
[c.type]: c.value,
};
}, {} as AnyObject);
const foodCategoryWeek = this.foodCategoryWeek.reduce((a, c) => {
return {
...a,
[c.type]: c.value,
};
}, {} as AnyObject);
const ingredient = this.ingredient.reduce((a, c) => {
return {
...a,
[c.name]: c.nutritions.reduce((an, cn) => {
const ul = cn.hasUl ? { ul: cn.ul } : {};
return {
...an,
[cn.nutrition]: {
min: cn.min,
max: cn.max,
...ul,
},
};
}, {} as AnyObject),
};
}, {} as AnyObject);
this.api
.saveStandard({ id: this.state.id, foodCategoryDay, foodCategoryWeek, ingredient }, true)
.subscribe((res) => {
this.msg.success(res.desc);
this.router.navigate(["/standard/list"]);
});
}
addFoodType(type: string) {
const item = type === "day" ? this.foodCategoryDay : this.foodCategoryWeek;
const withoutSelectType = this.globalEnum.category.find((f) => !item.some((s) => s.type === f.key));
item.push({ type: withoutSelectType?.key ?? "", value: 1 });
}
removeFoodType(type: string, foodType: string) {
if (type === "day") {
this.foodCategoryDay = this.foodCategoryDay.filter((f) => f.type !== foodType);
} else {
this.foodCategoryWeek = this.foodCategoryWeek.filter((f) => f.type !== foodType);
}
}
addPeopleGroup() {
this.ingredient.push({
name: "",
nutritions: [],
});
}
removePeopleGroup(idx: number) {
this.ingredient = this.ingredient.filter((_, i) => i !== idx);
}
addNutrition(idx: number) {
const current = this.ingredient[idx];
const withoutSelectNutritions = this.globalEnum.nutrient.find(
(f) => !current.nutritions.some((s) => s.nutrition === f.key)
);
current.nutritions.push({
nutrition: withoutSelectNutritions?.key ?? "",
min: 0,
max: 0,
hasUl: false,
});
}
removeNutrition(idx: number, nutrition: string) {
this.ingredient[idx].nutritions = this.ingredient[idx].nutritions.filter((f) => f.nutrition !== nutrition);
}
ulChange(idx: number, nutrition: string, checked: boolean) {
this.ingredient[idx].nutritions = this.ingredient[idx].nutritions.map((i) => {
return i.nutrition === nutrition ? { ...i, ul: checked ? 1 : void 0 } : i;
});
} }
} }

1
projects/cdk/src/dtos/enum.dto.ts

@ -2,6 +2,7 @@ export type GlobalEnum = {
category: CategoryDTO[]; category: CategoryDTO[];
mark: MarkDTO[]; mark: MarkDTO[];
nutrient: NutrientDTO[]; nutrient: NutrientDTO[];
venderType: CategoryDTO[];
}; };
export type CategoryDTO = { export type CategoryDTO = {

53
projects/cdk/src/services/api.service.ts

@ -151,8 +151,15 @@ export class ApiService {
}); });
} }
getOrgList() { getOrgList(query: {}) {
return this.http.get<ResponseType<OrgDTO[]>>("/api/vender"); const q = Utils.objectStringify(query);
return this.http.get<ResponseType<OrgDTO[]>>(`/api/vender/select?${q}`);
}
getOrgPage(p: {}, q: {}) {
let params = Utils.objectStringify({ ...p, ...q }, { skipEmptyString: false, skipNull: false });
return this.http.get<ResponseType<PageResult<OrgDTO>>>(`/api/vender?${params}`);
} }
checkOrgAccount(account: string) { checkOrgAccount(account: string) {
@ -170,9 +177,9 @@ export class ApiService {
} }
deleteOrg(id: number) { deleteOrg(id: number) {
const params = new HttpParams().set("vender", id); const params = Utils.objectToFormData({ venderId: id });
return this.http.delete<ResponseType>("/api/vender", { return this.http.delete<ResponseType>("/api/vender", {
params, body: params,
}); });
} }
@ -211,6 +218,11 @@ export class ApiService {
); );
} }
getFoodList(query: {}) {
const q = Utils.objectStringify(query);
return this.http.get<ResponseType<OrgDTO[]>>(`/api/vender/select?${q}`);
}
saveFood(food: AnyObject, isEdit?: boolean) { saveFood(food: AnyObject, isEdit?: boolean) {
const body = Utils.objectToFormData(food); const body = Utils.objectToFormData(food);
const method = isEdit ? "post" : "put"; const method = isEdit ? "post" : "put";
@ -230,6 +242,7 @@ export class ApiService {
const params = Utils.objectToFormData({ mark, ingredient }); const params = Utils.objectToFormData({ mark, ingredient });
return this.http.put<ResponseType>("/api/ingredient/mark", params); return this.http.put<ResponseType>("/api/ingredient/mark", params);
} }
removeFoodMark(ingredient: string) { removeFoodMark(ingredient: string) {
const params = Utils.objectToFormData({ ingredient }); const params = Utils.objectToFormData({ ingredient });
return this.http.delete<ResponseType>("/api/ingredient/mark", { body: params }); return this.http.delete<ResponseType>("/api/ingredient/mark", { body: params });
@ -258,8 +271,8 @@ export class ApiService {
} }
saveStandard(v: AnyObject, isEdit?: boolean) { saveStandard(v: AnyObject, isEdit?: boolean) {
const body = Utils.objectToFormData({ ...v, vendors: v["vendors"].join(",") }); const body = Utils.objectToFormData({ ...v, vendors: v["vendors"]?.join(",") });
const method = isEdit ? "post" : "put"; const method = v["id"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/nutrition", body); return this.http[method]<ResponseType>("/api/nutrition", body);
} }
@ -267,4 +280,32 @@ export class ApiService {
const params = Utils.objectToFormData({ id }); const params = Utils.objectToFormData({ id });
return this.http.delete<ResponseType>(`/api/nutrition`, { body: params }); return this.http.delete<ResponseType>(`/api/nutrition`, { body: params });
} }
getDishPage(p: {}, q: {}) {
const params = Utils.objectStringify({ ...p, ...q });
return this.http.get<ResponseType<PageResult>>(`/api/dish?${params}`).pipe(
map((r) => {
if (Array.isArray(r.body.content)) {
r.body.content = r.body.content.map((o) => {
return {
...o,
foodArr: Object.entries(o.ingredient).map(([k, v]) => {
return {
key: k,
value: v,
label: k,
};
}),
};
});
}
return r;
})
);
}
deleteDish(ids: string[]) {
const params = Utils.objectToFormData({ id: ids.join(",") });
return this.http.delete<ResponseType>(`/api/dish`, { body: params });
}
} }

1
projects/cdk/src/shared/components/index.ts

@ -0,0 +1 @@
export * from "./search-and-select/search-and-select.component";

47
projects/cdk/src/shared/components/search-and-select/search-and-select.component.html

@ -0,0 +1,47 @@
<div class="search-select flex flex-col">
<ng-template #suffixIconSearch>
<span nz-icon nzType="search"></span>
</ng-template>
<div class="flex">
<nz-input-group [nzSuffix]="suffixIconSearch" class="flex-1">
<input
class="w-full"
nz-input
nzShowSearch
nzServerSearch
[placeholder]="placeHolder"
[(ngModel)]="searchValue"
(ngModelChange)="search($event)" />
</nz-input-group>
<button nz-button nzType="primary" (click)="onSubmit()" class="ml-2">
确定
</button>
</div>
<div class="flex-1 select pt-2">
<nz-card nzSize="small">
<nz-empty *ngIf="listOfOption.length === 0"></nz-empty>
<ul
*ngIf="listOfOption.length > 0"
class="ant-dropdown-menu ant-dropdown-menu-root ant-dropdown-menu-light ant-dropdown-menu-vertical shadow-none">
<li
*ngFor="let o of listOfOption"
class="ant-dropdown-menu-item"
[ngClass]="{'ant-dropdown-menu-item-selected':selected.has(o.value)}"
(click)="handleSelect(o.value)">
{{o.label}}
</li>
</ul>
</nz-card>
</div>
<!-- <div class="flex items-center justify-end mt-2">
<nz-space>
<button *nzSpaceItem nz-button nzType="text">
取消
</button>
<button *nzSpaceItem nz-button nzType="primary">
确定
</button>
</nz-space>
</div> -->
</div>

17
projects/cdk/src/shared/components/search-and-select/search-and-select.component.less

@ -0,0 +1,17 @@
:host {
display: block;
width: 100%;
}
.search-select {
width: 100%;
min-height: 200px;
max-height: 400px;
.select {
overflow-y: auto;
}
}

58
projects/cdk/src/shared/components/search-and-select/search-and-select.component.ts

@ -0,0 +1,58 @@
import { Component, EventEmitter, Input, Output } from "@angular/core";
import { OptionItemInterface } from "@cdk/types";
import { NzSelectModeType } from "ng-zorro-antd/select";
import { Observable, debounceTime } from "rxjs";
@Component({
selector: "search-and-select",
templateUrl: "./search-and-select.component.html",
styleUrls: ["./search-and-select.component.less"],
})
export class SearchAndSelectComponent {
constructor() {}
@Input() handleSearh!: (keyword: string) => Observable<OptionItemInterface[]>;
@Input() placeHolder: string = "输入关键字搜索然后选择";
@Input() mode: NzSelectModeType = "multiple";
@Output() onSelect = new EventEmitter<Array<OptionItemInterface>>();
listOfOption: Array<OptionItemInterface> = [];
searchValue = "";
selected = new Set<string>();
nzFilterOption = (): boolean => true;
search(value: string) {
if (!value) {
return;
}
this.handleSearh
.call(this, value)
.pipe(debounceTime(500))
.subscribe((data) => {
this.listOfOption = data;
// this.listOfOption = Array.from({ length: 2 }, (_, i) => ({ label: "dd" + i, value: i.toString() }));
});
}
handleSelect(v: string) {
if (this.selected.has(v)) {
this.selected.delete(v);
} else {
this.selected.add(v);
}
}
onSubmit() {
const selects: OptionItemInterface[] = [];
this.selected.forEach((i) => {
selects.push(this.listOfOption.find((f) => f.value === i)!);
});
this.onSelect.emit(selects);
}
}

13
projects/cdk/src/shared/shared.module.ts

@ -20,6 +20,7 @@ import {
// import { environment } from "@manage/environments/environment"; // import { environment } from "@manage/environments/environment";
import { NgxPermissionsModule } from "ngx-permissions"; import { NgxPermissionsModule } from "ngx-permissions";
import { AppPageComponent } from "@cdk/app-page/app-page.component"; import { AppPageComponent } from "@cdk/app-page/app-page.component";
import { SearchAndSelectComponent } from "./components";
const ngModules = [CommonModule, HttpClientModule, FormsModule, RouterModule, ReactiveFormsModule]; const ngModules = [CommonModule, HttpClientModule, FormsModule, RouterModule, ReactiveFormsModule];
const components: any = []; const components: any = [];
@ -35,8 +36,16 @@ const cdks = [
] as any; ] as any;
@NgModule({ @NgModule({
declarations: [...components, ...directives], declarations: [...components, ...directives, SearchAndSelectComponent],
imports: [...ngZorroModules, ...ngModules, ...cdks, AppPageComponent], imports: [...ngZorroModules, ...ngModules, ...cdks, AppPageComponent],
exports: [...ngZorroModules, ...ngModules, ...components, ...directives, ...cdks, AppPageComponent], exports: [
...ngZorroModules,
...ngModules,
...components,
...directives,
...cdks,
AppPageComponent,
SearchAndSelectComponent,
],
}) })
export class SharedModule {} export class SharedModule {}

1
projects/cdk/src/table-list/table-list/table-list.component.ts

@ -141,6 +141,7 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
}) })
) )
.subscribe((f: ResponseType<PageResult>) => { .subscribe((f: ResponseType<PageResult>) => {
// alert(f.body.totalElements);
this.dataSource = f.body.content; this.dataSource = f.body.content;
this.props.pager.total = f.body.totalElements; this.props.pager.total = f.body.totalElements;
this.totalPages = Math.ceil(this.props.pager.total / this.props.pager.size); this.totalPages = Math.ceil(this.props.pager.total / this.props.pager.size);

5
projects/cdk/src/types/index.ts

@ -6,6 +6,11 @@ export type DecText = number | string;
export type Augmented<O extends object> = O & AnyObject; export type Augmented<O extends object> = O & AnyObject;
export type OptionItemInterface = Augmented<{
label: string;
value: string;
}>;
export interface ResponseType<T = any> { export interface ResponseType<T = any> {
body: T; body: T;
code: number; code: number;

12
projects/cdk/src/utils/index.ts

@ -1,7 +1,7 @@
import { HttpParams } from "@angular/common/http"; import { HttpParams } from "@angular/common/http";
import { AbstractControl, FormControl, FormGroup } from "@angular/forms"; import { AbstractControl, FormControl, FormGroup } from "@angular/forms";
import { AnyObject } from "@cdk/types"; import { AnyObject } from "@cdk/types";
import queryString from "query-string"; import queryString, { StringifyOptions } from "query-string";
export class Utils { export class Utils {
static validateFormGroup(formGroup: FormGroup) { static validateFormGroup(formGroup: FormGroup) {
@ -92,11 +92,13 @@ export class Utils {
} }
}; };
static objectStringify(o: AnyObject) { static objectStringify(o: AnyObject, option?: StringifyOptions) {
return queryString.stringify(o, { const config = {
skipEmptyString: true, skipEmptyString: true,
skipNull: true, skipNull: true,
arrayFormat: "comma", arrayFormat: "comma" as any,
}); ...option,
};
return queryString.stringify(o, config);
} }
} }

Loading…
Cancel
Save