Browse Source

业务端 界面

main
kkerwin 2 years ago
parent
commit
54a853e879
  1. 94
      angular.json
  2. 182
      doc/basic.md
  3. 15
      doc/change.md
  4. 42
      doc/index.md
  5. 0
      doc/readme.md
  6. 303
      doc/user.md
  7. 180
      doc/vender.md
  8. 6
      package.json
  9. 8
      projects/admin/src/app/app.module.ts
  10. 32
      projects/admin/src/app/components/app-layout/app-layout.component.html
  11. 14
      projects/admin/src/app/components/app-layout/app-layout.component.less
  12. 14
      projects/admin/src/app/components/app-layout/app-layout.component.ts
  13. 1
      projects/admin/src/app/components/index.ts
  14. 2
      projects/admin/src/app/pages/dish/dish.component.html
  15. 10
      projects/admin/src/app/pages/dish/dish.component.ts
  16. 64
      projects/admin/src/app/pages/food/food.component.html
  17. 65
      projects/admin/src/app/pages/food/food.component.ts
  18. 257
      projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.html
  19. 7
      projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.less
  20. 23
      projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.ts
  21. 49
      projects/admin/src/app/pages/ingredients/ingredient-review/ingredient-review.component.html
  22. 21
      projects/admin/src/app/pages/ingredients/ingredient-review/ingredient-review.component.ts
  23. 36
      projects/admin/src/styles.less
  24. 0
      projects/cdk/src/app-page/app-page.component.html
  25. 0
      projects/cdk/src/app-page/app-page.component.less
  26. 4
      projects/cdk/src/app-page/app-page.component.ts
  27. 0
      projects/cdk/src/icons/icons-provider.module.ts
  28. 127
      projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.html
  29. 0
      projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.less
  30. 8
      projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.ts
  31. 53
      projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.html
  32. 8
      projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.less
  33. 71
      projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.ts
  34. 252
      projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.html
  35. 0
      projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.less
  36. 21
      projects/cdk/src/ingredient/ingredient-meals/ingredient-meals.component.ts
  37. 12
      projects/cdk/src/ingredient/ingredient.module.ts
  38. 2
      projects/cdk/src/public-api.ts
  39. 0
      projects/cdk/src/shared/components/index.ts
  40. 0
      projects/cdk/src/shared/ng-zorro.ts
  41. 5
      projects/cdk/src/shared/shared.module.ts
  42. 2
      projects/cdk/src/table-list/table-list-options.ts
  43. 10
      projects/cdk/src/table-list/table-list/table-list.component.html
  44. 3
      projects/cdk/src/table-list/table-list/table-list.component.ts
  45. 52
      projects/client/src/app/app-routing.module.ts
  46. 1
      projects/client/src/app/app.component.html
  47. 0
      projects/client/src/app/app.component.less
  48. 29
      projects/client/src/app/app.component.spec.ts
  49. 10
      projects/client/src/app/app.component.ts
  50. 52
      projects/client/src/app/app.module.ts
  51. 73
      projects/client/src/app/components/app-layout/app-layout.component.html
  52. 64
      projects/client/src/app/components/app-layout/app-layout.component.less
  53. 35
      projects/client/src/app/components/app-layout/app-layout.component.ts
  54. 1
      projects/client/src/app/components/index.ts
  55. 5
      projects/client/src/app/pages/dashboard/dashboard.component.html
  56. 0
      projects/client/src/app/pages/dashboard/dashboard.component.less
  57. 10
      projects/client/src/app/pages/dashboard/dashboard.component.ts
  58. 1
      projects/client/src/app/pages/data-vis/data-vis.component.html
  59. 0
      projects/client/src/app/pages/data-vis/data-vis.component.less
  60. 10
      projects/client/src/app/pages/data-vis/data-vis.component.ts
  61. 74
      projects/client/src/app/pages/dish/dish.component.html
  62. 18
      projects/client/src/app/pages/dish/dish.component.less
  63. 99
      projects/client/src/app/pages/dish/dish.component.ts
  64. 87
      projects/client/src/app/pages/food/food.component.html
  65. 9
      projects/client/src/app/pages/food/food.component.less
  66. 83
      projects/client/src/app/pages/food/food.component.ts
  67. 6
      projects/client/src/app/pages/index.ts
  68. 65
      projects/client/src/app/pages/login/login.component.html
  69. 81
      projects/client/src/app/pages/login/login.component.less
  70. 42
      projects/client/src/app/pages/login/login.component.ts
  71. 48
      projects/client/src/app/pages/meal-setting/meal-setting.component.html
  72. 0
      projects/client/src/app/pages/meal-setting/meal-setting.component.less
  73. 10
      projects/client/src/app/pages/meal-setting/meal-setting.component.ts
  74. 25
      projects/client/src/app/services/client-api.service.ts
  75. 84
      projects/client/src/app/services/http.interceptor.ts
  76. 1
      projects/client/src/app/services/index.ts
  77. 0
      projects/client/src/assets/.gitkeep
  78. BIN
      projects/client/src/assets/images/jl-logo.png
  79. 17
      projects/client/src/assets/images/login-bg.svg
  80. BIN
      projects/client/src/assets/images/login.png
  81. 1
      projects/client/src/assets/k-icon/carrot.svg
  82. 1
      projects/client/src/assets/k-icon/food.svg
  83. BIN
      projects/client/src/favicon.ico
  84. 16
      projects/client/src/index.html
  85. 7
      projects/client/src/main.ts
  86. 59
      projects/client/src/styles.less
  87. 14
      projects/client/tsconfig.app.json
  88. 14
      projects/client/tsconfig.spec.json

94
angular.json

@ -72,7 +72,8 @@
"browserTarget": "admin:build:production" "browserTarget": "admin:build:production"
}, },
"development": { "development": {
"browserTarget": "admin:build:development" "browserTarget": "admin:build:development",
"port": 4200
} }
}, },
"defaultConfiguration": "development" "defaultConfiguration": "development"
@ -125,6 +126,97 @@
} }
} }
} }
},
"client": {
"projectType": "application",
"schematics": {
"@schematics/angular:component": {
"style": "less"
}
},
"root": "projects/client",
"sourceRoot": "projects/client/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser",
"options": {
"outputPath": "dist/client",
"index": "projects/client/src/index.html",
"main": "projects/client/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "projects/client/tsconfig.app.json",
"inlineStyleLanguage": "less",
"assets": [
"projects/client/src/favicon.ico",
"projects/client/src/assets",
{
"glob": "**/*",
"input": "./node_modules/@ant-design/icons-angular/src/inline-svg/",
"output": "/assets/"
}
],
"styles": ["projects/client/src/styles.less"],
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"vendorChunk": true,
"extractLicenses": false,
"sourceMap": true,
"namedChunks": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"browserTarget": "client:build:production"
},
"development": {
"browserTarget": "client:build:development",
"port": 4201
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular-devkit/build-angular:extract-i18n",
"options": {
"browserTarget": "client:build"
}
},
"test": {
"builder": "@angular-devkit/build-angular:karma",
"options": {
"polyfills": ["zone.js", "zone.js/testing"],
"tsConfig": "projects/client/tsconfig.spec.json",
"inlineStyleLanguage": "less",
"assets": ["projects/client/src/favicon.ico", "projects/client/src/assets"],
"styles": ["projects/client/src/styles.less"],
"scripts": []
}
}
}
} }
} }
} }

182
doc/basic.md

File diff suppressed because one or more lines are too long

15
doc/change.md

@ -0,0 +1,15 @@
# 修改记录
> 9.10
```
基础协议
用户协议
角色协议
单位协议
```
> 9.11
```
食材
菜品
食谱

42
doc/index.md

@ -0,0 +1,42 @@
# 协议约定
>协议格式: restfull + json + utf-8
>协议格式中,凡是用 * 标识字段均为必须字段,否则为可选字段。
>密码:<font color=red>协议中涉及password字段全部使用16位的MD5加密传输(MD5加密后取后16位,大写)</font>
### 协议列表
```
* [协议约定](protocol.md)
* [修改记录](change.md)
* [基础协议](basic.md)
* [用户权限](user.md)
* [单位协议](vender.md)
```
### 响应示例
```
{
"body": {......}, //返回的业务数据
"code":*1, //结果码
"desc":*"成功", //结果描述
}
```
### 返回码表
```
基础返回码:
success (200, "成功"),
invalid_user_password (300, "用户名或者密码错误!"),
expired_vender (301, "账户过期,请联系管理员续费!"),
illegal_argument (400, "参数错误!"),
need_login (401, "未登录!"),
not_support_operate (404, "不支持的请求!"),
not_privileged (405, "无权限执行该操作!"),
system_error (500, "系统异常!"),
operate_failure (503, "操作失败!");
```

0
projects/admin/src/app/shared/components/index.ts → doc/readme.md

303
doc/user.md

@ -0,0 +1,303 @@
# 用户部分
# 1. 检查UID的是否重复
> GET /api/user/check?uid=zzz
### 输出:
```
{
"body": false, // true标识uid未被占用
"code": 200,
"desc": "成功",
"success": true
}
```
# 2. 添加用户
> PUT /api/user
### 输入:
```
Content-Type:application/x-www-form-urlencoded
uid=ccc // 必填, 用户ID, 不能重复
name=曹 // 必填, 用户姓名
password=BE56E057F20F883E // 必填, MD5加密后大写取后16位,示例原密码为123456
roleId=2 //角色编号,只能是自己单位的角色,必填,从角色列表选择一个
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 3. 删除用户
> DELETE /api/user
### 输入:
```
Content-Type:application/x-www-form-urlencoded
uid=ccc // 必填,
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 4. 修改用户
> POST /api/user
### 输入:
```
Content-Type:application/x-www-form-urlencoded
uid=ccc // 用户ID, 必填
name=曹 // 修改姓名
password=BE56E057F20F883E // 修改密码
roleId=2 //修改角色, 0-标识回收角色,其他-标识分配角色
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 5. 获取用户列表
> GET /api/user
### 输出:
```
{
"body": [
{
"name": "业务端测试账号",
"phone": "13919103409",
"roleId": 2,
"roleName": "超级管理员",
"uid": "xxx"
}
],
"code": 200,
"desc": "成功",
"success": true
}
```
# 6. 获取当前用户所在端的权限项列表
> GET /api/role/item
### 输出:
```
{
"body": [
{
"category": "基础权限",
"id": 18,
"itemName": "使用流程",
"itemType": "业务端"
},
{
"category": "基础权限",
"id": 19,
"itemName": "数据大屏-大屏显示",
"itemType": "业务端"
},
{
"category": "基础权限",
"id": 20,
"itemName": "数据大屏-大屏显示(LED)",
"itemType": "业务端"
},
{
"category": "配餐设置",
"id": 21,
"itemName": "配餐设置-查看",
"itemType": "业务端"
},
{
"category": "配餐设置",
"id": 22,
"itemName": "配餐设置-编辑",
"itemType": "业务端"
},
{
"category": "食材管理",
"id": 23,
"itemName": "食材列表-查看",
"itemType": "业务端"
},
{
"category": "食材管理",
"id": 24,
"itemName": "食材-常用/忌用",
"itemType": "业务端"
},
{
"category": "菜品管理",
"id": 25,
"itemName": "菜品列表-查看",
"itemType": "业务端"
},
{
"category": "菜品管理",
"id": 26,
"itemName": "菜品-新增/编辑/删除",
"itemType": "业务端"
},
{
"category": "食谱管理",
"id": 27,
"itemName": "食谱列表-查看",
"itemType": "业务端"
},
{
"category": "食谱管理",
"id": 28,
"itemName": "食谱-新增/编辑/删除",
"itemType": "业务端"
},
{
"category": "食谱管理",
"id": 29,
"itemName": "食谱审核记录-查看",
"itemType": "业务端"
},
{
"category": "基础信息管理",
"id": 30,
"itemName": "单位基础信息-查看",
"itemType": "业务端"
},
{
"category": "基础信息管理",
"id": 31,
"itemName": "单位基础信息-修改",
"itemType": "业务端"
},
{
"category": "系统设置",
"id": 32,
"itemName": "用户列表-查看",
"itemType": "业务端"
},
{
"category": "系统设置",
"id": 33,
"itemName": "用户-新增/编辑/删除",
"itemType": "业务端"
},
{
"category": "系统设置",
"id": 34,
"itemName": "角色权限-查看/新增/编辑/删除",
"itemType": "业务端"
}
],
"code": 200,
"desc": "成功",
"success": true
}
```
# 7. 添加角色
> PUT /api/role
### 输入:
```
Content-Type:application/x-www-form-urlencoded
roleName=ccc // 必填, 角色名称
items=1,2,3 // 必填, 赋予的权限项
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 8. 删除角色
> DELETE /api/role
### 输入:
```
Content-Type:application/x-www-form-urlencoded
roleId=1 // 必填
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 9. 修改角色
> POST /api/role
### 输入:
```
Content-Type:application/x-www-form-urlencoded
roleId=1 // 必填
roleName=ccc // 角色名称
items=1,2,3 // 赋予的权限项
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 10. 获取角色列表
> GET /api/role
### 输出:
```
{
"body": [
{
"id": 2,
"roleItems": "[18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]",
"roleName": "超级管理员",
"roleType": "系统",
"vender": 1
}
],
"code": 200,
"desc": "成功",
"success": true
}
```

180
doc/vender.md

File diff suppressed because one or more lines are too long

6
package.json

@ -3,8 +3,10 @@
"version": "0.0.0", "version": "0.0.0",
"scripts": { "scripts": {
"ng": "ng", "ng": "ng",
"start": "ng serve", "start:admin": "ng serve admin",
"build": "ng build", "start:client": "ng serve client",
"build:admin": "ng build admin",
"build:client": "ng build client",
"watch": "ng build --watch --configuration development", "watch": "ng build --watch --configuration development",
"test": "ng test" "test": "ng test"
}, },

8
projects/admin/src/app/app.module.ts

@ -10,12 +10,9 @@ import zh from "@angular/common/locales/zh";
import { FormsModule } from "@angular/forms"; import { FormsModule } from "@angular/forms";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http"; import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations"; import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { IconsProviderModule } from "./icons-provider.module";
import { SharedModule } from "./shared/shared.module";
import { import {
AppLayoutComponent, AppLayoutComponent,
AppPageComponent,
FoodFormComponent, FoodFormComponent,
DishFormComponent, DishFormComponent,
IngredientFormBasicComponent, IngredientFormBasicComponent,
@ -39,6 +36,9 @@ import {
StandardSettingComponent, StandardSettingComponent,
} from "./pages"; } from "./pages";
import { HTTPInterceptor } from "./services/http.interceptor"; import { HTTPInterceptor } from "./services/http.interceptor";
import { IconsProviderModule } from "@cdk/public-api";
import { SharedModule } from "@cdk/shared/shared.module";
import { IngredientModule } from "@cdk/ingredient/ingredient.module";
registerLocaleData(zh); registerLocaleData(zh);
@ -46,7 +46,6 @@ registerLocaleData(zh);
declarations: [ declarations: [
AppComponent, AppComponent,
AppLayoutComponent, AppLayoutComponent,
AppPageComponent,
FoodFormComponent, FoodFormComponent,
DishFormComponent, DishFormComponent,
IngredientFormBasicComponent, IngredientFormBasicComponent,
@ -80,6 +79,7 @@ registerLocaleData(zh);
BrowserAnimationsModule, BrowserAnimationsModule,
IconsProviderModule, IconsProviderModule,
SharedModule, SharedModule,
IngredientModule,
], ],
providers: [ providers: [
{ provide: NZ_I18N, useValue: zh_CN }, { provide: NZ_I18N, useValue: zh_CN },

32
projects/admin/src/app/components/app-layout/app-layout.component.html

@ -1,18 +1,34 @@
<nz-layout class="app-layout"> <nz-layout class="app-layout">
<nz-header class="app-header"> <nz-header class="app-header">
<div class="flex items-center justify-between h-full"> <div class="flex items-center justify-between h-full">
<div class="logo flex items-center h-full"> <div></div>
<img class="block h-[40px] mr-2" src="../assets/images/jl-logo.png" /> <div class="profile">
<span class="text-lg text-white font-bold"> <button
智慧配餐管理后台 nz-button
</span> nzType="text"
nz-dropdown
[nzDropdownMenu]="menu">
admin
</button>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item (click)="logout()">
退出登录
</li>
</ul>
</nz-dropdown-menu>
</div> </div>
</div> </div>
</nz-header> </nz-header>
<nz-layout class="app-layout-main"> <nz-layout class="app-layout-main">
<nz-sider nzWidth="200px" nzTheme="light" class="sider-menu"> <nz-sider nzWidth="220px" nzTheme="dark" class="sider-menu">
<ul nz-menu nzMode="inline"> <div class="logo flex items-center py-4">
<img class="block h-[40px] mr-2" src="../assets/images/jl-logo.png" />
<span class="text-base text-white font-bold">
智慧配餐管理后台
</span>
</div>
<ul nz-menu nzMode="inline" nzTheme="dark">
<li nz-menu-item class="k-icon" [routerLink]="['/','food']" nzMatchRouter> <li nz-menu-item class="k-icon" [routerLink]="['/','food']" nzMatchRouter>
<span nz-icon nzType="k-icon:carrot" nzTheme="outline"></span> <span nz-icon nzType="k-icon:carrot" nzTheme="outline"></span>
<span>食材管理</span> <span>食材管理</span>

14
projects/admin/src/app/components/app-layout/app-layout.component.less

@ -11,17 +11,19 @@
flex-direction: column; flex-direction: column;
flex: 1; flex: 1;
height: 100%; height: 100%;
} }
.app-header { .app-header {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 220px;
right: 0; right: 0;
z-index: 100; z-index: 100;
height: @header-height; height: @header-height;
line-height: @header-height; line-height: @header-height;
padding: 0 24px; padding: 0 24px;
background-color: #fff;
} }
@ -29,13 +31,19 @@
::ng-deep { ::ng-deep {
.anticon { .anticon {
font-size: 16px; font-size: 16px;
color: #fff;
svg,
path {
fill: currentColor !important;
}
} }
} }
} }
.sider-menu { .sider-menu {
position: fixed; position: fixed;
top: @header-height; top: 0;
left: 0; left: 0;
bottom: 0; bottom: 0;
z-index: 100; z-index: 100;
@ -47,7 +55,7 @@
.inner-layout { .inner-layout {
padding-top: @header-height; padding-top: @header-height;
padding-left: 200px; padding-left: 220px;
::ng-deep { ::ng-deep {
router-outlet+* { router-outlet+* {

14
projects/admin/src/app/components/app-layout/app-layout.component.ts

@ -1,5 +1,7 @@
import { Component } from "@angular/core"; import { Component } from "@angular/core";
import { NavigationEnd, Router } from "@angular/router"; import { NavigationEnd, Router, RouterModule } from "@angular/router";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, filter, takeUntil } from "rxjs"; import { Subject, filter, takeUntil } from "rxjs";
@Component({ @Component({
@ -8,7 +10,7 @@ import { Subject, filter, takeUntil } from "rxjs";
styleUrls: ["./app-layout.component.less"], styleUrls: ["./app-layout.component.less"],
}) })
export class AppLayoutComponent { export class AppLayoutComponent {
constructor(private router: Router) { constructor(private router: Router, private modal: NzModalService) {
this.router.events this.router.events
.pipe( .pipe(
takeUntil(this.unSubscribe$), takeUntil(this.unSubscribe$),
@ -22,4 +24,12 @@ export class AppLayoutComponent {
unSubscribe$ = new Subject<void>(); unSubscribe$ = new Subject<void>();
currentUrl: string = ""; currentUrl: string = "";
logout() {
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要退出登录?",
nzOnOk: () => {},
});
}
} }

1
projects/admin/src/app/components/index.ts

@ -1,5 +1,4 @@
export * from "./app-layout/app-layout.component"; export * from "./app-layout/app-layout.component";
export * from "./app-page/app-page.component";
export * from "./food-form/food-form.component"; export * from "./food-form/food-form.component";
export * from "./dish-form/dish-form.component"; export * from "./dish-form/dish-form.component";

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

@ -2,7 +2,7 @@
<ng-template #pageExtraTpl> <ng-template #pageExtraTpl>
<nz-space> <nz-space>
<button *nzSpaceItem nz-button>批量删除</button> <button *nzSpaceItem nz-button>批量删除</button>
<button *nzSpaceItem nz-button>批量下载营养标签</button> <button *nzSpaceItem nz-button>批量打印营养标签</button>
<button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()"> <button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()">
<i nz-icon nzType="plus"></i> <i nz-icon nzType="plus"></i>
新增菜品 新增菜品

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

@ -4,6 +4,7 @@ import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api"; import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components"; import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services"; import { ApiService } from "@admin/app/services";
import { Subject } from "rxjs";
@Component({ @Component({
selector: "app-dish", selector: "app-dish",
@ -17,6 +18,8 @@ export class DishComponent {
private drawerRef?: NzDrawerRef; private drawerRef?: NzDrawerRef;
private destroy$ = new Subject<void>();
tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg"; tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg";
public tableList = new TableListOption(this.fetchData.bind(this), { public tableList = new TableListOption(this.fetchData.bind(this), {
@ -31,6 +34,11 @@ export class DishComponent {
this.initTableList(); this.initTableList();
} }
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
initTableList() { initTableList() {
this.tableList.scroll = { x: null }; this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([ this.tableList = this.tableList.setColumns([
@ -43,7 +51,7 @@ export class DishComponent {
this.tableList = this.tableList.setOptions([ this.tableList = this.tableList.setOptions([
{ {
title: "下载营养标签", title: "打印营养标签",
premissions: [], premissions: [],
onClick: this.showFoodForm.bind(this), onClick: this.showFoodForm.bind(this),
}, },

64
projects/admin/src/app/pages/food/food.component.html

@ -1,8 +1,8 @@
<app-page [scroll]="false"> <app-page [scroll]="false">
<ng-template #pageExtraTpl> <ng-template #pageExtraTpl>
<nz-space> <nz-space>
<button *nzSpaceItem nz-button>批量删除</button> <button *nzSpaceItem nz-button [disabled]="!selectedIds.length" (click)="deleteFood()">批量删除</button>
<button *nzSpaceItem nz-button>导入食材清单</button> <button *nzSpaceItem nz-button (click)="showImportForm(importFormTpl)">导入食材清单</button>
<button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()"> <button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()">
<i nz-icon nzType="plus"></i> <i nz-icon nzType="plus"></i>
新增食材 新增食材
@ -40,12 +40,13 @@
<nz-card [nzBordered]="false" nzTitle="食材管理" class="scroll-card-body"> <nz-card [nzBordered]="false" nzTitle="食材管理" class="scroll-card-body">
<div class="m-4"> <div class="m-4">
<table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" <table-list
[formGroup]="queryForm"> [props]="tableList"
[search]="searchTpl"
[action]="pageExtraTpl"
[formGroup]="queryForm"
[renderColumns]="renderColumnsTpl">
<ng-template #actionTpl>
<button nz-button>批量删除</button>
</ng-template>
<ng-template #searchTpl> <ng-template #searchTpl>
<nz-form-item> <nz-form-item>
<nz-form-control> <nz-form-control>
@ -53,6 +54,30 @@
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</ng-template> </ng-template>
<ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<ng-container [ngSwitch]="key">
<ng-container *ngSwitchCase="'name1'">
<a nz-popover
[nzPopoverContent]="popoverTpl"
nzPopoverTitle="营养素">
<b>10</b>中营养素
</a>
<ng-template #popoverTpl>
<div class=" max-w-sm max-h-60 overflow-auto">
<nz-tag *ngFor="let item of temp" class="m-1">
能量{{item}}:10kcal
</nz-tag>
</div>
</ng-template>
</ng-container>
<ng-container *ngSwitchDefault>
{{data}}
</ng-container>
</ng-container>
</ng-template>
</table-list> </table-list>
</div> </div>
</nz-card> </nz-card>
@ -61,6 +86,31 @@
</div> </div>
</app-page> </app-page>
<ng-template #importFormTpl>
<p class=" text-slate-400">
提示:<a>下载示例模版</a>,进行编辑,导入Excel文件,批量导入食材
</p>
<div>
<div class="upload-area flex-col"
(drop)="handleDrop($event)"
(dragenter)="suppress($event)"
(dragover)="suppress($event)">
<input type="file" id="file" (change)="onFileChange($event)" accept=".xlsx,.xls" />
<div class="text-center">
<p class="text-4xl mb-1">
<a nz-icon nzType="upload" nzTheme="outline"></a>
</p>
<p class="mb-1">
点击或将文件拖拽到这里上传
</p>
<p class="text-slate-400">
支持扩展名:.xls .xlsx
</p>
</div>
</div>
</div>
</ng-template>
<ng-template #foofFormFooterTpl> <ng-template #foofFormFooterTpl>
<nz-space> <nz-space>

65
projects/admin/src/app/pages/food/food.component.ts

@ -1,30 +1,48 @@
import { FoodFormComponent } from "@admin/app/components"; import { FoodFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services"; import { ApiService } from "@admin/app/services";
import { Component, OnInit, TemplateRef, ViewChild } from "@angular/core"; import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms"; import { FormControl, FormGroup } from "@angular/forms";
import { AnyObject, TableListOption } from "@cdk/public-api"; import { AnyObject, TableListOption } from "@cdk/public-api";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer"; import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, takeUntil } from "rxjs";
@Component({ @Component({
selector: "app-food", selector: "app-food",
templateUrl: "./food.component.html", templateUrl: "./food.component.html",
styleUrls: ["./food.component.less"], styleUrls: ["./food.component.less"],
}) })
export class FoodComponent implements OnInit { export class FoodComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ApiService) {} constructor(private drawer: NzDrawerService, private api: ApiService, private modal: NzModalService) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>; @ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
private drawerRef?: NzDrawerRef; private drawerRef?: NzDrawerRef;
public tableList = new TableListOption(this.fetchData.bind(this)); public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true,
});
public queryForm = new FormGroup({ public queryForm = new FormGroup({
name: new FormControl(""), name: new FormControl(""),
}); });
private destroy$ = new Subject<void>();
public selectedIds: string[] = [];
temp = Array.from({ length: 50 }, (_, i) => i);
ngOnInit(): void { ngOnInit(): void {
this.initTableList(); this.initTableList();
this.tableList.getState$.pipe(takeUntil(this.destroy$)).subscribe((res) => {
this.selectedIds = res.selectedKeys as Array<string>;
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
} }
initTableList() { initTableList() {
@ -33,7 +51,7 @@ export class FoodComponent implements OnInit {
{ key: "name", title: "食材编号" }, { key: "name", title: "食材编号" },
{ key: "name", title: "食材名称" }, { key: "name", title: "食材名称" },
{ key: "name", title: "食材类型" }, { key: "name", title: "食材类型" },
{ key: "name", title: "营养素(每100g可食部)", width: "50%" }, { key: "name1", title: "营养素(每100g可食部)" },
{ key: "name", title: "更新日期" }, { key: "name", title: "更新日期" },
]); ]);
@ -46,7 +64,7 @@ export class FoodComponent implements OnInit {
{ {
title: "删除", title: "删除",
premissions: [], premissions: [],
onClick: this.deleteItem.bind(this), onClick: this.deleteFood.bind(this),
}, },
]); ]);
} }
@ -68,5 +86,38 @@ export class FoodComponent implements OnInit {
this.drawerRef?.close(); this.drawerRef?.close();
} }
deleteItem() {} deleteFood(v?: any) {
const ids = v ? [v.id] : this.selectedIds;
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该食材?",
nzOkDanger: true,
nzOnOk: () => {},
});
}
showImportForm(nzContent: TemplateRef<{}>) {
this.modal.create({
nzTitle: "导入食材清单",
nzWidth: 520,
nzContent,
});
}
suppress(e: Event) {
e.stopPropagation();
e.preventDefault();
}
handleDrop = (e: Event) => {
e.stopPropagation();
e.preventDefault();
const event = e as DragEvent;
const files = event.dataTransfer?.files;
if (files) {
this.onFileChange(files);
}
};
onFileChange(e: Event | FileList | null) {}
} }

257
projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.html

@ -28,7 +28,7 @@
<button *nzSpaceItem nz-button> <button *nzSpaceItem nz-button>
食谱预览 食谱预览
</button> </button>
<button *nzSpaceItem nz-button nzType="primary"> <button *nzSpaceItem nz-button nzType="primary" (click)="confirmSave()">
保存 保存
</button> </button>
<button *nzSpaceItem nz-button> <button *nzSpaceItem nz-button>
@ -39,7 +39,32 @@
</nz-card> </nz-card>
<div class="p-4"> <div class="p-4">
<nz-card nzTitle="第一天" class="mb-4"> <nz-card
*ngFor="let i of ingredients"
[nzTitle]="dayTitleTpl"
class="day-item mb-4"
[nzExtra]="dayExtraTpl"
[nzBodyStyle]="{padding:0}">
<ng-template #dayTitleTpl>
<div class=" relative -left-4 z-10">
<button nz-button nzType="text" (click)="expandChange(i)">
<span nz-icon nzType="caret-right" nzTheme="outline"></span>
</button>
<span>
第{{i}}天
</span>
</div>
</ng-template>
<ng-template #dayExtraTpl>
<nz-space>
<button *nzSpaceItem nz-button nzType="link">
本天菜品应用到其他天
</button>
<button *nzSpaceItem nz-button nzType="link">
本日营养分析
</button>
</nz-space>
</ng-template>
<nz-card-tab> <nz-card-tab>
<nz-tabset nzSize="large"> <nz-tabset nzSize="large">
<nz-tab nzTitle="早餐"></nz-tab> <nz-tab nzTitle="早餐"></nz-tab>
@ -47,233 +72,11 @@
<nz-tab nzTitle="晚餐"></nz-tab> <nz-tab nzTitle="晚餐"></nz-tab>
</nz-tabset> </nz-tabset>
</nz-card-tab> </nz-card-tab>
<div> <div class="p-4" *ngIf="expanded.has(i)">
<nz-table nzTemplateMode [nzBordered]="true" nzSize="small"> <lib-ingredient-meals></lib-ingredient-meals>
<thead>
<tr>
<th>
菜品
</th>
<th>
食材
</th>
<th>
轻体力(体重过低)
</th>
<th>
轻体力(正常体重)
</th>
<th>
休息(超重/肥胖)
</th>
<th>
轻体力(体重过低)
</th>
<th>
轻体力(正常体重)
</th>
</tr>
</thead>
<tbody>
<tr>
<td [rowSpan]="2">
<div class="flex justify-between">
<span>
番茄煎蛋面
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
<div class="flex justify-between">
<span>
番茄
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
面条
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td [rowSpan]="4">
<div class="flex justify-between">
<span>
鸡蛋青菜面
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
<div class="flex justify-between">
<span>
食用油
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
小白菜[青菜]
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
鸡蛋(均值)
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
面条(均值)
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
</tbody>
</nz-table>
</div> </div>
</nz-card> </nz-card>
<nz-card nzTitle="第二天" class="mb-4"></nz-card>
<nz-card nzTitle="第三天" class="mb-4"></nz-card>
<nz-card nzTitle="第四天" class="mb-4"></nz-card>
<nz-card nzTitle="第五天" class="mb-4"></nz-card>
<nz-card nzTitle="第六天" class="mb-4"></nz-card>
<nz-card nzTitle="第七天" class="mb-4"></nz-card>
</div> </div>
</ng-container> </ng-container>
</app-page> </app-page>

7
projects/admin/src/app/pages/ingredients/ingredient-form/ingredient-form.component.less

@ -0,0 +1,7 @@
.day-item {
::ng-deep {
.ant-card-head-title {
overflow: visible;
}
}
}

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

@ -1,5 +1,6 @@
import { IngredientFormBasicComponent } from "@admin/app/components"; import { IngredientFormBasicComponent } from "@admin/app/components";
import { Component, OnInit } from "@angular/core"; import { Component, OnInit } from "@angular/core";
import { ConfirmIngredientComponent } from "@cdk/ingredient/confirm-ingredient/confirm-ingredient.component";
import { NzModalService } from "ng-zorro-antd/modal"; import { NzModalService } from "ng-zorro-antd/modal";
@Component({ @Component({
@ -12,6 +13,10 @@ export class IngredientFormComponent implements OnInit {
step = 1; step = 1;
expanded = new Set<number>();
ingredients = Array.from({ length: 7 }, (_, i) => 1 + i);
ngOnInit(): void {} ngOnInit(): void {}
onStepChange() { onStepChange() {
@ -19,7 +24,23 @@ export class IngredientFormComponent implements OnInit {
console.log(456); console.log(456);
} }
showForm(food?: any) {} shopDishForm() {}
cancelForm() {} cancelForm() {}
expandChange(i: number) {
if (this.expanded.has(i)) {
this.expanded.delete(i);
} else {
this.expanded.add(i);
}
}
confirmSave() {
this.modal.create({
nzTitle: "确认食谱信息",
nzContent: ConfirmIngredientComponent,
nzWidth: 650,
});
}
} }

49
projects/admin/src/app/pages/ingredients/ingredient-review/ingredient-review.component.html

@ -1,15 +1,21 @@
<app-page> <app-page>
<nz-card [nzBordered]="false"
class="h-full scroll-card-body ">
<nz-card-tab>
<nz-tabset nzSize="large" [nzSelectedIndex]="status" (nzSelectedIndexChange)="onStatusChange($event)">
<nz-tab [nzTitle]="'待审核31'"></nz-tab>
<nz-tab [nzTitle]="'已审核24'"></nz-tab>
<nz-tab [nzTitle]="'审核失败24'"></nz-tab>
</nz-tabset>
</nz-card-tab>
<div class="p-4">
<ng-template #pageExtraTpl> <ng-template #pageExtraTpl>
<nz-space> <nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()"> <button *nzSpaceItem nz-button nzType="primary">
<i nz-icon nzType="plus"></i>
批量通过 批量通过
</button> </button>
</nz-space> </nz-space>
</ng-template> </ng-template>
<div class="h-full overflow-hidden bg-white rounded-lg">
<nz-card [nzBordered]="false" nzTitle="食谱审核">
<table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" [formGroup]="queryForm" <table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" [formGroup]="queryForm"
[renderColumns]="renderColumnsTpl"> [renderColumns]="renderColumnsTpl">
@ -17,25 +23,23 @@
<button nz-button>批量删除</button> <button nz-button>批量删除</button>
</ng-template> </ng-template>
<ng-template #searchTpl> <ng-template #searchTpl>
<nz-form-item class="w-40"> <nz-form-item class="w-60">
<nz-form-control> <nz-form-control>
<nz-select nzPlaceHolder="单位" [nzOptions]="[]"></nz-select> <nz-select nzPlaceHolder="请选择单位" [nzOptions]="[]"></nz-select>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item class="w-60">
<nz-form-item>
<nz-form-control> <nz-form-control>
<input nz-input placeholder="请输入食谱名称" formControlName="name" /> <input nz-input placeholder="请输入食谱名称" />
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
</ng-template> </ng-template>
<ng-template #renderColumnsTpl let-data let-key="key" let-row="row"> <ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<ng-container [ngSwitch]="key"> <ng-container [ngSwitch]="key">
<!-- <ng-container *ngSwitchCase="'img'"> <ng-container *ngSwitchCase="'img'">
<div class="dish-img overflow-auto"
[ngStyle]="{'background-image':'url(' + tempImg + ')'}"> </ng-container>
</div>
</ng-container> -->
<ng-container *ngSwitchDefault> <ng-container *ngSwitchDefault>
{{data}} {{data}}
@ -44,18 +48,7 @@
</ng-container> </ng-container>
</ng-template> </ng-template>
</table-list> </table-list>
</nz-card>
</div> </div>
</app-page> </nz-card>
<ng-template #foofFormFooterTpl> </app-page>
<nz-space>
<button *nzSpaceItem nz-button (click)="cancelFoodForm()">
取消
</button>
<button *nzSpaceItem nz-button nzType="primary">
保存
</button>
</nz-space>
</ng-template>

21
projects/admin/src/app/pages/ingredients/ingredient-review/ingredient-review.component.ts

@ -4,6 +4,7 @@ import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api"; import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components"; import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services"; import { ApiService } from "@admin/app/services";
import { ActivatedRoute, Router } from "@angular/router";
@Component({ @Component({
selector: "app-ingredient-review", selector: "app-ingredient-review",
@ -11,12 +12,19 @@ import { ApiService } from "@admin/app/services";
styleUrls: ["./ingredient-review.component.less"], styleUrls: ["./ingredient-review.component.less"],
}) })
export class IngredientReviewComponent { export class IngredientReviewComponent {
constructor(private drawer: NzDrawerService, private api: ApiService) {} constructor(
private drawer: NzDrawerService,
private router: Router,
private route: ActivatedRoute,
private api: ApiService
) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>; @ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
private drawerRef?: NzDrawerRef; private drawerRef?: NzDrawerRef;
status = 0;
tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg"; tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg";
public tableList = new TableListOption(this.fetchData.bind(this), { public tableList = new TableListOption(this.fetchData.bind(this), {
@ -28,6 +36,7 @@ export class IngredientReviewComponent {
}); });
ngOnInit(): void { ngOnInit(): void {
this.status = Number(this.route.snapshot.queryParamMap.get("status")) || 0;
this.initTableList(); this.initTableList();
} }
@ -78,4 +87,14 @@ export class IngredientReviewComponent {
} }
deleteItem() {} deleteItem() {}
onStatusChange(index: number) {
this.status = index;
this.router.navigate(["/ingredient/review"], {
queryParams: {
status: index,
},
queryParamsHandling: "merge",
});
}
} }

36
projects/admin/src/styles.less

@ -38,6 +38,42 @@ li {
} }
} }
.dropUpload {
position: relative;
padding: 36px 0;
text-align: center;
font-size: 24px;
color: rgba(0, 0, 0, 0.35);
border: 1px dashed #e8e8e8;
border-radius: 4px;
background-color: #fafafa;
cursor: pointer;
}
.upload-area {
position: relative;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 160px;
border: 1px dashed #e8e8e8;
border-radius: 10px;
input {
width: 100%;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
opacity: 0;
z-index: 2;
cursor: pointer;
}
}
.fixed-footter { .fixed-footter {
box-shadow: 0 0 10px -2px rgba(0, 0, 0, .25) box-shadow: 0 0 10px -2px rgba(0, 0, 0, .25)
} }

0
projects/admin/src/app/components/app-page/app-page.component.html → projects/cdk/src/app-page/app-page.component.html

0
projects/admin/src/app/components/app-page/app-page.component.less → projects/cdk/src/app-page/app-page.component.less

4
projects/admin/src/app/components/app-page/app-page.component.ts → projects/cdk/src/app-page/app-page.component.ts

@ -1,6 +1,8 @@
import { CommonModule } from "@angular/common";
import { Component, Input, OnDestroy, TemplateRef } from "@angular/core"; import { Component, Input, OnDestroy, TemplateRef } from "@angular/core";
import { ActivatedRoute, NavigationEnd, Router, Scroll } from "@angular/router"; import { ActivatedRoute, NavigationEnd, Router, Scroll } from "@angular/router";
import { Subject, filter, startWith, take, takeUntil } from "rxjs"; import { Subject, filter, startWith, take, takeUntil } from "rxjs";
import { NzOutletModule } from "ng-zorro-antd/core/outlet";
interface BreadcrumbInterface { interface BreadcrumbInterface {
label: string; label: string;
@ -8,9 +10,11 @@ interface BreadcrumbInterface {
} }
@Component({ @Component({
standalone: true,
selector: "app-page", selector: "app-page",
templateUrl: "./app-page.component.html", templateUrl: "./app-page.component.html",
styleUrls: ["./app-page.component.less"], styleUrls: ["./app-page.component.less"],
imports: [CommonModule, NzOutletModule],
}) })
export class AppPageComponent implements OnDestroy { export class AppPageComponent implements OnDestroy {
constructor(private route: ActivatedRoute, private router: Router) { constructor(private route: ActivatedRoute, private router: Router) {

0
projects/admin/src/app/icons-provider.module.ts → projects/cdk/src/icons/icons-provider.module.ts

127
projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.html

@ -0,0 +1,127 @@
<form nz-form>
<div nz-row [nzGutter]="12">
<div nz-col nzSpan="12">
<nz-form-item>
<nz-form-label nzRequired="">菜品名称</nz-form-label>
<nz-form-control>
<nz-select nzShowSearch nzPlaceHolder="请输入菜品名称搜索"></nz-select>
</nz-form-control>
</nz-form-item>
</div>
<div nz-col nzSpan="12">
<nz-form-item>
<nz-form-label nzRequired="">菜品标签</nz-form-label>
<nz-form-control>
<nz-select nzPlaceHolder="请选择菜品标签"></nz-select>
</nz-form-control>
</nz-form-item>
</div>
</div>
<nz-form-item>
<nz-form-label nzRequired="">食材搜索方式</nz-form-label>
<nz-form-control>
<nz-radio-group nzButtonStyle="solid">
<label nz-radio nzValue="1" nz-radio-button>食材种类</label>
<label nz-radio nzValue="2" nz-radio-button>营养素</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
<div>
<nz-table nzTemplateMode [nzBordered]="true" nzSize="small">
<thead>
<tr>
<th>
食材
</th>
<th>
是否主料
</th>
<th>
轻体力1
</th>
<th>
轻体力2
</th>
<th>
操作
</th>
</tr>
</thead>
<tbody>
<tr>
<td>
味精
</td>
<td>
<nz-switch nzCheckedChildren="是" nzUnCheckedChildren="否"></nz-switch>
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<nz-space>
<button *nzSpaceItem nz-button nzType="link">
修改
</button>
<button *nzSpaceItem nz-button nzType="link" nzDanger>
删除
</button>
</nz-space>
</td>
</tr>
<tr>
<td>
味精
</td>
<td>
<nz-switch nzCheckedChildren="是" nzUnCheckedChildren="否"></nz-switch>
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<nz-space>
<button *nzSpaceItem nz-button nzType="link">
修改
</button>
<button *nzSpaceItem nz-button nzType="link" nzDanger>
删除
</button>
</nz-space>
</td>
</tr>
<tr>
<td>
味精
</td>
<td>
<nz-switch nzCheckedChildren="是" nzUnCheckedChildren="否"></nz-switch>
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<input nz-input placeholder="重量" />
</td>
<td>
<nz-space>
<button *nzSpaceItem nz-button nzType="link">
修改
</button>
<button *nzSpaceItem nz-button nzType="link" nzDanger>
删除
</button>
</nz-space>
</td>
</tr>
</tbody>
</nz-table>
</div>
</form>

0
projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.less

8
projects/cdk/src/ingredient/add-dish-to-ingredient/add-dish-to-ingredient.component.ts

@ -0,0 +1,8 @@
import { Component } from "@angular/core";
@Component({
selector: "lib-add-dish-to-ingredient",
templateUrl: "./add-dish-to-ingredient.component.html",
styleUrls: ["./add-dish-to-ingredient.component.less"],
})
export class AddDishToIngredientComponent {}

53
projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.html

@ -0,0 +1,53 @@
<form nz-form [formGroup]="formGroup">
<nz-form-item>
<nz-form-label nzSpan="4">
天数
</nz-form-label>
<nz-form-control nzSpan="12">
<nz-tag nzColor="cyan">7天</nz-tag>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="4">
餐次
</nz-form-label>
<nz-form-control nzSpan="12">
<nz-space>
<nz-tag *nzSpaceItem nzColor="blue">早餐</nz-tag>
<nz-tag *nzSpaceItem nzColor="blue">午餐</nz-tag>
<nz-tag *nzSpaceItem nzColor="blue">晚餐</nz-tag>
</nz-space>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="4" nzRequired>
食谱名称
</nz-form-label>
<nz-form-control nzSpan="16">
<input nz-input placeholder="请输入食谱名称" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="4" nzRequired>
适用月份
</nz-form-label>
<nz-form-control nzSpan="16">
<div>
<label
nz-checkbox
[(ngModel)]="allMonthChecked"
[ngModelOptions]="{standalone: true}"
(ngModelChange)="updateAllMonthChecked()"
[nzIndeterminate]="indeterminate">
全年
</label>
</div>
<nz-divider nzDashed class="my-2"></nz-divider>
<nz-checkbox-group [ngModel]="allMonth"
class="flex flex-wrap month-wrap"
formControlName="month"
(ngModelChange)="monthChecked()">
</nz-checkbox-group>
</nz-form-control>
</nz-form-item>
</form>

8
projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.less

@ -0,0 +1,8 @@
.month-wrap {
::ng-deep {
.ant-checkbox-wrapper {
margin: 6px 0;
flex-basis: calc(100% / 4);
}
}
}

71
projects/cdk/src/ingredient/confirm-ingredient/confirm-ingredient.component.ts

@ -0,0 +1,71 @@
import { Component } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { FormValidators } from "@cdk/public-api";
import { NzMessageService } from "ng-zorro-antd/message";
@Component({
selector: "lib-confirm-ingredient",
templateUrl: "./confirm-ingredient.component.html",
styleUrls: ["./confirm-ingredient.component.less"],
})
export class ConfirmIngredientComponent {
constructor(private fb: FormBuilder, private msg: NzMessageService) {}
formGroup!: FormGroup;
allMonth = [
{ value: "1", label: "一月", checked: false },
{ value: "2", label: "二月", checked: false },
{ value: "3", label: "三月", checked: false },
{ value: "4", label: "四月", checked: false },
{ value: "5", label: "五月", checked: false },
{ value: "6", label: "六月", checked: false },
{ value: "7", label: "七月", checked: false },
{ value: "8", label: "八月", checked: false },
{ value: "9", label: "九月", checked: false },
{ value: "10", label: "十月", checked: false },
{ value: "11", label: "十一月", checked: false },
{ value: "12", label: "十二月", checked: false },
];
allMonthChecked = false;
indeterminate = false;
ngOnInit(): void {
this.formGroup = this.fb.group({
id: this.fb.control("", [FormValidators.required()]),
name: this.fb.control("", [FormValidators.required()]),
month: this.fb.control([], [FormValidators.required()]),
});
}
updateAllMonthChecked() {
this.indeterminate = false;
if (this.allMonthChecked) {
this.allMonth = this.allMonth.map((item) => ({
...item,
checked: true,
}));
} else {
this.allMonth = this.allMonth.map((item) => ({
...item,
checked: false,
}));
}
}
monthChecked() {
if (this.allMonth.every((item) => !item.checked)) {
this.allMonthChecked = false;
this.indeterminate = false;
} else if (this.allMonth.every((item) => item.checked)) {
this.allMonthChecked = true;
this.indeterminate = false;
} else {
this.indeterminate = true;
}
}
}

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

@ -0,0 +1,252 @@
<div>
<div class="mb-3">
<nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="shopDishForm()">
添加菜品
</button>
<button *nzSpaceItem nz-button>
清空本餐
</button>
</nz-space>
</div>
<nz-table nzTemplateMode [nzBordered]="true" nzSize="small">
<thead>
<tr>
<th colSpan="2"></th>
<th [colSpan]="5">
重量/克
</th>
</tr>
<tr>
<th [rowSpan]="2">
菜品
</th>
<th [rowSpan]="2">
食材
</th>
<th>
轻体力(体重过低)
</th>
<th>
轻体力(正常体重)
</th>
<th>
休息(超重/肥胖)
</th>
<th>
轻体力(体重过低)
</th>
<th>
轻体力(正常体重)
</th>
</tr>
</thead>
<tbody>
<tr>
<td [rowSpan]="2">
<div class="flex justify-between">
<span>
番茄煎蛋面
</span>
<button nz-button nzType="text" nz-dropdown [nzDropdownMenu]="dishOptions">
<i nz-icon nzType="more"></i>
</button>
<nz-dropdown-menu #dishOptions="nzDropdownMenu">
<ul nz-menu nzSelectable class=" w-20">
<li nz-menu-item>编辑</li>
<li nz-menu-item nzDanger>删除</li>
</ul>
</nz-dropdown-menu>
</div>
</td>
<td>
<div class="flex justify-between">
<span>
番茄
</span>
<button nz-button nzType="text" nz-dropdown [nzDropdownMenu]="foodOptions">
<i nz-icon nzType="more"></i>
</button>
<nz-dropdown-menu #foodOptions="nzDropdownMenu">
<ul nz-menu nzSelectable class="w-100">
<li nz-menu-item>设置价格</li>
<li nz-menu-item nzDanger>删除</li>
</ul>
</nz-dropdown-menu>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
面条
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td [rowSpan]="4">
<div class="flex justify-between">
<span>
鸡蛋青菜面
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
<div class="flex justify-between">
<span>
食用油
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
小白菜[青菜]
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
鸡蛋(均值)
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
<tr>
<td>
<div class="flex justify-between">
<span>
面条(均值)
</span>
<button nz-button nzType="text">
<i nz-icon nzType="more"></i>
</button>
</div>
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
<td>
1
</td>
</tr>
</tbody>
</nz-table>
</div>

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

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

@ -0,0 +1,21 @@
import { Component } from "@angular/core";
import { NzModalService } from "ng-zorro-antd/modal";
import { AddDishToIngredientComponent } from "../add-dish-to-ingredient/add-dish-to-ingredient.component";
import { NzDrawerService } from "ng-zorro-antd/drawer";
@Component({
selector: "lib-ingredient-meals",
templateUrl: "./ingredient-meals.component.html",
styleUrls: ["./ingredient-meals.component.less"],
})
export class IngredientMealsComponent {
constructor(private modal: NzModalService, private drawer: NzDrawerService) {}
shopDishForm() {
this.drawer.create({
nzTitle: "添加菜品",
nzWidth: 1000,
nzContent: AddDishToIngredientComponent,
});
}
}

12
projects/cdk/src/ingredient/ingredient.module.ts

@ -0,0 +1,12 @@
import { NgModule } from "@angular/core";
import { SharedModule } from "@cdk/shared/shared.module";
import { AddDishToIngredientComponent } from "./add-dish-to-ingredient/add-dish-to-ingredient.component";
import { IngredientMealsComponent } from "./ingredient-meals/ingredient-meals.component";
import { ConfirmIngredientComponent } from "./confirm-ingredient/confirm-ingredient.component";
@NgModule({
declarations: [AddDishToIngredientComponent, IngredientMealsComponent, ConfirmIngredientComponent],
imports: [SharedModule],
exports: [AddDishToIngredientComponent, IngredientMealsComponent, ConfirmIngredientComponent],
})
export class IngredientModule {}

2
projects/cdk/src/public-api.ts

@ -12,3 +12,5 @@ export * from "./input-space-error/input-space-error.directive";
// export * from "./quick-date-range/quick-date-range.component"; // export * from "./quick-date-range/quick-date-range.component";
export * from "./table-list"; export * from "./table-list";
export * from "./storage"; export * from "./storage";
export * from "./icons/icons-provider.module";

0
projects/cdk/src/shared/components/index.ts

0
projects/admin/src/app/shared/ng-zorro.ts → projects/cdk/src/shared/ng-zorro.ts

5
projects/admin/src/app/shared/shared.module.ts → projects/cdk/src/shared/shared.module.ts

@ -19,6 +19,7 @@ import {
} from "@cdk/public-api"; } from "@cdk/public-api";
// import { environment } from "@manage/environments/environment"; // import { environment } from "@manage/environments/environment";
import { NgxPermissionsModule } from "ngx-permissions"; import { NgxPermissionsModule } from "ngx-permissions";
import { AppPageComponent } from "@cdk/app-page/app-page.component";
const ngModules = [CommonModule, HttpClientModule, FormsModule, RouterModule, ReactiveFormsModule]; const ngModules = [CommonModule, HttpClientModule, FormsModule, RouterModule, ReactiveFormsModule];
const components: any = []; const components: any = [];
@ -35,7 +36,7 @@ const cdks = [
@NgModule({ @NgModule({
declarations: [...components, ...directives], declarations: [...components, ...directives],
imports: [...ngZorroModules, ...ngModules, ...cdks], imports: [...ngZorroModules, ...ngModules, ...cdks, AppPageComponent],
exports: [...ngZorroModules, ...ngModules, ...components, ...directives, ...cdks], exports: [...ngZorroModules, ...ngModules, ...components, ...directives, ...cdks, AppPageComponent],
}) })
export class SharedModule {} export class SharedModule {}

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

@ -19,7 +19,7 @@ type ITableListConfig = {
}; };
type TableListState = { type TableListState = {
selectedKeys: number[]; selectedKeys: (number | string)[];
}; };
type TableListPager = { type TableListPager = {

10
projects/cdk/src/table-list/table-list/table-list.component.html

@ -1,15 +1,15 @@
<form nz-form class="query-form mb-2" [formGroup]="formGroup" [nzLayout]="searchLayout"> <form nz-form class="query-form mb-2" [formGroup]="formGroup" [nzLayout]="searchLayout">
<div <div>
*ngIf="search || action">
<div nz-row> <div nz-row>
<div nz-col nzFlex="auto" nz-row [nzGutter]="[12,12]" class="search-row"> <div nz-col nzFlex="auto" nz-row [nzGutter]="[12,12]" class="search-row">
<ng-container *ngIf="search"> <ng-container>
<ng-container [ngTemplateOutlet]="search"></ng-container> <ng-container [ngTemplateOutlet]="search!"></ng-container>
<div nz-col nzFlex="120px"> <div nz-col nzFlex="120px">
<nz-form-item> <nz-form-item>
<nz-form-label nzNoColon></nz-form-label> <nz-form-label nzNoColon></nz-form-label>
<nz-form-control> <nz-form-control>
<nz-space *ngIf="search"> <nz-space>
<button nz-button nzGhost nzType="primary" *nzSpaceItem (click)="doQuery()"> <button nz-button nzGhost nzType="primary" *nzSpaceItem (click)="doQuery()">
查询 查询
</button> </button>

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

@ -124,12 +124,14 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
} }
ngOnInit(): void { ngOnInit(): void {
console.log("this.formGroup", this.formGroup);
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);
// this.saveQueryDataToCache(query); // this.saveQueryDataToCache(query);
const pager = this.parsePager(); const pager = this.parsePager();
this.props this.props
@ -212,6 +214,7 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
this.cache = this.cacheService.initCache(cacheKey, [this.router.url, ...cacheTo]); this.cache = this.cacheService.initCache(cacheKey, [this.router.url, ...cacheTo]);
} }
const cacheData = this.cache?.getItem(); const cacheData = this.cache?.getItem();
if (cacheData) { if (cacheData) {
const { page, size, sort, total, ...query } = cacheData; const { page, size, sort, total, ...query } = cacheData;
this.props.pager = { this.props.pager = {

52
projects/client/src/app/app-routing.module.ts

@ -0,0 +1,52 @@
import { NgModule } from "@angular/core";
import { RouterModule, Routes } from "@angular/router";
import {
DashboardComponent,
DataVisComponent,
DishComponent,
FoodComponent,
LoginComponent,
MealSettingComponent,
} from "./pages";
import { AppLayoutComponent } from "./components";
const routes: Routes = [
{ path: "login", component: LoginComponent },
{
path: "",
component: AppLayoutComponent,
children: [
{
path: "",
pathMatch: "full",
redirectTo: "dashboard",
},
{
path: "dashboard",
component: DashboardComponent,
},
{
path: "meal-setting",
component: MealSettingComponent,
},
{
path: "data-vis",
component: DataVisComponent,
},
{
path: "food",
component: FoodComponent,
},
{
path: "dish",
component: DishComponent,
},
],
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}

1
projects/client/src/app/app.component.html

@ -0,0 +1 @@
<router-outlet></router-outlet>

0
projects/client/src/app/app.component.less

29
projects/client/src/app/app.component.spec.ts

@ -0,0 +1,29 @@
import { TestBed } from '@angular/core/testing';
import { RouterTestingModule } from '@angular/router/testing';
import { AppComponent } from './app.component';
describe('AppComponent', () => {
beforeEach(() => TestBed.configureTestingModule({
imports: [RouterTestingModule],
declarations: [AppComponent]
}));
it('should create the app', () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app).toBeTruthy();
});
it(`should have as title 'client'`, () => {
const fixture = TestBed.createComponent(AppComponent);
const app = fixture.componentInstance;
expect(app.title).toEqual('client');
});
it('should render title', () => {
const fixture = TestBed.createComponent(AppComponent);
fixture.detectChanges();
const compiled = fixture.nativeElement as HTMLElement;
expect(compiled.querySelector('.content span')?.textContent).toContain('client app is running!');
});
});

10
projects/client/src/app/app.component.ts

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
})
export class AppComponent {
title = 'client';
}

52
projects/client/src/app/app.module.ts

@ -0,0 +1,52 @@
import { NgModule } from "@angular/core";
import { BrowserModule } from "@angular/platform-browser";
import { 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";
import { FormsModule } from "@angular/forms";
import { HTTP_INTERCEPTORS, HttpClientModule } from "@angular/common/http";
import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { AppLayoutComponent } from "./components";
import { IconsProviderModule, TableListModule } from "@cdk/public-api";
import { SharedModule } from "@cdk/shared/shared.module";
import {
DashboardComponent,
LoginComponent,
MealSettingComponent,
DataVisComponent,
FoodComponent,
DishComponent,
} from "./pages";
registerLocaleData(zh);
@NgModule({
declarations: [
AppComponent,
AppLayoutComponent,
DashboardComponent,
LoginComponent,
MealSettingComponent,
DataVisComponent,
FoodComponent,
DishComponent,
],
imports: [
BrowserModule,
AppRoutingModule,
FormsModule,
HttpClientModule,
BrowserAnimationsModule,
IconsProviderModule,
SharedModule,
TableListModule,
],
providers: [{ provide: NZ_I18N, useValue: zh_CN }],
bootstrap: [AppComponent],
})
export class AppModule {}

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

@ -0,0 +1,73 @@
<nz-layout class="app-layout">
<nz-header class="app-header">
<div class="flex items-center justify-between h-full">
<div class="logo flex items-center h-full">
<img class="block h-[40px] mr-2" src="../assets/images/jl-logo.png" />
<span class="text-lg text-white font-bold">
智慧配餐系统
</span>
</div>
<div class="profile">
<button
nz-button
nzType="text"
nz-dropdown
[nzDropdownMenu]="menu"
class="text-white hover:text-white focus:text-white">
admin
</button>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>
<li nz-menu-item (click)="logout()">
退出登录
</li>
</ul>
</nz-dropdown-menu>
</div>
</div>
</nz-header>
<nz-layout class="app-layout-main">
<nz-sider nzWidth="200px" class="sider-menu" nzTheme="light">
<ul nz-menu nzMode="inline">
<li nz-menu-item class="k-icon" [routerLink]="['/','dashboard']" nzMatchRouter>
<span nz-icon nzType="question-circle" nzTheme="outline"></span>
<span>使用流程</span>
</li>
<li nz-menu-item class="k-icon" [routerLink]="['/','data-vis']" nzMatchRouter>
<span nz-icon nzType="fund" nzTheme="outline"></span>
<span>大屏显示</span>
</li>
<li nz-menu-item class="k-icon" [routerLink]="['/','meal-setting']" nzMatchRouter>
<span nz-icon nzType="k-icon:food" nzTheme="outline"></span>
<span>配餐设置</span>
</li>
<li nz-menu-item class="k-icon" [routerLink]="['/','food']" nzMatchRouter>
<span nz-icon nzType="k-icon:food" nzTheme="outline"></span>
<span>食材管理</span>
</li>
<li nz-menu-item class="k-icon" [routerLink]="['/','dish']" nzMatchRouter>
<span nz-icon nzType="k-icon:food" nzTheme="outline"></span>
<span>菜品管理</span>
</li>
<li nz-submenu nzTitle="食谱管理" nzIcon="book" [nzOpen]="currentUrl.includes('/ingredient/')">
<ul>
<li nz-menu-item nzMatchRouter [routerLink]="['/','ingredient','item']">食谱库</li>
<li nz-menu-item nzMatchRouter [routerLink]="['/','ingredient','release']">食谱发布计划</li>
</ul>
</li>
<li nz-submenu nzTitle="系统设置" nzIcon="setting" [nzOpen]="currentUrl.includes('/system/')">
<ul>
<li nz-menu-item nzMatchRouter [routerLink]="['/','system','user']">用户管理</li>
</ul>
</li>
</ul>
</nz-sider>
<nz-layout class="inner-layout overflow-hidden">
<router-outlet></router-outlet>
</nz-layout>
</nz-layout>
</nz-layout>

64
projects/client/src/app/components/app-layout/app-layout.component.less

@ -0,0 +1,64 @@
:host {
display: flex;
flex-direction: column;
height: 100%;
}
@header-height: 48px;
.app-layout {
display: flex;
flex-direction: column;
flex: 1;
height: 100%;
}
.app-header {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
height: @header-height;
line-height: @header-height;
padding: 0 24px;
}
.k-icon {
::ng-deep {
.anticon {
font-size: 16px;
svg,
path {
fill: currentColor !important;
}
}
}
}
.sider-menu {
position: fixed;
top: @header-height;
left: 0;
bottom: 0;
z-index: 100;
}
.app-layout-main {
height: 100%;
}
.inner-layout {
padding-top: @header-height;
padding-left: 200px;
::ng-deep {
router-outlet+* {
display: flex;
flex-direction: column;
height: 100%;
}
}
}

35
projects/client/src/app/components/app-layout/app-layout.component.ts

@ -0,0 +1,35 @@
import { Component } from "@angular/core";
import { NavigationEnd, Router, RouterModule } from "@angular/router";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, filter, takeUntil } from "rxjs";
@Component({
selector: "app-layout",
templateUrl: "./app-layout.component.html",
styleUrls: ["./app-layout.component.less"],
})
export class AppLayoutComponent {
constructor(private router: Router, private modal: NzModalService) {
this.router.events
.pipe(
takeUntil(this.unSubscribe$),
filter((e): e is NavigationEnd => e instanceof NavigationEnd)
)
.subscribe((e) => {
this.currentUrl = e.url;
});
}
unSubscribe$ = new Subject<void>();
currentUrl: string = "";
logout() {
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要退出登录?",
nzOnOk: () => {},
});
}
}

1
projects/client/src/app/components/index.ts

@ -0,0 +1 @@
export * from "./app-layout/app-layout.component";

5
projects/client/src/app/pages/dashboard/dashboard.component.html

@ -0,0 +1,5 @@
<app-page [pageTitle]="'使用流程'">
<nz-card>
</nz-card>
</app-page>

0
projects/client/src/app/pages/dashboard/dashboard.component.less

10
projects/client/src/app/pages/dashboard/dashboard.component.ts

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-dashboard',
templateUrl: './dashboard.component.html',
styleUrls: ['./dashboard.component.less']
})
export class DashboardComponent {
}

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

@ -0,0 +1 @@
<p>data-vis works!</p>

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

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

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-data-vis',
templateUrl: './data-vis.component.html',
styleUrls: ['./data-vis.component.less']
})
export class DataVisComponent {
}

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

@ -0,0 +1,74 @@
<app-page [scroll]="false">
<div nz-row class="h-full overflow-hidden bg-white rounded-lg">
<div nz-col nzFlex="220px" class="food-type">
<nz-card class="h-full" [nzBordered]="false" nzTitle="菜品分类" [nzBodyStyle]="{padding:'1px 0 0 0'}">
<ul nz-menu nzMode="inline">
<li nz-menu-item>
<a>
全部
</a>
</li>
<li nz-menu-item>
<a>
时令菜品
</a>
</li>
</ul>
</nz-card>
</div>
<div nz-col nzFlex="1" class="flex-1 overflow-hidden bg-white h-full">
<nz-card [nzBordered]="false" nzTitle="菜品列表" class="scroll-card-body">
<div class="m-4">
<ng-template #pageExtraTpl>
<nz-space>
<button *nzSpaceItem nz-button>批量删除</button>
<button *nzSpaceItem nz-button>批量打印营养标签</button>
<button *nzSpaceItem nz-button nzType="primary" (click)="showFoodForm()">
<i nz-icon nzType="plus"></i>
新增菜品
</button>
</nz-space>
</ng-template>
<table-list [props]="tableList" [search]="searchTpl" [action]="pageExtraTpl" [formGroup]="queryForm"
[renderColumns]="renderColumnsTpl">
<ng-template #actionTpl>
<button nz-button>批量删除</button>
</ng-template>
<ng-template #searchTpl>
<nz-form-item class="w-50">
<nz-form-control>
<nz-select nzPlaceHolder="菜品标签" [nzOptions]="[]"></nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<input nz-input placeholder="请输入菜品名称" formControlName="name" />
</nz-form-control>
</nz-form-item>
</ng-template>
<ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<ng-container [ngSwitch]="key">
<ng-container *ngSwitchCase="'img'">
<div class="dish-img overflow-auto"
[ngStyle]="{'background-image':'url(' + tempImg + ')'}">
</div>
</ng-container>
<ng-container *ngSwitchDefault>
{{data}}
</ng-container>
</ng-container>
</ng-template>
</table-list>
</div>
</nz-card>
</div>
</div>
</app-page>

18
projects/client/src/app/pages/dish/dish.component.less

@ -0,0 +1,18 @@
.food-type {
border-right: 1px solid #e8e8e8;
::ng-deep {
.ant-menu-inline {
border-right: none;
}
}
}
.dish-img {
width: 64px;
height: 64px;
border-radius: 10px;
background-size: cover;
background-position: center;
background-repeat: no-repeat;
}

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

@ -0,0 +1,99 @@
import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, takeUntil } from "rxjs";
import { ClientApiService } from "../../services";
@Component({
selector: "app-dish",
templateUrl: "./dish.component.html",
styleUrls: ["./dish.component.less"],
})
export class DishComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ClientApiService, private modal: NzModalService) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
tempImg = "https://cdn.pixabay.com/photo/2023/08/08/18/01/butterfly-8177925_1280.jpg";
public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true,
});
public queryForm = new FormGroup({
type: new FormControl({ value: "A", disabled: false }),
name: new FormControl("addd"),
});
private destroy$ = new Subject<void>();
public selectedIds: string[] = [];
temp = Array.from({ length: 50 }, (_, i) => i);
ngOnInit(): void {
this.initTableList();
this.tableList.getState$.pipe(takeUntil(this.destroy$)).subscribe((res) => {
this.selectedIds = res.selectedKeys as Array<string>;
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
initTableList() {
this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([
{ key: "img", title: "菜品图片", width: "66px" },
{ key: "name", title: "菜品名称" },
{ key: "name", title: "菜品标签" },
{ key: "name", title: "食材及含量", width: "30%" },
{ key: "name", title: "单位" },
]);
this.tableList = this.tableList.setOptions([
{
title: "打印营养标签",
premissions: [],
onClick: this.showFoodForm.bind(this),
},
{
title: "编辑",
premissions: [],
onClick: this.showFoodForm.bind(this),
},
{
title: "删除",
premissions: [],
onClick: this.deleteItem.bind(this),
},
]);
}
fetchData(pager: AnyObject, query: AnyObject) {
return this.api.page(pager, query);
}
deleteItem(v?: any) {
const ids = v ? [v.id] : this.selectedIds;
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该食材?",
nzOkDanger: true,
nzOnOk: () => {},
});
}
showFoodForm(food?: any) {
// this.drawerRef = this.drawer.create({
// nzTitle: food ? "编辑菜品" : "新增菜品",
// nzWidth: 700,
// nzContent: DishFormComponent,
// nzFooter: this.formFooterTpl,
// });
}
}

87
projects/client/src/app/pages/food/food.component.html

@ -0,0 +1,87 @@
<app-page [scroll]="false">
<div nz-row class="h-full overflow-hidden bg-white rounded-lg">
<div nz-col nzFlex="220px" class="food-type">
<nz-card class="h-full" [nzBordered]="false" nzTitle="食材类型" [nzBodyStyle]="{padding:'1px 0 0 0'}">
<ul nz-menu nzMode="inline">
<li nz-menu-item>
<a>
全部
</a>
</li>
<li nz-menu-item>
<a>
谷薯类
</a>
</li>
<li nz-menu-item>
<a>
大豆类及其制品
</a>
</li>
<li nz-menu-item>
<a>
蔬菜类
</a>
</li>
</ul>
</nz-card>
</div>
<div nz-col nzFlex="1" class="flex-1 overflow-hidden bg-white h-full">
<nz-card [nzBordered]="false" nzTitle="食材管理" class="scroll-card-body">
<div class="m-4">
<table-list
[props]="tableList"
[formGroup]="queryForm"
[search]="searchTpl"
[renderColumns]="renderColumnsTpl">
<ng-template #searchTpl>
<nz-form-item>
<nz-form-control>
<nz-radio-group nzButtonStyle="solid" formControlName="type">
<label nz-radio-button nzValue="A">全部</label>
<label nz-radio-button nzValue="B">常用</label>
<label nz-radio-button nzValue="c">忌用</label>
<label nz-radio-button nzValue="d">其他</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<input nz-input placeholder="请输入食材名称/编号" formControlName="name" />
</nz-form-control>
</nz-form-item>
</ng-template>
<ng-template #renderColumnsTpl let-data let-key="key" let-row="row">
<ng-container [ngSwitch]="key">
<ng-container *ngSwitchCase="'name1'">
<a nz-popover
[nzPopoverContent]="popoverTpl"
nzPopoverTitle="营养素">
<b>10</b>中营养素
</a>
<ng-template #popoverTpl>
<div class=" max-w-sm max-h-60 overflow-auto">
<nz-tag *ngFor="let item of temp" class="m-1">
能量{{item}}:10kcal
</nz-tag>
</div>
</ng-template>
</ng-container>
<ng-container *ngSwitchDefault>
{{data}}
</ng-container>
</ng-container>
</ng-template>
</table-list>
</div>
</nz-card>
</div>
</div>
</app-page>

9
projects/client/src/app/pages/food/food.component.less

@ -0,0 +1,9 @@
.food-type {
border-right: 1px solid #e8e8e8;
::ng-deep {
.ant-menu-inline {
border-right: none;
}
}
}

83
projects/client/src/app/pages/food/food.component.ts

@ -0,0 +1,83 @@
import { Component, OnDestroy, OnInit, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, takeUntil } from "rxjs";
import { ClientApiService } from "../../services";
@Component({
selector: "app-food",
templateUrl: "./food.component.html",
styleUrls: ["./food.component.less"],
})
export class FoodComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ClientApiService, private modal: NzModalService) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true,
});
public queryForm = new FormGroup({
type: new FormControl({ value: "A", disabled: false }),
name: new FormControl("addd"),
});
private destroy$ = new Subject<void>();
public selectedIds: string[] = [];
temp = Array.from({ length: 50 }, (_, i) => i);
ngOnInit(): void {
this.initTableList();
this.tableList.getState$.pipe(takeUntil(this.destroy$)).subscribe((res) => {
this.selectedIds = res.selectedKeys as Array<string>;
});
}
ngOnDestroy(): void {
this.destroy$.next();
this.destroy$.complete();
}
initTableList() {
this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([
{ key: "name", title: "食材编号" },
{ key: "name", title: "食材名称" },
{ key: "name", title: "食材类型" },
{ key: "name1", title: "营养素(每100g可食部)" },
{ key: "name", title: "更新日期" },
]);
this.tableList = this.tableList.setOptions([
{
title: "常用",
premissions: [],
onClick: this.deleteFood.bind(this),
},
{
title: "忌用",
premissions: [],
onClick: this.deleteFood.bind(this),
},
]);
}
fetchData(pager: AnyObject, query: AnyObject) {
return this.api.page(pager, query);
}
deleteFood(v?: any) {
const ids = v ? [v.id] : this.selectedIds;
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该食材?",
nzOkDanger: true,
nzOnOk: () => {},
});
}
}

6
projects/client/src/app/pages/index.ts

@ -0,0 +1,6 @@
export * from "./dashboard/dashboard.component";
export * from "./login/login.component";
export * from "./meal-setting/meal-setting.component";
export * from "./data-vis/data-vis.component";
export * from "./food/food.component";
export * from "./dish/dish.component";

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

@ -0,0 +1,65 @@
<section class="h-full ">
<div class="login">
<!-- <div>
<h1 class="text-center my-[25px]">
<img class="logo" [src]="'/assets/images/jl-logo.png'" />
</h1>
</div> -->
<div class="card ">
<div class="img flex items-center justify-center">
<img src="assets/images/login.png" />
</div>
<div class="form py-5 px-10 flex-1">
<div class="form-inner">
<h2 class="mt-4 text-3xl font-bold">智慧配餐管理</h2>
<h3 class="mt-10 text-xl">登录</h3>
<form nz-form [formGroup]="loginForm" class="mt-10">
<nz-form-item>
<nz-form-control [nzErrorTip]="formErrorTipsTpl">
<nz-input-group [nzPrefix]="prefixTemplateUser" nzSize="large">
<input nz-input nzSize="large" placeholder="账户" formControlName="uid" />
</nz-input-group>
<ng-template #prefixTemplateUser>
<span nz-icon nzType="user"></span>
<nz-divider nzType="vertical"></nz-divider>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control [nzErrorTip]="formErrorTipsTpl">
<nz-input-group [nzPrefix]="prefixTemplatePassword" nzSize="large">
<input nz-input type="password" placeholder="密码" formControlName="password" />
</nz-input-group>
<ng-template #prefixTemplatePassword>
<span nz-icon nzType="lock"></span>
<nz-divider nzType="vertical"></nz-divider>
</ng-template>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<button nz-button
nzType="primary"
nzBlock
class="btn"
nzSize="large"
(click)="onLogin()"
[nzLoading]="loading">
登录
</button>
</nz-form-control>
</nz-form-item>
</form>
</div>
</div>
</div>
</div>
</section>
<ng-template #formErrorTipsTpl let-control>
<div class="text-left">
<form-error-tips [control]="control"></form-error-tips>
</div>
</ng-template>

81
projects/client/src/app/pages/login/login.component.less

@ -0,0 +1,81 @@
//
:host {
display: block;
width: 100vw;
height: 100vh;
position: relative;
background-color: #edf0f5;
background-repeat: no-repeat;
background-size: cover;
}
.login {
width: 100vw;
height: 100vh;
position: relative;
z-index: 1;
background-color: #fff;
h1 {
margin-bottom: 24px;
font-size: 24px;
font-weight: 400;
color: #fff;
}
.logo {
display: inline-block;
height: 36px;
}
.card {
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
background-color: #fff;
position: relative;
overflow: hidden;
.img {
flex: 1;
height: 100%;
background-repeat: no-repeat;
background-position: center right;
background-size: auto 100%;
background-image: url('/assets/images/login-bg.svg');
img {
width: 40vw;
margin-left: -6vw;
}
}
}
.form {
margin: 0 auto;
background-color: #fff;
border-radius: 6px;
display: flex;
align-items: center;
.form-inner {
width: 400px;
margin-left: 5vw;
}
}
p {
font-size: 16px;
line-height: 24px;
letter-spacing: 0.23em;
text-shadow: 0px 8px 20px rgba(0, 0, 0, 0.1);
}
.btn {
margin-top: 16px;
}
}

42
projects/client/src/app/pages/login/login.component.ts

@ -0,0 +1,42 @@
import { Component } from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { Router } from "@angular/router";
import { NzMessageService } from "ng-zorro-antd/message";
import { FormValidators } from "projects/cdk/src/public-api";
import { Utils } from "projects/cdk/src/utils";
import { finalize, lastValueFrom } from "rxjs";
import { ClientApiService } from "../../services";
@Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.less"],
})
export class LoginComponent {
constructor(private msg: NzMessageService, private api: ClientApiService, private router: Router) {}
public loginForm = new FormGroup({
uid: new FormControl("", [FormValidators.required("请输入账户")]),
password: new FormControl("", [FormValidators.required("请输入密码")]),
});
public loading: boolean = false;
ngOnInit(): void {}
async onLogin() {
if (Utils.validateFormGroup(this.loginForm)) {
const { value } = this.loginForm;
this.loading = true;
const res = await lastValueFrom(
this.api.login(value).pipe(
finalize(() => {
this.loading = false;
})
)
);
this.msg.success(res.desc);
this.router.navigate(["/"]);
}
}
}

48
projects/client/src/app/pages/meal-setting/meal-setting.component.html

@ -0,0 +1,48 @@
<app-page>
<nz-card nzTitle="餐次、能量摄入比例配置">
<form nz-form nzLayout="vertical">
<nz-form-item>
<nz-form-label nzRequired>
早餐能量、营养摄入比例
</nz-form-label>
<nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzRequired>
午餐能量、营养摄入比例
</nz-form-label>
<nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzRequired>
晚餐能量、营养摄入比例
</nz-form-label>
<nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" />
</nz-input-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<nz-space>
<button *nzSpaceItem nz-button nzType="primary">
保存
</button>
<button *nzSpaceItem nz-button>
取消
</button>
</nz-space>
</nz-form-control>
</nz-form-item>
</form>
</nz-card>
</app-page>

0
projects/client/src/app/pages/meal-setting/meal-setting.component.less

10
projects/client/src/app/pages/meal-setting/meal-setting.component.ts

@ -0,0 +1,10 @@
import { Component } from '@angular/core';
@Component({
selector: 'app-meal-setting',
templateUrl: './meal-setting.component.html',
styleUrls: ['./meal-setting.component.less']
})
export class MealSettingComponent {
}

25
projects/client/src/app/services/client-api.service.ts

@ -0,0 +1,25 @@
import { HttpClient } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { map } from "rxjs";
@Injectable({
providedIn: "root",
})
export class ClientApiService {
constructor(private http: HttpClient) {}
login(v: {}) {
return this.http.post<any>("/", v);
}
page(v: {}, q: {}) {
return this.http.get<any>("https://jsonplaceholder.typicode.com/users", v).pipe(
map((r) => {
return {
total: 10,
content: r,
};
})
);
}
}

84
projects/client/src/app/services/http.interceptor.ts

@ -0,0 +1,84 @@
import { Inject, Injectable } from "@angular/core";
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor,
HttpErrorResponse,
HttpResponse,
} from "@angular/common/http";
import { catchError, Observable, switchMap, tap, throwError, timer } from "rxjs";
import { Router } from "@angular/router";
import { NzMessageService } from "ng-zorro-antd/message";
import { ResponseType } from "@cdk/types";
@Injectable({ providedIn: "root" })
export class HTTPInterceptor implements HttpInterceptor {
constructor(private router: Router, private msg: NzMessageService) {}
private msgFlag = false;
private localStroageKey = "catering";
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const token = localStorage.getItem(this.localStroageKey);
if (token) {
req = req.clone({
// headers: req.headers.set('Authorization', `Bearer ${token}`),
headers: req.headers.set("Authorization", token),
});
}
return this.handleResult(next, req);
}
private handleResult(next: HttpHandler, authReq: HttpRequest<any>): Observable<HttpEvent<any>> {
return next.handle(authReq).pipe(
tap((res) => {
if (res instanceof HttpResponse) {
const Authorization = res.headers.get("Authorization");
if (Authorization) {
localStorage.setItem(this.localStroageKey, Authorization);
}
// if (this.decConfig.triggerError) {
// this.decConfig.triggerError(res);
// }
if (res.body?.success === false && res.body.desc) {
throw new HttpErrorResponse({ error: res.body });
}
}
}),
catchError((err: HttpErrorResponse) => {
const throwErr = throwError(() => err);
if (this.msgFlag) {
return throwErr;
}
setTimeout(() => {
this.msgFlag = false;
}, 1500);
const error: ResponseType = err.error;
this.msgFlag = true;
if (error.success === false) {
this.msg.error(error.desc);
switch (error.code) {
case 401:
this.router.navigate(["/", "login"]);
break;
default:
break;
}
} else {
this.msg.error("服务器出错了!");
}
return throwErr;
})
);
}
}

1
projects/client/src/app/services/index.ts

@ -0,0 +1 @@
export * from "./client-api.service";

0
projects/client/src/assets/.gitkeep

BIN
projects/client/src/assets/images/jl-logo.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 192 KiB

17
projects/client/src/assets/images/login-bg.svg

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="6395" height="1079" viewBox="0 0 6395 1079">
<defs>
<clipPath id="clip-path">
<rect width="6395" height="1079" transform="translate(-5391)" fill="#fff"/>
</clipPath>
<linearGradient id="linear-gradient" x1="0.747" y1="0.222" x2="0.973" y2="0.807" gradientUnits="objectBoundingBox">
<stop offset="0" stop-color="#40a9ff"/>
<stop offset="1" stop-color="#1890ff"/>
</linearGradient>
</defs>
<g id="Mask_Group_1" data-name="Mask Group 1" transform="translate(5391)" clip-path="url(#clip-path)">
<g id="Group_118" data-name="Group 118" transform="translate(-419.333 -1.126)">
<path id="Path_142" data-name="Path 142" d="M6271.734-6.176s-222.478,187.809-55.349,583.254c44.957,106.375,81.514,205.964,84.521,277,8.164,192.764-156.046,268.564-156.046,268.564l-653.53-26.8L5475.065-21.625Z" transform="translate(-4876.383 0)" fill="#f1f5f8"/>
<path id="Union_6" data-name="Union 6" d="M-2631.1,1081.8v-1.6H-8230.9V.022H-2631.1V0H-1871.4s-187.845,197.448-91.626,488.844c49.167,148.9,96.309,256.289,104.683,362.118,7.979,100.852-57.98,201.711-168.644,254.286-65.858,31.29-144.552,42.382-223.028,42.383C-2441.2,1147.632-2631.1,1081.8-2631.1,1081.8Z" transform="translate(3259.524 0.803)" fill="url(#linear-gradient)"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
projects/client/src/assets/images/login.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

1
projects/client/src/assets/k-icon/carrot.svg

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691900436240" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="882" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M1013.78176 206.816c-56.256-21.568-124.96-15.264-180.48 14.432l-3.328-3.296c21.824-18.976 50.816-29.792 83.36-29.792a16 16 0 1 0 0-32c-19.392 0-37.696 3.552-54.816 9.376a169.6 169.6 0 0 0 9.376-54.816 16 16 0 1 0-32 0c0 32.48-10.784 61.44-29.792 83.36l-3.424-3.424c29.824-56.64 35.936-124.576 14.56-180.384a16 16 0 0 0-29.888 11.456c17.344 45.28 12.992 100.224-9.856 147.424-67.296-52.16-146.24-54.752-225.088-4.416-17.248 11.008-34.016 23.584-49.408 36.992-44.48 37.344-83.328 82.048-118.56 124.64A4205.664 4205.664 0 0 0 73.33376 750.4C19.89376 833.504-24.07424 917.76 14.77376 971.424a31.136 31.136 0 0 0 3.296 3.872l30.72 30.72a31.136 31.136 0 0 0 3.84 3.296c13.44 9.728 29.92 14.688 48.896 14.688 51.232 0 115.776-37.024 172.096-73.184 146.24-93.696 288.96-198.4 423.968-311.104 59.52-49.28 120.32-103.744 161.76-168.064 50.336-78.88 47.84-158.208-4.416-225.088 47.2-22.848 102.08-27.2 147.392-9.824a15.968 15.968 0 1 0 11.456-29.888z m-208.32 230.272c-38.464 59.712-96.928 110.432-148.8 153.408-133.088 111.04-273.568 214.144-417.6 306.432-26.848 17.216-98.144 63.04-137.536 63.04a24.896 24.896 0 0 1-9.984-1.728l-25.824-25.824c-5.024-9.76-6.112-42.24 61.44-147.456 11.424-17.824 23.488-35.392 35.232-53.12l19.296 19.328a15.936 15.936 0 0 0 22.624 0 16 16 0 0 0 0-22.624l-23.872-23.904a4104.96 4104.96 0 0 1 50.56-72.992l34.848 35.008a15.968 15.968 0 1 0 22.688-22.592l-38.72-38.88c17.184-23.904 34.624-47.584 52.352-71.168l48.16 48.16a15.936 15.936 0 0 0 22.624 0 16 16 0 0 0 0-22.624l-51.264-51.264c17.824-23.36 35.84-46.528 54.176-69.536l58.944 58.976a15.936 15.936 0 0 0 22.624 0 16 16 0 0 0 0-22.624l-61.344-61.376c12.544-15.456 24.832-31.168 37.568-46.464 5.856-7.072 11.936-14.24 18.016-21.376l67.648 67.648a15.936 15.936 0 0 0 22.624 0 16 16 0 0 0 0-22.624l-69.184-69.152a742.912 742.912 0 0 1 61.568-62.08l69.12 69.376a15.904 15.904 0 0 0 22.624 0.032 16 16 0 0 0 0.032-22.624L558.77376 238.912c9.184-7.232 18.496-14.112 28.096-20.256 60.928-38.88 114.176-35.872 166.08 12.96 1.6 1.376 2.944 2.752 4.32 4.16l31.04 31.008c1.376 1.376 2.752 2.72 5.12 5.408 47.04 49.92 50.944 103.904 12.032 164.896z" fill="#2c2c2c" p-id="883"></path></svg>

After

Width:  |  Height:  |  Size: 2.4 KiB

1
projects/client/src/assets/k-icon/food.svg

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1691900448064" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1032" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M225.745 428.82333334c4.655 4.655 9.31 9.31 16.291 9.30999999 4.655 0 6.982 0 11.637-2.32699999 9.309-4.655 11.636-18.618 6.982-27.92800001 0 0-60.51-95.418-9.31-160.581 27.928-37.237 32.582-81.455 13.964-130.32799999C251.345 82.06033333 230.4 56.46033334 230.4 54.13333333c-6.982-9.31-18.618-9.31-27.927-2.32699999-6.982 6.982-9.31 18.618-2.328 27.927 0 0 67.491 83.782 18.619 144.28999999-67.491 83.783 4.654 200.146 6.981 204.80000001z m558.546-6.98000001c4.654 4.654 9.309 9.308 16.29 9.30800001 4.655 0 6.983 0 11.637-2.32700001 9.31-4.655 11.637-18.618 6.982-27.927 0 0-60.51-95.419-9.31-160.58199999 27.928-34.91 32.583-81.455 13.965-130.327-13.964-34.91-34.91-60.51-34.91-62.837-6.981-9.309-18.618-9.309-27.927-2.32700001-9.309 6.982-9.309 18.618-2.327 27.927 0 0 67.49 83.782 18.618 144.291-69.818 86.11 2.327 200.146 6.982 204.80000001z m130.327 0c4.655 4.654 9.31 9.308 16.291 9.30800001 4.655 0 6.982 0 11.636-2.32700001 9.31-4.655 11.637-18.618 6.982-27.927 0 0-60.509-95.419-9.309-160.58199999 27.927-34.91 32.582-81.455 13.964-130.327-13.964-34.91-34.91-60.51-34.91-62.837-6.981-9.309-18.617-9.309-27.927-2.32700001-9.309 6.982-9.309 18.618-2.327 27.927 0 0 67.491 83.782 18.618 144.291-69.818 86.11 2.328 200.146 6.982 204.80000001z m-819.2 6.98000001c4.655 4.655 9.31 9.31 16.291 9.30999999 4.655 0 6.982 0 11.636-2.32699999 9.31-4.655 11.637-18.618 4.655-27.92800001 0 0-60.51-95.418-9.31-160.581 30.255-37.237 34.91-81.455 16.292-132.65499999-13.964-34.909-34.91-60.509-34.91-60.50900001-6.981-9.31-18.617-9.31-27.927-4.65499999-9.309 6.982-9.309 20.946-2.327 27.928 0 0 67.491 83.782 18.618 144.29-67.49 86.11 4.655 200.146 6.982 207.12799999z m870.4 409.59999999c-13.963-18.617-34.909-27.926-55.854-30.25399999-2.328-195.49-144.291-356.072-328.146-388.65400001 13.964-16.291 23.273-37.237 23.273-60.51 0-51.2-41.891-90.763-90.764-90.76299999-51.2 0-90.763 41.891-90.763 90.764 0 23.272 9.309 44.218 23.272 60.50899999-186.181 30.254-330.472 193.163-335.127 388.65400001-20.945 2.328-37.236 11.637-51.2 27.928-13.964 18.618-23.273 41.89-23.273 67.49 0 25.6 6.982 48.873 23.273 67.491C76.8 989.69733334 97.745 1001.33333334 121.02 1001.33333334h784.29c23.273 0 44.218-11.636 60.51-30.255 13.963-18.618 23.272-41.89 23.272-67.49 0-23.273-9.31-46.546-23.273-65.164z m-451.49-525.963c25.6 0 44.217 20.946 44.217 44.21800001 0 25.6-20.945 44.219-44.218 44.21899999-25.6 0-44.218-20.946-44.218-44.21899999 0-23.272 18.618-44.218 44.218-44.21800001z m414.254 628.36400001c-4.655 4.654-11.637 11.636-23.273 11.636L118.691 950.13333334c-11.636 0-20.946-6.982-23.273-13.96400001-6.982-9.309-11.636-23.272-11.636-37.236 0-13.964 4.654-27.927 11.636-37.236 4.655-4.655 11.637-11.637 23.273-11.637l514.327 2.328c13.964 0 23.273-9.31 23.273-23.27299999s-9.31-23.273-23.273-23.273l-474.763-2.32700001c2.327-190.837 160.581-346.764 351.418-346.764H512c193.164 0 349.09 155.927 349.09 349.09100001h-74.472c-13.963 0-23.273 9.31-23.273 23.273s9.31 23.273 23.273 23.27299999h116.364c11.636 0 20.945 6.981 23.273 13.96300001 6.981 9.31 11.636 23.273 11.636 37.237 2.327 13.963-2.327 27.927-9.31 37.236z" p-id="1033"></path></svg>

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
projects/client/src/favicon.ico

Binary file not shown.

After

Width:  |  Height:  |  Size: 948 B

16
projects/client/src/index.html

@ -0,0 +1,16 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>智慧配餐系统</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body>
<app-root></app-root>
</body>
</html>

7
projects/client/src/main.ts

@ -0,0 +1,7 @@
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));

59
projects/client/src/styles.less

@ -0,0 +1,59 @@
// Custom Theming for NG-ZORRO
// For more information: https://ng.ant.design/docs/customize-theme/en
@import "../../../node_modules/ng-zorro-antd/ng-zorro-antd.less";
// Override less variables to here
// View all variables: https://github.com/NG-ZORRO/ng-zorro-antd/blob/master/components/style/themes/default.less
// @primary-color: #1890ff;
/* You can add global styles to this file, and also import other style files */
@tailwind utilities;
html,
body {
height: 100vh;
}
ul,
li {
list-style: none;
margin: 0;
padding: 0;
}
.upload-btn {
position: relative;
input {
display: block;
height: 100%;
background-color: red;
position: absolute;
inset: 0;
opacity: 0;
font-size: 0;
cursor: pointer;
}
}
.fixed-footter {
box-shadow: 0 0 10px -2px rgba(0, 0, 0, .25)
}
.scroll-card-body {
height: 100%;
&>.ant-card-body {
overflow: auto;
height: calc(100% - 60px);
padding: 0
}
&>.ant-card-head {
position: relative;
z-index: 10;
}
}

14
projects/client/tsconfig.app.json

@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/app",
"types": []
},
"files": [
"src/main.ts"
],
"include": [
"src/**/*.d.ts"
]
}

14
projects/client/tsconfig.spec.json

@ -0,0 +1,14 @@
/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "../../out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.spec.ts",
"src/**/*.d.ts"
]
}
Loading…
Cancel
Save