Browse Source

复制标准&大屏修改&食谱优化

main
kkerwin 1 year ago
parent
commit
166d2c1091
  1. 216
      projects/admin/src/app/pages/ingredients/ingredient-list/ingredient-list.component.ts
  2. 23
      projects/admin/src/app/pages/standard/standard-list/standard-list.component.ts
  3. 4
      projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts
  4. 26
      projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.html
  5. 2
      projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.ts
  6. 10
      projects/cdk/src/services/api.service.ts
  7. 19
      projects/client/src/app/pages/data-vis/data-vis.component.html
  8. 6
      projects/client/src/app/pages/data-vis/data-vis.component.less
  9. 84
      projects/client/src/app/pages/data-vis/data-vis.component.ts

216
projects/admin/src/app/pages/ingredients/ingredient-list/ingredient-list.component.ts

@ -1,18 +1,18 @@
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, OrgDTO, TableListOption } from "@cdk/public-api";
import { ApiService } from "@cdk/services";
import { NzModalService } from "ng-zorro-antd/modal";
import { lastValueFrom, tap } from "rxjs";
import { NzMessageService } from "ng-zorro-antd/message";
import { MyResponse } from "@cdk/types";
import { Router } from "@angular/router";
import { Component, OnInit, TemplateRef, ViewChild } from '@angular/core'
import { FormControl, FormGroup } from '@angular/forms'
import { NzDrawerRef, NzDrawerService } from 'ng-zorro-antd/drawer'
import { AnyObject, OrgDTO, TableListOption } from '@cdk/public-api'
import { ApiService } from '@cdk/services'
import { NzModalService } from 'ng-zorro-antd/modal'
import { lastValueFrom, tap } from 'rxjs'
import { NzMessageService } from 'ng-zorro-antd/message'
import { MyResponse } from '@cdk/types'
import { Router } from '@angular/router'
@Component({
selector: "app-ingredient-list",
templateUrl: "./ingredient-list.component.html",
styleUrls: ["./ingredient-list.component.less"],
selector: 'app-ingredient-list',
templateUrl: './ingredient-list.component.html',
styleUrls: ['./ingredient-list.component.less'],
})
export class IngredientListComponent {
constructor(
@ -20,77 +20,77 @@ export class IngredientListComponent {
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService,
private router: Router
private router: Router,
) {}
globalEnum = this.api.globalEnum;
globalEnum = this.api.globalEnum
statusTextMap: Record<string, string> = {};
statusTextMap: Record<string, string> = {}
@ViewChild("releaseStartTimeTpl") releaseStartTimeTpl!: TemplateRef<{}>;
@ViewChild('releaseStartTimeTpl') releaseStartTimeTpl!: TemplateRef<{}>
private drawerRef?: NzDrawerRef;
private drawerRef?: NzDrawerRef
public tableList = new TableListOption(this.fetchData.bind(this), {
frontPagination: false,
});
})
public queryForm = new FormGroup({
name: new FormControl(""),
vender: new FormControl(""),
status: new FormControl(""),
});
name: new FormControl(''),
vender: new FormControl(''),
status: new FormControl(''),
})
startTime: Date | null = null;
startTime: Date | null = null
tableOrg: { [k: number]: OrgDTO } = {};
tableOrg: { [k: number]: OrgDTO } = {}
monthText = {
1: "一月",
2: "二月",
3: "三月",
4: "四月",
5: "五月",
6: "六月",
7: "七月",
8: "八月",
9: "九月",
10: "十月",
11: "十一月",
12: "十二月",
} as any;
1: '一月',
2: '二月',
3: '三月',
4: '四月',
5: '五月',
6: '六月',
7: '七月',
8: '八月',
9: '九月',
10: '十月',
11: '十一月',
12: '十二月',
} as any
ngOnInit(): void {
this.statusTextMap = this.globalEnum.menuStatus.reduce((a, c) => {
return {
...a,
[String(c.label)]: c.value,
};
}, {} as Record<string, string>);
this.initTableList();
}
}, {} as Record<string, string>)
this.initTableList()
}
initTableList() {
this.tableList.scroll = { x: "900px" };
this.tableList.scroll = { x: '900px' }
this.tableList = this.tableList.setColumns([
{ key: "name", title: "食谱名称", width: "200px" },
{ key: "vender", title: "单位", width: "200px" },
{ key: "meals", title: "包含餐次", width: "180px" },
{ key: "month", title: "适用月份" },
{ key: "day", title: "周期" },
{ key: "status", title: "状态", width: "120px" },
{ key: "modify", title: "更新时间", width: "170px" },
{ key: "operate", title: "创建人", width: "160px" },
]);
{ key: 'name', title: '食谱名称', width: '200px' },
{ key: 'vender', title: '单位', width: '200px' },
{ key: 'meals', title: '包含餐次', width: '180px' },
{ key: 'month', title: '适用月份' },
{ key: 'day', title: '周期' },
{ key: 'status', title: '状态', width: '120px' },
{ key: 'modify', title: '更新时间', width: '170px' },
{ key: 'operate', title: '创建人', width: '160px' },
])
this.tableList = this.tableList.setOptions([
{
title: "详情",
title: '详情',
premissions: [],
onClick: this.preview.bind(this),
},
{
title: "导出",
title: '导出',
premissions: [],
onClick: this.export.bind(this),
@ -109,11 +109,11 @@ export class IngredientListComponent {
// },
// },
{
title: "发布",
title: '发布',
premissions: [],
onClick: this.release.bind(this),
visible(v) {
return [0, 2].includes(v.status);
return [0, 2].includes(v.status)
},
},
// {
@ -124,35 +124,40 @@ export class IngredientListComponent {
// return [2].includes(v.status);
// },
// },
// {
// title: '复制',
// premissions: [],
// onClick: this.copy.bind(this),
// },
{
title: "编辑",
title: '编辑',
premissions: [],
onClick: (v) => {
this.router.navigate([`/ingredient/item/form/${v["id"]}`]);
this.router.navigate([`/ingredient/item/form/${v['id']}`])
},
visible(v) {
return [0, 3, 4].includes(v.status);
return [0, 3, 4].includes(v.status)
},
},
{
title: "删除",
title: '删除',
premissions: [],
onClick: this.deleteItem.bind(this),
},
]);
])
}
fetchData(query: AnyObject, pager: AnyObject) {
return this.api.getMenuPage(pager, query).pipe(
tap((res) => {
this.getTableColumData(res);
})
);
this.getTableColumData(res)
}),
)
}
getTableColumData(res: MyResponse) {
if (Array.isArray(res.body.content)) {
const vendors = res.body.content.map((i: any) => i.vender);
const vendors = res.body.content.map((i: any) => i.vender)
if (vendors.length > 0) {
this.api.getOrgList({ vendors }).subscribe((org) => {
@ -161,83 +166,102 @@ export class IngredientListComponent {
return {
...a,
[c.id]: c,
};
}, {} as AnyObject);
}
});
}, {} as AnyObject)
}
})
}
}
}
preview({ id }: any) {
window.open(`/ingredient/preview?id=${id}`);
window.open(`/ingredient/preview?id=${id}`)
}
export({ id }: any) {
this.msg.loading("导出中...");
this.msg.loading('导出中...')
this.api.exportMenu(id).subscribe(() => {
setTimeout(() => {
this.msg.remove();
}, 1500);
});
this.msg.remove()
}, 1500)
})
}
cancelFoodForm() {
this.drawerRef?.close();
this.drawerRef?.close()
}
copy(v: AnyObject) {
this.modal.confirm({
nzTitle: '警告',
nzContent: '是否要复制该食谱?',
nzOnOk: async () => {
const res = await lastValueFrom(
this.api.saveMenu({
name: v['name'] + '【复制】',
nutrient: v['nutrient'],
vendors: v['vendors'],
month: v,
}),
)
this.msg.success(res.desc)
this.tableList.run()
},
})
}
shenhe({ id }: any) {
this.modal.confirm({
nzTitle: "警告",
nzTitle: '警告',
nzContent: `是否要将该食谱提交审核?`,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.submitMenuForReview(id));
this.msg.success(res.desc);
this.tableList.run();
const res = await lastValueFrom(this.api.submitMenuForReview(id))
this.msg.success(res.desc)
this.tableList.run()
},
});
})
}
release({ id, day }: any) {
this.modal.create({
nzTitle: "发布食谱",
nzTitle: '发布食谱',
nzContent: this.releaseStartTimeTpl,
nzOnOk: async () => {
if (!this.startTime) {
this.msg.error("请选择发布日期");
return false;
this.msg.error('请选择发布日期')
return false
}
const res = await lastValueFrom(this.api.release(id, this.startTime, day));
this.msg.success(res.desc);
this.tableList.run();
return true;
const res = await lastValueFrom(this.api.release(id, this.startTime, day))
this.msg.success(res.desc)
this.tableList.run()
return true
},
});
})
}
deleteItem({ id }: any) {
this.modal.confirm({
nzTitle: "警告",
nzTitle: '警告',
nzContent: `是否要删除该食谱?`,
nzOkDanger: true,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteMenu(id));
this.msg.success(res.desc);
this.tableList.run();
const res = await lastValueFrom(this.api.deleteMenu(id))
this.msg.success(res.desc)
this.tableList.run()
},
});
})
}
disableMenu({ id }: any) {
this.modal.confirm({
nzTitle: "警告",
nzTitle: '警告',
nzContent: `是否要禁用该食谱?`,
nzOkDanger: true,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.disableMenu(id));
this.msg.success(res.desc);
this.tableList.run();
const res = await lastValueFrom(this.api.disableMenu(id))
this.msg.success(res.desc)
this.tableList.run()
},
});
})
}
}

23
projects/admin/src/app/pages/standard/standard-list/standard-list.component.ts

@ -51,6 +51,11 @@ export class StandardListComponent {
premissions: [],
onClick: this.toSetting.bind(this),
},
{
title: '复制',
premissions: [],
onClick: this.copy.bind(this),
},
{
title: '编辑',
premissions: [],
@ -68,6 +73,24 @@ export class StandardListComponent {
return this.api.getStandardPage(pager, query)
}
copy(standard: AnyObject) {
this.modal.confirm({
nzTitle: '警告',
nzContent: '是否要复制该标准?',
nzOnOk: async () => {
const res = await lastValueFrom(
this.api.saveStandard({
...standard,
name: standard['name'] + '【复制】',
id: null,
}),
)
this.msg.success(res.desc)
this.tableList.run()
},
})
}
toEdit(d: AnyObject) {
// this.standard.settingData$.next(d)
this.router.navigate([`/standard/form/${d['id']}`], {

4
projects/cdk/src/ingredient/ingredient-dish/ingredient-dish.component.ts

@ -107,14 +107,16 @@ export class IngredientDishComponent implements OnChanges {
const meals = this.menuBaisc.meals as string[]
console.log('this.menuBaisc', this.menuBaisc)
const day = this.menuBaisc.day as number[]
this.days = day.map((i) => {
this.days = day.map((i, idx) => {
const d = weekdayMap[i]
this.selectDay.push({
label: d,
value: String(i),
})
this.mealCurrentIndex[i] = 0
if (idx === 0) {
this.expanded.add(i)
}
// if (!this.menuObject) {
// this.menuObject = {};
// }

26
projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.html

@ -17,20 +17,16 @@
</tr> -->
<tr>
<th nzWidth="300px" class="placeholder-th">&nbsp;</th>
<th nzWidth="200px">
<th nzWidth="200px" nzLeft class="placeholder-th">&nbsp;</th>
<th nzWidth="200px" nzLeft>
<span class="absolute top-2 left-[500px] z-10 w-[150px] h-[40px]">重量/克</span>
</th>
<th
*ngFor="let p of peopleGroups; let last = last"
[ngClass]="{ 'placeholder-th': !last }"
nzWidth="150px"
></th>
<th *ngFor="let p of peopleGroups; let last = last" [ngClass]="{ 'placeholder-th': !last }"></th>
</tr>
<tr>
<th nzWidth="300px">菜品</th>
<th nzWidth="200px">食材</th>
<th nzWidth="200px" nzLeft>菜品</th>
<th nzWidth="200px" nzLeft>食材</th>
<th *ngFor="let p of peopleGroups">
{{ p }}
</th>
@ -39,16 +35,16 @@
<tbody>
<ng-container *ngFor="let dish of mealDishs; let dishIndex = index">
<ng-container *ngIf="dish['mealIndex'] === mealIndex && dish['day'] === day">
<ng-container *ngFor="let food of dish['items']; let first = first">
<ng-container *ngFor="let food of dish['items']; let first = first; let last = last">
<tr>
<td *ngIf="first" [rowSpan]="dish['items'].length">
<div class="flex justify-between">
<td nzLeft [ngStyle]="last ? {} : { 'border-bottom': 'none' }">
<div class="flex justify-between" *ngIf="first">
<div class="flex-1">
<div>
{{ dish['dishName'] }}
</div>
<div>
<nz-tag nzColor="blue" *ngFor="let lb of dish['dishLabel'] ?? []">
<nz-tag nzColor="blue" *ngFor="let lb of dish['label'] ?? []">
{{ lb }}
</nz-tag>
</div>
@ -65,7 +61,7 @@
</nz-dropdown-menu> -->
</div>
</td>
<td>
<td nzLeft>
<div class="flex justify-between">
<span>
{{ food['foodName'] }}
@ -89,7 +85,7 @@
</ng-container>
</ng-container>
<tr class="total">
<td colSpan="2" class="text-center">本餐生重总量</td>
<td [attr.colSpan]="2" nzLeft class="text-center">本餐生重总量</td>
<td *ngFor="let p of peopleGroups">
{{ $any(totalObj[p])?.toFixed(2) }}
</td>

2
projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.ts

@ -126,7 +126,7 @@ export class IngredientMealsComponent implements OnChanges, OnInit {
const { dish, foods } = this.drawerRef.getContentComponent() as AddDishToIngredientComponent
this.mealDishs.push({
dishLabel: dish.dishLabel,
label: dish.dishLabel,
day: this.day,
mealIndex: this.mealIndex,
meal: this.meals[this.mealIndex],

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

@ -578,12 +578,16 @@ export class ApiService {
return this.http.get<ResponseType>(`/api/menu/analysis/types?${params}`)
}
getCurrentDayDataVisList() {
getCurrentWeekdays() {
return this.http.get<ResponseType>(`/api/menu/display`)
}
getMenuDataVis(menuId: number) {
getMenusByDay(day: number) {
return this.http.get<ResponseType>(`/api/menu/display?day=${day}`)
}
getMenuDataVis(menuId: number, day: number) {
// return this.http.get<ResponseType>(`/api/menu/dish`);
return this.http.get<ResponseType>(`/api/menu/display?menuId=${menuId}`)
return this.http.get<ResponseType>(`/api/menu/display?menuId=${menuId}&day=${day}`)
}
}

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

@ -4,7 +4,18 @@
<img *ngIf="logo" [attr.src]="'/api/icon/' + logo" />
</div>
<h1 class="title">{{ orgName }}食谱营养报告</h1>
<div class="time">{{ showTime }}</div>
<div class="time">
<span *ngIf="weeks.length">
<select [(ngModel)]="day" class="select week" (ngModelChange)="dayChange()">
<option *ngFor="let w of weeks" [value]="w">
{{ weekdayMap[w] }}
</option>
</select>
</span>
<span>
{{ showTime }}
</span>
</div>
</div>
<div class="mainbox flex flex-1">
<!-- <div class="boxnav mapc">
@ -43,7 +54,11 @@
{{ item.name }}
</div>
</td>
<td>123</td>
<td>
<nz-tag nzColor="blue" *ngFor="let lb of item['label'] ?? []">
{{ lb }}
</nz-tag>
</td>
<td *ngFor="let p of peoples">
<div class="td">{{ item.people[p] }}g</div>
</td>

6
projects/client/src/app/pages/data-vis/data-vis.component.less

@ -232,6 +232,12 @@ i {
border: none;
outline: none;
&.week {
margin-right: 12px;
border-radius: 4px;
padding: 2px 12px;
}
option {
background-color: #fff;
color: #012059;

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

@ -1,7 +1,9 @@
import { AfterViewInit, Component, ElementRef, Renderer2, ViewChild } from '@angular/core'
import { AfterViewInit, Component, ElementRef, HostListener, Renderer2, ViewChild } from '@angular/core'
import { weekdayMap } from '@cdk/ingredient/ingredient-form-basic/ingredient-form-basic.component'
import { ApiService } from '@cdk/services'
import { format } from 'date-fns'
import { Subject, Subscription, finalize, interval, takeUntil } from 'rxjs'
import { NzMessageService } from 'ng-zorro-antd/message'
import { Subject, Subscription, filter, finalize, interval, takeUntil } from 'rxjs'
interface MenuDisplayItem {
name: string
@ -16,7 +18,7 @@ const changeTime = 1000 * 60 * 3
styleUrls: ['./data-vis.component.less'],
})
export class DataVisComponent implements AfterViewInit {
constructor(private api: ApiService, private rd2: Renderer2) {}
constructor(private api: ApiService, private rd2: Renderer2, private msg: NzMessageService) {}
@ViewChild('tableEl') tableEl!: ElementRef<HTMLElement>
@ -34,6 +36,10 @@ export class DataVisComponent implements AfterViewInit {
peoples: string[] = []
weeks: number[] = []
day?: number
globalEnum = this.api.globalEnum
analysisLoading = false
@ -54,10 +60,13 @@ export class DataVisComponent implements AfterViewInit {
lastTime: number = 0
weekdayMap = weekdayMap
scrollFlag = true
ngOnInit(): void {
this.api.getOrgInfo().subscribe((res) => {
const account = this.api.account
this.orgName = account?.vender?.name ?? ''
this.logo = account?.vender?.icon ?? ''
})
@ -75,15 +84,25 @@ export class DataVisComponent implements AfterViewInit {
}
this.currentMenu = this.menus[idx]
if (this.currentMenu) {
this.getDataVisData(this.currentMenu.id)
this.lastTime = now.getTime()
this.getDataVisData(this.currentMenu.id)
}
}
})
this.api.getCurrentDayDataVisList().subscribe((r) => {
this.api.getCurrentWeekdays().subscribe((r) => {
if (Array.isArray(r.body)) {
this.menus = r.body
this.weeks = r.body
if (this.weeks.length > 0) {
const day = new Date().getDay()
const currentWeekDay = day === 0 ? 7 : day
if (this.weeks.includes(currentWeekDay)) {
this.day = currentWeekDay
this.getMenuListByDay()
} else {
this.msg.error('今天没有食谱')
}
}
}
})
}
@ -95,14 +114,34 @@ export class DataVisComponent implements AfterViewInit {
this.destroy$.complete()
}
getMenuListByDay() {
if (!this.day) {
this.msg.error('请选择星期')
return
}
this.api.getMenusByDay(this.day).subscribe((res) => {
this.menus = res.body
//重置轮播时间
this.lastTime = new Date('2023-01-01').getTime()
})
}
dayChange() {
this.getMenuListByDay()
}
getDataVisData(id: number) {
this.analysis = null
this.peoples = []
this.people = ''
this.dishs = {}
this.scroll = {}
this.api.getMenuDataVis(id).subscribe((res) => {
if (!this.day) {
this.msg.error('请选择星期')
return
}
this.scrollFlag = false
this.api.getMenuDataVis(id, this.day).subscribe((res) => {
const dishs = res.body
if (Array.isArray(dishs)) {
this.peoples = Object.keys(dishs?.[0]?.ingredient?.[0]?.value)
@ -134,34 +173,51 @@ export class DataVisComponent implements AfterViewInit {
}
})
setTimeout(() => {
this.autoScroll(this.tableEl.nativeElement, '1')
}, 1000)
this.scrollFlag = true
this.autoScroll()
})
}
})
}
scrollSubs$?: Subscription
autoScroll(el: HTMLElement, scroll: string) {
@HostListener('window:resize')
onResize() {
this.autoScroll()
}
autoScroll() {
const el = this.tableEl.nativeElement
const scroll = '1'
this.scrollSubs$?.unsubscribe()
const child = el.children[0]
if (!child) {
return
}
this.rd2.setStyle(child, 'transform', `translateY(0px)`)
const elHeight = el.clientHeight
const childHeight = child.clientHeight
console.log(childHeight, elHeight)
if (childHeight <= elHeight) {
return
}
this.scrollSubs$ = interval(60)
.pipe(takeUntil(this.destroy$))
.subscribe(() => {
.pipe(
takeUntil(this.destroy$),
filter(() => this.scrollFlag),
)
.subscribe({
next: () => {
this.scroll[scroll] = (this.scroll[scroll] ?? 0) + 1
const paddingBottom = 100
if (this.scroll[scroll] - paddingBottom > childHeight - el.clientHeight) {
this.scroll[scroll] = 0
}
// console.log('this.scroll', this.scroll)
this.rd2.setStyle(child, 'transform', `translateY(-${this.scroll[scroll]}px)`)
},
})
}

Loading…
Cancel
Save