37 changed files with 935 additions and 530 deletions
@ -1,8 +1,63 @@ |
|||
import { Component } from "@angular/core"; |
|||
import { Component, Input, OnInit } from "@angular/core"; |
|||
import { ApiService } from "@cdk/services"; |
|||
|
|||
export interface FoodInDishInterface { |
|||
foodName: string; |
|||
key: string; |
|||
isMain: boolean; |
|||
groupValues: Array<{ |
|||
peopleName: string; |
|||
value: number; |
|||
}>; |
|||
value: Record<string, number>; |
|||
} |
|||
|
|||
@Component({ |
|||
selector: "lib-add-dish-to-ingredient", |
|||
selector: "app-add-dish-to-ingredient", |
|||
templateUrl: "./add-dish-to-ingredient.component.html", |
|||
styleUrls: ["./add-dish-to-ingredient.component.less"], |
|||
}) |
|||
export class AddDishToIngredientComponent {} |
|||
export class AddDishToIngredientComponent implements OnInit { |
|||
constructor(private api: ApiService) {} |
|||
|
|||
@Input() peopleGroups: string[] = []; |
|||
|
|||
globalEnum = this.api.globalEnum; |
|||
|
|||
dish: any = null; |
|||
|
|||
mark: string = ""; |
|||
|
|||
foods: FoodInDishInterface[] = []; |
|||
|
|||
ngOnInit(): void { |
|||
console.log("this.peopleGroups", this.peopleGroups); |
|||
} |
|||
|
|||
onDishChange(dish: any) { |
|||
if (dish) { |
|||
this.mark = dish.marks; |
|||
this.getFoodNameByKeys(dish.ingredient); |
|||
} |
|||
} |
|||
|
|||
getFoodNameByKeys(foods: any[]) { |
|||
const keys = foods.map((i) => i.key); |
|||
this.api.getFoodList({ keys }).subscribe((res) => { |
|||
this.foods = res.body.map((i) => { |
|||
return { |
|||
foodName: i.name, |
|||
key: i.key, |
|||
isMain: foods.find((f) => f.key === i.key)?.isMain ?? false, |
|||
groupValues: this.peopleGroups.map((p) => { |
|||
return { |
|||
peopleName: p, |
|||
value: 0, |
|||
}; |
|||
}), |
|||
value: {}, |
|||
}; |
|||
}); |
|||
}); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,60 @@ |
|||
<nz-spin [nzSpinning]="!menu"> |
|||
<ng-container> |
|||
<!-- {{ menuObject | json }} --> |
|||
<nz-card |
|||
*ngFor="let day of days" |
|||
[nzTitle]="dayTitleTpl" |
|||
class="day-item mb-4" |
|||
[ngClass]="{'expanded':expanded.has(day)}" |
|||
[nzExtra]="dayExtraTpl" |
|||
[nzBodyStyle]="{padding:0}"> |
|||
<ng-template #dayTitleTpl> |
|||
<div class=" relative -left-4 z-10"> |
|||
<button nz-button nzType="text" (click)="expandChange(day)"> |
|||
<span class="expanded-icon" nz-icon nzType="caret-right" nzTheme="outline"></span> |
|||
</button> |
|||
<span> |
|||
第 {{day + 1}} 天 |
|||
</span> |
|||
</div> |
|||
</ng-template> |
|||
<ng-template #dayExtraTpl> |
|||
<nz-space> |
|||
<button *nzSpaceItem nz-button nzType="link" (click)="reuse(day + 1, selectDayTpl)"> |
|||
本天菜品应用到其他天 |
|||
</button> |
|||
<button *nzSpaceItem nz-button nzType="link"> |
|||
本日营养分析 |
|||
</button> |
|||
</nz-space> |
|||
</ng-template> |
|||
<ng-container *ngIf="expanded.has(day)"> |
|||
<nz-card-tab> |
|||
<nz-tabset nzSize="large" [(nzSelectedIndex)]="mealCurrentIndex[day]"> |
|||
<nz-tab [nzTitle]="meal" *ngFor="let meal of menu.meals; let idx = index"> |
|||
</nz-tab> |
|||
</nz-tabset> |
|||
</nz-card-tab> |
|||
<div class="p-4"> |
|||
<ng-container *ngFor="let meal of menu.meals; let mealIndex = index "> |
|||
<app-ingredient-meals *ngIf="mealCurrentIndex[day] === mealIndex" |
|||
[day]="day + 1" |
|||
[mealIndex]="mealIndex" |
|||
[peopleGroups]="menu.crows" |
|||
[mealDishs]="mealDishList" |
|||
(onSaveDish)="onSaveDish($event,day+1,mealIndex)"> |
|||
<!-- (onSaveDish)="onSaveDish($event,day+1,mealIndex)" --> |
|||
</app-ingredient-meals> |
|||
</ng-container> |
|||
</div> |
|||
</ng-container> |
|||
</nz-card> |
|||
</ng-container> |
|||
</nz-spin> |
|||
|
|||
<ng-template #selectDayTpl> |
|||
<nz-alert nzType="warning" nzMessage="请注意,如果其它天已经存在菜品,新应用的菜品将会追加在其中,不影响已经存在的菜品" nzShowIcon> |
|||
|
|||
</nz-alert> |
|||
<nz-checkbox-group [(ngModel)]="selectDay" class="select-day mt-4"></nz-checkbox-group> |
|||
</ng-template> |
@ -0,0 +1,25 @@ |
|||
.day-item { |
|||
::ng-deep { |
|||
.ant-card-head-title { |
|||
overflow: visible; |
|||
} |
|||
} |
|||
|
|||
&.expanded { |
|||
.expanded-icon { |
|||
transform: rotate(90deg); |
|||
} |
|||
} |
|||
} |
|||
|
|||
.select-day { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
|
|||
::ng-deep { |
|||
>label { |
|||
margin: 0 0 12px 0; |
|||
flex-basis: 25%; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,119 @@ |
|||
import { Component, Input, OnChanges, SimpleChanges, TemplateRef } from "@angular/core"; |
|||
// import { MealDishInterface } from "../ingredient-meals/ingredient-meals.component";
|
|||
import { NzModalService } from "ng-zorro-antd/modal"; |
|||
import { Augmented, OptionItemInterface } from "@cdk/types"; |
|||
import { NzMessageService } from "ng-zorro-antd/message"; |
|||
|
|||
// export interface MenuObjectInterface {
|
|||
// [Day: number]: {
|
|||
// [MealIndex: number]: MealDishInterface[];
|
|||
// };
|
|||
// }
|
|||
|
|||
export type DishInterface = Augmented<{ |
|||
dishId: number; |
|||
day: number; |
|||
meal: string; |
|||
mark: string; |
|||
items: Array< |
|||
Augmented<{ |
|||
key: string; |
|||
isMain: boolean; |
|||
value: Record<string, number>; |
|||
}> |
|||
>; |
|||
}>; |
|||
|
|||
@Component({ |
|||
selector: "app-ingredient-dish", |
|||
templateUrl: "./ingredient-dish.component.html", |
|||
styleUrls: ["./ingredient-dish.component.less"], |
|||
}) |
|||
export class IngredientDishComponent implements OnChanges { |
|||
constructor(private modal: NzModalService, private msg: NzMessageService) {} |
|||
|
|||
@Input() menu: any | null; |
|||
|
|||
expanded = new Set<number>(); |
|||
|
|||
days: number[] = []; |
|||
|
|||
selectDay: OptionItemInterface[] = []; |
|||
|
|||
mealCurrentIndex: Record<number, number> = {}; |
|||
|
|||
// d = {
|
|||
// 1:{
|
|||
// zaocan:[
|
|||
// {dishId:1...}
|
|||
// ]
|
|||
// }
|
|||
// }
|
|||
// menuObject!: MenuObjectInterface;
|
|||
|
|||
mealDishList: DishInterface[] = []; |
|||
|
|||
ngOnChanges(changes: SimpleChanges): void { |
|||
if (changes["menu"]?.currentValue) { |
|||
this.initMenu(); |
|||
} |
|||
} |
|||
|
|||
initMenu() { |
|||
if (this.menu) { |
|||
const meals = this.menu.meals as string[]; |
|||
const day = this.menu.day as number; |
|||
this.days = Array.from({ length: day }, (_, i) => { |
|||
this.selectDay.push({ |
|||
label: `第${i + 1}天`, |
|||
value: String(i + 1), |
|||
}); |
|||
this.mealCurrentIndex[i] = 0; |
|||
// if (!this.menuObject) {
|
|||
// this.menuObject = {};
|
|||
// }
|
|||
// this.menuObject[i + 1] = meals.reduce((a, c, idx) => {
|
|||
// return {
|
|||
// ...a,
|
|||
// [idx]: [],
|
|||
// };
|
|||
// }, {} as Record<number, MealDishInterface[]>);
|
|||
return i; |
|||
}); |
|||
} |
|||
} |
|||
|
|||
expandChange(i: number) { |
|||
if (this.expanded.has(i)) { |
|||
this.expanded.delete(i); |
|||
} else { |
|||
this.expanded.add(i); |
|||
} |
|||
} |
|||
|
|||
onSaveDish(v: DishInterface[], day: number, mealIndex: number) { |
|||
this.mealDishList = JSON.parse(JSON.stringify(v)); |
|||
// this.menuObject[day][mealIndex] = JSON.parse(JSON.stringify(v));
|
|||
} |
|||
|
|||
reuse(day: number, nzContent: TemplateRef<{}>) { |
|||
const thisDayDishs = this.mealDishList.filter((f) => f.day === day); |
|||
console.log("dayDishs", day, this.mealDishList, thisDayDishs); |
|||
this.modal.create({ |
|||
nzTitle: "请选择应用到的日期", |
|||
nzContent, |
|||
nzOnOk: () => { |
|||
this.selectDay.forEach((i) => { |
|||
if (i["checked"]) { |
|||
thisDayDishs.forEach((reused) => { |
|||
this.mealDishList.push(JSON.parse(JSON.stringify({ ...reused, day: Number(i.value) }))); |
|||
}); |
|||
} |
|||
i["checked"] = false; |
|||
}); |
|||
this.mealDishList = JSON.parse(JSON.stringify(this.mealDishList)); |
|||
this.msg.success("操作成功"); |
|||
}, |
|||
}); |
|||
} |
|||
} |
@ -0,0 +1,6 @@ |
|||
.empty { |
|||
margin: 0; |
|||
padding: 24px; |
|||
border: 1px solid #f0f0f0; |
|||
border-top: none; |
|||
} |
@ -1,21 +1,130 @@ |
|||
import { Component } from "@angular/core"; |
|||
import { |
|||
Component, |
|||
EventEmitter, |
|||
Input, |
|||
OnChanges, |
|||
OnInit, |
|||
Output, |
|||
SimpleChanges, |
|||
TemplateRef, |
|||
ViewChild, |
|||
} from "@angular/core"; |
|||
import { NzModalService } from "ng-zorro-antd/modal"; |
|||
import { AddDishToIngredientComponent } from "../add-dish-to-ingredient/add-dish-to-ingredient.component"; |
|||
import { NzDrawerService } from "ng-zorro-antd/drawer"; |
|||
import { |
|||
AddDishToIngredientComponent, |
|||
FoodInDishInterface, |
|||
} from "../add-dish-to-ingredient/add-dish-to-ingredient.component"; |
|||
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer"; |
|||
import { NzMessageService } from "ng-zorro-antd/message"; |
|||
import { Augmented } from "@cdk/types"; |
|||
import { DishInterface } from "../ingredient-dish/ingredient-dish.component"; |
|||
|
|||
export type MealDishInterface = Augmented<{ |
|||
dishId: number; |
|||
dishName: string; |
|||
mark: string; |
|||
foods: FoodInDishInterface[]; |
|||
}>; |
|||
|
|||
@Component({ |
|||
selector: "lib-ingredient-meals", |
|||
selector: "app-ingredient-meals", |
|||
templateUrl: "./ingredient-meals.component.html", |
|||
styleUrls: ["./ingredient-meals.component.less"], |
|||
}) |
|||
export class IngredientMealsComponent { |
|||
constructor(private modal: NzModalService, private drawer: NzDrawerService) {} |
|||
export class IngredientMealsComponent implements OnChanges, OnInit { |
|||
constructor(private modal: NzModalService, private msg: NzMessageService, private drawer: NzDrawerService) {} |
|||
|
|||
@Input() day!: number; |
|||
|
|||
@Input() mealIndex!: number; |
|||
|
|||
@Input() peopleGroups: string[] = []; |
|||
|
|||
@Input() mealDishs: DishInterface[] = []; |
|||
|
|||
@Output() onSaveDish = new EventEmitter<DishInterface[]>(); |
|||
|
|||
@ViewChild("addDishFooter") addDishFooter!: TemplateRef<{}>; |
|||
|
|||
drawerRef?: NzDrawerRef; |
|||
|
|||
submitLoading = false; |
|||
|
|||
currentDishs: DishInterface[] = []; |
|||
|
|||
ngOnInit(): void { |
|||
this.init(); |
|||
} |
|||
|
|||
ngOnChanges(changes: SimpleChanges): void { |
|||
if (changes["mealDishs"]?.currentValue) { |
|||
this.init(); |
|||
} |
|||
} |
|||
|
|||
init() { |
|||
console.log(" ", this.mealDishs, this.day, this.mealIndex); |
|||
this.currentDishs = this.mealDishs.filter((f) => f.day === this.day && this.mealIndex === f["mealIndex"]); |
|||
} |
|||
shopDishForm() { |
|||
this.drawer.create({ |
|||
this.drawerRef = this.drawer.create({ |
|||
nzTitle: "添加菜品", |
|||
nzWidth: 1000, |
|||
nzWidth: 1200, |
|||
nzContent: AddDishToIngredientComponent, |
|||
nzFooter: this.addDishFooter, |
|||
nzContentParams: { |
|||
peopleGroups: this.peopleGroups, |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
clearThisMeal() { |
|||
this.modal.confirm({ |
|||
nzTitle: "警告", |
|||
nzContent: "是否要清空本餐?", |
|||
nzOkDanger: true, |
|||
nzOnOk: () => { |
|||
this.mealDishs = this.mealDishs.filter((f) => !(f.day === this.day && f["mealIndex"] === this.mealIndex)); |
|||
this.onSaveDish.emit(this.mealDishs); |
|||
}, |
|||
}); |
|||
} |
|||
|
|||
cancelForm() { |
|||
this.drawerRef?.close(); |
|||
} |
|||
|
|||
onSubmit() { |
|||
if (this.drawerRef) { |
|||
const { dish, foods } = this.drawerRef.getContentComponent() as AddDishToIngredientComponent; |
|||
|
|||
this.mealDishs.push({ |
|||
day: this.day, |
|||
mealIndex: this.mealIndex, |
|||
meal: "", |
|||
dishId: dish.id, |
|||
dishName: dish.name, |
|||
mark: dish.marks, |
|||
items: foods.map((f) => ({ |
|||
...f, |
|||
value: f.groupValues.reduce((a, c) => { |
|||
return { |
|||
...a, |
|||
[c.peopleName]: c.value, |
|||
}; |
|||
}, {} as Record<string, number>), |
|||
})), |
|||
}); |
|||
|
|||
this.onSaveDish.emit(this.mealDishs); |
|||
} |
|||
this.cancelForm(); |
|||
} |
|||
|
|||
onRemoveDish(d: DishInterface) { |
|||
this.mealDishs = this.mealDishs.filter( |
|||
(f) => !(f.dishId === d.dishId && f.day === this.day && f["mealIndex"] === this.mealIndex) |
|||
); |
|||
this.onSaveDish.emit(this.mealDishs); |
|||
} |
|||
} |
|||
|
@ -0,0 +1,22 @@ |
|||
<nz-select |
|||
nzShowSearch |
|||
nzServerSearch |
|||
|
|||
nzPlaceHolder="请输入菜品名称搜索" |
|||
[nzShowArrow]="false" |
|||
[nzFilterOption]="nzFilterOption" |
|||
[(ngModel)]="value" |
|||
(ngModelChange)="onSelectChange($event)" |
|||
(nzOnSearch)="search($event)"> |
|||
|
|||
<ng-container *ngIf="!loading"> |
|||
<nz-option *ngFor="let o of listOfOption" [nzLabel]="o.label" |
|||
[nzValue]="o.value"> |
|||
</nz-option> |
|||
</ng-container> |
|||
<nz-option *ngIf="loading" nzDisabled nzCustomContent> |
|||
<span nz-icon nzType="loading" class="loading-icon"></span> |
|||
数据加载中... |
|||
</nz-option> |
|||
|
|||
</nz-select> |
@ -0,0 +1,97 @@ |
|||
import { Component, OnInit } from "@angular/core"; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; |
|||
import { ApiService } from "@cdk/services"; |
|||
import { OptionItemInterface } from "@cdk/types"; |
|||
import { Subject, debounceTime, distinctUntilChanged, filter, finalize, switchMap, takeUntil, tap } from "rxjs"; |
|||
|
|||
@Component({ |
|||
selector: "app-dish-select", |
|||
templateUrl: "./dish-select.component.html", |
|||
styleUrls: ["./dish-select.component.less"], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
multi: true, |
|||
useExisting: DishSelectComponent, |
|||
}, |
|||
], |
|||
}) |
|||
export class DishSelectComponent implements OnInit, ControlValueAccessor { |
|||
constructor(private api: ApiService) {} |
|||
|
|||
private destroy$ = new Subject<void>(); |
|||
|
|||
private dishSearch$ = new Subject<string>(); |
|||
|
|||
nzFilterOption = (): boolean => true; |
|||
|
|||
value?: string | string[]; |
|||
|
|||
listOfOption: OptionItemInterface[] = []; |
|||
|
|||
loading = false; |
|||
|
|||
ngOnInit(): void { |
|||
this.dishSearch$ |
|||
.pipe( |
|||
filter((f) => !!f), |
|||
debounceTime(500), |
|||
distinctUntilChanged(), |
|||
takeUntil(this.destroy$), |
|||
tap(() => { |
|||
this.loading = true; |
|||
}), |
|||
switchMap((term: string) => |
|||
this.api.getDishPage({ pageNo: 0, pageSize: 5 }, { keyword: term }).pipe( |
|||
finalize(() => { |
|||
this.loading = false; |
|||
}) |
|||
) |
|||
) |
|||
) |
|||
.subscribe((data) => { |
|||
const listOfOption: Array<OptionItemInterface> = []; |
|||
data.body.content.forEach((item) => { |
|||
listOfOption.push({ |
|||
...item, |
|||
value: String(item.id), |
|||
label: item.name, |
|||
}); |
|||
}); |
|||
this.listOfOption = listOfOption; |
|||
}); |
|||
} |
|||
|
|||
ngOnDestroy(): void { |
|||
this.destroy$.next(); |
|||
this.destroy$.complete(); |
|||
} |
|||
|
|||
onSelectChange(v: any) { |
|||
const item = this.listOfOption.find((f) => f.value === v); |
|||
// console.log("v", item);
|
|||
if (item) { |
|||
this.onChange(item); |
|||
} |
|||
} |
|||
|
|||
search = (k: string) => { |
|||
this.dishSearch$.next(k); |
|||
}; |
|||
|
|||
onChange(v: OptionItemInterface) {} |
|||
|
|||
ontouch(v: any) {} |
|||
|
|||
writeValue(v?: string[] | string): void { |
|||
this.value = v; |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.onChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
this.ontouch = fn; |
|||
} |
|||
} |
@ -1,2 +1,4 @@ |
|||
export * from "./search-and-select/search-and-select.component"; |
|||
export * from "./month-select/month-select.component"; |
|||
export * from "./org-select/org-select.component"; |
|||
export * from "./dish-select/dish-select.component"; |
|||
|
@ -0,0 +1,13 @@ |
|||
<nz-select |
|||
nzShowSearch |
|||
nzServerSearch |
|||
nzPlaceHolder="请选择单位" |
|||
[nzShowArrow]="false" |
|||
[nzFilterOption]="nzFilterOption" |
|||
[(ngModel)]="value" |
|||
(ngModelChange)="onSelectChange($event)" |
|||
(nzOnSearch)="searchOrg($event)"> |
|||
<nz-option *ngFor="let o of listOfOption" [nzLabel]="o.label" |
|||
[nzValue]="o.value"> |
|||
</nz-option> |
|||
</nz-select> |
@ -0,0 +1,81 @@ |
|||
import { Component, OnInit } from "@angular/core"; |
|||
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms"; |
|||
import { ApiService } from "@cdk/services"; |
|||
import { OptionItemInterface } from "@cdk/types"; |
|||
import { Subject, debounceTime, distinctUntilChanged, filter, switchMap, takeUntil } from "rxjs"; |
|||
|
|||
@Component({ |
|||
selector: "app-org-select", |
|||
templateUrl: "./org-select.component.html", |
|||
styleUrls: ["./org-select.component.less"], |
|||
providers: [ |
|||
{ |
|||
provide: NG_VALUE_ACCESSOR, |
|||
multi: true, |
|||
useExisting: OrgSelectComponent, |
|||
}, |
|||
], |
|||
}) |
|||
export class OrgSelectComponent implements OnInit, ControlValueAccessor { |
|||
constructor(private api: ApiService) {} |
|||
|
|||
private destroy$ = new Subject<void>(); |
|||
|
|||
private orgSearch$ = new Subject<string>(); |
|||
|
|||
nzFilterOption = (): boolean => true; |
|||
|
|||
value?: string | string[]; |
|||
|
|||
listOfOption: OptionItemInterface[] = []; |
|||
|
|||
ngOnInit(): void { |
|||
this.orgSearch$ |
|||
.pipe( |
|||
filter((f) => !!f), |
|||
debounceTime(500), |
|||
distinctUntilChanged(), |
|||
takeUntil(this.destroy$), |
|||
switchMap((term: string) => this.api.getOrgList({ keyword: term })) |
|||
) |
|||
.subscribe((data) => { |
|||
const listOfOption: Array<OptionItemInterface> = []; |
|||
data.body.forEach((item) => { |
|||
listOfOption.push({ |
|||
value: String(item.id), |
|||
label: item.name, |
|||
}); |
|||
}); |
|||
this.listOfOption = listOfOption; |
|||
}); |
|||
} |
|||
|
|||
ngOnDestroy(): void { |
|||
this.destroy$.next(); |
|||
this.destroy$.complete(); |
|||
} |
|||
|
|||
onSelectChange(v: any) { |
|||
this.onChange(v); |
|||
} |
|||
|
|||
searchOrg = (k: string) => { |
|||
this.orgSearch$.next(k); |
|||
}; |
|||
|
|||
onChange(v: number[]) {} |
|||
|
|||
ontouch(v: any) {} |
|||
|
|||
writeValue(v?: string[] | string): void { |
|||
this.value = v; |
|||
} |
|||
|
|||
registerOnChange(fn: any): void { |
|||
this.onChange = fn; |
|||
} |
|||
|
|||
registerOnTouched(fn: any): void { |
|||
this.ontouch = fn; |
|||
} |
|||
} |
Loading…
Reference in new issue