Browse Source

业务端 基本信息

main
kkerwin 2 years ago
parent
commit
4eee651d36
  1. 172
      doc/ingredient.md
  2. 2
      projects/admin/src/app/pages/organization/organization-form/organization-form.component.ts
  3. 31
      projects/cdk/src/dtos/user.dto.ts
  4. 2
      projects/client/src/app/app-routing.module.ts
  5. 4
      projects/client/src/app/app.module.ts
  6. 1
      projects/client/src/app/components/index.ts
  7. 69
      projects/client/src/app/components/org-form/org-form.component.html
  8. 0
      projects/client/src/app/components/org-form/org-form.component.less
  9. 88
      projects/client/src/app/components/org-form/org-form.component.ts
  10. 14
      projects/client/src/app/pages/meal-setting/meal-setting.component.html
  11. 35
      projects/client/src/app/pages/meal-setting/meal-setting.component.ts
  12. 2
      projects/client/src/app/pages/system/org-info/org-info.component.html
  13. 38
      projects/client/src/app/pages/system/org-info/org-info.component.ts
  14. 17
      projects/client/src/app/services/auth.guard.ts
  15. 62
      projects/client/src/app/services/client-api.service.ts

172
doc/ingredient.md

@ -0,0 +1,172 @@
# 食材部分
# 1. 查询食材
> GET /api/ingredient
### 输入:
```
pageSize=20 // 默认20, 全部非必填
pageNo=0 // 默认0, 从0开始
type=谷薯类 // 食材类型
mark=常用 // 食材标记. 业务端用标记,管理端没用
keyword=01 // 查询关键字
```
### 输出:
```
{
"body": {
"content": [
{
"key": "011101",
"mark": "常用",
"name": "小麦",
"nutrient": {
"fat": 10,
"energy": 10,
"calcium": 12,
"protein": 15,
"vitamin-a": 23
},
"time": 1693759354000,
"type": "谷薯类"
}
],
"number": 0,
"size": 20,
"totalElements": 1,
"totalPages": 1
},
"code": 200,
"desc": "成功",
"success": true
}
```
# 2. 添加食材(管理端接口)
> PUT /api/ingredient
### 输入:
```
Content-Type:application/x-www-form-urlencoded
key=010101 // 必填
name=测试食材 // 必填
type=粗粮 // 必填 全部必填 取值范围见(/api/basic/enum) category
nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23}
// 必填 取值范围见(/api/basic/enum) nutrient
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 3. 修改食材(管理端接口)
> POST /api/ingredient
### 输入:
```
Content-Type:application/x-www-form-urlencoded
key=010101 // 必填
name=测试食材
type=粗粮
nutrient={"fat": 10, "energy": 10, "calcium": 12, "protein": 15, "vitamin-a": 23}
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 4. 删除食材(管理端接口)
> DELETE /api/ingredient
### 输入:
```
Content-Type:application/x-www-form-urlencoded
nutrients=010101,020202,030303 // 必填
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 5. 食材打标(业务端接口)
> PUT /api/ingredient/mark
### 输入:
```
Content-Type:application/x-www-form-urlencoded
nutrient=010101
mark=常用 // 必填, 取值: 常用/忌用
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 6. 取消打标(业务端接口)
> DELETE /api/ingredient/mark
### 输入:
```
Content-Type:application/x-www-form-urlencoded
nutrient=010101
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```
# 7. 批量导入(管理端接口)
> PUT /api/ingredient/mark
### 输入:
```
Content-Type: multipart/form-data
files // 必传
```
### 输出:
```
{
"code": 200,
"desc": "成功",
"success": true
}
```

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

@ -49,7 +49,7 @@ export class OrganizationFormComponent {
account: this.fb.control("", [FormValidators.required("账号不能为空")]), account: this.fb.control("", [FormValidators.required("账号不能为空")]),
password: this.fb.control("", [FormValidators.required("密码不能为空")]), password: this.fb.control("", [FormValidators.required("密码不能为空")]),
name: this.fb.control("", [FormValidators.required("单位名称不能为空")]), name: this.fb.control("", [FormValidators.required("单位名称不能为空")]),
expire: this.fb.control("", [FormValidators.required("账号到期事件不能为空")]), expire: this.fb.control("", [FormValidators.required("账号到期时间不能为空")]),
icon: this.fb.control("", []), icon: this.fb.control("", []),
address: this.fb.control("", []), address: this.fb.control("", []),
contacts: this.fb.control("", []), contacts: this.fb.control("", []),

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

@ -21,3 +21,34 @@ export type UserDTO = {
uid: string; uid: string;
time: string; time: string;
}; };
export type OrgConfigDTO = {
breakfast: number;
dinner: number;
lunch: number;
};
export type ClientAccountDTO = {
name: string;
roleId: number;
roleItems: PermItemDTO[];
roleName: string;
roleType: string;
time: number;
uid: string;
vender: AccountVenderDTO;
};
export type AccountVenderDTO = {
id: number;
name: string;
account: string;
status: true;
address?: string;
contacts?: string;
email?: string;
expire?: number;
icon?: string;
phone?: string;
};

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

@ -10,12 +10,14 @@ import {
OrgInfoComponent, OrgInfoComponent,
} from "./pages"; } from "./pages";
import { AppLayoutComponent } from "./components"; import { AppLayoutComponent } from "./components";
import { authGuard } from "./services/auth.guard";
const routes: Routes = [ const routes: Routes = [
{ path: "login", component: LoginComponent }, { path: "login", component: LoginComponent },
{ {
path: "", path: "",
component: AppLayoutComponent, component: AppLayoutComponent,
canActivate: [authGuard],
children: [ children: [
{ {
path: "", path: "",

4
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 { AppRoutingModule } from "./app-routing.module";
import { AppComponent } from "./app.component"; import { AppComponent } from "./app.component";
import { AppLayoutComponent } from "./components";
import { IconsProviderModule, TableListModule } from "@cdk/public-api"; import { IconsProviderModule, TableListModule } from "@cdk/public-api";
import { SharedModule } from "@cdk/shared/shared.module"; import { SharedModule } from "@cdk/shared/shared.module";
import { AppLayoutComponent, OrgFormComponent } from "./components";
import { import {
DashboardComponent, DashboardComponent,
LoginComponent, LoginComponent,
@ -31,6 +31,8 @@ registerLocaleData(zh);
declarations: [ declarations: [
AppComponent, AppComponent,
AppLayoutComponent, AppLayoutComponent,
OrgFormComponent,
DashboardComponent, DashboardComponent,
LoginComponent, LoginComponent,
MealSettingComponent, MealSettingComponent,

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

@ -1 +1,2 @@
export * from "./app-layout/app-layout.component"; export * from "./app-layout/app-layout.component";
export * from "./org-form/org-form.component";

69
projects/client/src/app/components/org-form/org-form.component.html

@ -0,0 +1,69 @@
<form nz-form [formGroup]="formGroup" nzLayout="vertical">
<div>
<nz-form-item>
<nz-form-label nzRequired>
单位名称
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input nz-input placeholder="请输入单位名称" formControlName="name" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label>
单位Logo
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<div class="mb-2" *ngIf="icon">
<img [src]="icon" class="h-20 w-20" />
</div>
<button class="vlock upload-btn " nz-button [nzLoading]="uploadLoading">
<i nz-icon nzType="upload"></i>
上传图片
<input type="file" (change)="onFileChange($event)" />
<input type="hidden" formControlName="icon" (change)="onFileChange($event)" />
</button>
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label>
地址
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input nz-input placeholder="请输入单位地址" formControlName="address" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label>
联系人
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input nz-input placeholder="请输入联系人" formControlName="contacts" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label>
联系电话
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input nz-input placeholder="请输入联系电话" formControlName="phone" />
</nz-form-control>
</nz-form-item>
<nz-form-item>
<nz-form-label>
邮箱
</nz-form-label>
<nz-form-control [nzErrorTip]="formControlErrorTpl">
<input nz-input placeholder="请输入邮箱" formControlName="email" />
</nz-form-control>
</nz-form-item>
</div>
</form>
<ng-template #formControlErrorTpl let-control>
<form-error-tips [control]="control"></form-error-tips>
</ng-template>

0
projects/client/src/app/components/org-form/org-form.component.less

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

@ -0,0 +1,88 @@
import { ApiService } from "@admin/app/services";
import { Component, inject } from "@angular/core";
import { FormBuilder, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";
import { FormValidators } from "@cdk/validators";
import { MD5 } from "crypto-js";
import { NzMessageService } from "ng-zorro-antd/message";
import { lastValueFrom } from "rxjs";
import { format } from "date-fns";
import { ActivatedRoute, Router } from "@angular/router";
import { AnyObject } from "@cdk/types";
import { NZ_MODAL_DATA } from "ng-zorro-antd/modal";
@Component({
selector: "app-org-form",
templateUrl: "./org-form.component.html",
styleUrls: ["./org-form.component.less"],
})
export class OrgFormComponent {
constructor(
private fb: FormBuilder,
private api: ApiService,
private msg: NzMessageService,
private router: Router
) {}
state = inject<AnyObject>(NZ_MODAL_DATA);
formGroup!: FormGroup;
uploadLoading = false;
get icon() {
return this.formGroup.get("icon")?.value;
}
ngOnInit(): void {
console.log("this.state", this.state);
this.formGroup = this.fb.group({
id: this.fb.control(""),
name: this.fb.control("", [FormValidators.required("单位名称不能为空")]),
icon: this.fb.control("", []),
address: this.fb.control("", []),
contacts: this.fb.control("", []),
phone: this.fb.control("", []),
email: this.fb.control("", []),
});
this.formGroup.patchValue(this.state);
}
async onSubmit() {
if (Utils.validateFormGroup(this.formGroup)) {
const org = { ...(this.state ?? {}), ...this.formGroup.value };
org["password"] = MD5(org.password!).toString().substring(16).toUpperCase();
org["expire"] = format(org["expire"], "yyyy-MM-dd");
org["venderId"] = org.id;
// const account = await lastValueFrom(this.api.checkOrgAccount(org.account!));
// const name = await lastValueFrom(this.api.checkOrgName(org.name!));
// if (!account.body) {
// this.msg.error("账号重复");
// return;
// }
// if (!name.body) {
// this.msg.error("单位名称重复");
// return;
// }
const res = await lastValueFrom(this.api.saveOrg(org));
this.msg.success(res.desc);
this.router.navigate(["/organization/list"]);
}
}
onFileChange(e: Event) {
const target = e.target as HTMLInputElement;
const file = target.files![0];
target.value = "";
const fileReader = new FileReader();
fileReader.onload = () => {
const base64 = fileReader.result as string;
this.formGroup.patchValue({
icon: base64,
});
};
fileReader.readAsDataURL(file);
}
}

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

@ -1,13 +1,13 @@
<app-page> <app-page>
<nz-card nzTitle="餐次、能量摄入比例配置"> <nz-card nzTitle="餐次、能量摄入比例配置">
<form nz-form nzLayout="vertical"> <form nz-form nzLayout="vertical" [formGroup]="formGroup">
<nz-form-item> <nz-form-item>
<nz-form-label nzRequired> <nz-form-label nzRequired>
早餐能量、营养摄入比例 早餐能量、营养摄入比例
</nz-form-label> </nz-form-label>
<nz-form-control nzSpan="10"> <nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%"> <nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" /> <input nz-input type="number" placeholder="请输入" formControlName="breakfast" />
</nz-input-group> </nz-input-group>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
@ -17,7 +17,7 @@
</nz-form-label> </nz-form-label>
<nz-form-control nzSpan="10"> <nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%"> <nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" /> <input nz-input type="number" placeholder="请输入" formControlName="lunch" />
</nz-input-group> </nz-input-group>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
@ -27,19 +27,17 @@
</nz-form-label> </nz-form-label>
<nz-form-control nzSpan="10"> <nz-form-control nzSpan="10">
<nz-input-group nzAddOnAfter="%"> <nz-input-group nzAddOnAfter="%">
<input nz-input type="number" placeholder="请输入" /> <input nz-input type="number" placeholder="请输入" formControlName="dinner" />
</nz-input-group> </nz-input-group>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>
<nz-form-item> <nz-form-item>
<nz-form-control> <nz-form-control>
<nz-space> <nz-space>
<button *nzSpaceItem nz-button nzType="primary"> <button *nzSpaceItem nz-button nzType="primary" (click)="onSubmit()">
保存 保存
</button> </button>
<button *nzSpaceItem nz-button>
取消
</button>
</nz-space> </nz-space>
</nz-form-control> </nz-form-control>
</nz-form-item> </nz-form-item>

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

@ -1,10 +1,35 @@
import { Component } from '@angular/core'; import { Component, OnInit } from "@angular/core";
import { ClientApiService } from "../../services";
import { FormControl, FormGroup } from "@angular/forms";
import { Utils } from "@cdk/utils";
@Component({ @Component({
selector: 'app-meal-setting', selector: "app-meal-setting",
templateUrl: './meal-setting.component.html', templateUrl: "./meal-setting.component.html",
styleUrls: ['./meal-setting.component.less'] styleUrls: ["./meal-setting.component.less"],
}) })
export class MealSettingComponent { export class MealSettingComponent implements OnInit {
constructor(private api: ClientApiService) {}
account = this.api.account;
formGroup = new FormGroup({
breakfast: new FormControl(0, []),
dinner: new FormControl(0, []),
lunch: new FormControl(0, []),
});
ngOnInit(): void {
this.api.getOrgConfig().subscribe((res) => {
this.formGroup.patchValue(res.body);
});
}
onSubmit() {
if (Utils.validateFormGroup(this.formGroup)) {
this.api.saveOrgConfig({ ...this.formGroup.value, vender: this.account.vender.id }).subscribe((res) => {
// this.formGroup.
});
}
}
} }

2
projects/client/src/app/pages/system/org-info/org-info.component.html

@ -5,7 +5,7 @@
单位基础信息 单位基础信息
</span> </span>
<small class="ml-2"> <small class="ml-2">
<a> <a (click)="updateOrg()">
编辑 编辑
</a> </a>
</small> </small>

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

@ -6,6 +6,9 @@ import { FormControl, FormGroup } from "@angular/forms";
import { FormValidators } from "@cdk/validators"; import { FormValidators } from "@cdk/validators";
import { Utils } from "@cdk/utils"; import { Utils } from "@cdk/utils";
import { NzMessageService } from "ng-zorro-antd/message"; import { NzMessageService } from "ng-zorro-antd/message";
import { OrgFormComponent } from "../../../components";
import { lastValueFrom } from "rxjs";
import { MD5 } from "crypto-js";
@Component({ @Component({
selector: "app-org-info", selector: "app-org-info",
@ -15,7 +18,7 @@ import { NzMessageService } from "ng-zorro-antd/message";
export class OrgInfoComponent implements OnInit { export class OrgInfoComponent implements OnInit {
constructor(private api: ClientApiService, private modal: NzModalService, private msg: NzMessageService) {} constructor(private api: ClientApiService, private modal: NzModalService, private msg: NzMessageService) {}
account: any = null; account: any = this.api.account;
pwdForm = new FormGroup({ pwdForm = new FormGroup({
oldPwd: new FormControl("", [FormValidators.required("请输入原密码")]), oldPwd: new FormControl("", [FormValidators.required("请输入原密码")]),
@ -23,14 +26,7 @@ export class OrgInfoComponent implements OnInit {
rePwd: new FormControl("", [FormValidators.required("请再次输入新密码")]), rePwd: new FormControl("", [FormValidators.required("请再次输入新密码")]),
}); });
ngOnInit(): void { ngOnInit(): void {}
try {
const strageAccount = localStorage.getItem(this.api.accountKey);
if (strageAccount) {
this.account = JSON.parse(strageAccount);
}
} catch (error) {}
}
updateAccount(type: string | number, nzContent: TemplateRef<{}>) { updateAccount(type: string | number, nzContent: TemplateRef<{}>) {
const nzTitle = const nzTitle =
@ -44,6 +40,22 @@ export class OrgInfoComponent implements OnInit {
}); });
} }
updateOrg() {
this.modal.create({
nzTitle: "修改单位基础信息",
nzContent: OrgFormComponent,
nzData: this.account.vender,
nzOnOk: async (e) => {
if (Utils.validateFormGroup(e.formGroup)) {
const res = await lastValueFrom(this.api.saveOrg({ ...e.formGroup.value, venderId: this.account.vender.id }));
this.msg.success(res.desc);
return true;
}
return false;
},
});
}
cancelPwdForm() { cancelPwdForm() {
this.pwdForm.reset(); this.pwdForm.reset();
} }
@ -59,6 +71,14 @@ export class OrgInfoComponent implements OnInit {
this.msg.error("两次密码输入不一致"); this.msg.error("两次密码输入不一致");
return false; return false;
} }
const res = await lastValueFrom(
this.api.updatePassword({
password: MD5(value.newPwd!).toString().slice(-16),
// name:
})
);
this.msg.success(res.desc);
return true;
} }
return false; return false;
}, },

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

@ -0,0 +1,17 @@
import { inject } from "@angular/core";
import { Router } from "@angular/router";
import { map } from "rxjs";
import { ClientApiService } from "./client-api.service";
export const authGuard = () => {
const router = inject(Router);
const api = inject(ClientApiService);
const stragedAccount = localStorage.getItem(api.accountKey);
if (!stragedAccount) {
router.navigate(["/login"]);
return false;
}
return api.getAllEnum();
};

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

@ -1,16 +1,39 @@
import { HttpClient, HttpParams } from "@angular/common/http"; import { HttpClient, HttpParams } from "@angular/common/http";
import { Injectable } from "@angular/core"; import { Injectable } from "@angular/core";
import { Utils, AnyObject, ResponseType, UserRoleDTO, PermItemDTO, UserDTO } from "@cdk/public-api"; import {
import { map } from "rxjs"; Utils,
AnyObject,
ResponseType,
UserRoleDTO,
PermItemDTO,
UserDTO,
GlobalEnum,
OrgConfigDTO,
ClientAccountDTO,
} from "@cdk/public-api";
import { Observable, map, of, tap } from "rxjs";
@Injectable({ @Injectable({
providedIn: "root", providedIn: "root",
}) })
export class ClientApiService { export class ClientApiService {
constructor(private http: HttpClient) {} constructor(private http: HttpClient) {
try {
const strageAccount = localStorage.getItem(this.accountKey);
if (strageAccount) {
this.account = JSON.parse(strageAccount);
}
} catch (error) {
console.error("获取用户信息失败", error);
}
}
public account!: ClientAccountDTO;
public accountKey = "CATERING_CLIENT_ACCOUNT"; public accountKey = "CATERING_CLIENT_ACCOUNT";
globalEnum!: GlobalEnum;
page(v: {}, q: {}) { page(v: {}, q: {}) {
return this.http.get<any>("https://jsonplaceholder.typicode.com/users", v).pipe( return this.http.get<any>("https://jsonplaceholder.typicode.com/users", v).pipe(
map((r) => { map((r) => {
@ -22,6 +45,20 @@ export class ClientApiService {
); );
} }
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: {}) { login(v: {}) {
const params = Utils.objectToHttpParams(v); const params = Utils.objectToHttpParams(v);
return this.http.get<ResponseType>("/api/login", { params }); return this.http.get<ResponseType>("/api/login", { params });
@ -31,6 +68,11 @@ export class ClientApiService {
return this.http.get<ResponseType>("/api/logout"); return this.http.get<ResponseType>("/api/logout");
} }
updatePassword(v: {}) {
const body = Utils.objectToFormData(v);
return this.http.post<ResponseType>("/api/basic/user", body);
}
getRoleList() { getRoleList() {
return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role"); return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role");
} }
@ -75,4 +117,18 @@ export class ClientApiService {
params, params,
}); });
} }
saveOrg(org: AnyObject) {
const body = Utils.objectToFormData(org);
return this.http.post<ResponseType>(" /api/vender", body);
}
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);
}
} }

Loading…
Cancel
Save