Angular BaseView抽离页面公用属性

news2024/11/26 19:29:56

前言

如果有一系列的页面布局很类似,为了节省时间,我们可以把这些类似的页面所通用的属性和方法抽离成一个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
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【测试运维】性能测试笔记文档第2篇:性能测试分类和指标(已分享,附代码)

本系列文章md笔记&#xff08;已分享&#xff09;主要讨论性能测试相关知识。入门阶段&#xff1a;认识性能测试分类-(负载测试、压力测试、并发测试、稳定性测试)&#xff0c;常用性能测试指标-(吞吐量、并发数、响应时间、点击数…)&#xff0c;性能测试工具选择。性能脚本&a…

Linux文件编译

目录 一、GCC编译 1.直接编译 2.分步编译 预处理&#xff1a; 编译&#xff1a; 汇编&#xff1a; 链接&#xff1a; 3.多文件编译 4.G 二、Make 1.概述 2.使用步骤 3.makefile创建规则 3.1一个基本规则 3.2两个常用函数 4.示例文件 三、GDB 示例&#xff1a;…

STM32单片机的基本原理与应用(六)

串口测试实验 基本原理 在串口实验中&#xff0c;是通过mini_USB线搭建终端与电脑端&#xff08;也可称终端&#xff0c;为做区分称电脑端&#xff09;的“桥梁”&#xff0c;电脑端的串口调试助手通过mini_USB线向终端发送信息&#xff0c;由CH340芯片将USB接口进行转换&…

PyTorch 2.2 中文官方教程(十五)

&#xff08;beta&#xff09;计算机视觉的量化迁移学习教程 原文&#xff1a;pytorch.org/tutorials/intermediate/quantized_transfer_learning_tutorial.html 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 提示 为了充分利用本教程&#xff0c;我们建议使用这个C…

linux下的预编译、编译、汇编、连接,生成单独文件,感受编译过程

linux下的预编译、编译、汇编、连接&#xff0c;生成单独文件。首先需要确认系统安装了gcc编译器&#xff0c;输入gcc -v或者g -v&#xff0c;如果能看到版本号等信息就是已经存在了&#xff0c;如图&#xff08;centos7&#xff09;&#xff1a; 然后随便vim产生一个.cpp文件&…

什么是前端工程化,请举例说明

前端工程化 前端工程化的定义为什么需要前端工程化前端工程化的核心概念 模块化开发&#xff1a;组件化开发&#xff1a;规范化开发&#xff1a;自动化开发&#xff1a;持续集成 前端工程化的主要工具前端工程化的应用总结&#xff1a; 前端工程化 前端工程化的定义 前端工程…

zabbix监控mariadb数据库

zabbix监控mariadb数据库 1.创建监控用户及授权 [rootchang ~]# mysql -uroot -p123qqq.A MariaDB [(none)]> CREATE USER monitor% IDENTIFIED BY 123qqq.A; MariaDB [(none)]> GRANT REPLICATION CLIENT,PROCESS,SHOW DATABASES,SHOW VIEW ON *.* TO monitor%; Maria…

使用最大边界相关算法处理文章自动摘要

一、需求背景 对于博客或者文章来说&#xff0c;摘要是普遍性的需求。但是我们不可能让作者自己手动填写摘要或者直接暴力截取文章的部分段落作为摘要&#xff0c;这样既不符合逻辑又不具有代表性&#xff0c;那么&#xff0c;是否有相关的算法或者数学理论能够完成这个需求呢&…

【高质量精品】2024美赛B题22页word版高质量半成品论文+多版保奖思路+数据+前四问思路代码等(后续会更新)

一定要点击文末的卡片&#xff0c;进入后&#xff0c;获取完整论文&#xff01;&#xff01; B 题整体模型构建 1. 潜水器动力系统失效&#xff1a;模型需要考虑潜水器在无推进力情况下的行为。 2. 失去与主船通信&#xff1a;考虑无法从主船接收指令或发送位置信息的情况。…

基于若依的ruoyi-nbcio流程管理系统自定义业务实现一种简单的动态任务标题需求

更多ruoyi-nbcio功能请看演示系统 gitee源代码地址 前后端代码&#xff1a; https://gitee.com/nbacheng/ruoyi-nbcio 演示地址&#xff1a;RuoYi-Nbcio后台管理系统 更多nbcio-boot功能请看演示系统 gitee源代码地址 后端代码&#xff1a; https://gitee.com/nbacheng/n…

深度学习介绍

对于具备完善业务逻辑的任务&#xff0c;大多数情况下&#xff0c;正常的人都可以给出一个符合业务逻辑的应用程序。但是对于一些包含超过人类所能考虑到的逻辑的任务&#xff0c;例如面对如下任务&#xff1a; 编写一个应用程序&#xff0c;接受地理信息、卫星图像和一些历史…

【51单片机】开发板&开发软件(Keil5&STC-ISP)简介&下载安装破译传送门(1)

前言 大家好吖&#xff0c;欢迎来到 YY 滴单片机系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过单片机的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的…

【目标跟踪】相机运动补偿

文章目录 一、前言二、简介三、改进思路3.1、状态定义3.2、相机运动补偿3.3、iou和ReID融合3.4、改进总结 四、相机运动补偿 一、前言 目前 MOT (Multiple Object Tracking) 最有效的方法仍然是 Tracking-by-detection。今天给大家分享一篇论文 BoT-SORT。论文地址 &#xff0…

wireshark分析数据包:追踪流

打开追踪流的界面 方法 1 方法 2 选中数据包&#xff0c;右键弹出菜单 说明&#xff1a; 流内容的显示顺序和它在网络上出现的顺序相同。不可打印的字符被点代替。从客户端到服务器的流量被标记为红色&#xff0c;而从服务器到客户端的流量被标记为蓝色。这些颜色可以通过下…

No matching client found for package name ‘com.unity3d.player‘

2024年2月5日更新 下面的一系列操作最终可能都无用&#xff0c;大致这问题出现原因是我在Unity采用了Android方式接入Firebase&#xff0c;而Android接入实际上和Unity接入方式有配置上的不一样&#xff0c;我就是多做了几步操作如下。https://firebase.google.com/docs/androi…

【计算机网络】Socket的SO_TIMEOUT与连接超时时间

SO_TIMEOUT选项是Socket的一个选项&#xff0c;用于设置读取数据的超时时间。它指定了在读取数据时等待的最长时间&#xff0c;如果在指定的时间内没有数据可读取&#xff0c;将抛出SocketTimeoutException异常。 SO_TIMEOUT的设置 默认情况下&#xff0c;SO_TIMEOUT选项的值…

用Jmeter进行接口测试

web接口测试工具&#xff1a; 手工测试的话可以用postman &#xff0c;自动化测试多是用到 Jmeter&#xff08;开源&#xff09;、soupUI&#xff08;开源&商业版&#xff09;。 下面将对前一篇Postman做接口测试中的接口用Jmeter来实现。 一、Jmeter 的使用步骤 打开Jme…

【vscode】windows11在vscode终端控制台中打印console.log()出现中文乱码问题解决

1. 问题描述 在前端开发过程中使用vscode编写node.js&#xff0c;需要在控制台中打印一些中文信息&#xff0c;但是一直出现中文乱码问题&#xff0c;英文和数字都显示正常。在网上试了很多设置的办法&#xff0c;最终找到windos11设置中解决。 2. 原因 首先打开控制台&…

通过html2canvas和jsPDF将网页内容导出成pdf

jsPDF参考&#xff1a;https://github.com/parallax/jsPDF html2canvas参考&#xff1a;https://github.com/niklasvh/html2canvas 或者 https://html2canvas.hertzen.com 思路 使用html2canvas将选中DOM生成截图对象将截图对象借助jsPDF导出为PDF文件 代码 这是一个示例&a…

《幻兽帕鲁》解锁基地和工作帕鲁数量上限

帕鲁私服的游戏参数通常可通过配置文件 PalWorldSettings.ini 来进行修改&#xff0c;然而这个配置文件有个别参数对游戏不生效&#xff0c;让人很是头疼。没错&#xff01;我说的就是终端最大的帕鲁数量&#xff01; 其实还有另外一种更加高级的参数修改方式&#xff0c;那就…