diff --git a/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html b/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html new file mode 100644 index 0000000..bdd69ef --- /dev/null +++ b/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html @@ -0,0 +1,35 @@ + + +
+
+ + + + + +
+
+ + + + + +
+
+
+ + 餐次: + +
+ {{item}} +
+
+ +
+ + + +
+
+ +
\ No newline at end of file diff --git a/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.less b/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.less new file mode 100644 index 0000000..e69de29 diff --git a/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.ts b/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.ts new file mode 100644 index 0000000..096a1ac --- /dev/null +++ b/projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.ts @@ -0,0 +1,51 @@ +import { Component, Input, OnInit } from "@angular/core"; +import { ActivatedRoute } from "@angular/router"; +import { ApiService } from "@cdk/services"; +import { finalize } from "rxjs"; + +@Component({ + selector: "lib-ingredient-analysis", + templateUrl: "./ingredient-analysis.component.html", + styleUrls: ["./ingredient-analysis.component.less"], +}) +export class IngredientAnalysisComponent implements OnInit { + constructor(private api: ApiService) {} + + @Input() menu: any; + + @Input() current: any; + + currentDay: number = 1; + + currentPeople!: string; + + id!: string; + + analysis: any; + + analysisLoading = false; + + ngOnInit(): void { + this.currentDay = this.current.day; + this.currentPeople = this.menu.crows[0]; + this.getAnalysis(); + } + + getAnalysis() { + if (this.currentDay && this.currentPeople) { + this.analysisLoading = true; + this.api + .getAnalysis(this.menu.id, this.currentDay, this.currentPeople) + .pipe( + finalize(() => { + this.analysisLoading = false; + }) + ) + .subscribe((res) => { + this.analysis = res.body; + }); + } else { + console.error(this.currentDay, this.currentPeople, "this.currentDay && this.currentPeople 不存在"); + } + } +} diff --git a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html index 3b98503..eaef699 100644 --- a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html +++ b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html @@ -24,7 +24,7 @@ - diff --git a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.less b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.less index a184201..f4abfea 100644 --- a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.less +++ b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.less @@ -22,4 +22,20 @@ flex-basis: 25%; } } +} + +::ng-deep { + .analysis-drawer { + + .ant-drawer-header-title { + position: absolute; + right: 0; + top: 24px; + } + + .ant-drawer-body { + padding-top: 0; + } + + } } \ No newline at end of file diff --git a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts index 9b536d9..94f590c 100644 --- a/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts +++ b/projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts @@ -5,6 +5,8 @@ import { Augmented, OptionItemInterface } from "@cdk/types"; import { NzMessageService } from "ng-zorro-antd/message"; import { forkJoin } from "rxjs"; import { ApiService } from "@cdk/services"; +import { NzDrawerService } from "ng-zorro-antd/drawer"; +import { IngredientAnalysisComponent } from "../ingredient-analysis/ingredient-analysis.component"; // export interface MenuObjectInterface { // [Day: number]: { @@ -32,7 +34,12 @@ export type DishInterface = Augmented<{ styleUrls: ["./ingredient-dish.component.less"], }) export class IngredientDishComponent implements OnChanges { - constructor(private modal: NzModalService, private msg: NzMessageService, private api: ApiService) {} + constructor( + private modal: NzModalService, + private msg: NzMessageService, + private api: ApiService, + private drawer: NzDrawerService + ) {} @Input() menuBaisc: any | null; @@ -149,4 +156,21 @@ export class IngredientDishComponent implements OnChanges { }, }); } + + analysis(day: number) { + this.drawer.create({ + nzWidth: 620, + nzWrapClassName: "analysis-drawer", + nzContent: IngredientAnalysisComponent, + nzContentParams: { + menu: { + ...this.menuBaisc, + days: this.days, + }, + current: { + day, + }, + }, + }); + } } diff --git a/projects/cdk/src/ingredient/ingredient.module.ts b/projects/cdk/src/ingredient/ingredient.module.ts index 5fa1433..02ff120 100644 --- a/projects/cdk/src/ingredient/ingredient.module.ts +++ b/projects/cdk/src/ingredient/ingredient.module.ts @@ -6,6 +6,8 @@ import { ConfirmIngredientComponent } from "./confirm-ingredient/confirm-ingredi import { IngredientDishComponent } from "./ingredient-dish/ingredient-dish.component"; import { IngredientFormBasicComponent } from "./ingredient-form-basic/ingredient-form-basic.component"; import { IngredientPreviewComponent } from "./ingredient-preview/ingredient-preview.component"; +import { IngredientAnalysisComponent } from "./ingredient-analysis/ingredient-analysis.component"; +import { NutritionTableComponent } from "./nutrition-table/nutrition-table.component"; @NgModule({ declarations: [ @@ -15,6 +17,8 @@ import { IngredientPreviewComponent } from "./ingredient-preview/ingredient-prev IngredientFormBasicComponent, IngredientDishComponent, IngredientPreviewComponent, + IngredientAnalysisComponent, + NutritionTableComponent, ], imports: [SharedModule], exports: [ @@ -24,6 +28,8 @@ import { IngredientPreviewComponent } from "./ingredient-preview/ingredient-prev IngredientFormBasicComponent, IngredientDishComponent, IngredientPreviewComponent, + IngredientAnalysisComponent, + NutritionTableComponent, ], }) export class IngredientModule {} diff --git a/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html new file mode 100644 index 0000000..1a190b0 --- /dev/null +++ b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + +
+ 营养素 + + 实际摄入 + + 标准范围 + + UL值 + + 溢出范围 +
+
+ {{item.nutrition}} +
+
+
+ {{item.virtual}} +
+
+
+ {{item.standard}} +
+
+
+ {{item.ul}} +
+
+
+ {{item.overload}} +
+
\ No newline at end of file diff --git a/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.less b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.less new file mode 100644 index 0000000..d4a1a6f --- /dev/null +++ b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.less @@ -0,0 +1,44 @@ +.dark { + .nutritions-table { + .inner { + color: #fff; + } + } +} + +.nutritions-table { + width: 100%; + + tr { + + td, + th { + padding: 4px 0; + font-size: 14px; + } + } + + .inner { + padding: 6px; + background-color: transparent; + + } + + .row { + &.less .inner { + background-color: #a7a7a7; + } + + &.success .inner { + background-color: #1bbc9b; + } + + &.warning .inner { + background-color: #f3c200; + } + + &.danger .inner { + background-color: #f5222d; + } + } +} \ No newline at end of file diff --git a/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts new file mode 100644 index 0000000..a648f59 --- /dev/null +++ b/projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts @@ -0,0 +1,14 @@ +import { Component, Input } from "@angular/core"; + +@Component({ + selector: "lib-nutrition-table", + templateUrl: "./nutrition-table.component.html", + styleUrls: ["./nutrition-table.component.less"], +}) +export class NutritionTableComponent { + constructor() {} + + @Input() nutritions: any[] = []; + + @Input() dark = false; +} diff --git a/projects/cdk/src/services/api.service.ts b/projects/cdk/src/services/api.service.ts index fd2584d..bb4710c 100644 --- a/projects/cdk/src/services/api.service.ts +++ b/projects/cdk/src/services/api.service.ts @@ -421,10 +421,6 @@ export class ApiService { return this.http.get(`/api/menu/dish?menuId=${menuId}`); } - getMenuDataVis() { - return this.http.get(`/api/menu/dish`); - } - saveMenuDist(d: {}) { return this.http.put(`/api/menu/dish/batch`, d); } @@ -456,4 +452,13 @@ export class ApiService { }) ); } + + getAnalysis(id: number, day?: number, crow?: string) { + const params = Utils.objectStringify({ id, day, crow }); + return this.http.get(`/api/menu/dish/analysis?${params}`); + } + + getMenuDataVis() { + return this.http.get(`/api/menu/dish`); + } } diff --git a/projects/client/src/app/pages/data-vis/data-vis.component.html b/projects/client/src/app/pages/data-vis/data-vis.component.html index b3c093e..113ec00 100644 --- a/projects/client/src/app/pages/data-vis/data-vis.component.html +++ b/projects/client/src/app/pages/data-vis/data-vis.component.html @@ -1,35 +1,85 @@ -
+
-

成都市实验小学西区分校食谱营养报告

+

{{orgName}}食谱营养报告

{{showTime}}
-
+
-
-
-
今日带量食谱
-
+
+
+
+
今日带量食谱
+
+ + + + + + + + + + + + + + + + + + + + +
+ + + 菜品名称 + +
+ {{p}} +
+
+ {{meal.label}} + +
+ {{ item.name}} +
+
+
+ {{item.people[p]}}g +
+
+
-
-
今日食材种类
-
+
+
+
今日食材种类
+
+
今日营养分析
-
- +
+
+ + + + +
diff --git a/projects/client/src/app/pages/data-vis/data-vis.component.less b/projects/client/src/app/pages/data-vis/data-vis.component.less index 0f76381..076ccee 100644 --- a/projects/client/src/app/pages/data-vis/data-vis.component.less +++ b/projects/client/src/app/pages/data-vis/data-vis.component.less @@ -165,7 +165,7 @@ i { .mainbox { padding: 0px 10px 10px; - + overflow: hidden; } .nav1 { @@ -179,9 +179,12 @@ i { } .box { + display: flex; + flex-direction: column; + height: 100%; border: 1px solid rgba(7, 118, 181, 0.5); box-shadow: inset 0 0 10px rgba(7, 118, 181, 0.4); - margin-bottom: 12px; + position: relative; } @@ -224,7 +227,24 @@ i { } .boxnav { - padding: 10px; + flex: 1; + + table { + + td, + th { + padding: 10px; + font-weight: normal; + border: 1px solid rgba(7, 118, 181, 0.7); + + } + + .td { + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + } } diff --git a/projects/client/src/app/pages/data-vis/data-vis.component.ts b/projects/client/src/app/pages/data-vis/data-vis.component.ts index c71c598..8fd48ce 100644 --- a/projects/client/src/app/pages/data-vis/data-vis.component.ts +++ b/projects/client/src/app/pages/data-vis/data-vis.component.ts @@ -1,20 +1,42 @@ -import { Component } from "@angular/core"; +import { AfterViewInit, Component, ElementRef, Renderer2, ViewChild } from "@angular/core"; import { ApiService } from "@cdk/services"; import { format } from "date-fns"; -import { Subject, interval, takeUntil } from "rxjs"; +import { Subject, finalize, interval, takeUntil } from "rxjs"; @Component({ selector: "app-data-vis", templateUrl: "./data-vis.component.html", styleUrls: ["./data-vis.component.less"], }) -export class DataVisComponent { - constructor(private api: ApiService) {} +export class DataVisComponent implements AfterViewInit { + constructor(private api: ApiService, private rd2: Renderer2) {} + + @ViewChild("tableEl") tableEl!: ElementRef; + + @ViewChild("nutritionEl") nutritionEl!: ElementRef; + destroy$ = new Subject(); showTime: string = ""; + orgName = ""; + + dishs: { + [K: string]: any[]; + } = {}; + + peoples: string[] = []; + + globalEnum = this.api.globalEnum; + + analysisLoading = false; + + analysis: any; + + scroll: Record = {}; + ngOnInit(): void { + this.orgName = this.api.account?.vender?.name ?? ""; interval(1000) .pipe(takeUntil(this.destroy$)) .subscribe(() => { @@ -22,12 +44,84 @@ export class DataVisComponent { }); this.api.getMenuDataVis().subscribe((res) => { - console.log("res", res); + const dishs = res.body; + if (Array.isArray(dishs)) { + this.peoples = Object.keys(dishs?.[0]?.ingredient?.[0]?.value); + + if (!this.peoples) { + console.error("dishs?.[0]?.ingredient?.[0]?.value 数据错误:", dishs); + return; + } + this.getAnalysis(dishs?.[0]?.menu); + + dishs.forEach((i: any) => { + // 把每个食材按照不同的人群将重量加起来 + this.peoples.forEach((people) => { + const foods = i.ingredient as any[]; + const c = foods.reduce((a, c) => { + return a + c.value[people]; + }, 0); + if (!i.people) { + i.people = {}; + } + i.people[people] = c; + }); + + if (Array.isArray(this.dishs[i.meal])) { + this.dishs[i.meal].push(i); + } else { + this.dishs[i.meal] = [i]; + } + }); + setTimeout(() => { + this.autoScroll(this.tableEl.nativeElement, "1"); + }, 1000); + } }); } + ngAfterViewInit(): void {} + ngOnDestroy(): void { this.destroy$.next(null); this.destroy$.complete(); } + + autoScroll(el: HTMLElement, scroll: string) { + const child = el.children[0]; + if (!child) { + return; + } + const elHeight = el.clientHeight; + const childHeight = child.clientHeight; + if (childHeight <= elHeight) { + return; + } + interval(60) + .pipe(takeUntil(this.destroy$)) + .subscribe(() => { + this.scroll[scroll] = (this.scroll[scroll] ?? 0) + 1; + const paddingBottom = 100; + if (this.scroll[scroll] - paddingBottom > childHeight - el.clientHeight) { + this.scroll[scroll] = 0; + } + this.rd2.setStyle(child, "transform", `translateY(-${this.scroll[scroll]}px)`); + }); + } + + getAnalysis(menu: number) { + this.api + .getAnalysis(menu) + .pipe( + finalize(() => { + this.analysisLoading = false; + }) + ) + .subscribe((res) => { + this.analysis = res.body; + setTimeout(() => { + this.autoScroll(this.nutritionEl.nativeElement, "2"); + }, 1000); + }); + } }