Angular BaseView抽离页面公用属性

前言

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

页面设计

在这里插入图片描述

组件抽离

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
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/370522.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

npm ERR! code CERT_HAS_EXPIRED

执行npm i报错&#xff1a; npm ERR! code ETIMEDOUT npm ERR! syscall connect npm ERR! errno ETIMEDOUT npm ERR! network request to https://registry.npmjs.org/react-redux failed, reason: connect ETIMEDOUT 104.16.2.35:443 npm ERR! network This is a problem rel…

[设计模式Java实现附plantuml源码~结构型]处理多维度变化——桥接模式

前言&#xff1a; 为什么之前写过Golang 版的设计模式&#xff0c;还在重新写Java 版&#xff1f; 答&#xff1a;因为对于我而言&#xff0c;当然也希望对正在学习的大伙有帮助。Java作为一门纯面向对象的语言&#xff0c;更适合用于学习设计模式。 为什么类图要附上uml 因为很…

python_蓝桥杯刷题记录_笔记_全AC代码_入门3

前言 记录我的解法以及笔记思路&#xff0c;谢谢观看。 题单目录 1.P2141 [NOIP2014 普及组] 珠心算测验 2.P1567 统计天数 3.P1055 [NOIP2008 普及组] ISBN 号码 4.P1200 [USACO1.1] 你的飞碟在这儿 Your Ride Is Here 5.P1308 [NOIP2011 普及组] 统计单词数 6.P1047 […

[职场] CAD设计师简历模板 #微信#知识分享#学习方法

CAD设计师简历模板 一份优秀的简历能帮助你更好地让你所应聘的公司了解你&#xff0c;并能更好地找到你想要的工作。因此&#xff0c;如何撰写一份优异的简历显得尤为重要。以下是申请“CAD设计师”职位时的一份简历模板&#xff0c;供大家参考阅读。 蓝山简历 求职意向&#…

CSDN文章导出工具

源码地址&#xff1a; github:https://github.com/lishuangquan1987/CSDNExportergitee:https://gitee.com/lishuangquan1987/csdnexporter 介绍 最近有CSDN博客导出来的需求&#xff0c;翻看了很多开源工具&#xff0c;都不能用或者不好用&#xff0c;于是决定自己做一个。…

vulhub中Adminer远程文件读取漏洞复现(CVE-2021-43008)

Adminer是一个PHP编写的开源数据库管理工具&#xff0c;支持MySQL、MariaDB、PostgreSQL、SQLite、MS SQL、Oracle、Elasticsearch、MongoDB等数据库。 在其版本1.12.0到4.6.2之间存在一处因为MySQL LOAD DATA LOCAL导致的文件读取漏洞。 参考链接&#xff1a; https://gith…

外包干了10个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…

【JavaEE进阶】 图书管理系统开发日记——肆

文章目录 &#x1f343;前言&#x1f38d;约定前后端交互接⼝&#x1f340;服务器代码实现&#x1f6a9;控制层&#x1f6a9;业务层&#x1f6a9;数据层 &#x1f334;前端代码的修改⭕总结 &#x1f343;前言 今天我们来实现修改图书模块 首先我们先来看一下&#xff0c;需要…

深度学习在智能交互中的应用:人与机器的和谐共生

深度学习与人类的智能交互是当前人工智能领域研究的热点之一。深度学习作为机器学习的一个重要分支&#xff0c;具有强大的特征学习和模式识别能力&#xff0c;可以模拟人脑的神经网络进行数据分析和预测。而人类的智能交互则是指人类与机器之间的信息交流和操作互动&#xff0…

运维自动化bingo前端

项目目录结构介绍 项目创建完成之后&#xff0c;我们会看到bingo_web项目其实是一个文件夹&#xff0c;我们进入到文件夹内部就会发现一些目录和文件&#xff0c;我们简单回顾一下里面的部分核心目录与文件。 ├─node_modules/ # node的包目录&#xff0c;项目运行的依赖包…

逆向基础-破解密码

1.通过study PE查看.exe程序的位数 打开 x32dbg 从暂停到运行程序 原理&#xff1a;软件算出的密码与用户输入的密码作比较 破解流程&#xff1a;查信息 --> 找内存关键数据 --> 测试

Optimizer:基于.Net开发的、提升Windows系统性能的终极开源工具

我们电脑使用久了后&#xff0c;就会产生大量的垃圾文件、无用的配置等&#xff0c;手动删除非常麻烦&#xff0c;今天推荐一个开源工具&#xff0c;可以快速帮助我们更好的优化Windos电脑。 01 项目简介 Optimizer是一个面向Windows系统的优化工具&#xff0c;旨在提升计算机…

[GN] 23种设计模式 —— 常见设计模式学习总结

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言创建型模式 —— 创建的艺术结构型模式 —— 组合的艺术适配器模式 -- 不兼容结构的协调对象适配器类适配器模式优缺点适用场景 组合模式 -- 树形结构的处理例子…

微信小程序课设(基于云开发)

微信小程序通过Laf云平台接入ChatGPT实现聊天&#xff0c;回答方式采用流式回答。 以下是图片展示其页面 回答次数通过卡密兑换 以下是对话页面的代码 <!--pages/content/content.wxml--><view class"container"><view class"div" hidde…

前端工程化基础(二):前端包管理工具npm/yarn/cnpm/npx/pnpm

前端包管理工具 代码共享方案 创建自己的官网&#xff0c; 将代码放到官网上面将代码提交到GitHub上面&#xff0c;负责让使用者下载将代码提交到npm registry上面 下载比较方便&#xff0c;使用npm install xxx即可下载相应的代码npm管理的包 npm配置文件 主要用于存储项目…

视云闪播截图

视云闪播截图 1. 截图设置2. 热键设置3. 视频截取3.1. 保存 -> 完成 References 深度学习图像数据获取工具。 视云闪播 https://www.netposa.com/Service/Download.html 1. 截图设置 视云闪播 -> 系统设置 -> 截图设置 2. 热键设置 视云闪播 -> 系统设置 ->…

洛谷 P3694 邦邦的大合唱站队 【状压DP】

数据约定&#xff1a; N ≤ 1 0 5 , M ≤ 20 N \leq 10^5, M \leq 20 N≤105,M≤20 思路 对于最终排好的状态&#xff0c;如果我们枚举排在最后一位的团队编号 j j j&#xff0c;可以发现&#xff1a;如果这个团队总共有 x x x 人的话&#xff0c;那么 [ n − x 1 , n ] …

计算机设计大赛 深度学习 植物识别算法系统

文章目录 0 前言2 相关技术2.1 VGG-Net模型2.2 VGG-Net在植物识别的优势(1) 卷积核&#xff0c;池化核大小固定(2) 特征提取更全面(3) 网络训练误差收敛速度较快 3 VGG-Net的搭建3.1 Tornado简介(1) 优势(2) 关键代码 4 Inception V3 神经网络4.1 网络结构 5 开始训练5.1 数据集…

一篇文章搞懂CNN(卷积神经网络)及其所含概念

目录 1. 什么是卷积神经网络&#xff1a;2. 应用领域&#xff1a;3. 架构&#xff1a;4. 卷积层的参数和名词参数&#xff1a;名词&#xff1a; 5. 注意&#xff1a;6. 经典网络&#xff1a;小结&#xff1a; 当下&#xff0c;计算机视觉在人工智能领域中扮演着至关重要的角色。…

java数组学习

目录 1.数组概念 2.数组的定义 3.数组的静态初始化 4.地址值 5.数组元素访问 6.索引 7.数组的遍历 8.数组的动态初始化 9.数组两种初始化方式的区别 10.数组常见问题 1.数组概念 数组是一种容器&#xff0c;可以同来存储同种数据类型的多个值。但是数组容器在存储数据…