From 26f42c272f192bd7d16bbddb326769726001474c Mon Sep 17 00:00:00 2001 From: kkerwin Date: Sun, 17 Dec 2023 19:47:46 +0800 Subject: [PATCH] =?UTF-8?q?=E7=B3=96=E6=B2=B9=E7=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/app/pages/dish/dish.component.html | 6 +- projects/cdk/src/services/api.service.ts | 6 +- projects/client/src/app/app-routing.module.ts | 2 +- projects/client/src/app/app.module.ts | 13 +- .../app-layout/app-layout.component.html | 11 + .../src/app/pages/dish/dish.component.html | 6 +- .../salt-oil-sugar.component.html | 47 ++-- .../salt-oil-sugar.component.ts | 258 +++++++++++++++--- projects/client/src/assets/k-icon/sugar.svg | 1 + 9 files changed, 271 insertions(+), 79 deletions(-) create mode 100644 projects/client/src/assets/k-icon/sugar.svg diff --git a/projects/admin/src/app/pages/dish/dish.component.html b/projects/admin/src/app/pages/dish/dish.component.html index 7cde65e..36093ae 100644 --- a/projects/admin/src/app/pages/dish/dish.component.html +++ b/projects/admin/src/app/pages/dish/dish.component.html @@ -111,18 +111,18 @@ 名称 每100克(g) - 营养参考值%(NVR%) + 营养参考值%(NRV%) {{ th.name }} {{ th.nutrition }} - {{ th.nvr }} + {{ th.nrv }} -
主要原料:{{ item.ingredients.join(',') }}
+
主要原料:{{ item.ingredients.join(',') }}
1毫克(mg)钠相当于2.5毫克食盐
diff --git a/projects/cdk/src/services/api.service.ts b/projects/cdk/src/services/api.service.ts index ca89299..70e88cb 100644 --- a/projects/cdk/src/services/api.service.ts +++ b/projects/cdk/src/services/api.service.ts @@ -598,17 +598,17 @@ export class ApiService { getSaltOilSugarPage(p: {}, q: {}) { const params = Utils.objectStringify({ ...p, ...q }) - return this.http.get>(`/api/menu?${params}`) + return this.http.get>(`/api/sugar?${params}`) } saveSaltOilSugar(v: AnyObject) { const body = Utils.objectToFormData(v) const method = v['id'] ? 'post' : 'put' - return this.http[method]('/api/dish', body) + return this.http[method]('/api/sugar', body) } deleteSaltOilSugar(id: string | number) { const params = Utils.objectToFormData({ id }) - return this.http.delete(`/api/menu`, { body: params }) + return this.http.delete(`/api/sugar`, { body: params }) } } diff --git a/projects/client/src/app/app-routing.module.ts b/projects/client/src/app/app-routing.module.ts index a7572a3..3b74948 100644 --- a/projects/client/src/app/app-routing.module.ts +++ b/projects/client/src/app/app-routing.module.ts @@ -54,7 +54,7 @@ const routes: Routes = [ canActivate: [ngxPermissionsGuard], data: { permissions: { - only: ['18'], + only: ['38'], redirectTo: '/forbidden', }, }, diff --git a/projects/client/src/app/app.module.ts b/projects/client/src/app/app.module.ts index 03b6f75..686b69e 100644 --- a/projects/client/src/app/app.module.ts +++ b/projects/client/src/app/app.module.ts @@ -1,7 +1,7 @@ import { NgModule } from '@angular/core' import { BrowserModule } from '@angular/platform-browser' -import { NZ_I18N } from 'ng-zorro-antd/i18n' +import { NZ_DATE_CONFIG, NZ_DATE_LOCALE, NZ_I18N } from 'ng-zorro-antd/i18n' import { zh_CN } from 'ng-zorro-antd/i18n' import { registerLocaleData } from '@angular/common' import zh from '@angular/common/locales/zh' @@ -13,6 +13,7 @@ import { AppRoutingModule } from './app-routing.module' import { AppComponent } from './app.component' import { IconsProviderModule, PROJECT_NAME, TableListModule } from '@cdk/public-api' import { SharedModule } from '@cdk/shared/shared.module' +import { zhCN } from 'date-fns/locale' import { AppLayoutComponent, OrgFormComponent, @@ -84,6 +85,16 @@ registerLocaleData(zh) { provide: NZ_I18N, useValue: zh_CN }, { provide: HTTP_INTERCEPTORS, useClass: HTTPInterceptor, multi: true }, { provide: TitleStrategy, useClass: TemplatePageTitleStrategy }, + { + provide: NZ_DATE_LOCALE, + useValue: zhCN, + }, + { + provide: NZ_DATE_CONFIG, + useValue: { + firstDayOfWeek: 1, + }, + }, ], bootstrap: [AppComponent], }) diff --git a/projects/client/src/app/components/app-layout/app-layout.component.html b/projects/client/src/app/components/app-layout/app-layout.component.html index a2aa28f..562c596 100644 --- a/projects/client/src/app/components/app-layout/app-layout.component.html +++ b/projects/client/src/app/components/app-layout/app-layout.component.html @@ -106,6 +106,17 @@ +
  • + + 糖油盐管理 +
  • +
  • 名称 每100克(g) - 营养参考值%(NVR%) + 营养参考值%(NRV%) {{ th.name }} {{ th.nutrition }} - {{ th.nvr }} + {{ th.nrv }} -
    主要原料:{{ item.ingredients.join(',') }}
    +
    主要原料:{{ item.ingredients.join(',') }}
    1毫克(mg)钠相当于2.5毫克食盐
    diff --git a/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.html b/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.html index 44e7b23..4392f09 100644 --- a/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.html +++ b/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.html @@ -26,32 +26,16 @@ - - {{ data | date : 'yyyy-MM-dd HH:mm:ss' }} + + {{ data | date : 'yyyy-MM-dd' }} ~ {{ row['endTime'] | date : 'yyyy-MM-dd' }} - - {{ tableOrg[data] ? tableOrg[data].name : '-' }} + {{ data }}天 + + {{ data }}g/日均{{ row['sugarDay'] }}g + {{ data }}g/日均{{ row['saltDay'] }}g + {{ data }}g/日均{{ row['oilDay'] }}g - - {{ item }} - - 周{{ data }} - - {{ statusTextMap[data] }} - - -
    - - 全年 - - - - {{ item }} - - -
    -
    {{ data }} @@ -93,7 +77,7 @@ [nzMin]="0" [nzPrecision]="0" nzPlaceHolder="请输入糖用量" - formControlName="sugar" + formControlName="sugarWeek" > @@ -107,7 +91,7 @@ [nzPrecision]="0" [nzMin]="0" nzPlaceHolder="请输入油用量" - formControlName="oil" + formControlName="oilWeek" > @@ -121,7 +105,7 @@ [nzMin]="0" [nzPrecision]="0" nzPlaceHolder="请输入盐用量" - formControlName="salt" + formControlName="saltWeek" > @@ -135,13 +119,18 @@
    -
    +
    - + +
    -
    + + + + +
    diff --git a/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.ts b/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.ts index eeeb149..df0cb2d 100644 --- a/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.ts +++ b/projects/client/src/app/pages/salt-oil-sugar/salt-oil-sugar.component.ts @@ -1,4 +1,4 @@ -import { Component, ElementRef, OnInit, TemplateRef, ViewChild } from '@angular/core' +import { ChangeDetectorRef, Component, ElementRef, OnInit, TemplateRef, ViewChild } from '@angular/core' import { FormControl, FormGroup } from '@angular/forms' import { NzDrawerRef, NzDrawerService } from 'ng-zorro-antd/drawer' import { AnyObject, FormValidators, OrgDTO, TableListOption, Utils } from '@cdk/public-api' @@ -7,16 +7,89 @@ import { NzModalService } from 'ng-zorro-antd/modal' import { lastValueFrom, tap } from 'rxjs' import { NzMessageService } from 'ng-zorro-antd/message' import { Router } from '@angular/router' -import { format, getWeek } from 'date-fns' +import { endOfWeek, format, getWeek, startOfWeek } from 'date-fns' import { EChartsType, init } from 'echarts' +type SugarItemInterface = { + day: number + endTime: number + id: number + name: string + oilDay: number + oilWeek: number + saltDay: number + saltWeek: number + startTime: number + sugarDay: number + sugarWeek: number + vender: number +} + +const trendTextMapping = new Map([ + ['oilDay', { text: '日均油用量/g' }], + ['saltDay', { text: '日均盐用量/g' }], + ['sugarDay', { text: '日均糖用量/g' }], + ['oilWeek', { text: '油用量(周)/g' }], + ['saltWeek', { text: '盐用量(周)/g' }], + ['sugarWeek', { text: '糖用量(周)/g' }], +]) + +const defaultTrendSeriesObj = { + xAxis: [] as string[], + day: [ + { + type: 'line', + name: 'oilDay', + key: 'oilDay', + data: [] as number[], + }, + { + type: 'line', + name: 'saltDay', + key: 'saltDay', + data: [] as number[], + }, + { + type: 'line', + name: 'sugarDay', + key: 'sugarDay', + data: [] as number[], + }, + ], + week: [ + { + type: 'line', + name: 'oilWeek', + key: 'oilWeek', + data: [] as number[], + }, + { + type: 'line', + name: 'saltWeek', + key: 'saltWeek', + data: [] as number[], + }, + { + type: 'line', + name: 'sugarWeek', + key: 'sugarWeek', + data: [] as number[], + }, + ], +} + @Component({ selector: 'app-salt-oil-sugar', templateUrl: './salt-oil-sugar.component.html', styleUrls: ['./salt-oil-sugar.component.less'], }) export class SaltOilSugarComponent { - constructor(private api: ApiService, private modal: NzModalService, private msg: NzMessageService) {} + constructor( + private api: ApiService, + private modal: NzModalService, + private msg: NzMessageService, + private cdr: ChangeDetectorRef, + ) {} globalEnum = this.api.globalEnum @@ -41,9 +114,10 @@ export class SaltOilSugarComponent { public recordForm = new FormGroup({ week: new FormControl('', [FormValidators.required('请选择周')]), day: new FormControl(5, [FormValidators.required('请选择供餐天数')]), - sugar: new FormControl('', []), - oil: new FormControl('', []), - salt: new FormControl('', []), + sugarWeek: new FormControl('', []), + id: new FormControl('', []), + oilWeek: new FormControl('', []), + saltWeek: new FormControl('', []), }) startTime: Date | null = null @@ -64,12 +138,11 @@ export class SaltOilSugarComponent { this.tableList.scroll = { x: '900px' } this.tableList = this.tableList.setColumns([ { key: 'name', title: '周' }, - - { key: 'meals', title: '日期' }, - { key: 'month', title: '供餐天数' }, - { key: 'day', title: '糖用量(周)/日均用量' }, - { key: 'status', title: '油用量(周)/日均用量' }, - { key: 'modify', title: '盐用量(周)/日均用量' }, + { key: 'startTime', title: '日期' }, + { key: 'day', title: '供餐天数' }, + { key: 'sugarWeek', title: '糖用量(周)/日均用量' }, + { key: 'oilWeek', title: '油用量(周)/日均用量' }, + { key: 'saltWeek', title: '盐用量(周)/日均用量' }, ]) this.tableList = this.tableList.setOptions([ @@ -87,24 +160,54 @@ export class SaltOilSugarComponent { ]) } - fetchData(query: AnyObject, pager: AnyObject) { - return this.api.getSaltOilSugarPage(pager, query).pipe() + fetchData(p: AnyObject, q: AnyObject) { + const range = q['date'] + if (Array.isArray(range)) { + if (range[0]) { + q['startTime'] = format(range[0], 'yyyy-MM-dd') + } + if (range[1]) { + q['endTime'] = format(range[1], 'yyyy-MM-dd') + } + } + return this.api.getSaltOilSugarPage(p, q).pipe() + } + + getWeekRange(date: Date | string) { + if (!(date instanceof Date)) { + date = new Date(date) + } + + const startTime = format(startOfWeek(date, { weekStartsOn: 1 }), 'yyyy-MM-dd') + const endTime = format(endOfWeek(date, { weekStartsOn: 1 }), 'yyyy-MM-dd') + + return { + startTime, + endTime, + } } openModal(record?: any) { + if (record) { + const week = new Date(record['startTime']) + this.recordForm.patchValue({ ...record, week }) + } this.modal.create({ nzTitle: record ? '编辑记录' : '新增记录', nzContent: this.formTpl, + nzOnCancel: this.handleCancel.bind(this), nzOnOk: async () => { if (Utils.validateFormGroup(this.recordForm)) { const res = await lastValueFrom( this.api.saveSaltOilSugar({ ...this.recordForm.value, - week: format(this.recordForm.value.week as unknown as Date, 'yyyy-ww'), + ...this.getWeekRange(this.recordForm.value.week!), }), ) this.msg.success(res.desc) this.tableList.run() + this.handleCancel() + return true } return false @@ -112,6 +215,12 @@ export class SaltOilSugarComponent { }) } + handleCancel() { + this.recordForm.reset({ + day: 5, + }) + } + deleteItem({ id }: any) { this.modal.confirm({ nzTitle: '警告', @@ -125,7 +234,50 @@ export class SaltOilSugarComponent { }) } + thisYearTrend: SugarItemInterface[] = [] + + trendSeriesObj = defaultTrendSeriesObj + + trendType: 'day' | 'week' = 'day' + + onTrendTypeChange(idx: number) { + this.trendType = idx === 0 ? 'day' : 'week' + this.genEchart() + } + trend() { + this.trendType = 'day' + this.trendSeriesObj = JSON.parse(JSON.stringify(defaultTrendSeriesObj)) + this.fetchData({ pageNo: 0, pageSize: 60 }, { week: [] }).subscribe((res) => { + this.thisYearTrend = res.body.content + this.thisYearTrend + .sort((a, b) => a.endTime - b.endTime) + .forEach((i) => { + this.trendSeriesObj.xAxis.push(i.name) + this.trendSeriesObj.day.forEach((day) => { + day.data.push(i[day.key as keyof SugarItemInterface] as number) + day.name = trendTextMapping.get(day.key)!.text + //@ts-ignore + day.encode = { + x: 'category', + y: day.key, + } + }) + this.trendSeriesObj.week.forEach((week) => { + week.data.push(i[week.key as keyof SugarItemInterface] as number) + week.name = trendTextMapping.get(week.key)!.text + //@ts-ignore + week.encode = { + x: 'category', + y: week.key, + } + }) + }) + this.cdr.markForCheck() + setTimeout(() => { + this.genEchart() + }) + }) this.modal.create({ nzTitle: '糖油盐趋势', nzContent: this.trendTpl, @@ -134,30 +286,58 @@ export class SaltOilSugarComponent { }) } - saveToImage() {} - genEchart() { - // const xAxis: string[] = Object.keys(this.suger['oil']).map((i: any) => weekdayMap[i]) - // const series: any[] = [] - // Object.entries(this.suger).forEach(([k, v]) => { - // if (k !== 'crow') { - // series.push({ - // type: 'line', - // name: sugerMap.get(k), - // data: Object.values(v as any), - // }) - // } - // }) - // const option = { - // legend: {}, - // tooltip: { trigger: 'axis' }, - // xAxis: { type: 'category', data: xAxis }, - // yAxis: {}, - // series, - // } - // if (!this.sugerRef) { - // this.sugerRef = init(this.trendElementTpl.nativeElement) - // } - // this.sugerRef.setOption(option) + const option = { + color: ['#1191ff', '#facd0c', '#2bc35b'], + legend: {}, + tooltip: { + trigger: 'axis', + // formatter: function (v: any[]) { + // return v.map((i) => `${trendTextMapping.get(i.seriesName)?.text ?? ''}:${i.data}`).join('
    ') + // }, + }, + xAxis: { type: 'category', data: this.trendSeriesObj.xAxis }, + yAxis: {}, + series: this.trendSeriesObj[this.trendType], + } + if (this.sugerRef) { + this.sugerRef.dispose() + } + if (this.trendElementTpl?.nativeElement) { + this.sugerRef = init(this.trendElementTpl.nativeElement) + this.sugerRef.setOption(option) + } + } + + saveToImage() { + const echart = this.sugerRef + if (!echart) { + this.msg.error('没有找到趋势图') + return + } + const base64String = echart.getDataURL() + const filename = `${this.trendType === 'day' ? '日均用量' : '周用量'}_${Date.now()}.png` + const byteString = atob(base64String.split(',')[1]) + const mimeString = base64String.split(',')[0].split(':')[1].split(';')[0] + const ab = new ArrayBuffer(byteString.length) + const ia = new Uint8Array(ab) + + for (let i = 0; i < byteString.length; i++) { + ia[i] = byteString.charCodeAt(i) + } + + const blob = new Blob([ab], { type: mimeString }) + const url = URL.createObjectURL(blob) + + const link = document.createElement('a') + link.href = url + link.download = filename + document.body.appendChild(link) + link.click() + + setTimeout(() => { + document.body.removeChild(link) + URL.revokeObjectURL(url) + }, 0) } } diff --git a/projects/client/src/assets/k-icon/sugar.svg b/projects/client/src/assets/k-icon/sugar.svg new file mode 100644 index 0000000..d699fcf --- /dev/null +++ b/projects/client/src/assets/k-icon/sugar.svg @@ -0,0 +1 @@ + \ No newline at end of file