Browse Source

25年3月 需求修改

main
kely 3 months ago
parent
commit
de406872e2
  1. 7
      angular.json
  2. 14912
      package-lock.json
  3. 1
      package.json
  4. 3
      projects/admin/src/app/components/dish-form/dish-form.component.html
  5. 30
      projects/admin/src/app/components/dish-form/dish-form.component.ts
  6. 63
      projects/admin/src/app/pages/dish/dish.component.html
  7. 4
      projects/admin/src/app/pages/dish/dish.component.less
  8. 7
      projects/admin/src/app/pages/dish/dish.component.ts
  9. 11
      projects/admin/src/app/pages/standard/standard-form/standard-form.component.html
  10. 30
      projects/admin/src/app/pages/standard/standard-form/standard-form.component.ts
  11. 77
      projects/admin/src/styles.less
  12. 43
      projects/cdk/src/dtos/enum.dto.ts
  13. 3
      projects/cdk/src/ingredient/ingredient-analysis/ingredient-analysis.component.html
  14. 150
      projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.html
  15. 16
      projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts
  16. 6
      projects/cdk/src/services/api.service.ts
  17. 4
      projects/cdk/src/shared/components/print/print.component.html
  18. 81
      projects/cdk/src/shared/components/print/print.component.ts
  19. 2
      projects/cdk/src/table-list/table-list/table-list.component.ts
  20. 21
      projects/client/src/app/components/app-layout/app-layout.component.html
  21. 2
      projects/client/src/app/components/dish-form/dish-form.component.html
  22. 26
      projects/client/src/app/components/dish-form/dish-form.component.ts
  23. 65
      projects/client/src/app/pages/dish/dish.component.html
  24. 6
      projects/client/src/app/pages/dish/dish.component.ts
  25. 70
      projects/client/src/styles.less

7
angular.json

@ -2,8 +2,8 @@
"$schema": "./node_modules/@angular/cli/lib/config/schema.json", "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1, "version": 1,
"cli": { "cli": {
"packageManager": "pnpm", "packageManager": "pnpm",
"analytics": false "analytics": false
}, },
"newProjectRoot": "projects", "newProjectRoot": "projects",
"projects": { "projects": {
@ -37,7 +37,8 @@
} }
], ],
"styles": ["projects/admin/src/styles.less"], "styles": ["projects/admin/src/styles.less"],
"scripts": [] "scripts": [],
"allowedCommonJsDependencies": ["crypto-js"]
}, },
"configurations": { "configurations": {
"production": { "production": {

14912
package-lock.json

File diff suppressed because it is too large

1
package.json

@ -22,6 +22,7 @@
"@angular/platform-browser-dynamic": "^16.1.0", "@angular/platform-browser-dynamic": "^16.1.0",
"@angular/router": "^16.1.0", "@angular/router": "^16.1.0",
"@ant-design/icons-angular": "^16.0.0", "@ant-design/icons-angular": "^16.0.0",
"@ctrl/tinycolor": "^4.1.0",
"crypto-js": "^4.1.1", "crypto-js": "^4.1.1",
"date-fns": "^2.30.0", "date-fns": "^2.30.0",
"echarts": "^5.4.3", "echarts": "^5.4.3",

3
projects/admin/src/app/components/dish-form/dish-form.component.html

@ -35,6 +35,7 @@
[nzMode]="'multiple'" [nzMode]="'multiple'"
nzPlaceHolder="请选择单位,多选时在多个单位均添加此菜品" nzPlaceHolder="请选择单位,多选时在多个单位均添加此菜品"
[nzShowArrow]="false" [nzShowArrow]="false"
(ngModelChange)="checkName()"
formControlName="vendors" formControlName="vendors"
> >
<nz-option *ngFor="let o of orgListOfOption" [nzLabel]="o.text" [nzValue]="o.value"></nz-option> <nz-option *ngFor="let o of orgListOfOption" [nzLabel]="o.text" [nzValue]="o.value"></nz-option>
@ -44,7 +45,7 @@
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired> 菜品名称 </nz-form-label> <nz-form-label nzRequired> 菜品名称 </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl"> <nz-form-control [nzErrorTip]="formControlErrorTpl">
<input placeholder="请输入菜品名称" nz-input formControlName="name" /> <input placeholder="请输入菜品名称" nz-input formControlName="name" (ngModelChange)="checkName()" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>

30
projects/admin/src/app/components/dish-form/dish-form.component.ts

@ -195,6 +195,34 @@ export class DishFormComponent {
} }
onFoodSelected(v: string[]) { onFoodSelected(v: string[]) {
// 过滤掉不在 v 中的项
this.foodItemSelected = this.foodItemSelected.filter((item) => v.includes(item.value))
// 添加新的选择项
const newSelectedItems = this.searchedFood.filter(
(item) => v.includes(item.value) && !this.foodItemSelected.some((s) => s.value === item.value),
)
this.foodItemSelected = [...this.foodItemSelected, ...newSelectedItems]
console.log('this.foodItemSelected', this.searchedFood, v)
}
checkName() {
const name = this.formGroup.get('name')?.value
const vendors = this.formGroup.get('vendors')?.value
if (!name || !vendors) {
return
}
this.api.checkFoodName(name, vendors).subscribe((res) => {
if (!res.body) {
this.msg.error('菜品名称已存在')
this.formGroup.get('name')?.setValue('')
}
})
}
onFoodSelected1(v: string[]) {
const temp = this.foodItemSelected
this.foodItemSelected = [] this.foodItemSelected = []
this.searchedFood.forEach((item) => { this.searchedFood.forEach((item) => {
if (this.foodItemSelected.some((s) => s.value === item.value)) { if (this.foodItemSelected.some((s) => s.value === item.value)) {
@ -205,6 +233,8 @@ export class DishFormComponent {
} }
}) })
// console.log('this.foodItemSelected', this.searchedFood, v)
// this.foodItemSelected = this.searchedFood.filter((f) => { // this.foodItemSelected = this.searchedFood.filter((f) => {
// return v.includes(f.value) // return v.includes(f.value)
// }); // });

63
projects/admin/src/app/pages/dish/dish.component.html

@ -97,32 +97,41 @@
<app-print #print [content]="printContent"> </app-print> <app-print #print [content]="printContent"> </app-print>
<ng-template #printContent> <ng-template #printContent>
<div class="printContent" *ngFor="let item of printData"> <div class="print-wrapper">
<table class="print-table"> <div class="print-content" *ngFor="let page of paginatedData">
<tbody> <div class="print-item" *ngFor="let item of page; let i = index">
<tr> <div>
<th colspan="3"> <table class="print-table">
{{ item.name }} <tbody>
</th> <tr>
</tr> <th colspan="3">
<tr> {{ item.name }}
<th colspan="3">营养成分表</th> <span *ngIf="item?.label?.length > 0">
</tr> <span *ngFor="let label of item.label" class="label-tag">{{ label }}</span>
<tr> </span>
<th class="text-left">名称</th> </th>
<th class="text-center">每100克(g)</th> </tr>
<th class="text-center">营养参考值%(NRV%)</th> <tr>
</tr> <th colspan="3">营养成分表</th>
</tbody> </tr>
<tbody> <tr>
<tr *ngFor="let th of item.component"> <th class="text-left">名称</th>
<td [width]="'38.2%'">{{ th.name }}</td> <th class="text-center">每100克</th>
<td class="text-center">{{ th.nutrition }}</td> <th class="text-center">营养参考值%(NRV%)</th>
<td class="text-center">{{ th.nrv }}</td> </tr>
</tr> </tbody>
</tbody> <tbody>
</table> <tr *ngFor="let th of item.component">
<div *ngIf="item.ingredients.length > 0">主要原料:{{ item.ingredients.join(',') }}</div> <td [width]="'38.2%'">{{ th.name }}</td>
<div>1毫克(mg)钠相当于2.5毫克食盐</div> <td class="text-center">{{ th.nutrition }}</td>
<td class="text-center">{{ th.nrv }}</td>
</tr>
</tbody>
</table>
<div *ngIf="item.ingredients.length > 0">主要原料:{{ item.ingredients.join(',') }}</div>
<div>1毫克钠相当于2.5毫克食盐</div>
</div>
</div>
</div>
</div> </div>
</ng-template> </ng-template>

4
projects/admin/src/app/pages/dish/dish.component.less

@ -5,8 +5,4 @@
background-size: cover; background-size: cover;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
}
.printContent {
page-break-after: always;
} }

7
projects/admin/src/app/pages/dish/dish.component.ts

@ -156,9 +156,16 @@ export class DishComponent {
) )
.subscribe((res) => { .subscribe((res) => {
this.printData = res.body this.printData = res.body
console.log('this.printData ', this.printData)
this.printRef.print() this.printRef.print()
}) })
} }
get paginatedData() {
const chunkSize = 4
return Array.from({ length: Math.ceil(this.printData.length / chunkSize) }, (_, i) =>
this.printData.slice(i * chunkSize, i * chunkSize + chunkSize),
)
}
fetchData(query: AnyObject, pager: AnyObject) { fetchData(query: AnyObject, pager: AnyObject) {
return this.api.getDishPage(pager, query).pipe( return this.api.getDishPage(pager, query).pipe(

11
projects/admin/src/app/pages/standard/standard-form/standard-form.component.html

@ -28,16 +28,19 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<ng-container *ngFor="let item of api.globalEnum.nutrient"> <ng-container *ngFor="let item of nutrientStandard">
<tr *ngIf="item.key === 'energy'"> <!-- <tr *ngIf="item.key === 'energy'"> -->
<tr>
<td> <td>
{{ item.value }} {{ item.value }}
</td> </td>
<td> <td>
<nz-input-group nzAddOnBefore="±" nzAddOnAfter="%"> <nz-input-group nzAddOnBefore="±" nzAddOnAfter="%">
<input <nz-input-number
class="w-full"
nz-input nz-input
min="0" [nzMin]="item['min']"
[nzMax]="item['max']"
[(ngModel)]="overflows[item.key]" [(ngModel)]="overflows[item.key]"
[ngModelOptions]="{ standalone: true }" [ngModelOptions]="{ standalone: true }"
/> />

30
projects/admin/src/app/pages/standard/standard-form/standard-form.component.ts

@ -1,7 +1,7 @@
import { Component } from '@angular/core' import { Component } from '@angular/core'
import { FormBuilder, FormGroup } from '@angular/forms' import { FormBuilder, FormGroup } from '@angular/forms'
import { ActivatedRoute, Router } from '@angular/router' import { ActivatedRoute, Router } from '@angular/router'
import { OrgDTO } from '@cdk/dtos' import { NutrientDTO, OrgDTO } from '@cdk/dtos'
import { ApiService } from '@cdk/services' import { ApiService } from '@cdk/services'
import { OptionItemInterface } from '@cdk/types' import { OptionItemInterface } from '@cdk/types'
import { Utils } from '@cdk/utils' import { Utils } from '@cdk/utils'
@ -51,6 +51,7 @@ export class StandardFormComponent {
vendors: OrgDTO[] = [] vendors: OrgDTO[] = []
nutrientStandard: (NutrientDTO & { [k: string]: any })[] = []
// nzPopoverVisible = false; // nzPopoverVisible = false;
listOfOption: (OrgDTO & CheckboxItem)[] = [] listOfOption: (OrgDTO & CheckboxItem)[] = []
@ -67,6 +68,7 @@ export class StandardFormComponent {
// vendors: this.fb.control([], [FormValidators.required()]), // vendors: this.fb.control([], [FormValidators.required()]),
overflow: this.fb.control('0', [FormValidators.required()]), overflow: this.fb.control('0', [FormValidators.required()]),
}) })
this.setStandardData()
if (this.state) { if (this.state) {
this.formGroup.patchValue(this.state) this.formGroup.patchValue(this.state)
this.overflows = this.state?.overflows ?? {} this.overflows = this.state?.overflows ?? {}
@ -98,6 +100,32 @@ export class StandardFormComponent {
// }); // });
} }
setStandardData() {
this.nutrientStandard = this.api.globalEnum.nutrient.map((i) => {
let min: undefined | number = -20
let max: undefined | number = 20
switch (i.key) {
case 'protein':
min = void 0
max = 180
break
case 'energy':
min = -10
max = 10
break
default:
break
}
return {
...i,
max,
min,
}
})
}
searchOrg = (k: string) => { searchOrg = (k: string) => {
this.orgSearch$.next(k) this.orgSearch$.next(k)
} }

77
projects/admin/src/styles.less

@ -116,6 +116,9 @@ li {
} }
@media print { @media print {
#root { #root {
display: none; display: none;
@ -128,30 +131,78 @@ li {
} }
body { body {
// padding: 12px;
background-color: transparent !important; background-color: transparent !important;
} }
* { * {
color: #000 !important; color: #000 !important;
} }
}
.print-wrapper {
.print-table {
width: 100%;
margin-bottom: 12px; width: 100%;
border-collapse: collapse; height: 100%;
border-spacing: 0; // height: 297mm;
border: 1px solid #e8e8e8;
td, .print-content {
th { display: grid;
padding: 6px 12px; grid-template-columns: 1fr 1fr;
border: 1px solid #000; /* 每行 2 列 */
font-weight: normal; grid-template-rows: 1fr 1fr;
/* 每页 2 行 */
gap: 5mm;
/* 适当增加间距 */
page-break-after: always;
/* 每个 .print-content 之后换页 */
.print-item {
width: 100%;
height: 100%;
/* 让它们均匀填充 */
padding: 0 12px;
box-sizing: border-box;
}
// table {
// margin: 10px;
// }
}
} }
.print-table {
width: 100%;
margin-bottom: 12px;
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #e8e8e8;
td,
th {
padding: 6px 12px;
border: 1px solid #000;
font-weight: normal;
}
}
} }
.label-tag {
display: inline-block;
padding: 2px 5px;
border-radius: 2px;
font-size: 12px;
color: #fff;
border: 1px solid #000;
margin-right: 5px;
}
.analysis-drawer { .analysis-drawer {
.ant-drawer-header-title { .ant-drawer-header-title {

43
projects/cdk/src/dtos/enum.dto.ts

@ -1,28 +1,29 @@
import { OptionItemInterface } from "@cdk/types"; import { OptionItemInterface } from '@cdk/types'
export type GlobalEnum = { export type GlobalEnum = {
category: CategoryDTO[]; category: CategoryDTO[]
mark: MarkDTO[]; mark: MarkDTO[]
nutrient: NutrientDTO[]; nutrient: NutrientDTO[]
venderType: CategoryDTO[]; venderType: CategoryDTO[]
mealType: OptionItemInterface[]; mealType: OptionItemInterface[]
menuStatus: OptionItemInterface[]; menuStatus: OptionItemInterface[]
measurementType: CategoryDTO[]; measurementType: CategoryDTO[]
poly: { key: string; name: string }[]; poly: { key: string; name: string }[]
}; }
export type CategoryDTO = { export type CategoryDTO = {
key: string; key: string
value: string; value: string
}; }
export type MarkDTO = { export type MarkDTO = {
key: string; key: string
value: string; value: string
}; }
export type NutrientDTO = { export type NutrientDTO = {
key: string; key: string
value: string; value: string
measurement: string; measurement: string
nrv: number; nrv: number
}; sort: number
}

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

@ -3,7 +3,8 @@
<div nz-row [nzGutter]="12"> <div nz-row [nzGutter]="12">
<div nz-col nzSpan="8" *ngIf="menu.days.length"> <div nz-col nzSpan="8" *ngIf="menu.days.length">
<nz-select class="w-full" [(ngModel)]="currentDay" (ngModelChange)="getAnalysis()"> <nz-select class="w-full" [(ngModel)]="currentDay" (ngModelChange)="getAnalysis()">
<nz-option [nzLabel]="'日平均'" [nzValue]="0"> </nz-option> <!-- <nz-option [nzLabel]="'日平均'" [nzValue]="0"> </nz-option> -->
<nz-option [nzLabel]="'周平均'" [nzValue]="0"> </nz-option>
<nz-option *ngFor="let item of menu.days" [nzLabel]="weekdayMap[item]" [nzValue]="item"> <nz-option *ngFor="let item of menu.days" [nzLabel]="weekdayMap[item]" [nzValue]="item">
</nz-option> </nz-option>
</nz-select> </nz-select>

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

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

16
projects/cdk/src/ingredient/nutrition-table/nutrition-table.component.ts

@ -1,14 +1,18 @@
import { Component, Input } from "@angular/core"; import { Component, Input } from '@angular/core'
@Component({ @Component({
selector: "lib-nutrition-table", selector: 'lib-nutrition-table',
templateUrl: "./nutrition-table.component.html", templateUrl: './nutrition-table.component.html',
styleUrls: ["./nutrition-table.component.less"], styleUrls: ['./nutrition-table.component.less'],
}) })
export class NutritionTableComponent { export class NutritionTableComponent {
constructor() {} constructor() {}
@Input() nutritions: any[] = []; @Input() nutritions: any[] = []
@Input() dark = false; @Input() dark = false
ngOnInit() {
console.log('this.nutritions', this.nutritions)
}
} }

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

@ -275,6 +275,12 @@ export class ApiService {
) )
} }
checkFoodName(name: string, vender: any) {
const params = new HttpParams().set('name', name).set('vender', vender)
return this.http.get<ResponseType<boolean>>('/api/dish/check/name', {
params,
})
}
getFoodList(query: {}) { getFoodList(query: {}) {
const q = Utils.objectStringify(query) const q = Utils.objectStringify(query)
return this.http.get<ResponseType<any[]>>(`/api/ingredient/select?${q}`) return this.http.get<ResponseType<any[]>>(`/api/ingredient/select?${q}`)

4
projects/cdk/src/shared/components/print/print.component.html

@ -1,3 +1 @@
<iframe #iframe <iframe #iframe style="display: none"> </iframe>
style="display: none;">
</iframe>

81
projects/cdk/src/shared/components/print/print.component.ts

@ -1,4 +1,4 @@
import { DomPortalOutlet, PortalOutlet, TemplatePortal } from "@angular/cdk/portal"; import { DomPortalOutlet, PortalOutlet, TemplatePortal } from '@angular/cdk/portal'
import { import {
ApplicationRef, ApplicationRef,
Component, Component,
@ -10,83 +10,86 @@ import {
TemplateRef, TemplateRef,
ViewChild, ViewChild,
ViewContainerRef, ViewContainerRef,
} from "@angular/core"; } from '@angular/core'
@Component({ @Component({
selector: "app-print", selector: 'app-print',
templateUrl: "./print.component.html", templateUrl: './print.component.html',
styleUrls: ["./print.component.less"], styleUrls: ['./print.component.less'],
}) })
export class PrintComponent implements OnInit { export class PrintComponent implements OnInit {
@Input() content!: TemplateRef<any>; @Input() content!: TemplateRef<any>
@ViewChild("iframe") iframe!: ElementRef<HTMLIFrameElement>; @ViewChild('iframe') iframe!: ElementRef<HTMLIFrameElement>
private portalHost?: PortalOutlet; private portalHost?: PortalOutlet
public printed: boolean = false; public printed: boolean = false
constructor( constructor(
private componentFactoryResolver: ComponentFactoryResolver, private componentFactoryResolver: ComponentFactoryResolver,
private injector: Injector, private injector: Injector,
private appRef: ApplicationRef, private appRef: ApplicationRef,
private viewContainerRef: ViewContainerRef private viewContainerRef: ViewContainerRef,
) {} ) {}
ngOnInit(): void {} ngOnInit(): void {}
testCall() { testCall() {}
alert("testCall");
}
private _attachStyles(targetWindow: Window): void { private _attachStyles(targetWindow: Window): void {
// Copy styles from parent window // Copy styles from parent window
document.querySelectorAll("style").forEach((htmlElement) => { document.querySelectorAll('style').forEach((htmlElement) => {
targetWindow.document.head.appendChild(htmlElement.cloneNode(true)); targetWindow.document.head.appendChild(htmlElement.cloneNode(true))
}); })
// Copy stylesheet link from parent window // Copy stylesheet link from parent window
const styleSheetElement = this._getStyleSheetElement(); const styleSheetElement = this._getStyleSheetElement()
targetWindow.document.head.appendChild(styleSheetElement); styleSheetElement.forEach((element) => {
targetWindow.document.head.appendChild(element)
})
} }
private _getStyleSheetElement() { private _getStyleSheetElement() {
const styleSheetElement = document.createElement("link"); const links: HTMLLinkElement[] = []
document.querySelectorAll("link").forEach((htmlElement) => {
if (htmlElement.rel === "stylesheet") { document.querySelectorAll('link').forEach((htmlElement) => {
const absoluteUrl = new URL(htmlElement.href).href; if (htmlElement.rel === 'stylesheet') {
styleSheetElement.rel = "stylesheet"; const styleSheetElement = document.createElement('link')
styleSheetElement.type = "text/css"; const absoluteUrl = new URL(htmlElement.href).href
styleSheetElement.href = absoluteUrl; styleSheetElement.rel = 'stylesheet'
styleSheetElement.type = 'text/css'
styleSheetElement.href = absoluteUrl
links.push(styleSheetElement)
} }
}); })
return styleSheetElement; return links
} }
print(): void { print(): void {
const iframe = this.iframe.nativeElement; const iframe = this.iframe.nativeElement
const { contentDocument, contentWindow } = iframe; const { contentDocument, contentWindow } = iframe
if (contentDocument && contentWindow) { if (contentDocument && contentWindow) {
this.portalHost = new DomPortalOutlet( this.portalHost = new DomPortalOutlet(
contentDocument.body, contentDocument.body,
this.componentFactoryResolver, this.componentFactoryResolver,
this.appRef, this.appRef,
this.injector this.injector,
); )
const portal = new TemplatePortal(this.content, this.viewContainerRef); const portal = new TemplatePortal(this.content, this.viewContainerRef)
if (!this.printed) { if (!this.printed) {
this._attachStyles(contentWindow); this._attachStyles(contentWindow)
} }
this.portalHost.attach(portal); this.portalHost.attach(portal)
window.onafterprint = () => { window.onafterprint = () => {
// Chrome 不会触发 // Chrome 不会触发
contentDocument.body.innerHTML = ""; contentDocument.body.innerHTML = ''
}; }
setTimeout(() => { setTimeout(() => {
contentWindow.print(); contentWindow.print()
contentDocument.body.innerHTML = ""; contentDocument.body.innerHTML = ''
}, 500); }, 500)
} }
} }
} }

2
projects/cdk/src/table-list/table-list/table-list.component.ts

@ -127,7 +127,7 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
this.props.trigger$.pipe(debounceTime(100)).subscribe((e?: any) => { this.props.trigger$.pipe(debounceTime(100)).subscribe((e?: any) => {
if (this.props.fetchData) { if (this.props.fetchData) {
this.props.pager.loading = true this.props.pager.loading = true
// this.selection.clear(); this.selection.clear()
this.emitState() this.emitState()
const query = this.formGroup.getRawValue() const query = this.formGroup.getRawValue()
// console.log("query", query); // console.log("query", query);

21
projects/client/src/app/components/app-layout/app-layout.component.html

@ -43,16 +43,7 @@
<span nz-icon nzType="question-circle" nzTheme="outline"></span> <span nz-icon nzType="question-circle" nzTheme="outline"></span>
<span>使用流程</span> <span>使用流程</span>
</li> </li>
<li
nz-menu-item
class="k-icon"
(click)="openDataVis()"
nzMatchRouter
*ngxPermissionsOnly="['19', '20']"
>
<span nz-icon nzType="fund" nzTheme="outline"></span>
<span>大屏显示</span>
</li>
<!-- <li nz-menu-item [routerLink]="['/','meal-setting']" nzMatchRouter <!-- <li nz-menu-item [routerLink]="['/','meal-setting']" nzMatchRouter
*ngxPermissionsOnly="['21','22']"> *ngxPermissionsOnly="['21','22']">
<span nz-icon nzType="setting" nzTheme="outline"></span> <span nz-icon nzType="setting" nzTheme="outline"></span>
@ -145,6 +136,16 @@
</li> </li>
</ul> </ul>
</li> </li>
<li
nz-menu-item
class="k-icon"
(click)="openDataVis()"
nzMatchRouter
*ngxPermissionsOnly="['19', '20']"
>
<span nz-icon nzType="fund" nzTheme="outline"></span>
<span>大屏显示</span>
</li>
</ul> </ul>
</nz-sider> </nz-sider>
<nz-layout class="inner-layout overflow-hidden"> <nz-layout class="inner-layout overflow-hidden">

2
projects/client/src/app/components/dish-form/dish-form.component.html

@ -23,7 +23,7 @@
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired> 菜品名称 </nz-form-label> <nz-form-label nzRequired> 菜品名称 </nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl"> <nz-form-control [nzErrorTip]="formControlErrorTpl">
<input placeholder="请输入菜品名称" nz-input formControlName="name" /> <input (ngModelChange)="checkName()" placeholder="请输入菜品名称" nz-input formControlName="name" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>

26
projects/client/src/app/components/dish-form/dish-form.component.ts

@ -189,6 +189,32 @@ export class DishFormComponent {
} }
onFoodSelected(v: string[]) { onFoodSelected(v: string[]) {
// 过滤掉不在 v 中的项
this.foodItemSelected = this.foodItemSelected.filter((item) => v.includes(item.value))
// 添加新的选择项
const newSelectedItems = this.searchedFood.filter(
(item) => v.includes(item.value) && !this.foodItemSelected.some((s) => s.value === item.value),
)
this.foodItemSelected = [...this.foodItemSelected, ...newSelectedItems]
console.log('this.foodItemSelected', this.searchedFood, v)
}
checkName() {
const name = this.formGroup.get('name')?.value
const vendors = this.api.account.vender.id
if (!name || !vendors) {
return
}
this.api.checkFoodName(name, vendors).subscribe((res) => {
if (!res.body) {
this.msg.error('菜品名称已存在')
this.formGroup.get('name')?.setValue('')
}
})
}
onFoodSelected1(v: string[]) {
this.foodItemSelected = [] this.foodItemSelected = []
this.searchedFood.forEach((item) => { this.searchedFood.forEach((item) => {
if (this.foodItemSelected.some((s) => s.value === item.value)) { if (this.foodItemSelected.some((s) => s.value === item.value)) {

65
projects/client/src/app/pages/dish/dish.component.html

@ -108,32 +108,43 @@
<app-print #print [content]="printContent"> </app-print> <app-print #print [content]="printContent"> </app-print>
<ng-template #printContent> <ng-template #printContent>
<div class="printContent" *ngFor="let item of printData"> <div class="print-wrapper">
<table class="print-table"> <div class="print-content" *ngFor="let page of paginatedData">
<tbody> <div class="print-item" *ngFor="let item of page; let i = index">
<tr> <div>
<th colspan="3"> <table class="print-table">
{{ item.name }} <tbody>
</th> <tr>
</tr> <th colspan="3">
<tr> {{ item.name }}
<th colspan="3">营养成分表</th> <span *ngIf="item?.label?.length > 0">
</tr> <span class="label-tag" *ngFor="let label of item.label" nzColor="blue">{{
<tr> label
<th class="text-left">名称</th> }}</span>
<th class="text-center">每100克(g)</th> </span>
<th class="text-center">营养参考值%(NRV%)</th> </th>
</tr> </tr>
</tbody> <tr>
<tbody> <th colspan="3">营养成分表</th>
<tr *ngFor="let th of item.component"> </tr>
<td [width]="'38.2%'">{{ th.name }}</td> <tr>
<td class="text-center">{{ th.nutrition }}</td> <th class="text-left">名称</th>
<td class="text-center">{{ th.nrv }}</td> <th class="text-center">每100克</th>
</tr> <th class="text-center">营养参考值%(NRV%)</th>
</tbody> </tr>
</table> </tbody>
<div *ngIf="item.ingredients.length > 0">主要原料:{{ item.ingredients.join(',') }}</div> <tbody>
<div>1毫克(mg)钠相当于2.5毫克食盐</div> <tr *ngFor="let th of item.component">
<td [width]="'38.2%'">{{ th.name }}</td>
<td class="text-center">{{ th.nutrition }}</td>
<td class="text-center">{{ th.nrv }}</td>
</tr>
</tbody>
</table>
<div *ngIf="item.ingredients.length > 0">主要原料:{{ item.ingredients.join(',') }}</div>
<div>1毫克钠相当于2.5毫克食盐</div>
</div>
</div>
</div>
</div> </div>
</ng-template> </ng-template>

6
projects/client/src/app/pages/dish/dish.component.ts

@ -149,6 +149,12 @@ export class DishComponent {
} }
} }
get paginatedData() {
const chunkSize = 4
return Array.from({ length: Math.ceil(this.printData.length / chunkSize) }, (_, i) =>
this.printData.slice(i * chunkSize, i * chunkSize + chunkSize),
)
}
printTag(v?: any) { printTag(v?: any) {
this.msg.loading('数据请求中,请不要刷新页面', { this.msg.loading('数据请求中,请不要刷新页面', {
nzDuration: 0, nzDuration: 0,

70
projects/client/src/styles.less

@ -91,28 +91,74 @@ li {
} }
body { body {
// padding: 12px;
background-color: transparent !important; background-color: transparent !important;
} }
* { * {
color: #000 !important; color: #000 !important;
} }
}
.print-wrapper {
.print-table {
width: 100%;
margin-bottom: 12px;
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #e8e8e8;
td, width: 100%;
th { height: 100%;
padding: 6px 12px; // height: 297mm;
border: 1px solid #000;
font-weight: normal;
.print-content {
display: grid;
grid-template-columns: 1fr 1fr;
/* 每行 2 列 */
grid-template-rows: 1fr 1fr;
/* 每页 2 行 */
gap: 5mm;
/* 适当增加间距 */
page-break-after: always;
/* 每个 .print-content 之后换页 */
.print-item {
width: 100%;
height: 100%;
/* 让它们均匀填充 */
padding: 0 12px;
box-sizing: border-box;
}
// table {
// margin: 10px;
// }
}
}
.print-table {
width: 100%;
margin-bottom: 12px;
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #e8e8e8;
td,
th {
padding: 6px 12px;
border: 1px solid #000;
font-weight: normal;
}
} }
}
.label-tag {
display: inline-block;
padding: 2px 5px;
border-radius: 2px;
font-size: 12px;
color: #fff;
border: 1px solid #000;
margin-right: 5px;
} }
.analysis-drawer { .analysis-drawer {

Loading…
Cancel
Save