2024-02-04
Angular
00
请注意,本文编写于 668 天前,最后修改于 632 天前,其中某些信息可能已经过时。

目录

页面设计
组件抽离
BaseViewComponent
ConfigService
config.yml
demo
demo-component.ts
合并后的配置对象config

如果有一系列的页面布局很类似,为了节省时间,我们可以把这些类似的页面所通用的属性和方法抽离成一个BaseView,让其它页面继承该基础页面,同时将一些经常改变的属性和差异的属性写到配置文件里。例如树容器初始时是否展开、某些图表是否显示等都可以写到配置文件里面。本文将带你实现该功能,抽离出BaseView页面组件,鉴于json文件无法写注释的情况,配置文件采取yml的格式

页面设计

image.png

组件抽离

BaseViewComponent

import { Injectable, OnDestroy, OnInit } from "@angular/core"; import { ConfigService } from "@app/core/config/config.service"; import { Subject } from "rxjs"; import { takeUntil } from "rxjs/operators"; import { deepMergeKey } from "../utils"; import { HandlebarCalendarModelType } from "./baseView.type"; import { TimeRange } from "topdsm-lib/core/util" import dayjs from 'dayjs'; var customParseFormat = require('dayjs/plugin/advancedFormat') @Injectable() export class BaseViewComponent implements OnInit, OnDestroy { /** 页面通用属性 */ pageNum = 1 pageSize = 10 tableData = [] tableTotal = 0 public unsubscribe$ = new Subject<void>(); /** 原始config */ public originalConfig: Record<string, any> = null /** 页面config */ public config: Record<string, any> = { leftPanelExpand: true, handlebarCalendarModel: [], query: {} } constructor( public viewKey: string, // 页面唯一key public configService: ConfigService // 用来读取json配置文件 ) { console.log("BaseViewComponent constructor"); } ngOnInit(): void { console.log(); this.configService.change.pipe(takeUntil(this.unsubscribe$)).subscribe(async (config) => { console.log(config); if (config) { this.originalConfig = config if (this['configServiceReaderBefore']) { await this['configServiceReaderBefore'](config) } this.handleConfig() if (this['configServiceReaderAfter']) { await this['configServiceReaderAfter'](config) } } }); } ngOnDestroy(): void { const { unsubscribe$ } = this; unsubscribe$.next(); unsubscribe$.complete(); } handleConfig() { deepMergeKey(this.config, true, this.originalConfig.global, this.originalConfig?.modules?.[this.viewKey]) this.handleCalendarTime() this.handleBarBtn() console.log(this.config); } /** * handlebar 日历组件初始值处理, * 获取开始时间和结束时间,该逻辑可以根据自己的业务场景自定义 */ handleCalendarTime() { let tg = { start: "", end: "" } switch (this.config.handlebarCalendarModelType) { case HandlebarCalendarModelType.CUSTOM: if (this.config.handlebarCalendarModel.length === 1) { tg.start = this.config.handlebarCalendarModel[0] tg.end = dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss").substring(0, 10) + " 23:59:59" } else if (this.config.handlebarCalendarModel.length === 2) { tg.start = this.config.handlebarCalendarModel[0] tg.end = this.config.handlebarCalendarModel[1] } break; case HandlebarCalendarModelType.LASTYEAY: // 上一年 tg = TimeRange.getLastYearRange() break; case HandlebarCalendarModelType.LASTQUATER: // 上一季度 tg = TimeRange.getLastQuarterRange() break; case HandlebarCalendarModelType.LASTMONTH: // 上一月 tg = TimeRange.getLastMonthRange() break; case HandlebarCalendarModelType.LAST7DAY: // 近7天 tg = { start: TimeRange.getLast7dayRange().start, end: dayjs(new Date()).format("YYYY-MM-DD HH:mm:ss").substring(0, 10) + " 23:59:59" } break; case HandlebarCalendarModelType.MONTH: // 本月 tg = TimeRange.getMonthRange() break; case HandlebarCalendarModelType.QUATER: // 本年度 tg = TimeRange.getQuarterRange() break; case HandlebarCalendarModelType.YEAR: // 本年度 tg = TimeRange.getYearRange() break; default: break; } if (tg.start !== "" && tg.end !== "") { tg.start = tg.start.substring(0, 10) + " 00:00:00" tg.end = tg.end.substring(0, 10) + " 23:59:59" } this.config.query.startTimeStr = tg.start this.config.query.endTimeStr = tg.end if (tg.start !== "" && tg.end !== "") { this.config.handlebarCalendarModel = [ dayjs(this.config.query.startTimeStr, "YYYY-MM-DD HH:mm:ss").toDate(), dayjs(this.config.query.endTimeStr, "YYYY-MM-DD HH:mm:ss").toDate(), ] } else { this.config.handlebarCalendarModel = [] } } handleBarBtn() { let btnSelected = { } this.config.handlebarRightBtn = this.config.handlebarRightBtn.filter(item => item.show) this.config.handlebarRightBtn.forEach(item => { btnSelected[item.key] = item.selected }) this.config.handlebarRightBtnSelected = btnSelected } }

时间段枚举

export enum HandlebarCalendarModelType { /** 自定义 */ CUSTOM = "0", /** 上一年 */ LASTYEAY = "1", /** 上一季度 */ LASTQUATER = "2", /** 上一月 */ LASTMONTH = "3", /** 上一周 */ LASTWEEK = "4", /** 本周 */ WEEK = "5", /** 本月 */ MONTH = "6", /** 本季度 */ QUATER = "7", /** 本年度 */ YEAR = "8", /** 近7天 */ LAST7DAY = "9" }

属性合并的函数

/** * Deep merge object. * * 深度合并对象 * * @param original 原始对象 * @param arrayProcessMethod 数组处理方式 * - `true` 表示替换新值,不管新值为哪种类型 * - `false` 表示会合并整个数组(将旧数据与新数据合并成新数组) * @param objects 要合并的对象 */ export function deepMergeKey(original: unknown, arrayProcessMethod: boolean, ...objects: NzSafeAny[]): NzSafeAny { if (Array.isArray(original) || typeof original !== 'object') return original; const isObject = (v: unknown): boolean => typeof v === 'object'; const merge = (target: NzSafeAny, obj: NzSafeAny): NzSafeAny => { Object.keys(obj) .filter(key => key !== '__proto__' && Object.prototype.hasOwnProperty.call(obj, key)) .forEach(key => { const fromValue = obj[key]; const toValue = target[key]; if (Array.isArray(toValue)) { target[key] = arrayProcessMethod ? fromValue : [...toValue, ...fromValue]; } else if (typeof fromValue === 'function') { target[key] = fromValue; } else if (fromValue != null && isObject(fromValue) && toValue != null && isObject(toValue)) { target[key] = merge(toValue, fromValue); } else { target[key] = deepCopy(fromValue); } }); return target; }; objects.filter(v => v != null && isObject(v)).forEach(v => merge(original, v)); return original; }

ConfigService

读取yml配置文件

import { Injectable } from "@angular/core"; import { environment } from "@env/environment"; import { BehaviorSubject, Observable } from "rxjs"; import yaml from "js-yaml" import axios from "axios" @Injectable({ providedIn: 'root' }) export class ConfigService { private change$ = new BehaviorSubject(null); constructor() { this.getGlobalConfig() } get change(): Observable<any> { return this.change$.asObservable(); } getGlobalConfig() { return new Promise((resolve, reject) => { let url = "/assets/config.yml" if(environment.production){ url = environment.assetBaseUrl + "/assets/config.yml" } axios.get(url).then(res => { const config = yaml.load(res.data) this.change$.next(config); }).catch(err => { reject(err) }) }) } }

config.yml

global: handlebarCalendarModelType: "0" handlebarCalendarModel: - "2023-01-01 00:00:00" leftPanelWidth: "200px" # 左侧树容器宽度 leftPanelExpand: true # 左侧容器初始是否展开 true: 展开 false: 收起 handlebarRight: true # 是否展示 handlebar右侧的操作按钮 handlebarRightBtn: # hanlebar右侧操作按钮 控制图标统计区域的显示与否 - selected: true # 是否选中 show: true # 是否显示 label: "总量统计" icon: "icon-proxy" key: "cardNumStatis" # 每个按钮都应该有唯一的key - selected: true show: true label: "对比统计" icon: "icon-pie1" key: "pieAndBar" - selected: true show: false label: "趋势统计" icon: "icon-pie1" key: "lineTrend" barStyleConfig: grid: bottom: 30 xAxis: axisLabel: width: 80 modules: demoPage: leftPanelExpand: true barStyleConfig: grid: bottom: 45 xAxis: axisLabel: overflow: "truncate" # 截断 rotate: 330 # 旋转度数

demo

demo-component.ts

import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { ActivatedRoute, NavigationEnd, Router, RouterEvent } from "@angular/router"; import { BaseViewComponent } from "@app/core/config/base-view.component"; import { ConfigService } from "@app/core/config/config.service"; import { DEMOPAGE } from "@app/core/config/view-key"; import { removeNullProperty } from "@app/core/utils"; import { AccountAssetService } from "@app/services/data-asset-management/account-asset/account-asset.service"; import { format } from "date-fns"; @Component({ selector: 'app-demo', templateUrl: './demo.component.html', styleUrls: ['./demo.component.less'] }) export class DemoComponent extends BaseViewComponent { q = { } constructor( private router: Router, public activatedRoute: ActivatedRoute, public configService: ConfigService, private apiService: AccountAssetService, ) { super(DEMOPAGE, configService) console.log("DemoComponent constructor"); } ngOnInit(): void { console.log("DemoComponent ngOnInit"); super.ngOnInit() } ngOnDestroy(): void { console.log("DemoComponent ngOnDestroy"); super.ngOnDestroy() } configServiceReaderAfter(config) { console.log("configServiceReaderAfter..."); return new Promise(async (resolve, reject) => { this.refsh() resolve(null) }) } async refsh() { //await this.getAccountTypeTreeL1() if (this.config.handlebarRight) { this.getPieChart1Data() this.getPieChart2Data() this.getBarChartData() this.getAccountCard() } this.getData() } getAccountCard() { } getPieChart1Data() { } getPieChart2Data() { } getBarChartData() { } getData() { let params: { [key: string]: any } = { ...this.q, pageNum: this.pageNum, pageSize: this.pageSize, } if (this.config.handlebarRight) { params.startTimeStr = this.config.query.startTimeStr params.endTimeStr = this.config.query.endTimeStr } this.apiService.getAccountListByPageApi(removeNullProperty(params)).then((res: resType) => { if (res.resultStat == "0") { this.tableData = res.data.list this.tableTotal = res.data.total } }) } /** handlebar 操作栏 */ handleChange(e: any) { console.log(e); if(e.type === "button"){ this.config.handlebarRightBtnSelected[e.data.key] = e.data.selected }else if(e.type === "calendar"){ if(e.data.length === 2){ this.config.query = { startTimeStr: format(e.data[0], 'yyyy-MM-dd HH:mm:ss').substring(0,10)+ " 00:00:00", endTimeStr: format(e.data[1], 'yyyy-MM-dd HH:mm:ss').substring(0,10)+ " 23:59:59", } }else{ this.config.query = { startTimeStr: "", endTimeStr: "" } } this.refsh() } } }

合并后的配置对象config

{ "leftPanelExpand": true, "handlebarCalendarModel": [ "2022-12-31T16:00:00.000Z", "2024-02-04T15:59:59.000Z" ], "query": { "startTimeStr": "2023-01-01 00:00:00", "endTimeStr": "2024-02-04 23:59:59" }, "handlebarCalendarModelType": "0", "leftPanelWidth": "200px", "handlebarRight": true, "handlebarRightBtn": [ { "selected": true, "show": true, "label": "总量统计", "icon": "icon-proxy", "key": "cardNumStatis" }, { "selected": true, "show": true, "label": "对比统计", "icon": "icon-pie1", "key": "pieAndBar" } ], "barStyleConfig": { "grid": { "bottom": 45 }, "xAxis": { "axisLabel": { "width": 80, "overflow": "truncate", "rotate": 330 } } }, "handlebarRightBtnSelected": { "cardNumStatis": true, "pieAndBar": true } }
如果对你有用的话,可以打赏哦
打赏
ali pay
wechat pay

本文作者:千寻

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!