88 changed files with 2912 additions and 309 deletions
File diff suppressed because one or more lines are too long
@ -0,0 +1,15 @@ |
|||
# 修改记录 |
|||
|
|||
> 9.10 |
|||
``` |
|||
基础协议 |
|||
用户协议 |
|||
角色协议 |
|||
单位协议 |
|||
``` |
|||
|
|||
> 9.11 |
|||
``` |
|||
食材 |
|||
菜品 |
|||
食谱 |
|||
@ -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,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 |
|||
} |
|||
``` |
|||
File diff suppressed because one or more lines are too long
@ -0,0 +1,7 @@ |
|||
.day-item { |
|||
::ng-deep { |
|||
.ant-card-head-title { |
|||
overflow: visible; |
|||
} |
|||
} |
|||
} |
|||
@ -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,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 {} |
|||
@ -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> |
|||
@ -0,0 +1,8 @@ |
|||
.month-wrap { |
|||
::ng-deep { |
|||
.ant-checkbox-wrapper { |
|||
margin: 6px 0; |
|||
flex-basis: calc(100% / 4); |
|||
} |
|||
} |
|||
} |
|||
@ -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; |
|||
} |
|||
} |
|||
} |
|||
@ -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,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, |
|||
}); |
|||
} |
|||
} |
|||
@ -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 {} |
|||
@ -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 {} |
|||
@ -0,0 +1 @@ |
|||
<router-outlet></router-outlet> |
|||
@ -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!'); |
|||
}); |
|||
}); |
|||
@ -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'; |
|||
} |
|||
@ -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 {} |
|||
@ -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> |
|||
@ -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%; |
|||
} |
|||
} |
|||
} |
|||
@ -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: () => {}, |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from "./app-layout/app-layout.component"; |
|||
@ -0,0 +1,5 @@ |
|||
<app-page [pageTitle]="'使用流程'"> |
|||
<nz-card> |
|||
|
|||
</nz-card> |
|||
</app-page> |
|||
@ -0,0 +1,10 @@ |
|||
import { Component } from '@angular/core'; |
|||
|
|||
@Component({ |
|||
selector: 'app-dashboard', |
|||
templateUrl: './dashboard.component.html', |
|||
styleUrls: ['./dashboard.component.less'] |
|||
}) |
|||
export class DashboardComponent { |
|||
|
|||
} |
|||
@ -0,0 +1 @@ |
|||
<p>data-vis works!</p> |
|||
@ -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 { |
|||
|
|||
} |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
@ -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,
|
|||
// });
|
|||
} |
|||
} |
|||
@ -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> |
|||
@ -0,0 +1,9 @@ |
|||
.food-type { |
|||
border-right: 1px solid #e8e8e8; |
|||
|
|||
::ng-deep { |
|||
.ant-menu-inline { |
|||
border-right: none; |
|||
} |
|||
} |
|||
} |
|||
@ -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: () => {}, |
|||
}); |
|||
} |
|||
} |
|||
@ -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"; |
|||
@ -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> |
|||
@ -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; |
|||
} |
|||
} |
|||
@ -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(["/"]); |
|||
} |
|||
} |
|||
} |
|||
@ -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,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 { |
|||
|
|||
} |
|||
@ -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, |
|||
}; |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
@ -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; |
|||
}) |
|||
); |
|||
} |
|||
} |
|||
@ -0,0 +1 @@ |
|||
export * from "./client-api.service"; |
|||
|
After Width: | Height: | Size: 192 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 170 KiB |
|
After Width: | Height: | Size: 2.4 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 948 B |
@ -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> |
|||
@ -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)); |
|||
@ -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; |
|||
} |
|||
|
|||
} |
|||
@ -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" |
|||
] |
|||
} |
|||
@ -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…
Reference in new issue