Browse Source

大屏 & 营养分析

main
kkerwin 2 years ago
parent
commit
fec5afc8a4
  1. 35
      projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html
  2. 0
      projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.less
  3. 51
      projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.ts
  4. 2
      projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html
  5. 16
      projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.less
  6. 26
      projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts
  7. 6
      projects/cdk/src/ingredient/ingredient.module.ts
  8. 55
      projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html
  9. 44
      projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.less
  10. 14
      projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts
  11. 13
      projects/cdk/src/services/api.service.ts
  12. 74
      projects/client/src/app/pages/data-vis/data-vis.component.html
  13. 26
      projects/client/src/app/pages/data-vis/data-vis.component.less
  14. 104
      projects/client/src/app/pages/data-vis/data-vis.component.ts

35
projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html

@ -0,0 +1,35 @@
<nz-tabset>
<nz-tab nzTitle="营养分析">
<div nz-row [nzGutter]="12">
<div nz-col nzSpan="8">
<nz-select class="w-full" [(ngModel)]="currentDay" (ngModelChange)="getAnalysis()">
<nz-option *ngFor="let item of menu.days" [nzLabel]="'' + (item + 1) + ''" [nzValue]="item+1">
</nz-option>
</nz-select>
</div>
<div nz-col nzSpan="16">
<nz-select class="w-full" [(ngModel)]="currentPeople" (ngModelChange)="getAnalysis()">
<nz-option *ngFor="let item of menu.crows" [nzLabel]="item" nzValue="{{item}}">
</nz-option>
</nz-select>
</div>
</div>
<div class="flex mt-4">
<span>
餐次:
</span>
<div class="flex-1">
<nz-tag *ngFor="let item of menu.meals">{{item}}</nz-tag>
</div>
</div>
<nz-divider nzDashed></nz-divider>
<div>
<nz-spin [nzSpinning]="analysisLoading">
<lib-nutrition-table *ngIf="analysis" [nutritions]="analysis.ingredient"></lib-nutrition-table>
</nz-spin>
</div>
</nz-tab>
<!-- <nz-tab nzTitle="食材种类">Content of Tab Pane 2</nz-tab> -->
</nz-tabset>

0
projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.less

51
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 不存在");
}
}
}

2
projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.html

@ -24,7 +24,7 @@
<button *nzSpaceItem nz-button nzType="link" (click)="reuse(day + 1, selectDayTpl)">
本天菜品应用到其他天
</button>
<button *nzSpaceItem nz-button nzType="link">
<button *nzSpaceItem nz-button nzType="link" (click)="analysis(day + 1)">
本日营养分析
</button>
</nz-space>

16
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;
}
}
}

26
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,
},
},
});
}
}

6
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 {}

55
projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html

@ -0,0 +1,55 @@
<table *ngIf="nutritions" class="nutritions-table" [ngClass]="{'dark':dark}">
<thead>
<tr>
<th class=" text-left">
营养素
</th>
<th class=" text-right">
实际摄入
</th>
<th class=" text-right">
标准范围
</th>
<th class=" text-right">
UL值
</th>
<th class=" text-right">
溢出范围
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of nutritions" class="row text-right" [ngClass]="{
less:item.conclusion === '不足',
success:item.conclusion === '适量',
warning:item.conclusion === '过量',
danger:item.conclusion === '严重超标',
}">
<td>
<div class="inner text-left">
{{item.nutrition}}
</div>
</td>
<td>
<div class="inner">
{{item.virtual}}
</div>
</td>
<td>
<div class="inner">
{{item.standard}}
</div>
</td>
<td>
<div class="inner">
{{item.ul}}
</div>
</td>
<td>
<div class="inner">
{{item.overload}}
</div>
</td>
</tr>
</tbody>
</table>

44
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;
}
}
}

14
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;
}

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

@ -421,10 +421,6 @@ export class ApiService {
return this.http.get<ResponseType>(`/api/menu/dish?menuId=${menuId}`);
}
getMenuDataVis() {
return this.http.get<ResponseType>(`/api/menu/dish`);
}
saveMenuDist(d: {}) {
return this.http.put<ResponseType>(`/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<ResponseType>(`/api/menu/dish/analysis?${params}`);
}
getMenuDataVis() {
return this.http.get<ResponseType>(`/api/menu/dish`);
}
}

74
projects/client/src/app/pages/data-vis/data-vis.component.html

@ -1,35 +1,85 @@
<div class="body">
<div class="body flex flex-col">
<div class="head clearfix">
<div class="logo">
<img src="/assets/images/jl-logo.png" />
</div>
<h1 class="title">成都市实验小学西区分校食谱营养报告</h1>
<h1 class="title">{{orgName}}食谱营养报告</h1>
<div class="time">{{showTime}}</div>
</div>
<div class="mainbox flex">
<div class="mainbox flex flex-1">
<!-- <div class="boxnav mapc">
</div> -->
<div class="flex-1 ">
<div class="box">
<div class="tit">今日带量食谱</div>
<div class="boxnav">
<div class="flex-1 flex flex-col">
<div class="flex-1 overflow-hidden">
<div class="box mb-2 ">
<div class="tit">今日带量食谱</div>
<div class="boxnav overflow-hidden" #tableEl>
<table class="w-full">
<thead>
<tr>
<th width="70px">
</th>
<th>
菜品名称
</th>
<th *ngFor="let p of peoples">
<div class="td">
{{p}}
</div>
</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let meal of globalEnum.mealType">
<ng-container *ngIf="dishs[meal.value] as mealDish">
<tr *ngFor="let item of mealDish;let first = first">
<td *ngIf="first" [attr.rowspan]="mealDish.length">
{{meal.label}}
</td>
<th>
<div class="td">
{{ item.name}}
</div>
</th>
<th *ngFor="let p of peoples">
<div class="td">
{{item.people[p]}}g
</div>
</th>
</tr>
</ng-container>
</ng-container>
</tbody>
</table>
</div>
</div>
</div>
<div class="box">
<div class="tit">今日食材种类</div>
<div class="boxnav">
<div class="flex-1 overflow-hidden">
<div class="box mt-2">
<div class="tit">今日食材种类</div>
<div class="boxnav">
</div>
</div>
</div>
</div>
<div class="w-1/3 pl-4">
<div class="box">
<div class="tit">今日营养分析</div>
<div class="boxnav">
<div class="boxnav overflow-hidden" #nutritionEl>
<div class="p-4">
<nz-spin [nzSpinning]="analysisLoading">
<lib-nutrition-table *ngIf="analysis"
[dark]="true"
[nutritions]="analysis.ingredient">
</lib-nutrition-table>
</nz-spin>
</div>
</div>
</div>
</div>

26
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;
}
}
}

104
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<HTMLElement>;
@ViewChild("nutritionEl") nutritionEl!: ElementRef<HTMLElement>;
destroy$ = new Subject();
showTime: string = "";
orgName = "";
dishs: {
[K: string]: any[];
} = {};
peoples: string[] = [];
globalEnum = this.api.globalEnum;
analysisLoading = false;
analysis: any;
scroll: Record<string, number> = {};
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);
});
}
}

Loading…
Cancel
Save