Browse Source

大屏轮播

main
kkerwin 1 year ago
parent
commit
24fb762a2e
  1. 92
      projects/admin/src/app/components/food-form/food-form.component.ts
  2. 18
      projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.ts
  3. 2
      projects/client/src/app/pages/data-vis/data-vis.component.html
  4. 126
      projects/client/src/app/pages/data-vis/data-vis.component.ts
  5. 155
      projects/client/src/app/pages/ingredients/ingredient-form/ingredient-form.component.ts

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

@ -1,95 +1,95 @@
import { ApiService } from "@cdk/services"; import { ApiService } from '@cdk/services'
import { Component, Input, OnInit } from "@angular/core"; import { Component, Input, OnInit } from '@angular/core'
import { FormArray, FormBuilder, FormGroup } from "@angular/forms"; import { FormArray, FormBuilder, FormGroup } from '@angular/forms'
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'
@Component({ @Component({
selector: "app-food-form", selector: 'app-food-form',
templateUrl: "./food-form.component.html", templateUrl: './food-form.component.html',
styleUrls: ["./food-form.component.less"], styleUrls: ['./food-form.component.less'],
}) })
export class FoodFormComponent implements OnInit { export class FoodFormComponent implements OnInit {
constructor(private fb: FormBuilder, private api: ApiService, private msg: NzMessageService) {} constructor(private fb: FormBuilder, private api: ApiService, private msg: NzMessageService) {}
formGroup!: FormGroup; formGroup!: FormGroup
@Input() food: any; @Input() food: any
public globalEnum = this.api.globalEnum; public globalEnum = this.api.globalEnum
get nutrition(): FormArray { get nutrition(): FormArray {
return this.formGroup.get("_nutrition") as FormArray; return this.formGroup.get('_nutrition') as FormArray
} }
ngOnInit(): void { ngOnInit(): void {
this.formGroup = this.fb.group({ this.formGroup = this.fb.group({
key: this.fb.control("", [FormValidators.required("请输入食材编号")]), key: this.fb.control('', [FormValidators.required('请输入食材编号')]),
name: this.fb.control("", [FormValidators.required("请输入食材名称")]), name: this.fb.control('', [FormValidators.required('请输入食材名称')]),
type: this.fb.control("", [FormValidators.required("请选择食材类型")]), type: this.fb.control('', [FormValidators.required('请选择食材类型')]),
nutrient: this.fb.control(null, [FormValidators.required("请输入营养素")]), nutrient: this.fb.control(null, [FormValidators.required('请输入营养素')]),
_nutrition: this.fb.array([], []), _nutrition: this.fb.array([], []),
}); })
if (this.food) { if (this.food) {
this.formGroup.patchValue(this.food); this.formGroup.patchValue(this.food)
this.formGroup.get("key")?.disable(); this.formGroup.get('key')?.disable()
this.food.nutrientArr?.forEach((n: any) => { this.food.nutrientArr?.forEach((n: any) => {
this.createNutrition(n); this.createNutrition(n)
}); })
} }
} }
nutritionChange(v: string, idx: number) { nutritionChange(v: string, idx: number) {
const nutrient = this.globalEnum.nutrient.find((f) => f.key === v); const nutrient = this.globalEnum.nutrient.find((f) => f.key === v)
if (this.nutrition.value?.filter((s: any) => s.nutritionName === nutrient?.key).length < 2) { if (this.nutrition.value?.filter((s: any) => s.nutritionName === nutrient?.key).length < 2) {
this.nutrition.at(idx).patchValue({ this.nutrition.at(idx).patchValue({
measurement: nutrient?.measurement, measurement: nutrient?.measurement,
}); })
} else { } else {
this.msg.error("重复的营养素"); this.msg.error('重复的营养素')
this.nutrition.at(idx).patchValue( this.nutrition.at(idx).patchValue(
{ {
nutritionName: "", nutritionName: '',
measurement: "", measurement: '',
}, },
{ emitEvent: false } { emitEvent: false },
); )
} }
} }
createNutrition(v?: any) { createNutrition(v?: any) {
this.nutrition.push( this.nutrition.push(
this.fb.group({ this.fb.group({
nutritionName: this.fb.control(v?.key ?? ""), nutritionName: this.fb.control(v?.key ?? ''),
nutritionNum: this.fb.control(v?.value ?? 0), nutritionNum: this.fb.control(v?.value ?? 0),
measurement: this.fb.control(v?.measurement ?? ""), measurement: this.fb.control(v?.measurement ?? ''),
}) }),
); )
const nutrient = this.formGroup.get("nutrient"); const nutrient = this.formGroup.get('nutrient')
if (!nutrient?.value) { if (!nutrient?.value) {
nutrient?.patchValue({}); nutrient?.patchValue({})
} }
} }
removeNutrition(idx: number) { removeNutrition(idx: number) {
this.nutrition.removeAt(idx); this.nutrition.removeAt(idx)
} }
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.getRawValue(); 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) {
let num = Number(n.nutritionNum); let num = Number(n.nutritionNum)
if (!n.nutritionName || !num) { if (!n.nutritionName) {
this.msg.error("请输入正确的营养素"); this.msg.error('请选择正确的营养素')
return; return
} }
nutrient[n.nutritionName] = num; nutrient[n.nutritionName] = num
} }
} }
values = { values = {
@ -97,8 +97,8 @@ export class FoodFormComponent implements OnInit {
name, name,
type, type,
nutrient, nutrient,
}; }
} }
return values; return values
} }
} }

18
projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.ts

@ -9,7 +9,7 @@ import { OptionItemInterface } from '@cdk/types'
import { NzDrawerService } from 'ng-zorro-antd/drawer' import { NzDrawerService } from 'ng-zorro-antd/drawer'
import { NzMessageService } from 'ng-zorro-antd/message' import { NzMessageService } from 'ng-zorro-antd/message'
import { NzModalService } from 'ng-zorro-antd/modal' import { NzModalService } from 'ng-zorro-antd/modal'
import { forkJoin } from 'rxjs' import { forkJoin, lastValueFrom } from 'rxjs'
@Component({ @Component({
selector: 'app-ingredient-form', selector: 'app-ingredient-form',
@ -142,17 +142,17 @@ export class IngredientFormComponent implements OnInit {
nzContent: ConfirmIngredientComponent, nzContent: ConfirmIngredientComponent,
nzData: this.menuItem, nzData: this.menuItem,
nzWidth: 650, nzWidth: 650,
nzOnOk: () => { nzOnOk: async () => {
const { mealDishList } = this.menuDish const { mealDishList } = this.menuDish
this.api const res = await lastValueFrom(
.saveMenuDist({ this.api.saveMenuDist({
menuIds: String(this.id)?.split(','), menuIds: String(this.id)?.split(','),
dishes: this.formatData(mealDishList), dishes: this.formatData(mealDishList),
}) }),
.subscribe((res) => { )
this.msg.success(res.desc)
this.router.navigate(['/ingredient/item/list']) this.msg.success(res.desc)
}) this.router.navigate(['/ingredient/item/list'])
}, },
}) })
} }

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

@ -90,7 +90,7 @@
<div class="tit flex justify-between items-center"> <div class="tit flex justify-between items-center">
<span> 今日营养分析 </span> <span> 今日营养分析 </span>
<span *ngIf="peoples.length"> <span *ngIf="peoples.length">
<select [(ngModel)]="people" class="select" (ngModelChange)="getAnalysis()"> <select [(ngModel)]="people" class="select" (ngModelChange)="peopleChange()">
<option *ngFor="let p of peoples" [value]="p"> <option *ngFor="let p of peoples" [value]="p">
{{ p }} {{ p }}
</option> </option>

126
projects/client/src/app/pages/data-vis/data-vis.component.ts

@ -10,7 +10,8 @@ interface MenuDisplayItem {
id: number id: number
} }
const changeTime = 1000 * 60 * 3 const menuChangeTime = 1000 * 60 * 3
const peopleChangeTime = 1000 * 10
@Component({ @Component({
selector: 'app-data-vis', selector: 'app-data-vis',
@ -58,12 +59,18 @@ export class DataVisComponent implements AfterViewInit {
currentMenu: MenuDisplayItem | null = null currentMenu: MenuDisplayItem | null = null
lastTime: number = 0 menuChangeLastTime: number = 0
peopleChangeLastTime: number = 0
weekdayMap = weekdayMap weekdayMap = weekdayMap
scrollFlag = true scrollFlag = true
dishCache = new Map<string, any>()
analysisCache = new Map<string, any>()
ngOnInit(): void { ngOnInit(): void {
this.api.getOrgInfo().subscribe((res) => { this.api.getOrgInfo().subscribe((res) => {
const account = this.api.account const account = this.api.account
@ -76,7 +83,8 @@ export class DataVisComponent implements AfterViewInit {
.subscribe(() => { .subscribe(() => {
const now = new Date() const now = new Date()
this.showTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss') this.showTime = format(new Date(), 'yyyy-MM-dd HH:mm:ss')
if (now.getTime() - this.lastTime > changeTime) {
if (now.getTime() - this.menuChangeLastTime > menuChangeTime) {
const currentIndex = this.menus.findIndex((f) => f.id === this.currentMenu?.id) const currentIndex = this.menus.findIndex((f) => f.id === this.currentMenu?.id)
let idx = currentIndex + 1 let idx = currentIndex + 1
if (idx > this.menus.length - 1) { if (idx > this.menus.length - 1) {
@ -84,8 +92,25 @@ export class DataVisComponent implements AfterViewInit {
} }
this.currentMenu = this.menus[idx] this.currentMenu = this.menus[idx]
if (this.currentMenu) { if (this.currentMenu) {
this.lastTime = now.getTime() this.menuChangeLastTime = now.getTime()
this.getDataVisData(this.currentMenu.id) this.getDishData(this.currentMenu.id)
}
}
if (now.getTime() - this.peopleChangeLastTime > peopleChangeTime) {
const currentPeopleIndex = this.peoples.findIndex((f) => f === this.people)
if (this.peoples.length > 1) {
let idx = currentPeopleIndex + 1
if (idx > this.peoples.length - 1) {
idx = 0
}
this.people = this.peoples[idx]
} else if (this.peoples.length === 1) {
this.people = this.peoples[0]
}
if (this.people) {
this.peopleChangeLastTime = now.getTime()
this.getAnalysis()
} }
} }
}) })
@ -107,13 +132,6 @@ export class DataVisComponent implements AfterViewInit {
}) })
} }
ngAfterViewInit(): void {}
ngOnDestroy(): void {
this.destroy$.next(null)
this.destroy$.complete()
}
getMenuListByDay() { getMenuListByDay() {
if (!this.day) { if (!this.day) {
this.msg.error('请选择星期') this.msg.error('请选择星期')
@ -122,15 +140,11 @@ export class DataVisComponent implements AfterViewInit {
this.api.getMenusByDay(this.day).subscribe((res) => { this.api.getMenusByDay(this.day).subscribe((res) => {
this.menus = res.body this.menus = res.body
//重置轮播时间 //重置轮播时间
this.lastTime = new Date('2023-01-01').getTime() this.resetAutoPlay()
}) })
} }
dayChange() { getDishData(id: number) {
this.getMenuListByDay()
}
getDataVisData(id: number) {
this.analysis = null this.analysis = null
this.peoples = [] this.peoples = []
this.people = '' this.people = ''
@ -145,13 +159,15 @@ export class DataVisComponent implements AfterViewInit {
const dishs = res.body const dishs = res.body
if (Array.isArray(dishs)) { if (Array.isArray(dishs)) {
this.peoples = Object.keys(dishs?.[0]?.ingredient?.[0]?.value) this.peoples = Object.keys(dishs?.[0]?.ingredient?.[0]?.value)
this.people = this.peoples[0] // this.people = this.peoples[0]
if (!this.peoples) { if (!this.peoples) {
this.msg.error('没有找到当前食谱适用的人群')
console.error('dishs?.[0]?.ingredient?.[0]?.value 数据错误:', dishs) console.error('dishs?.[0]?.ingredient?.[0]?.value 数据错误:', dishs)
return return
} }
this.peopleChangeLastTime = 0
this.menuId = dishs?.[0]?.menu this.menuId = dishs?.[0]?.menu
this.getAnalysis() // this.getAnalysis()
dishs.forEach((i: any) => { dishs.forEach((i: any) => {
// 把每个食材按照不同的人群将重量加起来 // 把每个食材按照不同的人群将重量加起来
@ -180,6 +196,57 @@ export class DataVisComponent implements AfterViewInit {
}) })
} }
ngAfterViewInit(): void {}
ngOnDestroy(): void {
this.destroy$.next(null)
this.destroy$.complete()
}
resetAutoPlay() {
this.menuChangeLastTime = 0
this.peopleChangeLastTime = 0
}
resetData() {
this.analysis = null
this.menus = []
this.people = ''
this.peoples = []
this.dishs = {}
}
dayChange() {
this.resetData()
this.getMenuListByDay()
}
peopleChange() {
this.peopleChangeLastTime = this.peopleChangeLastTime + peopleChangeTime
this.analysis = null
this.getAnalysis()
}
getAnalysis() {
if (!this.menuId) {
return
}
this.api
.getAnalysis(this.menuId, this.day, this.people)
.pipe(
finalize(() => {
this.analysisLoading = false
}),
)
.subscribe((res) => {
this.analysis = res.body
// setTimeout(() => {
// this.autoScroll(this.nutritionEl.nativeElement, "2");
// }, 1000);
})
}
scrollSubs$?: Subscription scrollSubs$?: Subscription
@HostListener('window:resize') @HostListener('window:resize')
@ -220,23 +287,4 @@ export class DataVisComponent implements AfterViewInit {
}, },
}) })
} }
getAnalysis() {
if (!this.menuId) {
return
}
this.api
.getAnalysis(this.menuId, this.day, this.people)
.pipe(
finalize(() => {
this.analysisLoading = false
}),
)
.subscribe((res) => {
this.analysis = res.body
// setTimeout(() => {
// this.autoScroll(this.nutritionEl.nativeElement, "2");
// }, 1000);
})
}
} }

155
projects/client/src/app/pages/ingredients/ingredient-form/ingredient-form.component.ts

@ -1,19 +1,19 @@
import { Component, OnInit, ViewChild } from "@angular/core"; import { Component, OnInit, ViewChild } from '@angular/core'
import { ActivatedRoute, Router } from "@angular/router"; import { ActivatedRoute, Router } from '@angular/router'
import { ConfirmIngredientComponent } from "@cdk/ingredient/confirm-ingredient/confirm-ingredient.component"; import { ConfirmIngredientComponent } from '@cdk/ingredient/confirm-ingredient/confirm-ingredient.component'
import { IngredientAnalysisComponent } from "@cdk/ingredient/ingredient-analysis/ingredient-analysis.component"; import { IngredientAnalysisComponent } from '@cdk/ingredient/ingredient-analysis/ingredient-analysis.component'
import { DishInterface, IngredientDishComponent } from "@cdk/ingredient/ingredient-dish/ingredient-dish.component"; import { DishInterface, IngredientDishComponent } from '@cdk/ingredient/ingredient-dish/ingredient-dish.component'
import { ApiService } from "@cdk/services"; import { ApiService } from '@cdk/services'
import { OptionItemInterface } from "@cdk/types"; import { OptionItemInterface } from '@cdk/types'
import { NzDrawerService } from "ng-zorro-antd/drawer"; import { NzDrawerService } from 'ng-zorro-antd/drawer'
import { NzMessageService } from "ng-zorro-antd/message"; import { NzMessageService } from 'ng-zorro-antd/message'
import { NzModalService } from "ng-zorro-antd/modal"; import { NzModalService } from 'ng-zorro-antd/modal'
import { forkJoin } from "rxjs"; import { forkJoin, lastValueFrom } from 'rxjs'
@Component({ @Component({
selector: "app-ingredient-form", selector: 'app-ingredient-form',
templateUrl: "./ingredient-form.component.html", templateUrl: './ingredient-form.component.html',
styleUrls: ["./ingredient-form.component.less"], styleUrls: ['./ingredient-form.component.less'],
}) })
export class IngredientFormComponent implements OnInit { export class IngredientFormComponent implements OnInit {
constructor( constructor(
@ -22,58 +22,58 @@ export class IngredientFormComponent implements OnInit {
private router: Router, private router: Router,
private route: ActivatedRoute, private route: ActivatedRoute,
private api: ApiService, private api: ApiService,
private drawer: NzDrawerService private drawer: NzDrawerService,
) { ) {
this.id = this.route.snapshot.paramMap.get("id"); this.id = this.route.snapshot.paramMap.get('id')
} }
@ViewChild("menuDish") menuDish!: IngredientDishComponent; @ViewChild('menuDish') menuDish!: IngredientDishComponent
step = 0; step = 0
id: string | null = ""; id: string | null = ''
menuItem: any = null; menuItem: any = null
menuDishFormServer: any = null; menuDishFormServer: any = null
ngOnInit(): void { ngOnInit(): void {
this.step = this.id && this.id !== "create" ? 1 : 0; this.step = this.id && this.id !== 'create' ? 1 : 0
this.getDetail(); this.getDetail()
} }
getDetail() { getDetail() {
if (this.id && this.id !== "create") { if (this.id && this.id !== 'create') {
this.api.getMenuItem(this.id).subscribe((res) => { this.api.getMenuItem(this.id).subscribe((res) => {
if (res.body) { if (res.body) {
this.menuItem = res.body; this.menuItem = res.body
} }
}); })
this.api.getMenuDist(this.id).subscribe((res) => { this.api.getMenuDist(this.id).subscribe((res) => {
if (Array.isArray(res.body)) { if (Array.isArray(res.body)) {
res.body.forEach((d) => { res.body.forEach((d) => {
d.ingredient.forEach((f: any) => { d.ingredient.forEach((f: any) => {
f["groupValues"] = Object.entries(f.value).map(([peopleName, value]) => { f['groupValues'] = Object.entries(f.value).map(([peopleName, value]) => {
return { return {
peopleName, peopleName,
value, value,
}; }
}); })
}); })
}); })
this.initMenuDish(res.body); this.initMenuDish(res.body)
} }
}); })
} }
} }
initMenuDish(menuDishFormServer: any[]) { initMenuDish(menuDishFormServer: any[]) {
const foodIds = new Set<number>(); const foodIds = new Set<number>()
menuDishFormServer.forEach((i: any) => { menuDishFormServer.forEach((i: any) => {
i.ingredient.map((food: any) => { i.ingredient.map((food: any) => {
foodIds.add(food.key); foodIds.add(food.key)
}); })
}); })
forkJoin([this.api.getFoodList({ keys: Array.from(foodIds) })]).subscribe(([res]) => { forkJoin([this.api.getFoodList({ keys: Array.from(foodIds) })]).subscribe(([res]) => {
this.menuDishFormServer = menuDishFormServer.map((i: any) => { this.menuDishFormServer = menuDishFormServer.map((i: any) => {
return { return {
@ -81,35 +81,35 @@ export class IngredientFormComponent implements OnInit {
dishName: i.name, dishName: i.name,
mealIndex: this.menuItem.meals.findIndex((m: string) => m === i.meal), mealIndex: this.menuItem.meals.findIndex((m: string) => m === i.meal),
items: i.ingredient.map((food: any) => { items: i.ingredient.map((food: any) => {
const fd = res.body.find((f) => f.key === food.key); const fd = res.body.find((f) => f.key === food.key)
food.foodName = fd.name; food.foodName = fd.name
return food; return food
}), }),
}; }
}); })
}); })
} }
onStepChange(idStr: string) { onStepChange(idStr: string) {
this.step = 1; this.step = 1
this.id = idStr; this.id = idStr
this.getDetail(); this.getDetail()
} }
createNewMenu() { createNewMenu() {
this.modal.confirm({ this.modal.confirm({
nzTitle: "警告", nzTitle: '警告',
nzContent: "新建食谱将清空本次所有的配餐数据,确认要清空吗?", nzContent: '新建食谱将清空本次所有的配餐数据,确认要清空吗?',
nzOnOk: () => { nzOnOk: () => {
this.menuDish.mealDishList = []; this.menuDish.mealDishList = []
}, },
}); })
} }
analysis() { analysis() {
this.drawer.create({ this.drawer.create({
nzWidth: 720, nzWidth: 720,
nzWrapClassName: "analysis-drawer", nzWrapClassName: 'analysis-drawer',
nzContent: IngredientAnalysisComponent, nzContent: IngredientAnalysisComponent,
nzContentParams: { nzContentParams: {
menu: { menu: {
@ -118,60 +118,59 @@ export class IngredientFormComponent implements OnInit {
}, },
current: {}, current: {},
}, },
}); })
} }
previewMenu() { previewMenu() {
const { mealDishList } = this.menuDish; const { mealDishList } = this.menuDish
const snapshot = Date.now().toString(); const snapshot = Date.now().toString()
sessionStorage.setItem( sessionStorage.setItem(
snapshot, snapshot,
JSON.stringify({ JSON.stringify({
basic: this.menuItem, basic: this.menuItem,
dishs: this.formatData(mealDishList), dishs: this.formatData(mealDishList),
}) }),
); )
window.open(`/ingredient/preview?snapshot=${snapshot}`); window.open(`/ingredient/preview?snapshot=${snapshot}`)
} }
confirmSave() { confirmSave() {
this.modal.create({ this.modal.create({
nzTitle: "确认食谱信息", nzTitle: '确认食谱信息',
nzContent: ConfirmIngredientComponent, nzContent: ConfirmIngredientComponent,
nzData: this.menuItem, nzData: this.menuItem,
nzWidth: 650, nzWidth: 650,
nzOnOk: () => { nzOnOk: async () => {
const { mealDishList } = this.menuDish; const { mealDishList } = this.menuDish
const res = await lastValueFrom(
this.api this.api.saveMenuDist({
.saveMenuDist({ menuIds: this.id?.split(','),
menuIds: this.id?.split(","),
dishes: this.formatData(mealDishList), dishes: this.formatData(mealDishList),
}) }),
.subscribe((res) => { )
this.msg.success(res.desc);
this.router.navigate(["/ingredient/item/list"]); this.msg.success(res.desc)
}); this.router.navigate(['/ingredient/item/list'])
}, },
}); })
} }
formatData(d: DishInterface[]) { formatData(d: DishInterface[]) {
const data: DishInterface[] = JSON.parse(JSON.stringify(d)); const data: DishInterface[] = JSON.parse(JSON.stringify(d))
data.forEach((dish) => { data.forEach((dish) => {
dish["dishId"] = dish.dish; dish['dishId'] = dish.dish
dish.items = dish.items.map((i) => { dish.items = dish.items.map((i) => {
return { return {
...i, ...i,
value: (i["groupValues"] as any[]).reduce((a, c) => { value: (i['groupValues'] as any[]).reduce((a, c) => {
return { return {
...a, ...a,
[c.peopleName]: c.value, [c.peopleName]: c.value,
}; }
}, {} as Record<string, number>), }, {} as Record<string, number>),
}; }
}); })
}); })
return data; return data
} }
} }

Loading…
Cancel
Save