Browse Source

食材管理

main
kkerwin 2 years ago
parent
commit
cd37531b56
  1. 1
      package.json
  2. 27
      pnpm-lock.yaml
  3. 8
      projects/admin/src/app/app.module.ts
  4. 2
      projects/admin/src/app/components/app-layout/app-layout.component.html
  5. 5
      projects/admin/src/app/components/app-layout/app-layout.component.ts
  6. 15
      projects/admin/src/app/components/food-form/food-form.component.html
  7. 8
      projects/admin/src/app/components/food-form/food-form.component.less
  8. 83
      projects/admin/src/app/components/food-form/food-form.component.ts
  9. 2
      projects/admin/src/app/components/role-permission/role-permission.component.ts
  10. 2
      projects/admin/src/app/components/user-list/user-list.component.ts
  11. 2
      projects/admin/src/app/pages/dish/dish.component.ts
  12. 24
      projects/admin/src/app/pages/food/food.component.html
  13. 89
      projects/admin/src/app/pages/food/food.component.ts
  14. 2
      projects/admin/src/app/pages/ingredients/ingredient-list/ingredient-list.component.ts
  15. 2
      projects/admin/src/app/pages/ingredients/ingredient-release/ingredient-release.component.ts
  16. 2
      projects/admin/src/app/pages/ingredients/ingredient-review/ingredient-review.component.ts
  17. 3
      projects/admin/src/app/pages/login/login.component.ts
  18. 2
      projects/admin/src/app/pages/organization/organization-form/organization-form.component.ts
  19. 2
      projects/admin/src/app/pages/organization/organization-list/organization-list.component.ts
  20. 2
      projects/admin/src/app/pages/standard/standard-list/standard-list.component.ts
  21. 3
      projects/admin/src/app/pages/system/user-manage/user-manage.component.ts
  22. 129
      projects/admin/src/app/services/api.service.ts
  23. 8
      projects/admin/src/app/services/auth.guard.ts
  24. 2
      projects/admin/src/app/services/index.ts
  25. 10
      projects/cdk/src/dtos/user.dto.ts
  26. 1
      projects/cdk/src/public-api.ts
  27. 234
      projects/cdk/src/services/api.service.ts
  28. 1
      projects/cdk/src/services/index.ts
  29. 4
      projects/cdk/src/table-list/table-list-options.ts
  30. 3
      projects/cdk/src/table-list/table-list/table-list.component.html
  31. 16
      projects/cdk/src/table-list/table-list/table-list.component.ts
  32. 3
      projects/cdk/src/types/index.ts
  33. 34
      projects/cdk/src/utils/index.ts
  34. 5
      projects/client/src/app/app-routing.module.ts
  35. 9
      projects/client/src/app/app.module.ts
  36. 2
      projects/client/src/app/components/app-layout/app-layout.component.html
  37. 14
      projects/client/src/app/components/app-layout/app-layout.component.ts
  38. 2
      projects/client/src/app/components/index.ts
  39. 2
      projects/client/src/app/components/org-form/org-form.component.ts
  40. 25
      projects/client/src/app/components/role-permission/role-permission.component.html
  41. 0
      projects/client/src/app/components/role-permission/role-permission.component.less
  42. 82
      projects/client/src/app/components/role-permission/role-permission.component.ts
  43. 86
      projects/client/src/app/components/user-list/user-list.component.html
  44. 0
      projects/client/src/app/components/user-list/user-list.component.less
  45. 122
      projects/client/src/app/components/user-list/user-list.component.ts
  46. 4
      projects/client/src/app/pages/dish/dish.component.ts
  47. 49
      projects/client/src/app/pages/food/food.component.html
  48. 85
      projects/client/src/app/pages/food/food.component.ts
  49. 1
      projects/client/src/app/pages/index.ts
  50. 5
      projects/client/src/app/pages/login/login.component.ts
  51. 3
      projects/client/src/app/pages/meal-setting/meal-setting.component.html
  52. 22
      projects/client/src/app/pages/meal-setting/meal-setting.component.ts
  53. 4
      projects/client/src/app/pages/system/org-info/org-info.component.ts
  54. 92
      projects/client/src/app/pages/system/user-manage/user-manage.component.html
  55. 29
      projects/client/src/app/pages/system/user-manage/user-manage.component.less
  56. 147
      projects/client/src/app/pages/system/user-manage/user-manage.component.ts
  57. 5
      projects/client/src/app/services/auth.guard.ts
  58. 9
      projects/client/src/app/services/client-api.service.ts
  59. 2
      projects/client/src/app/services/index.ts

1
package.json

@ -27,6 +27,7 @@
"immer": "^10.0.2",
"ng-zorro-antd": "16.1.0",
"ngx-permissions": "^15.0.1",
"query-string": "^8.1.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.13.0"

27
pnpm-lock.yaml

@ -50,6 +50,9 @@ dependencies:
ngx-permissions:
specifier: ^15.0.1
version: 15.0.1(@angular/core@16.1.0)(@angular/router@16.1.0)(rxjs@7.8.0)
query-string:
specifier: ^8.1.0
version: 8.1.0
rxjs:
specifier: ~7.8.0
version: 7.8.0
@ -3451,6 +3454,11 @@ packages:
ms: 2.1.2
dev: true
/decode-uri-component@0.4.1:
resolution: {integrity: sha512-+8VxcR21HhTy8nOt6jf20w0c9CADrw1O8d+VZ/YzzCt4bJ3uBjw+D1q2osAB8RnpwwaeYBxy0HyKQxD5JBMuuQ==}
engines: {node: '>=14.16'}
dev: false
/deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
engines: {node: '>=0.10.0'}
@ -3911,6 +3919,11 @@ packages:
to-regex-range: 5.0.1
dev: true
/filter-obj@5.1.0:
resolution: {integrity: sha512-qWeTREPoT7I0bifpPUXtxkZJ1XJzxWtfoWWkdVGqa+eCr3SHW/Ocp89o8vLvbUuQnadybJpjOKu4V+RwO6sGng==}
engines: {node: '>=14.16'}
dev: false
/finalhandler@1.1.2:
resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
engines: {node: '>= 0.8'}
@ -5987,6 +6000,15 @@ packages:
side-channel: 1.0.4
dev: true
/query-string@8.1.0:
resolution: {integrity: sha512-BFQeWxJOZxZGix7y+SByG3F36dA0AbTy9o6pSmKFcFz7DAj0re9Frkty3saBn3nHo3D0oZJ/+rx3r8H8r8Jbpw==}
engines: {node: '>=14.16'}
dependencies:
decode-uri-component: 0.4.1
filter-obj: 5.1.0
split-on-first: 3.0.0
dev: false
/queue-microtask@1.2.3:
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
dev: true
@ -6613,6 +6635,11 @@ packages:
- supports-color
dev: true
/split-on-first@3.0.0:
resolution: {integrity: sha512-qxQJTx2ryR0Dw0ITYyekNQWpz6f8dGd7vffGNflQQ3Iqj9NJ6qiZ7ELpZsJ/QBhIVAiDfXdag3+Gp8RvWa62AA==}
engines: {node: '>=12'}
dev: false
/sprintf-js@1.0.3:
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
dev: true

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

@ -36,7 +36,7 @@ import {
StandardSettingComponent,
} from "./pages";
import { HTTPInterceptor } from "./services/http.interceptor";
import { IconsProviderModule } from "@cdk/public-api";
import { IconsProviderModule, PROJECT_NAME } from "@cdk/public-api";
import { SharedModule } from "@cdk/shared/shared.module";
import { IngredientModule } from "@cdk/ingredient/ingredient.module";
@ -49,6 +49,8 @@ registerLocaleData(zh);
FoodFormComponent,
DishFormComponent,
IngredientFormBasicComponent,
UserListComponent,
RolePermissionComponent,
HomeComponent,
LoginComponent,
@ -64,9 +66,6 @@ registerLocaleData(zh);
UserManageComponent,
UserListComponent,
RolePermissionComponent,
StandardListComponent,
StandardFormComponent,
StandardSettingComponent,
@ -83,6 +82,7 @@ registerLocaleData(zh);
],
providers: [
{ provide: NZ_I18N, useValue: zh_CN },
{ provide: PROJECT_NAME, useValue: "admin" },
{ provide: HTTP_INTERCEPTORS, useClass: HTTPInterceptor, multi: true },
],
bootstrap: [AppComponent],

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

@ -8,7 +8,7 @@
nzType="text"
nz-dropdown
[nzDropdownMenu]="menu">
admin
{{account.name}}
</button>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>

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

@ -1,4 +1,4 @@
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Component, OnInit } from "@angular/core";
import { NavigationEnd, Router, RouterModule } from "@angular/router";
import { NzMessageService } from "ng-zorro-antd/message";
@ -27,6 +27,9 @@ export class AppLayoutComponent implements OnInit {
this.currentUrl = e.url;
});
}
account = this.api.adminAccount;
ngOnInit(): void {}
unSubscribe$ = new Subject<void>();

15
projects/admin/src/app/components/food-form/food-form.component.html

@ -4,7 +4,7 @@
食材编号
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input placeholder="请输入食材编号" nz-input formControlName="id" />
<input placeholder="请输入食材编号" nz-input formControlName="key" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
@ -20,7 +20,13 @@
食材类型
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input placeholder="请输入食材类型" nz-input formControlName="type" />
<nz-select nzPlaceHolder="请选择" formControlName="type">
<nz-option
*ngFor="let item of globalEnum.category"
[nzValue]="item.value"
[nzLabel]="item.key">
</nz-option>
</nz-select>
</nz-form-control>
</nz-form-item>
<nz-form-item>
@ -37,8 +43,9 @@
</a>
</div>
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<ul formArrayName="nutrition">
<nz-form-control [nzErrorTip]="formControlErrorTpl" class="hide-input">
<input type="hidden" formControlName="nutrient" />
<ul formArrayName="_nutrition">
<li class="mb-2" *ngFor="let n of nutrition.controls;let i = index" [formGroupName]="i">
<div class="flex">
<div>

8
projects/admin/src/app/components/food-form/food-form.component.less

@ -5,4 +5,12 @@
width: 100%;
}
}
}
.hide-input {
::ng-deep {
.ant-form-item-control-input {
min-height: 0;
}
}
}

83
projects/admin/src/app/components/food-form/food-form.component.ts

@ -1,7 +1,9 @@
import { ApiService } from "@admin/app/services";
import { Component, OnInit } from "@angular/core";
import { ApiService } from "@cdk/services";
import { Component, Input, OnInit } from "@angular/core";
import { FormArray, FormBuilder, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";
import { FormValidators } from "@cdk/validators";
import { NzMessageService } from "ng-zorro-antd/message";
@Component({
selector: "app-food-form",
@ -9,44 +11,93 @@ import { FormValidators } from "@cdk/validators";
styleUrls: ["./food-form.component.less"],
})
export class FoodFormComponent implements OnInit {
constructor(private fb: FormBuilder, private api: ApiService) {}
constructor(private fb: FormBuilder, private api: ApiService, private msg: NzMessageService) {}
formGroup!: FormGroup;
@Input() food: any;
public globalEnum = this.api.globalEnum;
get nutrition(): FormArray {
return this.formGroup.get("nutrition") as FormArray;
return this.formGroup.get("_nutrition") as FormArray;
}
ngOnInit(): void {
this.formGroup = this.fb.group({
id: this.fb.control("", [FormValidators.required()]),
name: this.fb.control("", [FormValidators.required()]),
type: this.fb.control("", [FormValidators.required()]),
nutrition: this.fb.array([], [FormValidators.required()]),
key: this.fb.control("", [FormValidators.required("请输入食材编号")]),
name: this.fb.control("", [FormValidators.required("请输入食材名称")]),
type: this.fb.control("", [FormValidators.required("请选择食材类型")]),
nutrient: this.fb.control(null, [FormValidators.required("请输入营养素")]),
_nutrition: this.fb.array([], []),
});
this.api.getAllEnum().subscribe((r) => {});
if (this.food) {
this.formGroup.patchValue(this.food);
this.food.nutrientArr?.forEach((n: any) => {
this.createNutrition(n);
});
}
}
nutritionChange(v: string, idx: number) {
const nutrient = this.globalEnum.nutrient.find((f) => f.key === v);
this.nutrition.at(idx).patchValue({
measurement: nutrient?.measurement,
});
if (this.nutrition.value?.filter((s: any) => s.nutritionName === nutrient?.key).length < 2) {
this.nutrition.at(idx).patchValue({
measurement: nutrient?.measurement,
});
} else {
this.msg.error("重复的营养素");
this.nutrition.at(idx).patchValue(
{
nutritionName: "",
measurement: "",
},
{ emitEvent: false }
);
}
}
createNutrition() {
createNutrition(v?: any) {
this.nutrition.push(
this.fb.group({
nutritionName: this.fb.control("", [FormValidators.required()]),
nutritionNum: this.fb.control(0, [FormValidators.required()]),
measurement: this.fb.control("", [FormValidators.required()]),
nutritionName: this.fb.control(v?.key ?? ""),
nutritionNum: this.fb.control(v?.value ?? 0),
measurement: this.fb.control(v?.measurement ?? ""),
})
);
const nutrient = this.formGroup.get("nutrient");
if (!nutrient?.value) {
nutrient?.patchValue({});
}
}
removeNutrition(idx: number) {
this.nutrition.removeAt(idx);
}
public getValues() {
let values = null;
if (Utils.validateFormGroup(this.formGroup)) {
const { _nutrition, key, name, type } = this.formGroup.value;
let nutrient = Object.create(null);
if (Array.isArray(_nutrition)) {
for (const n of _nutrition) {
let num = Number(n.nutritionNum);
if (!n.nutritionName || !num) {
this.msg.error("请输入正确的营养素");
return;
}
nutrient[n.nutritionName] = num;
}
}
values = {
key,
name,
type,
nutrient,
};
}
return values;
}
}

2
projects/admin/src/app/components/role-permission/role-permission.component.ts

@ -1,5 +1,5 @@
import { PermItemDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { AnyObject } from "@cdk/types";

2
projects/admin/src/app/components/user-list/user-list.component.ts

@ -1,5 +1,4 @@
import { UserDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import { ApiService } from "@admin/app/services";
import {
Component,
EventEmitter,
@ -18,6 +17,7 @@ import { MD5 } from "crypto-js";
import { NzMessageService } from "ng-zorro-antd/message";
import { NzModalService } from "ng-zorro-antd/modal";
import { lastValueFrom } from "rxjs";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-user-list",

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

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Subject } from "rxjs";
@Component({

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

@ -14,12 +14,13 @@
<nz-card class="h-full" [nzBordered]="false" nzTitle="食材类型"
[nzBodyStyle]="{padding:'1px 0 0 0', height:'100%',overflow:'auto'}">
<ul nz-menu nzMode="inline">
<li nz-menu-item>
<li nz-menu-item (click)="onTypeChange('')" [nzSelected]="!queryForm.get('type')?.value">
<a>
全部
</a>
</li>
<li nz-menu-item *ngFor="let cate of globalEnum.category">
<li nz-menu-item [nzSelected]="cate.key === queryForm.get('type')?.value"
*ngFor="let cate of globalEnum.category" (click)="onTypeChange(cate.key)">
<a>
{{cate.key}}
</a>
@ -41,27 +42,30 @@
<ng-template #searchTpl>
<nz-form-item>
<nz-form-control>
<input nz-input placeholder="请输入食材名称/编号" formControlName="name" />
<input nz-input placeholder="请输入食材名称/编号" formControlName="keyword" />
</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'">
<ng-container *ngSwitchCase="'nutrientArr'">
<a nz-popover
[nzPopoverContent]="popoverTpl"
nzPopoverTitle="营养素">
<b>10</b>中营养素
<b>{{data.length}}</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 *ngFor="let item of data" class="m-1">
{{item.label}}:{{item.value}}{{item.measurement}}
</nz-tag>
</div>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="'time'">
{{data | date:'yyyy-MM-dd HH:mm:ss'}}
</ng-container>
<ng-container *ngSwitchDefault>
{{data}}
@ -86,7 +90,7 @@
(drop)="handleDrop($event)"
(dragenter)="suppress($event)"
(dragover)="suppress($event)">
<input type="file" id="file" (change)="onFileChange($event)" accept=".xlsx,.xls" />
<input type="file" id="file" (change)="onFileChange($any($event.target).files)" accept=".xlsx,.xls" />
<div class="text-center">
<p class="text-4xl mb-1">
<a nz-icon nzType="upload" nzTheme="outline"></a>
@ -105,10 +109,10 @@
<ng-template #foofFormFooterTpl>
<nz-space>
<button *nzSpaceItem nz-button (click)="cancelFoodForm()">
<button *nzSpaceItem nz-button (click)="cancelFoodForm()" type="button">
取消
</button>
<button *nzSpaceItem nz-button nzType="primary">
<button *nzSpaceItem nz-button nzType="primary" [nzLoading]="submitLoading" (click)="onFoodSubmit()">
保存
</button>
</nz-space>

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

@ -1,12 +1,12 @@
import { FoodFormComponent } from "@admin/app/components";
import { GlobalEnum } from "@cdk/dtos";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
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 { NzModalRef, NzModalService } from "ng-zorro-antd/modal";
import { Subject, finalize, lastValueFrom, map, takeUntil } from "rxjs";
import { NzMessageService } from "ng-zorro-antd/message";
@Component({
selector: "app-food",
@ -14,34 +14,43 @@ import { Subject, takeUntil } from "rxjs";
styleUrls: ["./food.component.less"],
})
export class FoodComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ApiService, private modal: NzModalService) {}
constructor(
private drawer: NzDrawerService,
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService
) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
private drawerRef?: NzDrawerRef;
private importModalRef?: NzModalRef;
public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true,
frontPagination: false,
rowKey: "key",
});
public globalEnum = this.api.globalEnum;
public queryForm = new FormGroup({
name: new FormControl(""),
keyword: new FormControl(""),
type: new FormControl(""),
});
private destroy$ = new Subject<void>();
public selectedIds: string[] = [];
temp = Array.from({ length: 50 }, (_, i) => i);
submitLoading = false;
ngOnInit(): void {
this.initTableList();
this.tableList.getState$.pipe(takeUntil(this.destroy$)).subscribe((res) => {
this.selectedIds = res.selectedKeys as Array<string>;
});
console.log("this.globalEnum", this.globalEnum);
}
ngOnDestroy(): void {
@ -52,11 +61,11 @@ export class FoodComponent implements OnInit, OnDestroy {
initTableList() {
this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([
{ key: "name", title: "食材编号" },
{ key: "key", title: "食材编号" },
{ key: "name", title: "食材名称" },
{ key: "name", title: "食材类型" },
{ key: "name1", title: "营养素(每100g可食部)" },
{ key: "name", title: "更新日期" },
{ key: "type", title: "食材类型" },
{ key: "nutrientArr", title: "营养素(每100g可食部)" },
{ key: "time", title: "更新日期" },
]);
this.tableList = this.tableList.setOptions([
@ -74,37 +83,71 @@ export class FoodComponent implements OnInit, OnDestroy {
}
fetchData(query: AnyObject, pager: AnyObject) {
return this.api.page(pager, query);
return this.api.getFoodPage(pager, query).pipe();
}
onTypeChange(type: string) {
this.queryForm.patchValue({ type });
this.tableList.reset();
}
showFoodForm(food?: any) {
this.drawerRef = this.drawer.create({
nzTitle: food ? "编辑食材" : "新增食材",
nzWidth: 520,
nzContentParams: {
food,
},
nzContent: FoodFormComponent,
nzFooter: this.foofFormFooterTpl,
});
}
onFoodSubmit() {
if (this.drawerRef) {
const com = this.drawerRef.getContentComponent() as FoodFormComponent;
const val = com.getValues();
if (val) {
this.submitLoading = true;
this.api
.saveFood(val)
.pipe(
finalize(() => {
this.submitLoading = false;
})
)
.subscribe((res) => {
this.msg.success(res.desc);
this.cancelFoodForm();
this.tableList.run();
});
}
}
}
cancelFoodForm() {
this.drawerRef?.close();
}
deleteFood(v?: any) {
const ids = v ? [v.id] : this.selectedIds;
const ids = v ? [v.key] : this.selectedIds;
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该食材?",
nzContent: `是否要删除${ids.length}个食材?`,
nzOkDanger: true,
nzOnOk: () => {},
nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteFoods(ids));
this.msg.success(res.desc);
this.tableList.run();
},
});
}
showImportForm(nzContent: TemplateRef<{}>) {
this.modal.create({
this.importModalRef = this.modal.create({
nzTitle: "导入食材清单",
nzWidth: 520,
nzContent,
nzFooter: null,
});
}
@ -123,5 +166,15 @@ export class FoodComponent implements OnInit, OnDestroy {
}
};
onFileChange(e: Event | FileList | null) {}
onFileChange(files: FileList) {
if (files?.length > 0) {
const formData = new FormData();
formData.append("files", files[0]);
this.api.importFood(formData).subscribe((res) => {
this.msg.success(res.desc);
this.tableList.run();
this.importModalRef?.close();
});
}
}
}

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

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-ingredient-list",

2
projects/admin/src/app/pages/ingredients/ingredient-release/ingredient-release.component.ts

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-ingredient-release",

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

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { ActivatedRoute, Router } from "@angular/router";
@Component({

3
projects/admin/src/app/pages/login/login.component.ts

@ -7,7 +7,7 @@ import { MD5 } from "crypto-js";
import { FormValidators } from "projects/cdk/src/public-api";
import { Utils } from "projects/cdk/src/utils";
import { ApiService } from "../../services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-login",
@ -39,7 +39,6 @@ export class LoginComponent {
})
)
);
localStorage.setItem("account", JSON.stringify(res.body));
this.msg.success(res.desc);
this.router.navigate(["/"]);
}

2
projects/admin/src/app/pages/organization/organization-form/organization-form.component.ts

@ -1,4 +1,4 @@
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Component } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";

2
projects/admin/src/app/pages/organization/organization-list/organization-list.component.ts

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { OrgDTO } from "@cdk/dtos";
import { Router } from "@angular/router";
import { NzModalService } from "ng-zorro-antd/modal";

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

@ -3,7 +3,7 @@ import { FormControl, FormGroup } from "@angular/forms";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { AnyObject, TableListOption } from "@cdk/public-api";
import { DishFormComponent } from "@admin/app/components";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-standard-list",

3
projects/admin/src/app/pages/system/user-manage/user-manage.component.ts

@ -1,6 +1,6 @@
import { FoodFormComponent } from "@admin/app/components";
import { PermItemDTO, UserDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Component, OnChanges, OnInit, SimpleChanges, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup, FormGroupName } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
@ -124,6 +124,7 @@ export class UserManageComponent {
const res = await lastValueFrom(this.api.updateRole(this.roleForm.value));
this.msg.success(res.desc);
this.getRoleList();
this.roleForm.reset();
return true;
}
return false;

129
projects/admin/src/app/services/api.service.ts

@ -1,129 +0,0 @@
import { HttpClient, HttpParams } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { AnyObject, ResponseType } from "@cdk/types";
import { Utils } from "@cdk/utils";
import { Observable, map, of, tap } from "rxjs";
import { PermItemDTO, UserDTO, UserRoleDTO, GlobalEnum, OrgDTO } from "@cdk/public-api";
@Injectable({
providedIn: "root",
})
export class ApiService {
constructor(private http: HttpClient) {}
globalEnum!: GlobalEnum;
page(v: {}, q: {}) {
return this.http.get<any>("https://jsonplaceholder.typicode.com/users", v).pipe(
map((r) => {
return {
total: 10,
content: r,
};
})
);
}
getAllEnum(force?: boolean): Observable<GlobalEnum> {
if (this.globalEnum && !force) {
return of(this.globalEnum);
}
return this.http.get<ResponseType<GlobalEnum>>("/api/basic/enum").pipe(
map((res) => {
return res.body;
}),
tap((r) => {
this.globalEnum = r;
})
);
}
login(v: {}) {
const params = Utils.objectToHttpParams(v);
return this.http.get<ResponseType>("/api/login", { params });
}
logout() {
return this.http.get<ResponseType>("/api/logout");
}
getRoleList() {
return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role");
}
deleteRole(roleId: string) {
const params = new HttpParams().set("roleId", roleId);
return this.http.delete<ResponseType>("/api/role", {
params,
});
}
updateRole(rule: AnyObject) {
const body = Utils.objectToFormData(rule);
const method = rule["roleId"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/role", body);
}
getRolePerms() {
return this.http.get<ResponseType<PermItemDTO[]>>("/api/role/item");
}
getUserList() {
return this.http.get<ResponseType<UserDTO[]>>("/api/user");
}
checkUid(uid: string) {
const params = new HttpParams().set("uid", uid);
return this.http.delete<ResponseType>("/api/user/check", {
params,
});
}
saveUser(user: AnyObject, edit: boolean) {
const body = Utils.objectToFormData(user);
const method = edit ? "post" : "put";
return this.http[method]<ResponseType>("/api/user", body);
}
deleteUser(uid: string) {
const params = new HttpParams().set("uid", uid);
return this.http.delete<ResponseType>("/api/user", {
params,
});
}
getOrgList() {
return this.http.get<ResponseType<OrgDTO[]>>("/api/vender");
}
checkOrgAccount(account: string) {
const params = new HttpParams().set("account", account);
return this.http.get<ResponseType<boolean>>("/api/vender/check/account", {
params,
});
}
checkOrgName(name: string) {
const params = new HttpParams().set("name", name);
return this.http.get<ResponseType<boolean>>("/api/vender/check/name", {
params,
});
}
deleteOrg(id: number) {
const params = new HttpParams().set("vender", id);
return this.http.delete<ResponseType>("/api/vender", {
params,
});
}
saveOrg(org: AnyObject) {
const body = Utils.objectToFormData(org);
const method = org["id"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/vender", body);
}
// getOrgConfig() {
// return this.http.get<ResponseType>("/api/vender/config");
// }
}

8
projects/admin/src/app/services/auth.guard.ts

@ -1,16 +1,14 @@
import { inject } from "@angular/core";
import { Router } from "@angular/router";
import { map } from "rxjs";
import { ApiService } from "./api.service";
const accountTokenName = "account";
import { ApiService } from "@cdk/services";
export const authGuard = () => {
const router = inject(Router);
const token = localStorage.getItem(accountTokenName);
const api = inject(ApiService);
const account = localStorage.getItem(api.accountKey);
if (!token) {
if (!account) {
router.navigate(["/login"]);
return false;
}

2
projects/admin/src/app/services/index.ts

@ -1 +1 @@
export * from "./api.service";
export * from "../../../../cdk/src/services/api.service";

10
projects/cdk/src/dtos/user.dto.ts

@ -28,6 +28,16 @@ export type OrgConfigDTO = {
lunch: number;
};
export type AdminAccountDTO = {
name: string;
phone: string;
roleId: number;
roleItems: PermItemDTO[];
roleName: string;
roleType: string;
time: number;
uid: string;
};
export type ClientAccountDTO = {
name: string;
roleId: number;

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

@ -16,3 +16,4 @@ export * from "./storage";
export * from "./icons/icons-provider.module";
export * from "./dtos";
export * from "./services";

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

@ -0,0 +1,234 @@
import { HttpClient, HttpParams } from "@angular/common/http";
import { Inject, Injectable, InjectionToken } from "@angular/core";
import { AnyObject, PageResult, ResponseType } from "@cdk/types";
import { Utils } from "@cdk/utils";
import { Observable, map, of, tap } from "rxjs";
import {
PermItemDTO,
UserDTO,
UserRoleDTO,
GlobalEnum,
OrgDTO,
ClientAccountDTO,
AdminAccountDTO,
OrgConfigDTO,
} from "@cdk/public-api";
export const PROJECT_NAME = new InjectionToken<string>("projectName");
@Injectable({
providedIn: "root",
})
export class ApiService {
constructor(private http: HttpClient, @Inject(PROJECT_NAME) public project: string) {
this.initAccount();
}
public account!: ClientAccountDTO;
public adminAccount!: AdminAccountDTO;
public accountKey = this.project === "admin" ? "CATERING_ADMIN_ACCOUNT" : "CATERING_CLIENT_ACCOUNT";
globalEnum!: GlobalEnum;
page(v: {}, q: {}) {
return this.http.get<any>("https://jsonplaceholder.typicode.com/users", v).pipe(
map((r) => {
return {
total: 10,
content: r,
};
})
);
}
initAccount() {
try {
const stragedAccount = localStorage.getItem(this.accountKey);
if (stragedAccount) {
if (this.project === "admin") {
this.adminAccount = JSON.parse(stragedAccount);
} else {
this.account = JSON.parse(stragedAccount);
}
}
} catch (error) {
console.error("获取用户信息失败", error);
}
}
getAllEnum(force?: boolean): Observable<GlobalEnum> {
if (this.globalEnum && !force) {
return of(this.globalEnum);
}
return this.http.get<ResponseType<GlobalEnum>>("/api/basic/enum").pipe(
map((res) => {
return res.body;
}),
tap((r) => {
this.globalEnum = r;
})
);
}
login(v: {}) {
const params = Utils.objectToHttpParams(v);
return this.http.get<ResponseType>("/api/login", { params }).pipe(
tap((res) => {
localStorage.setItem(this.accountKey, JSON.stringify(res.body));
this.initAccount();
})
);
}
logout() {
return this.http.get<ResponseType>("/api/logout").pipe(
tap((res) => {
if (res.success) {
localStorage.removeItem(this.accountKey);
}
})
);
}
getOrgConfig() {
return this.http.get<ResponseType<OrgConfigDTO>>("/api/vender/config");
}
saveOrgConfig(config: AnyObject) {
const body = Utils.objectToFormData(config);
return this.http.post<ResponseType>("/api/vender/config", body);
}
updatePassword(v: {}) {
const body = Utils.objectToFormData(v);
return this.http.post<ResponseType>("/api/basic/user", body);
}
getRoleList() {
return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role");
}
deleteRole(roleId: string) {
const params = new HttpParams().set("roleId", roleId);
return this.http.delete<ResponseType>("/api/role", {
params,
});
}
updateRole(rule: AnyObject) {
const body = Utils.objectToFormData(rule);
const method = rule["roleId"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/role", body);
}
getRolePerms() {
return this.http.get<ResponseType<PermItemDTO[]>>("/api/role/item");
}
getUserList() {
return this.http.get<ResponseType<UserDTO[]>>("/api/user");
}
checkUid(uid: string) {
const params = new HttpParams().set("uid", uid);
return this.http.delete<ResponseType>("/api/user/check", {
params,
});
}
saveUser(user: AnyObject, edit: boolean) {
const body = Utils.objectToFormData(user);
const method = edit ? "post" : "put";
return this.http[method]<ResponseType>("/api/user", body);
}
deleteUser(uid: string) {
const params = new HttpParams().set("uid", uid);
return this.http.delete<ResponseType>("/api/user", {
params,
});
}
getOrgList() {
return this.http.get<ResponseType<OrgDTO[]>>("/api/vender");
}
checkOrgAccount(account: string) {
const params = new HttpParams().set("account", account);
return this.http.get<ResponseType<boolean>>("/api/vender/check/account", {
params,
});
}
checkOrgName(name: string) {
const params = new HttpParams().set("name", name);
return this.http.get<ResponseType<boolean>>("/api/vender/check/name", {
params,
});
}
deleteOrg(id: number) {
const params = new HttpParams().set("vender", id);
return this.http.delete<ResponseType>("/api/vender", {
params,
});
}
saveOrg(org: AnyObject) {
const body = Utils.objectToFormData(org);
const method = org["id"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/vender", body);
}
// getOrgConfig() {
// return this.http.get<ResponseType>("/api/vender/config");
// }
getFoodPage(p: {}, q: {}) {
const params = Utils.objectStringify({ ...p, ...q });
return this.http.get<ResponseType<PageResult>>(`/api/ingredient?${params}`).pipe(
map((r) => {
if (Array.isArray(r.body.content)) {
r.body.content = r.body.content.map((o) => {
return {
...o,
nutrientArr: Object.entries(o.nutrient).map(([k, v]) => {
const nutrient = this.globalEnum.nutrient.find((f) => f.key === k);
return {
key: k,
value: v,
label: nutrient?.value,
measurement: nutrient?.measurement,
};
}),
};
});
}
return r;
})
);
}
saveFood(food: AnyObject) {
const body = Utils.objectToFormData(food);
const method = food["id"] ? "post" : "put";
return this.http[method]<ResponseType>("/api/ingredient", body);
}
deleteFoods(ids: number[]) {
const params = Utils.objectStringify({ nutrients: ids });
console.log("params", params, { nutrients: ids });
return this.http.delete<ResponseType>(`/api/ingredient?${params}`);
}
importFood(f: FormData) {
return this.http.put<ResponseType>("/api/ingredient/mark", f);
}
markFood(mark: string, nutrient: string) {
const params = Utils.objectToFormData({ mark, nutrient }, false);
return this.http.put<ResponseType>("/api/ingredient/mark", params);
}
}

1
projects/cdk/src/services/index.ts

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

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

@ -59,7 +59,7 @@ export class TableListOption {
withOutDefaultColumns?: boolean;
pageFromZero?: boolean;
pageFromZero?: boolean = true;
theadSettable: boolean = false;
@ -85,7 +85,7 @@ export class TableListOption {
this.withOutDefaultColumns = config?.withOutDefaultColumns;
this.theadSettable = config?.theadSettable ?? false;
this.columnKey = config?.columnKey;
this.pageFromZero = config?.pageFromZero;
this.pageFromZero = config?.pageFromZero ?? true;
this.frontPagination = config?.frontPagination ?? true;
}

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

@ -36,6 +36,7 @@
<ng-template #renderTableTpl>
<div class="table-card table-list shadow-sm ">
<div #tableEl class="overflow-auto">
<nz-table
#basicTable
@ -45,7 +46,7 @@
[(nzPageSize)]="props.pager.size"
[(nzPageIndex)]="props.pager.page"
[nzTotal]="props.pager.total"
[nzShowTotal]="totalTpl"
[nzFrontPagination]="props.frontPagination"
[nzShowPagination]="true"
[nzShowSizeChanger]="true"

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

@ -27,7 +27,7 @@ import { debounceTime, finalize } from "rxjs/operators";
import { FormsModule, FormControl, FormGroup, AbstractControl } from "@angular/forms";
import { NzDrawerModule, NzDrawerService } from "ng-zorro-antd/drawer";
import { DecSafeAny, PageResult, TableListColumns, TableOperation } from "../../types";
import { DecSafeAny, PageResult, ResponseType, TableListColumns, TableOperation } from "../../types";
import { TableListOption } from "../table-list-options";
import { CacheItem, CacheService, StorageService } from "../../storage";
import { TableOperationComponent } from "../table-operation/table-operation.component";
@ -124,14 +124,13 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
}
ngOnInit(): void {
console.log("this.formGroup", this.formGroup);
this.props.trigger$.pipe(debounceTime(100)).subscribe((e?: any) => {
if (this.props.fetchData) {
this.props.pager.loading = true;
this.selection.clear();
this.emitState();
const query = this.formGroup.getRawValue();
console.log("query", query);
// console.log("query", query);
// this.saveQueryDataToCache(query);
const pager = this.parsePager();
this.props
@ -141,11 +140,10 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
this.props.pager.loading = false;
})
)
.subscribe((f: PageResult) => {
this.dataSource = f.records ?? f.content;
console.log("this.dataSource", f);
this.props.pager.total = f.total;
this.totalPages = Math.ceil(f.total / this.props.pager.size);
.subscribe((f: ResponseType<PageResult>) => {
this.dataSource = f.body.content;
this.props.pager.total = f.body.totalElements;
this.totalPages = Math.ceil(this.props.pager.total / this.props.pager.size);
this.checkPage();
});
}
@ -234,7 +232,7 @@ export class TableListComponent implements OnInit, OnChanges, AfterViewInit, OnD
if (this.props.pageFromZero) {
page = page - 1;
}
const pager = { page: page, current: page, size, sort: this.formatSort() };
const pager = { pageNo: page, pageSize: size, sort: this.formatSort() };
return pager;
}

3
projects/cdk/src/types/index.ts

@ -39,7 +39,8 @@ export interface TableOperation {
export interface PageResult<T = DecSafeAny> {
total: number;
content: T[];
records: T[];
totalElements: number;
totalPages: number;
}
export interface AuthInterface {

34
projects/cdk/src/utils/index.ts

@ -1,6 +1,7 @@
import { HttpParams } from "@angular/common/http";
import { AbstractControl, FormControl, FormGroup } from "@angular/forms";
import { AnyObject } from "@cdk/types";
import queryString from "query-string";
export class Utils {
static validateFormGroup(formGroup: FormGroup) {
@ -60,13 +61,18 @@ export class Utils {
return params;
}
static objectToFormData(obj: AnyObject): FormData {
static objectToFormData(obj: AnyObject, skipEmpty: boolean = true): FormData {
let params = new FormData();
for (const key in obj) {
if (obj.hasOwnProperty(key)) {
const value = obj[key];
if (![void 0, null, ""].includes(value)) {
let value = obj[key];
if (skipEmpty && [void 0, null, ""].includes(value)) {
break;
}
if (typeof value === "object") {
params.append(key, JSON.stringify(value));
} else {
params.append(key, value);
}
}
@ -75,7 +81,23 @@ export class Utils {
return params;
}
// static detectionImagePath(jobId: string, imgName: string) {
// return `/record/${jobId}/detect/${imgName}`;
// }
static buildFormData = (formData: FormData, data: any, parentKey = "") => {
if (data && typeof data === "object" && !(data instanceof Date) && !(data instanceof File)) {
Object.keys(data).forEach((key) => {
Utils.buildFormData(formData, data[key], parentKey ? `${parentKey}[${key}]` : key);
});
} else {
const value = data == null ? "" : data;
formData.append(parentKey, value);
}
};
static objectStringify(o: AnyObject) {
return queryString.stringify(o, {
skipEmptyString: true,
skipNull: true,
arrayFormat: "comma",
});
}
}

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

@ -8,6 +8,7 @@ import {
LoginComponent,
MealSettingComponent,
OrgInfoComponent,
ClientUserManageComponent,
} from "./pages";
import { AppLayoutComponent } from "./components";
import { authGuard } from "./services/auth.guard";
@ -51,6 +52,10 @@ const routes: Routes = [
path: "org",
component: OrgInfoComponent,
},
{
path: "user",
component: ClientUserManageComponent,
},
],
},
],

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

@ -11,9 +11,9 @@ import { BrowserAnimationsModule } from "@angular/platform-browser/animations";
import { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component";
import { IconsProviderModule, TableListModule } from "@cdk/public-api";
import { IconsProviderModule, PROJECT_NAME, TableListModule } from "@cdk/public-api";
import { SharedModule } from "@cdk/shared/shared.module";
import { AppLayoutComponent, OrgFormComponent } from "./components";
import { AppLayoutComponent, OrgFormComponent, UserListComponent, RolePermissionComponent } from "./components";
import {
DashboardComponent,
LoginComponent,
@ -22,6 +22,7 @@ import {
FoodComponent,
DishComponent,
OrgInfoComponent,
ClientUserManageComponent,
} from "./pages";
import { HTTPInterceptor } from "./services/http.interceptor";
@ -32,6 +33,8 @@ registerLocaleData(zh);
AppComponent,
AppLayoutComponent,
OrgFormComponent,
UserListComponent,
RolePermissionComponent,
DashboardComponent,
LoginComponent,
@ -40,6 +43,7 @@ registerLocaleData(zh);
FoodComponent,
DishComponent,
OrgInfoComponent,
ClientUserManageComponent,
],
imports: [
BrowserModule,
@ -52,6 +56,7 @@ registerLocaleData(zh);
TableListModule,
],
providers: [
{ provide: PROJECT_NAME, useValue: "client" },
{ provide: NZ_I18N, useValue: zh_CN },
{ provide: HTTP_INTERCEPTORS, useClass: HTTPInterceptor, multi: true },
],

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

@ -14,7 +14,7 @@
nz-dropdown
[nzDropdownMenu]="menu"
class="text-white hover:text-white focus:text-white">
admin
{{account.name}}
</button>
<nz-dropdown-menu #menu="nzDropdownMenu">
<ul nz-menu nzSelectable>

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

@ -1,22 +1,22 @@
import { Component } from "@angular/core";
import { Component, OnInit } from "@angular/core";
import { NavigationEnd, Router, RouterModule } from "@angular/router";
import { NzMessageService } from "ng-zorro-antd/message";
import { NzModalService } from "ng-zorro-antd/modal";
import { Subject, filter, lastValueFrom, takeUntil } from "rxjs";
import { ClientApiService } from "../../services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-layout",
templateUrl: "./app-layout.component.html",
styleUrls: ["./app-layout.component.less"],
})
export class AppLayoutComponent {
export class AppLayoutComponent implements OnInit {
constructor(
private router: Router,
private modal: NzModalService,
private msg: NzMessageService,
private api: ClientApiService
private api: ApiService
) {
this.router.events
.pipe(
@ -28,10 +28,16 @@ export class AppLayoutComponent {
});
}
account = this.api.account;
unSubscribe$ = new Subject<void>();
currentUrl: string = "";
ngOnInit(): void {
console.log("this.account", this.account);
}
logout() {
this.modal.confirm({
nzTitle: "警告",

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

@ -1,2 +1,4 @@
export * from "./app-layout/app-layout.component";
export * from "./org-form/org-form.component";
export * from "./user-list/user-list.component";
export * from "./role-permission/role-permission.component";

2
projects/client/src/app/components/org-form/org-form.component.ts

@ -1,4 +1,4 @@
import { ApiService } from "@admin/app/services";
import { ApiService } from "@cdk/services";
import { Component, inject } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";

25
projects/client/src/app/components/role-permission/role-permission.component.html

@ -0,0 +1,25 @@
<div class="pb-10">
<nz-card *ngFor="let group of permissions | keyvalue : returenZero;" nzType="inner" [nzTitle]="group.key"
class="mb-4">
<div nz-row [nzGutter]="[12,12]">
<div nz-col nzSpan="8" *ngFor="let perm of group.value">
<label nz-checkbox
[nzChecked]="hasPermissions.has(perm.value)"
(nzCheckedChange)="onPermissionChange($event,perm.value)">
{{perm.label}}
</label>
</div>
</div>
</nz-card>
<div class="fixed-footter bg-white py-2 absolute left-0 bottom-0 right-0">
<div class="flex pl-16">
<nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()" [nzLoading]="loading">
保存
</button>
</nz-space>
</div>
</div>
</div>

0
projects/client/src/app/components/role-permission/role-permission.component.less

82
projects/client/src/app/components/role-permission/role-permission.component.ts

@ -0,0 +1,82 @@
import { PermItemDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { AnyObject } from "@cdk/types";
import { NzMessageService } from "ng-zorro-antd/message";
import { finalize } from "rxjs";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-role-permission",
templateUrl: "./role-permission.component.html",
styleUrls: ["./role-permission.component.less"],
})
export class RolePermissionComponent implements OnChanges {
constructor(private api: ApiService, private route: ActivatedRoute, private msg: NzMessageService) {}
@Input() perms: PermItemDTO[] = [];
@Input() role!: UserRoleDTO;
@Output() onReload = new EventEmitter();
loading = false;
ngOnChanges(changes: SimpleChanges): void {
if (changes["perms"]?.currentValue) {
const perms: PermItemDTO[] = changes["perms"].currentValue;
this.permissions = perms.reduce((a, c) => {
const key = c["category"];
const o = { ...c, label: c.itemName, value: c.id };
if (a[key]) {
a[key].push(o);
} else {
a[key] = [o];
}
return a;
}, {} as Record<string, Array<{ label: string; value: number } & AnyObject>>);
}
if (changes["role"]?.currentValue) {
const role: UserRoleDTO = changes["role"].currentValue;
let perms: number[] = role.roleItems ?? [];
this.hasPermissions.clear();
perms.forEach((p) => {
this.hasPermissions.add(p);
});
}
}
permissions = {};
hasPermissions = new Set<number>();
ngOnInit(): void {}
returenZero() {
return 0;
}
onPermissionChange(checked: boolean, key: number) {
if (!checked && this.hasPermissions.has(key)) {
this.hasPermissions.delete(key);
} else {
this.hasPermissions.add(key);
}
}
onSubmit() {
const role = { ...this.role, roleId: this.role.id, items: Array.from(this.hasPermissions) };
this.loading = true;
this.api
.updateRole(role)
.pipe(
finalize(() => {
this.loading = false;
})
)
.subscribe((res) => {
this.msg.success(res.desc);
this.onReload.emit();
});
}
}

86
projects/client/src/app/components/user-list/user-list.component.html

@ -0,0 +1,86 @@
<ng-template #searchTpl>
</ng-template>
<div class="flex items-center justify-between">
<div>
<nz-form-item>
<nz-form-control>
<nz-input-group [nzSuffix]="suffixIconSearch">
<input nz-input placeholder="请输入用户" [(ngModel)]="searchValue" (ngModelChange)="onSearch($event)" />
</nz-input-group>
<ng-template #suffixIconSearch>
<span nz-icon nzType="search"></span>
</ng-template>
</nz-form-control>
</nz-form-item>
</div>
<div>
<nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="openForm()">
<i nz-icon nzType="plus"></i>
新增用户
</button>
</nz-space>
</div>
</div>
<nz-table #table [nzData]="visibledUsers">
<thead>
<tr>
<th>账号</th>
<th>姓名</th>
<th>角色</th>
<th>添加时间</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let data of table.data">
<td>{{data.uid}}</td>
<td>{{data.name}}</td>
<td>{{data.roleName}}</td>
<td>{{data.time | date:'yyyy-MM-dd HH:mm:ss'}}</td>
<td>
<a (click)="openForm(data)">编辑</a>
<nz-divider nzType="vertical"></nz-divider>
<a (click)="deleteItem(data.uid)">删除</a>
</td>
</tr>
</tbody>
</nz-table>
<ng-template #userFormTpl>
<form nz-form [formGroup]="userFrom">
<nz-form-item>
<nz-form-label nzSpan="6" nzRequired>
姓名
</nz-form-label>
<nz-form-control nzSpan="12" [nzErrorTip]="formErrorTipsTpl">
<input nz-input placeholder="请输入用户姓名" formControlName="name" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="6" nzRequired>
账号
</nz-form-label>
<nz-form-control nzSpan="12" [nzErrorTip]="formErrorTipsTpl">
<input nz-input placeholder="请输入用户账号" formControlName="uid" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label nzSpan="6" nzRequired>
密码
</nz-form-label>
<nz-form-control nzSpan="12" [nzErrorTip]="formErrorTipsTpl">
<input nz-input placeholder="请输入用户密码" type="password" formControlName="password" />
</nz-form-control>
</nz-form-item>
</form>
</ng-template>
<ng-template #formErrorTipsTpl let-control>
<form-error-tips [control]="control"></form-error-tips>
</ng-template>

0
projects/client/src/app/components/user-list/user-list.component.less

122
projects/client/src/app/components/user-list/user-list.component.ts

@ -0,0 +1,122 @@
import { UserDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import {
Component,
EventEmitter,
Input,
OnChanges,
OnInit,
Output,
SimpleChanges,
TemplateRef,
ViewChild,
} from "@angular/core";
import { FormControl, FormGroup } from "@angular/forms";
import { ActivatedRoute } from "@angular/router";
import { AnyObject, FormValidators, TableListOption, Utils } from "@cdk/public-api";
import { MD5 } from "crypto-js";
import { NzMessageService } from "ng-zorro-antd/message";
import { NzModalService } from "ng-zorro-antd/modal";
import { lastValueFrom } from "rxjs";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-user-list",
templateUrl: "./user-list.component.html",
styleUrls: ["./user-list.component.less"],
})
export class UserListComponent implements OnChanges {
constructor(
private api: ApiService,
private route: ActivatedRoute,
private modal: NzModalService,
private msg: NzMessageService
) {}
@ViewChild("userFormTpl") public userFormTpl!: TemplateRef<void>;
@Input() users: UserDTO[] = [];
@Input() role!: UserRoleDTO;
@Output() onReload = new EventEmitter();
searchValue = "";
visibledUsers: UserDTO[] = [];
public userFrom = new FormGroup({
uid: new FormControl("", [FormValidators.required("请输入账号")]),
name: new FormControl("", [FormValidators.required("请输入姓名")]),
password: new FormControl("", [FormValidators.required("请输入密码")]),
});
ngOnInit(): void {}
ngOnChanges(changes: SimpleChanges): void {
const u = changes["users"]?.currentValue;
if (Array.isArray(u)) {
this.onSearch(this.searchValue);
}
}
onSearch(kw: string) {
this.visibledUsers = this.users.filter((f) => f.name.includes(kw) || f.uid.includes(kw));
}
deleteItem(uid: string) {
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该用户?",
nzOkDanger: true,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteUser(uid));
if (res.success) {
this.msg.success(res.desc);
this.onReload.emit();
return true;
}
return false;
},
});
}
resetUserForm() {
this.userFrom.reset();
}
openForm(item?: any) {
if (item) {
this.userFrom.patchValue(item);
}
this.modal.create({
nzTitle: item ? "编辑用户" : "新增用户",
nzContent: this.userFormTpl,
nzOnCancel: () => {
this.resetUserForm();
},
nzOnOk: async () => {
if (Utils.validateFormGroup(this.userFrom)) {
const user = { ...this.userFrom.value, roleId: this.role.id };
user["password"] = MD5(user.password!).toString().substring(16).toUpperCase();
let notExist = true;
if ((item && item.uid !== user.uid) || !item) {
const check = await lastValueFrom(this.api.checkUid(user.uid!));
notExist = check.body;
}
if (notExist) {
const res = await lastValueFrom(this.api.saveUser(user, Boolean(item)));
this.msg.success(res.desc);
this.onReload.emit();
this.resetUserForm();
return true;
} else {
this.msg.error("账号重复");
return false;
}
}
return false;
},
});
}
}

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

@ -4,7 +4,7 @@ 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";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-dish",
@ -12,7 +12,7 @@ import { ClientApiService } from "../../services";
styleUrls: ["./dish.component.less"],
})
export class DishComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ClientApiService, private modal: NzModalService) {}
constructor(private drawer: NzDrawerService, private api: ApiService, private modal: NzModalService) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;

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

@ -2,26 +2,18 @@
<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'}">
<nz-card class="h-full" [nzBordered]="false" nzTitle="食材类型"
[nzBodyStyle]="{padding:'1px 0 0 0', height:'100%',overflow:'auto'}">
<ul nz-menu nzMode="inline">
<li nz-menu-item>
<li nz-menu-item (click)="onTypeChange('')" [nzSelected]="!queryForm.get('type')?.value">
<a>
全部
</a>
</li>
<li nz-menu-item>
<li nz-menu-item [nzSelected]="cate.key === queryForm.get('type')?.value"
*ngFor="let cate of globalEnum.category" (click)="onTypeChange(cate.key)">
<a>
谷薯类
</a>
</li>
<li nz-menu-item>
<a>
大豆类及其制品
</a>
</li>
<li nz-menu-item>
<a>
蔬菜类
{{cate.key}}
</a>
</li>
</ul>
@ -33,44 +25,51 @@
<div class="m-4">
<table-list
[props]="tableList"
[formGroup]="queryForm"
[search]="searchTpl"
[formGroup]="queryForm"
[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 nzButtonStyle="solid" [(ngModel)]="mark"
(ngModelChange)="onMarkChange()"
[ngModelOptions]="{standalone: true}">
<label nz-radio-button nzValue="">全部</label>
<label nz-radio-button nzValue="常用">常用</label>
<label nz-radio-button nzValue="忌用">忌用</label>
<label nz-radio-button nzValue="其他">其他</label>
</nz-radio-group>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-control>
<input nz-input placeholder="请输入食材名称/编号" formControlName="name" />
<input nz-input placeholder="请输入食材名称/编号" formControlName="keyword" />
</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'">
<ng-container *ngSwitchCase="'nutrientArr'">
<a nz-popover
[nzPopoverContent]="popoverTpl"
nzPopoverTitle="营养素">
<b>10</b>中营养素
<b>{{data.length}}</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 *ngFor="let item of data" class="m-1">
{{item.label}}:{{item.value}}{{item.measurement}}
</nz-tag>
</div>
</ng-template>
</ng-container>
<ng-container *ngSwitchCase="'mark'">
<nz-tag *ngIf="data === '常用'" nzColor="success">{{data}}</nz-tag>
<nz-tag *ngIf="data === '忌用'" nzColor="error">{{data}}</nz-tag>
</ng-container>
<ng-container *ngSwitchDefault>
{{data}}

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

@ -3,8 +3,9 @@ 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";
import { Subject, lastValueFrom, takeUntil } from "rxjs";
import { ApiService } from "@cdk/services";
import { NzMessageService } from "ng-zorro-antd/message";
@Component({
selector: "app-food",
@ -12,24 +13,33 @@ import { ClientApiService } from "../../services";
styleUrls: ["./food.component.less"],
})
export class FoodComponent implements OnInit, OnDestroy {
constructor(private drawer: NzDrawerService, private api: ClientApiService, private modal: NzModalService) {}
@ViewChild("foofFormFooterTpl") foofFormFooterTpl!: TemplateRef<{}>;
constructor(
private drawer: NzDrawerService,
private api: ApiService,
private modal: NzModalService,
private msg: NzMessageService
) {}
public tableList = new TableListOption(this.fetchData.bind(this), {
selectable: true,
frontPagination: false,
rowKey: "key",
});
public globalEnum = this.api.globalEnum;
public queryForm = new FormGroup({
type: new FormControl({ value: "A", disabled: false }),
name: new FormControl("addd"),
keyword: new FormControl(""),
type: new FormControl(""),
});
public mark = "";
private destroy$ = new Subject<void>();
public selectedIds: string[] = [];
temp = Array.from({ length: 50 }, (_, i) => i);
submitLoading = false;
ngOnInit(): void {
this.initTableList();
@ -46,38 +56,73 @@ export class FoodComponent implements OnInit, OnDestroy {
initTableList() {
this.tableList.scroll = { x: null };
this.tableList = this.tableList.setColumns([
{ key: "name", title: "食材编号" },
{ key: "key", title: "食材编号" },
{ key: "name", title: "食材名称" },
{ key: "name", title: "食材类型" },
{ key: "name1", title: "营养素(每100g可食部)" },
{ key: "name", title: "更新日期" },
{ key: "type", title: "食材类型" },
{ key: "mark", title: "标签" },
{ key: "nutrientArr", title: "营养素(每100g可食部)" },
]);
this.tableList = this.tableList.setOptions([
{
title: "常用",
premissions: [],
onClick: this.deleteFood.bind(this),
onClick: this.markFood.bind(this, "常用", "设为常用"),
visible(v) {
return v.mark !== "常用";
},
},
{
title: "取消常用",
premissions: [],
onClick: this.markFood.bind(this, "", "取消常用"),
visible(v) {
return v.mark === "常用";
},
},
{
title: "忌用",
premissions: [],
onClick: this.deleteFood.bind(this),
onClick: this.markFood.bind(this, "忌用", "设为忌用"),
visible(v) {
return v.mark !== "忌用";
},
},
{
title: "取消忌用",
premissions: [],
onClick: this.markFood.bind(this, "", "取消忌用"),
visible(v) {
return v.mark === "忌用";
},
},
]);
}
fetchData(pager: AnyObject, query: AnyObject) {
return this.api.page(pager, query);
fetchData(query: AnyObject, pager: AnyObject) {
query["mark"] = this.mark;
return this.api.getFoodPage(pager, query).pipe();
}
onTypeChange(type: string) {
this.queryForm.patchValue({ type });
this.tableList.reset();
}
deleteFood(v?: any) {
const ids = v ? [v.id] : this.selectedIds;
onMarkChange() {
this.tableList.reset();
}
markFood(mark: string, text: string, v: any) {
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该食材?",
nzContent: `是否要将食材该食材${text}`,
nzOkDanger: true,
nzOnOk: () => {},
nzOnOk: async () => {
const res = await lastValueFrom(this.api.markFood(mark, v.key));
this.msg.success(res.desc);
this.tableList.run();
},
});
}
}

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

@ -6,3 +6,4 @@ export * from "./food/food.component";
export * from "./dish/dish.component";
export * from "./system/org-info/org-info.component";
export * from "./system/user-manage/user-manage.component";

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

@ -6,7 +6,7 @@ import { FormValidators } from "projects/cdk/src/public-api";
import { Utils } from "projects/cdk/src/utils";
import { finalize, lastValueFrom } from "rxjs";
import { MD5 } from "crypto-js";
import { ClientApiService } from "../../services";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-login",
@ -14,7 +14,7 @@ import { ClientApiService } from "../../services";
styleUrls: ["./login.component.less"],
})
export class LoginComponent {
constructor(private msg: NzMessageService, private api: ClientApiService, private router: Router) {}
constructor(private msg: NzMessageService, private api: ApiService, private router: Router) {}
public loginForm = new FormGroup({
uid: new FormControl("", [FormValidators.required("请输入账户")]),
@ -38,7 +38,6 @@ export class LoginComponent {
})
)
);
localStorage.setItem(this.api.accountKey, JSON.stringify(res.body));
this.msg.success(res.desc);
this.router.navigate(["/"]);
}

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

@ -34,10 +34,9 @@
<nz-form-item>
<nz-form-control>
<nz-space>
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()">
<button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()" [nzLoading]="loading">
保存
</button>
</nz-space>
</nz-form-control>
</nz-form-item>

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

@ -1,7 +1,9 @@
import { Component, OnInit } from "@angular/core";
import { ClientApiService } from "../../services";
import { ApiService } from "@cdk/services";
import { FormControl, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";
import { NzMessageService } from "ng-zorro-antd/message";
import { finalize } from "rxjs";
@Component({
selector: "app-meal-setting",
@ -9,10 +11,12 @@ import { Utils } from "@cdk/utils";
styleUrls: ["./meal-setting.component.less"],
})
export class MealSettingComponent implements OnInit {
constructor(private api: ClientApiService) {}
constructor(private api: ApiService, private msg: NzMessageService) {}
account = this.api.account;
loading = false;
formGroup = new FormGroup({
breakfast: new FormControl(0, []),
dinner: new FormControl(0, []),
@ -27,9 +31,17 @@ export class MealSettingComponent implements OnInit {
onSubmit() {
if (Utils.validateFormGroup(this.formGroup)) {
this.api.saveOrgConfig({ ...this.formGroup.value, vender: this.account.vender.id }).subscribe((res) => {
// this.formGroup.
});
this.loading = true;
this.api
.saveOrgConfig({ ...this.formGroup.value, vender: this.account.vender.id })
.pipe(
finalize(() => {
this.loading = false;
})
)
.subscribe((res) => {
this.msg.success(res.desc);
});
}
}
}

4
projects/client/src/app/pages/system/org-info/org-info.component.ts

@ -1,7 +1,7 @@
import { Component, OnInit, TemplateRef } from "@angular/core";
import { NzModalService } from "ng-zorro-antd/modal";
import { format } from "date-fns";
import { ClientApiService } from "../../../services";
import { ApiService } from "@cdk/services";
import { FormControl, FormGroup } from "@angular/forms";
import { FormValidators } from "@cdk/validators";
import { Utils } from "@cdk/utils";
@ -16,7 +16,7 @@ import { MD5 } from "crypto-js";
styleUrls: ["./org-info.component.less"],
})
export class OrgInfoComponent implements OnInit {
constructor(private api: ClientApiService, private modal: NzModalService, private msg: NzMessageService) {}
constructor(private api: ApiService, private modal: NzModalService, private msg: NzMessageService) {}
account: any = this.api.account;

92
projects/client/src/app/pages/system/user-manage/user-manage.component.html

@ -0,0 +1,92 @@
<app-page [scroll]="false">
<div nz-row class="h-full overflow-hidden bg-white rounded-lg">
<div nz-col nzFlex="220px" class="user-type">
<nz-card
class="h-full "
[nzBordered]="false"
nzTitle="用户角色"
[nzBodyStyle]="{padding:'1px 0 0 0'}">
<ul nz-menu nzMode="inline">
<li
*ngFor="let item of roleList"
nz-menu-item
(click)="onRoleChange(item.id)"
[nzSelected]="role?.id === item.id"
class="flex items-center justify-between role-item">
<span>
{{item.roleName}}
</span>
<nz-space nzSize="small" class="options">
<a *nzSpaceItem nz-button nzType="link" nzSize="small"
(click)="openForm(roleFormTpl,$event,item)">
<span nz-icon nzType="edit"></span>
</a>
<a *nzSpaceItem nz-button nzType="link" nzSize="small"
(click)="deleteItem($event,item.id)">
<span nz-icon nzType="delete"></span>
</a>
</nz-space>
</li>
</ul>
<div class="px-4 py-2">
<button nz-button nzType="dashed" nzBlock (click)="openForm(roleFormTpl,$event)">
<i nz-icon nzType="plus"></i>
<span>
新增角色
</span>
</button>
</div>
</nz-card>
</div>
<div nz-col nzFlex="1" class="flex-1 h-full overflow-hidden bg-white ">
<nz-card [nzBordered]="false"
class="h-full scroll-card-body ">
<nz-card-tab>
<nz-tabset nzSize="large" [nzSelectedIndex]="tab" (nzSelectedIndexChange)="onTabChange($event)">
<nz-tab nzTitle="用户列表"></nz-tab>
<nz-tab nzTitle="权限管理"></nz-tab>
</nz-tabset>
</nz-card-tab>
<div class="p-4">
<app-user-list
*ngIf="role && tab.toString() === '0'"
[users]="currentUserList"
[role]="role"
(onReload)="reloadUserList()">
</app-user-list>
<app-role-permission
*ngIf="role && tab.toString() === '1'"
[perms]="allPerms"
[role]="role"
(onReload)="getRoleList()">
</app-role-permission>
</div>
</nz-card>
</div>
</div>
</app-page>
<ng-template #roleFormTpl>
<form nz-form [formGroup]="roleForm">
<nz-form-item>
<nz-form-label nzSpan="6" nzRequired>
角色名称
</nz-form-label>
<nz-form-control nzSpan="12" [nzErrorTip]="formErrorTipsTpl">
<input nz-input placeholder="请输入角色名称" formControlName="roleName" />
</nz-form-control>
</nz-form-item>
</form>
</ng-template>
<ng-template #formErrorTipsTpl let-control>
<div class="text-left">
<form-error-tips [control]="control"></form-error-tips>
</div>
</ng-template>

29
projects/client/src/app/pages/system/user-manage/user-manage.component.less

@ -0,0 +1,29 @@
.user-type {
border-right: 1px solid #e8e8e8;
::ng-deep {
.ant-menu-inline {
border-right: none;
}
}
}
.role-head {
::ng-deep {
.ant-card-extra {
padding: 10px 0;
}
}
}
.role-item {
.options {
display: none;
}
&:hover {
.options {
display: flex;
}
}
}

147
projects/client/src/app/pages/system/user-manage/user-manage.component.ts

@ -0,0 +1,147 @@
import { PermItemDTO, UserDTO, UserRoleDTO } from "@cdk/dtos/user.dto";
import { Component, OnChanges, OnInit, SimpleChanges, TemplateRef, ViewChild } from "@angular/core";
import { FormControl, FormGroup, FormGroupName } from "@angular/forms";
import { ActivatedRoute, Router } from "@angular/router";
import { AnyObject, FormValidators, TableListOption, Utils } from "@cdk/public-api";
import { NzDrawerRef, NzDrawerService } from "ng-zorro-antd/drawer";
import { NzMessageService } from "ng-zorro-antd/message";
import { NzModalService } from "ng-zorro-antd/modal";
import { lastValueFrom } from "rxjs";
import { ApiService } from "@cdk/services";
@Component({
selector: "app-client-user-manage",
templateUrl: "./user-manage.component.html",
styleUrls: ["./user-manage.component.less"],
})
export class ClientUserManageComponent {
constructor(
private route: ActivatedRoute,
private api: ApiService,
private router: Router,
private modal: NzModalService,
private msg: NzMessageService
) {}
public tab = 0;
public role: UserRoleDTO | null = null;
public roleForm = new FormGroup({
roleId: new FormControl(""),
roleName: new FormControl("", [FormValidators.required("请输入角色名称")]),
items: new FormControl([]),
});
public allPerms: PermItemDTO[] = [];
public userList: UserDTO[] = [];
public currentUserList: UserDTO[] = [];
roleList: UserRoleDTO[] = [];
ngOnInit(): void {
this.tab = Number(this.route.snapshot.queryParamMap.get("tab")) || 0;
this.api.getRolePerms().subscribe((res) => {
this.allPerms = res.body;
});
this.getRoleList();
this.getUserList();
}
getUserList(reload?: boolean) {
this.api.getUserList().subscribe((res) => {
this.userList = res.body;
this.currentUserList = this.userList.filter((f) => Number(f.roleId) === Number(this.role!.id));
});
}
reloadUserList() {
this.getUserList(true);
}
getRoleList() {
this.api.getRoleList().subscribe((res) => {
this.roleList = res.body.map((i) => ({ ...i, id: i.id.toString() }));
const roleId = this.route.snapshot.queryParamMap.get("roleId");
const role = this.roleList.find((f) => f.id === roleId);
if (role) {
this.role = role;
this.onRoleChange(role.id);
return;
}
if (this.roleList.length > 0) {
this.onRoleChange(this.roleList[0].id);
}
});
}
onRoleChange(roleId: string) {
const role = this.roleList.find((f) => f.id === roleId);
if (role) {
this.router
.navigate(["/system/user"], {
queryParams: {
roleId,
},
queryParamsHandling: "merge",
})
.then(() => {
this.role = role;
this.currentUserList = this.userList.filter((f) => Number(f.roleId) === Number(role.id));
});
}
}
onTabChange(index: number) {
this.tab = index;
this.router.navigate(["/system/user"], {
queryParams: {
tab: index,
},
queryParamsHandling: "merge",
});
}
openForm(nzContent: TemplateRef<{}>, event: MouseEvent, role?: UserRoleDTO) {
event.stopPropagation();
if (role) {
this.roleForm.patchValue({
...role,
roleId: role.id,
});
}
this.modal.create({
nzTitle: role ? "编辑角色" : "新增角色",
nzContent,
nzOnCancel: () => {
this.roleForm.reset();
},
nzOnOk: async () => {
if (Utils.validateFormGroup(this.roleForm)) {
const res = await lastValueFrom(this.api.updateRole(this.roleForm.value));
this.msg.success(res.desc);
this.roleForm.reset();
this.getRoleList();
return true;
}
return false;
},
});
}
deleteItem(event: MouseEvent, id: string) {
event.stopPropagation();
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要删除该角色?",
nzOkDanger: true,
nzOnOk: async () => {
const res = await lastValueFrom(this.api.deleteRole(id));
this.msg.success(res.desc);
this.getRoleList();
},
});
}
}

5
projects/client/src/app/services/auth.guard.ts

@ -1,11 +1,10 @@
import { inject } from "@angular/core";
import { Router } from "@angular/router";
import { map } from "rxjs";
import { ClientApiService } from "./client-api.service";
import { ApiService } from "@cdk/services";
export const authGuard = () => {
const router = inject(Router);
const api = inject(ClientApiService);
const api = inject(ApiService);
const stragedAccount = localStorage.getItem(api.accountKey);
if (!stragedAccount) {

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

@ -16,7 +16,7 @@ import { Observable, map, of, tap } from "rxjs";
@Injectable({
providedIn: "root",
})
export class ClientApiService {
export class ClientApiServicde {
constructor(private http: HttpClient) {
try {
const strageAccount = localStorage.getItem(this.accountKey);
@ -30,7 +30,7 @@ export class ClientApiService {
public account!: ClientAccountDTO;
public accountKey = "CATERING_CLIENT_ACCOUNT";
public accountKey = "CATERING_ACCOUNT";
globalEnum!: GlobalEnum;
@ -68,11 +68,6 @@ export class ClientApiService {
return this.http.get<ResponseType>("/api/logout");
}
updatePassword(v: {}) {
const body = Utils.objectToFormData(v);
return this.http.post<ResponseType>("/api/basic/user", body);
}
getRoleList() {
return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role");
}

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

@ -1 +1 @@
export * from "./client-api.service";
export * from "./auth.guard";

Loading…
Cancel
Save