Browse Source

feat(client): 添加网站配置并优化相关功能

- 新增 config.json 文件用于网站配置
- 添加 ConfigService 服务加载配置信息
- 更新 App 组件和 Login 组件以使用新配置
- 修改版权组件以动态显示备案号和公司名称
- 优化组织信息组件,添加技术支持联系功能
- 更新页面标题策略以使用配置中的网站名称
main
kely 21 hours ago
parent
commit
bb422b7c31
  1. 17
      projects/admin/src/assets/config.json
  2. 2
      projects/cdk/src/app-settings/index.ts
  3. 10
      projects/cdk/src/shared/components/copyright/copyright.component.html
  4. 12
      projects/cdk/src/shared/components/copyright/copyright.component.ts
  5. 105
      projects/cdk/src/types/index.ts
  6. 27
      projects/client/src/app/app.component.ts
  7. 15
      projects/client/src/app/app.module.ts
  8. 4
      projects/client/src/app/components/app-layout/app-layout.component.html
  9. 64
      projects/client/src/app/components/app-layout/app-layout.component.ts
  10. 54
      projects/client/src/app/pages/login/login.component.html
  11. 68
      projects/client/src/app/pages/login/login.component.ts
  12. 4
      projects/client/src/app/pages/system/org-info/org-info.component.html
  13. 106
      projects/client/src/app/pages/system/org-info/org-info.component.ts
  14. 94
      projects/client/src/app/services/client-api.service.ts
  15. 38
      projects/client/src/app/services/config.service.ts
  16. 23
      projects/client/src/app/services/title.service.ts
  17. 17
      projects/client/src/assets/config.json
  18. 0
      projects/client/src/assets/favicon.ico
  19. 26
      projects/client/src/index.html

17
projects/admin/src/assets/config.json

@ -0,0 +1,17 @@
{
"websiteInfo": {
"name": "营养配餐实训系统"
},
"assets": {
"logo": "/assets/images/jl-logo.png",
"favicon": "/assets/favicon.ico",
"loginBackground": "/assets/images/jl-logo.png"
},
"contactInfo": {
"phoneNumber": "19181752603"
},
"copyrightInfo": {
"icpLicense": "蜀ICP备2023038072号",
"companyName": ""
}
}

2
projects/cdk/src/app-settings/index.ts

@ -1 +1 @@
export const appName = "营养配餐系统";
export const appName = '营养配餐系统'

10
projects/cdk/src/shared/components/copyright/copyright.component.html

@ -1,10 +1,6 @@
<div class="flex items-center justify-center pb-2">
<a
href="https://beian.miit.gov.cn/"
target="_blank"
class="text-black text-opacity-70"
>
蜀ICP备2023038072号
<a *ngIf="icpLicense" href="https://beian.miit.gov.cn/" target="_blank" class="text-black text-opacity-70">
{{ icpLicense }}
</a>
<span class="text-black text-opacity-70 ml-4"> ©{{ year }} 成都积络科技有限公司</span>
<span *ngIf="companyName" class="text-black text-opacity-70 ml-4"> ©{{ year }} {{ companyName }}</span>
</div>

12
projects/cdk/src/shared/components/copyright/copyright.component.ts

@ -1,10 +1,12 @@
import { Component } from "@angular/core";
import { Component, Input } from '@angular/core'
@Component({
selector: "lib-copyright",
templateUrl: "./copyright.component.html",
styleUrls: ["./copyright.component.less"],
selector: 'lib-copyright',
templateUrl: './copyright.component.html',
styleUrls: ['./copyright.component.less'],
})
export class CopyrightComponent {
year = new Date().getFullYear();
@Input() icpLicense?: string
@Input() companyName?: string
year = new Date().getFullYear()
}

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

@ -1,59 +1,86 @@
export type AnyObject = { [k: string]: any };
export type AnyObject = { [k: string]: any }
export type DecSafeAny = any;
export type DecSafeAny = any
export type DecText = number | string;
export type DecText = number | string
export type Augmented<O extends object> = O & AnyObject;
export type Augmented<O extends object> = O & AnyObject
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>;
export type Optional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
export type NutrientInterface = {
key: string;
measurement: string;
nrv: number;
value: string;
};
key: string
measurement: string
nrv: number
value: string
}
export type OptionItemInterface = Augmented<{
label: string;
value: string;
}>;
label: string
value: string
}>
export interface ResponseType<T = any> {
body: T;
code: number;
desc: string;
success: boolean;
body: T
code: number
desc: string
success: boolean
}
export type MyResponse<R = any> = ResponseType<R>;
export type MyResponse<R = any> = ResponseType<R>
export interface TableListColumns {
key: string;
title: string;
visible?: boolean;
width?: string;
sort?: boolean;
order?: number;
disabled?: boolean;
coverStorage?: boolean;
key: string
title: string
visible?: boolean
width?: string
sort?: boolean
order?: number
disabled?: boolean
coverStorage?: boolean
}
export interface TableOperation {
title: string;
href?: string;
link?: string[];
target?: string;
premissions: string[];
danger?: boolean;
disabled?: boolean;
onClick?: (v: DecSafeAny) => void;
visible?: (v: DecSafeAny) => boolean;
title: string
href?: string
link?: string[]
target?: string
premissions: string[]
danger?: boolean
disabled?: boolean
onClick?: (v: DecSafeAny) => void
visible?: (v: DecSafeAny) => boolean
}
export interface PageResult<T = DecSafeAny> {
total: number;
content: T[];
totalElements: number;
totalPages: number;
total: number
content: T[]
totalElements: number
totalPages: number
}
export interface WebSiteConfig {
websiteInfo: WebsiteInfo
assets: Assets
contactInfo: ContactInfo
copyrightInfo: CopyrightInfo
}
export interface Assets {
logo: string
favicon: string
loginBackground: string
}
export interface ContactInfo {
phoneNumber: string
}
export interface CopyrightInfo {
icpLicense: string
companyName: string
yeah: number
}
export interface WebsiteInfo {
name: string
}

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

@ -1,10 +1,27 @@
import { Component } from '@angular/core';
import { Component } from '@angular/core'
import { WebSiteConfig } from '@cdk/types'
import { ConfigService } from './services/config.service'
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less']
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.less'],
})
export class AppComponent {
title = 'client';
title = 'client'
appConfig: WebSiteConfig | null = null
constructor(private configService: ConfigService) {}
ngOnInit() {
this.appConfig = this.configService.getConfig()
if (this.appConfig) {
if (this.appConfig?.assets?.favicon) {
;(document.querySelector('#favicon') as HTMLLinkElement).href = this.appConfig.assets.favicon
}
if (this.appConfig?.websiteInfo?.name) {
document.title = this.appConfig.websiteInfo.name + (document.title ? ' - ' + document.title : '')
}
}
}
}

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

@ -1,4 +1,4 @@
import { NgModule } from '@angular/core'
import { APP_INITIALIZER, NgModule } from '@angular/core'
import { BrowserModule } from '@angular/platform-browser'
import { NZ_DATE_CONFIG, NZ_DATE_LOCALE, NZ_I18N } from 'ng-zorro-antd/i18n'
@ -41,6 +41,11 @@ import { IngredientModule } from '@cdk/ingredient/ingredient.module'
import { NgxPermissionsModule } from 'ngx-permissions'
import { TemplatePageTitleStrategy } from './services/title.service'
import { TitleStrategy } from '@angular/router'
import { ConfigService } from './services/config.service'
function initializeApp(configService: ConfigService) {
return () => configService.loadConfig().toPromise() // 返回一个 Promise
}
registerLocaleData(zh)
@ -95,6 +100,14 @@ registerLocaleData(zh)
firstDayOfWeek: 1,
},
},
ConfigService,
{
provide: APP_INITIALIZER,
useFactory: initializeApp,
deps: [ConfigService],
multi: true,
},
],
bootstrap: [AppComponent],
})

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

@ -7,9 +7,9 @@
<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-1" src="../assets/images/jl-logo.png" />
<img class="block h-[40px] mr-1" src="{{ appConfig?.assets?.logo }}" />
<span class="text-lg text-white font-bold">
{{ appName }}
{{ appConfig?.websiteInfo?.name }}
</span>
</div>
<div class="profile">

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

@ -1,60 +1,64 @@
import { Component, OnInit } from "@angular/core";
import { NavigationEnd, Router, RouterModule } from "@angular/router";
import { NzMessageService } from "ng-zorro-antd/message";
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 { ApiService } from "@cdk/services";
import { appName } from "@cdk/app-settings";
import { NzModalService } from 'ng-zorro-antd/modal'
import { Subject, filter, lastValueFrom, takeUntil } from 'rxjs'
import { ApiService } from '@cdk/services'
import { WebSiteConfig } from '@cdk/types'
import { ConfigService } from '@client/app/services/config.service'
@Component({
selector: "app-layout",
templateUrl: "./app-layout.component.html",
styleUrls: ["./app-layout.component.less"],
selector: 'app-layout',
templateUrl: './app-layout.component.html',
styleUrls: ['./app-layout.component.less'],
})
export class AppLayoutComponent implements OnInit {
constructor(
private router: Router,
private modal: NzModalService,
private msg: NzMessageService,
private api: ApiService
private api: ApiService,
private configService: ConfigService,
) {
this.router.events
.pipe(
takeUntil(this.unSubscribe$),
filter((e): e is NavigationEnd => e instanceof NavigationEnd)
filter((e): e is NavigationEnd => e instanceof NavigationEnd),
)
.subscribe((e) => {
this.currentUrl = e.url;
this.fullPage = ["/ingredient/preview", "/data-vis"].some((s) => e.url.startsWith(s));
});
this.currentUrl = e.url
this.fullPage = ['/ingredient/preview', '/data-vis'].some((s) => e.url.startsWith(s))
})
}
fullPage = false;
account = this.api.account;
appName = appName;
fullPage = false
account = this.api.account
unSubscribe$ = new Subject<void>();
unSubscribe$ = new Subject<void>()
currentUrl: string = "";
currentUrl: string = ''
appConfig: WebSiteConfig | null = null
ngOnInit(): void {}
ngOnInit(): void {
this.appConfig = this.configService.getConfig()
}
openDataVis() {
window.open("/data-vis");
window.open('/data-vis')
}
logout() {
this.modal.confirm({
nzTitle: "警告",
nzContent: "是否要退出登录?",
nzTitle: '警告',
nzContent: '是否要退出登录?',
nzOnOk: async () => {
const res = await lastValueFrom(this.api.logout());
this.msg.success(res.desc);
localStorage.removeItem(this.api.accountKey);
this.router.navigate(["/login"]);
const res = await lastValueFrom(this.api.logout())
this.msg.success(res.desc)
localStorage.removeItem(this.api.accountKey)
this.router.navigate(['/login'])
},
});
})
}
}

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

@ -8,58 +8,32 @@
</div> -->
<div class="card">
<div class="img flex items-center justify-center">
<img src="assets/images/jl-logo.png" />
<img src="{{ appConfig?.assets?.loginBackground }}" />
</div>
<div class="form py-5 px-10 flex-1">
<div class="form-inner">
<h2 class="mt-4 text-3xl font-bold">{{ appName }}</h2>
<h2 class="mt-4 text-3xl font-bold">{{ appConfig?.websiteInfo?.name }}</h2>
<h3 class="mt-10 text-xl">登录</h3>
<form
nz-form
[formGroup]="loginForm"
class="mt-10"
>
<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 [nzPrefix]="prefixTemplateUser" nzSize="large">
<input nz-input nzSize="large" placeholder="账户" formControlName="uid" />
</nz-input-group>
<ng-template #prefixTemplateUser>
<span
nz-icon
nzType="user"
></span>
<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="pwd"
/>
<nz-input-group [nzPrefix]="prefixTemplatePassword" nzSize="large">
<input nz-input type="password" placeholder="密码" formControlName="pwd" />
</nz-input-group>
<ng-template #prefixTemplatePassword>
<span
nz-icon
nzType="lock"
></span>
<span nz-icon nzType="lock"></span>
<nz-divider nzType="vertical"></nz-divider>
</ng-template>
</nz-form-control>
@ -85,14 +59,14 @@
</div>
</div>
<div class="copyright">
<lib-copyright></lib-copyright>
<lib-copyright
[icpLicense]="appConfig?.copyrightInfo?.icpLicense"
[companyName]="appConfig?.copyrightInfo?.companyName"
></lib-copyright>
</div>
</section>
<ng-template
#formErrorTipsTpl
let-control
>
<ng-template #formErrorTipsTpl let-control>
<div class="text-left">
<form-error-tips [control]="control"></form-error-tips>
</div>

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

@ -1,48 +1,56 @@
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 { MD5 } from "crypto-js";
import { ApiService } from "@cdk/services";
import { appName } from "@cdk/app-settings";
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, WebSiteConfig } from 'projects/cdk/src/public-api'
import { Utils } from 'projects/cdk/src/utils'
import { finalize, lastValueFrom } from 'rxjs'
import { MD5 } from 'crypto-js'
import { ApiService } from '@cdk/services'
import { ConfigService } from '@client/app/services/config.service'
@Component({
selector: "app-login",
templateUrl: "./login.component.html",
styleUrls: ["./login.component.less"],
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.less'],
})
export class LoginComponent {
constructor(private msg: NzMessageService, private api: ApiService, private router: Router) {}
constructor(
private configService: ConfigService,
private msg: NzMessageService,
private api: ApiService,
private router: Router,
) {}
public loginForm = new FormGroup({
uid: new FormControl("", [FormValidators.required("请输入账户")]),
pwd: new FormControl("", [FormValidators.required("请输入密码")]),
});
uid: new FormControl('', [FormValidators.required('请输入账户')]),
pwd: new FormControl('', [FormValidators.required('请输入密码')]),
})
public loading: boolean = false;
public loading: boolean = false
appName = appName;
appConfig: WebSiteConfig | null = null
ngOnInit(): void {}
ngOnInit(): void {
this.appConfig = this.configService.getConfig()
}
async onLogin() {
if (Utils.validateFormGroup(this.loginForm)) {
const { value } = this.loginForm;
const pwd = value.pwd as string;
value["pwd"] = MD5(pwd).toString().slice(-16).toUpperCase();
this.loading = true;
const { value } = this.loginForm
const pwd = value.pwd as string
value['pwd'] = MD5(pwd).toString().slice(-16).toUpperCase()
this.loading = true
const res = await lastValueFrom(
this.api.login(value).pipe(
finalize(() => {
this.loading = false;
})
)
);
this.msg.success(res.desc);
this.router.navigate(["/"]);
this.loading = false
}),
),
)
this.msg.success(res.desc)
this.router.navigate(['/'])
}
}
}

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

@ -72,7 +72,7 @@
<nz-form-label> 账号到期时间 </nz-form-label>
<nz-form-control>
{{ org?.expire | date : 'yyyy-MM-dd' }}
<a class="ml-4" (click)="updateAccount(org?.expire, updateAccountInfoTpl)"> 续费 </a>
<a class="ml-4" (click)="updateAccount(org?.expire, updateAccountInfoTpl)"> 技术支持 </a>
</nz-form-control>
</nz-form-item>
</div>
@ -84,7 +84,7 @@
<!-- <span>
{{org.contacts ?? '-'}}
</span> -->
<span> 028-85463212 </span>
<span>{{ appConfig?.contactInfo?.phoneNumber ?? '-' }} </span>
</div>
</ng-template>

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

@ -1,20 +1,22 @@
import { ChangeDetectorRef, Component, OnInit, TemplateRef } from "@angular/core";
import { NzModalService } from "ng-zorro-antd/modal";
import { format } from "date-fns";
import { ApiService } from "@cdk/services";
import { FormControl, FormGroup } from "@angular/forms";
import { FormValidators } from "@cdk/validators";
import { Utils } from "@cdk/utils";
import { NzMessageService } from "ng-zorro-antd/message";
import { OrgFormComponent } from "../../../components";
import { lastValueFrom } from "rxjs";
import { MD5 } from "crypto-js";
import { Router } from "@angular/router";
import { ChangeDetectorRef, Component, OnInit, TemplateRef } from '@angular/core'
import { NzModalService } from 'ng-zorro-antd/modal'
import { format } from 'date-fns'
import { ApiService } from '@cdk/services'
import { FormControl, FormGroup } from '@angular/forms'
import { FormValidators } from '@cdk/validators'
import { Utils } from '@cdk/utils'
import { NzMessageService } from 'ng-zorro-antd/message'
import { OrgFormComponent } from '../../../components'
import { lastValueFrom } from 'rxjs'
import { MD5 } from 'crypto-js'
import { Router } from '@angular/router'
import { WebSiteConfig } from '@cdk/public-api'
import { ConfigService } from '@client/app/services/config.service'
@Component({
selector: "app-org-info",
templateUrl: "./org-info.component.html",
styleUrls: ["./org-info.component.less"],
selector: 'app-org-info',
templateUrl: './org-info.component.html',
styleUrls: ['./org-info.component.less'],
})
export class OrgInfoComponent implements OnInit {
constructor(
@ -22,40 +24,46 @@ export class OrgInfoComponent implements OnInit {
private modal: NzModalService,
private msg: NzMessageService,
private router: Router,
private cdr: ChangeDetectorRef
private configService: ConfigService,
) {}
account: any = this.api.account;
account: any = this.api.account
pwdForm = new FormGroup({
oldPwd: new FormControl("", [FormValidators.required("请输入原密码")]),
newPwd: new FormControl("", [FormValidators.required("请输入新密码")]),
rePwd: new FormControl("", [FormValidators.required("请再次输入新密码")]),
});
oldPwd: new FormControl('', [FormValidators.required('请输入原密码')]),
newPwd: new FormControl('', [FormValidators.required('请输入新密码')]),
rePwd: new FormControl('', [FormValidators.required('请再次输入新密码')]),
})
ngOnInit(): void {}
appConfig: WebSiteConfig | null = null
ngOnInit(): void {
this.appConfig = this.configService.getConfig()
}
updateAccount(type: string | number, nzContent: TemplateRef<{}>) {
const nzTitle =
type === "uid"
? "如需修改超级管理员账号,请联系:"
: `账号将于 ${format(type as number, "yyyy-MM-dd")} 到期,续费请联系`;
type === 'uid'
? '如需修改超级管理员账号,请联系:'
: `账号将于 ${format(type as number, 'yyyy-MM-dd')} 到期,续费请联系`
this.modal.info({
nzTitle,
nzTitle: '技术支持请联系',
nzContent,
nzOkText: "知道了",
});
nzOkText: '知道了',
})
}
updateOrg() {
this.modal.create({
nzTitle: "修改单位基础信息",
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);
const res = await lastValueFrom(
this.api.saveOrg({ ...e.formGroup.value, venderId: this.account.vender.id }),
)
this.msg.success(res.desc)
// localStorage.setItem(
// this.api.accountKey,
// JSON.stringify({
@ -68,46 +76,46 @@ export class OrgInfoComponent implements OnInit {
// })
// );
// window.location.reload();
this.api.getOrgInfo().subscribe(() => {});
return true;
this.api.getOrgInfo().subscribe(() => {})
return true
}
return false;
return false
},
});
})
}
cancelPwdForm() {
this.pwdForm.reset();
this.pwdForm.reset()
}
changePassword(nzContent: TemplateRef<{}>) {
this.modal.create({
nzTitle: "修改密码",
nzTitle: '修改密码',
nzContent,
nzOnOk: async () => {
if (Utils.validateFormGroup(this.pwdForm)) {
const { value } = this.pwdForm;
const { value } = this.pwdForm
if (value.newPwd !== value.rePwd) {
this.msg.error("两次密码输入不一致");
return false;
this.msg.error('两次密码输入不一致')
return false
}
const res = await lastValueFrom(
this.api.updatePassword({
oldPassword: MD5(value.oldPwd!).toString().slice(-16),
password: MD5(value.newPwd!).toString().slice(-16),
// name:
})
);
this.msg.success(res.desc);
}),
)
this.msg.success(res.desc)
this.api.logout().subscribe(() => {
this.msg.success("请重新登录");
this.router.navigate(["/login"]);
});
return true;
this.msg.success('请重新登录')
this.router.navigate(['/login'])
})
return true
}
return false;
return false
},
nzOnCancel: this.cancelPwdForm.bind(this),
});
})
}
}

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

@ -1,5 +1,5 @@
import { HttpClient, HttpParams } from "@angular/common/http";
import { Injectable } from "@angular/core";
import { HttpClient, HttpParams } from '@angular/common/http'
import { Injectable } from '@angular/core'
import {
Utils,
AnyObject,
@ -10,120 +10,120 @@ import {
GlobalEnum,
OrgConfigDTO,
ClientAccountDTO,
} from "@cdk/public-api";
import { Observable, map, of, tap } from "rxjs";
} from '@cdk/public-api'
import { Observable, map, of, tap } from 'rxjs'
@Injectable({
providedIn: "root",
providedIn: 'root',
})
export class ClientApiServicde {
constructor(private http: HttpClient) {
try {
const strageAccount = localStorage.getItem(this.accountKey);
const strageAccount = localStorage.getItem(this.accountKey)
if (strageAccount) {
this.account = JSON.parse(strageAccount);
this.account = JSON.parse(strageAccount)
}
} catch (error) {
console.error("获取用户信息失败", error);
console.error('获取用户信息失败', error)
}
}
public account!: ClientAccountDTO;
public account!: ClientAccountDTO
public accountKey = "CATERING_ACCOUNT";
public accountKey = 'CATERING_ACCOUNT'
globalEnum!: GlobalEnum;
globalEnum!: GlobalEnum
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) => {
return {
total: 10,
content: r,
};
})
);
}
}),
)
}
getAllEnum(force?: boolean): Observable<GlobalEnum> {
if (this.globalEnum && !force) {
return of(this.globalEnum);
return of(this.globalEnum)
}
return this.http.get<ResponseType<GlobalEnum>>("/api/basic/enum").pipe(
return this.http.get<ResponseType<GlobalEnum>>('/api/basic/enum').pipe(
map((res) => {
return res.body;
return res.body
}),
tap((r) => {
this.globalEnum = r;
})
);
this.globalEnum = r
}),
)
}
login(v: {}) {
const params = Utils.objectToHttpParams(v);
return this.http.get<ResponseType>("/api/login", { params });
const params = Utils.objectToHttpParams(v)
return this.http.get<ResponseType>('/api/login', { params })
}
logout() {
return this.http.get<ResponseType>("/api/logout");
return this.http.get<ResponseType>('/api/logout')
}
getRoleList() {
return this.http.get<ResponseType<UserRoleDTO[]>>("/api/role");
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", {
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);
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");
return this.http.get<ResponseType<PermItemDTO[]>>('/api/role/item')
}
getUserList() {
return this.http.get<ResponseType<UserDTO[]>>("/api/user");
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", {
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);
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", {
const params = new HttpParams().set('uid', uid)
return this.http.delete<ResponseType>('/api/user', {
params,
});
})
}
saveOrg(org: AnyObject) {
const body = Utils.objectToFormData(org);
return this.http.post<ResponseType>(" /api/vender", body);
const body = Utils.objectToFormData(org)
return this.http.post<ResponseType>(' /api/vender', body)
}
getOrgConfig() {
return this.http.get<ResponseType<OrgConfigDTO>>("/api/vender/config");
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);
const body = Utils.objectToFormData(config)
return this.http.post<ResponseType>('/api/vender/config', body)
}
}

38
projects/client/src/app/services/config.service.ts

@ -0,0 +1,38 @@
import { Injectable } from '@angular/core'
import { HttpClient } from '@angular/common/http'
import { Observable, of, throwError } from 'rxjs'
import { tap, catchError } from 'rxjs/operators'
import { WebSiteConfig } from '@cdk/public-api'
@Injectable({
providedIn: 'root',
})
export class ConfigService {
private configUrl = 'assets/config.json'
private appConfig: WebSiteConfig | null = null
constructor(private http: HttpClient) {}
loadConfig(): Observable<WebSiteConfig> {
if (this.appConfig) {
return of(this.appConfig)
}
console.log(123)
return this.http.get<WebSiteConfig>(this.configUrl).pipe(
tap((config) => {
this.appConfig = config
}),
catchError((error) => {
console.error('加载 config.json 失败:', error)
return throwError(() => new Error('无法加载应用配置'))
}),
)
}
getConfig(): WebSiteConfig | null {
return this.appConfig
}
}

23
projects/client/src/app/services/title.service.ts

@ -1,19 +1,22 @@
import { Injectable } from "@angular/core";
import { Title } from "@angular/platform-browser";
import { RouterStateSnapshot, TitleStrategy } from "@angular/router";
import { OnInit } from '@angular/core'
import { ConfigService } from '@client/app/services/config.service'
import { Injectable } from '@angular/core'
import { Title } from '@angular/platform-browser'
import { RouterStateSnapshot, TitleStrategy } from '@angular/router'
@Injectable({ providedIn: "root" })
@Injectable({ providedIn: 'root' })
export class TemplatePageTitleStrategy extends TitleStrategy {
constructor(private readonly title: Title) {
super();
fullTitle = ''
constructor(private readonly title: Title, private ConfigService: ConfigService) {
super()
}
override updateTitle(routerState: RouterStateSnapshot) {
const title = this.buildTitle(routerState);
let fullTitle = "营养配餐系统";
const title = this.buildTitle(routerState)
let fullTitle = this.ConfigService.getConfig()?.websiteInfo?.name || ''
if (title !== undefined) {
fullTitle += ` | ${title}`;
fullTitle += ` | ${title}`
}
this.title.setTitle(fullTitle);
this.title.setTitle(fullTitle)
}
}

17
projects/client/src/assets/config.json

@ -0,0 +1,17 @@
{
"websiteInfo": {
"name": "营养配餐实训系统"
},
"assets": {
"logo": "/assets/images/jl-logo.png",
"favicon": "/assets/favicon.ico",
"loginBackground": "/assets/images/jl-logo.png"
},
"contactInfo": {
"phoneNumber": "19181752603"
},
"copyrightInfo": {
"icpLicense": "蜀ICP备2023038072号",
"companyName": ""
}
}

0
projects/client/src/favicon.ico → projects/client/src/assets/favicon.ico

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

26
projects/client/src/index.html

@ -1,16 +1,14 @@
<!doctype html>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<base href="/" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" id="favicon" />
</head>
<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>
<body>
<app-root></app-root>
</body>
</html>

Loading…
Cancel
Save