Angular组件(一) 分割面板ShrinkSplitter

Angular组件(一) 分割面板ShrinkSplitter

前言

分割面板在日常开发中经常使用,可将一片区域,分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件,用angular实现该功能,支持拖拽和[(ngModel)]双向绑定的方式控制区域的展示收起和拖拽功能。

module.ts

import { CommonModule } from "@angular/common";
import { NgModule } from "@angular/core";
import { TlShrinkSplitterComponent } from "./shrink-splitter.component";
import{NzToolTipModule} from "ng-zorro-antd/tooltip"const COMMENT = [TlShrinkSplitterComponent];@NgModule({declarations: [...COMMENT],exports: [...COMMENT],imports: [CommonModule,NzToolTipModule,]
})
export class TlShrinkSplitterModule {}

component.ts

import { AfterContentInit, AfterViewInit, ChangeDetectorRef, Component, ContentChildren, ElementRef, EventEmitter, forwardRef, Input, OnInit, Output, QueryList, TemplateRef, ViewChild } from "@angular/core";
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from "@angular/forms";
import { TlTemplateDirective } from "topdsm-lib/common"
import { isFalsy } from "topdsm-lib/core/util";
import { off, on } from "./util";@Component({selector: "tl-shrink-splitter",templateUrl: "./shrink-splitter.component.html",providers: [{provide: NG_VALUE_ACCESSOR,useExisting: forwardRef(() => TlShrinkSplitterComponent),multi: true}],host: {class: "tl-shrink-splitter",'[class.expand]': 'tlExpand','[class.contract]': '!tlExpand','[class.contract-left]': 'tlColsedMode === "left"','[class.contract-right]': 'tlColsedMode === "right"','[class.contract-top]': 'tlColsedMode === "top"','[class.contract-bottom]': 'tlColsedMode === "bottom"','[style.z-index]': 'tlZIndex',}
})
export class TlShrinkSplitterComponent implements OnInit, AfterContentInit, AfterViewInit, ControlValueAccessor {prefix = "tl-shrink-splitter"offset = 0oldOffset: number | string = 0isMoving = falseinitOffset = 0_value: number | string = 0.5isOpen = true@Input()tlZIndex = 10// @Input()// tlMode: "horizontal" | "vertical" = "horizontal"/** 是否展示收起icon */@Input()tlShowExpandIcon = true/** 收起容器模式,上下左右哪一个容器应用收起展开的状态 */@Input()tlColsedMode: "left" | "right" | "top" | "bottom" = "left"@Input()tlMin = "40px"@Input()tlMax = "40px"@Input()tlExpandTooltipContent = ""@Input()tlContractTooltipContent = ""get value() {return this._value}set value(val: number | string) {this._value = valthis.onChange(val)this.computeOffset()}expandValueCache: string | number = 0/** 展开状态 */get tlExpand() {return this.isOpen;}@Input()set tlExpand(val: boolean) {if (val !== this.isOpen) {this.isOpen = val;this.tlExpandChange.emit(val);this.changeExpand(val)}}/** 容器展开状态切换 */changeExpand(status: boolean) {if (!status) {// 收起this.expandValueCache = this.valueif (this.tlColsedMode === "left") {this.value = 0} else if (this.tlColsedMode === "right") {this.value = 1} else if (this.tlColsedMode === "top") {this.value = 0} else if (this.tlColsedMode === "bottom") {this.value = 1}} else {// 展开this.value = this.expandValueCachethis.expandValueCache = 0}}/** 展开收缩切换事件 */@Output() readonly tlExpandChange = new EventEmitter<boolean>();@Output() readonly onMoveStart = new EventEmitter();@Output() readonly onMoving = new EventEmitter<MouseEvent>();@Output() readonly onMoveEnd = new EventEmitter();expandChange(e: MouseEvent) {e.stopPropagation();e.preventDefault()this.tlExpand = !this.isOpen}@ContentChildren(TlTemplateDirective)templates?: QueryList<TlTemplateDirective>leftTemplate?: TemplateRef<void> | null = nullrightTemplate?: TemplateRef<void> | null = nulltopTemplate?: TemplateRef<void> | null = nullbottomTemplate?: TemplateRef<void> | null = null@ViewChild('outerWrapper')outerWrapper: ElementRef;get isHorizontal() {//return this.tlMode === 'horizontal';return this.tlColsedMode === "left" || this.tlColsedMode === "right"}get computedMin() {return this.getComputedThresholdValue('tlMin');}get computedMax() {return this.getComputedThresholdValue('tlMax');}get anotherOffset() {return 100 - this.offset;}get valueIsPx() {return typeof this.value === 'string';}get offsetSize() {return this.isHorizontal ? 'offsetWidth' : 'offsetHeight';}get paneClasses() {let classes = {}classes[`${this.prefix}-pane`] = trueclasses[`${this.prefix}-pane-moving`] = this.isMovingreturn classes}/** 展开收起触发器icon */get triggrrClass() {let classes = {}if (this.tlColsedMode === "left" && this.isOpen) {classes["icon-caret-left"] = true} else if (this.tlColsedMode === "left" && !this.isOpen) {classes["icon-caret-right"] = true} else if (this.tlColsedMode === "right" && this.isOpen) {classes["icon-caret-right"] = true} else if (this.tlColsedMode === "right" && !this.isOpen) {classes["icon-caret-left"] = true} else if (this.tlColsedMode === "top" && this.isOpen) {classes["icon-caret-left"] = true} else if (this.tlColsedMode === "top" && !this.isOpen) {classes["icon-caret-right"] = true} else if (this.tlColsedMode === "bottom" && this.isOpen) {classes["icon-caret-right"] = true} else if (this.tlColsedMode === "bottom" && !this.isOpen) {classes["icon-caret-left"] = true}return classes}get tooltipPosition() {let position = "right"if (this.tlColsedMode === "right" && !this.isOpen) {position = "left"}return position}get tooltipContent() {let tooltip = ""if (this.tlColsedMode === "left" && this.isOpen) {tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起左侧内容" : this.tlExpandTooltipContent} else if (this.tlColsedMode === "left" && !this.isOpen) {tooltip = isFalsy(this.tlContractTooltipContent) ? "展开左侧内容" : this.tlContractTooltipContent} else if (this.tlColsedMode === "right" && this.isOpen) {tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起右侧内容" : this.tlExpandTooltipContent} else if (this.tlColsedMode === "right" && !this.isOpen) {tooltip = isFalsy(this.tlContractTooltipContent) ? "展开右侧内容" : this.tlContractTooltipContent} else if (this.tlColsedMode === "top" && this.isOpen) {tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起顶部内容" : this.tlExpandTooltipContent} else if (this.tlColsedMode === "top" && !this.isOpen) {tooltip = isFalsy(this.tlContractTooltipContent) ? "展开顶部内容" : this.tlContractTooltipContent} else if (this.tlColsedMode === "bottom" && this.isOpen) {tooltip = isFalsy(this.tlExpandTooltipContent) ? "收起底部内容" : this.tlExpandTooltipContent} else if (this.tlColsedMode === "bottom" && !this.isOpen) {tooltip = isFalsy(this.tlContractTooltipContent) ? "展开底部内容" : this.tlContractTooltipContent}return tooltip}px2percent(numerator: string | number, denominator: string | number) {return parseFloat(numerator + "") / parseFloat(denominator + "");}computeOffset() {this.offset = (this.valueIsPx ? this.px2percent(this.value as string, this.outerWrapper.nativeElement[this.offsetSize]) : this.value) as number * 10000 / 100}getComputedThresholdValue(type) {let size = this.outerWrapper.nativeElement[this.offsetSize];if (this.valueIsPx) return typeof this[type] === 'string' ? this[type] : size * this[type];else return typeof this[type] === 'string' ? this.px2percent(this[type], size) : this[type];}getMin(value1, value2) {if (this.valueIsPx) return `${Math.min(parseFloat(value1), parseFloat(value2))}px`;else return Math.min(value1, value2);}getMax(value1, value2) {if (this.valueIsPx) return `${Math.max(parseFloat(value1), parseFloat(value2))}px`;else return Math.max(value1, value2);}getAnotherOffset(value) {let res: string | number = 0;if (this.valueIsPx) res = `${this.outerWrapper.nativeElement[this.offsetSize] - parseFloat(value)}px`;else res = 1 - value;return res;}handleMove = (e) => {let pageOffset = this.isHorizontal ? e.pageX : e.pageY;let offset = pageOffset - this.initOffset;let outerWidth = this.outerWrapper.nativeElement[this.offsetSize];let value: string | number = ""if (this.valueIsPx) {value = `${parseFloat(this.oldOffset as string) + offset}px`} else {value = this.px2percent(outerWidth * (this.oldOffset as number) + offset, outerWidth)}let anotherValue = this.getAnotherOffset(value);if (parseFloat(value + "") <= parseFloat(this.computedMin + "")) value = this.getMax(value, this.computedMin);if (parseFloat(anotherValue + "") <= parseFloat(this.computedMax)) value = this.getAnotherOffset(this.getMax(anotherValue, this.computedMax));e.atMin = this.value === this.computedMin;e.atMax = this.valueIsPx ? this.getAnotherOffset(this.value) === this.computedMax : (this.getAnotherOffset(this.value) as number).toFixed(5) === this.computedMax.toFixed(5);this.value = valuethis.onMoving.emit(e)}handleUp = (e) => {this.isMoving = false;off(document, 'mousemove', this.handleMove);off(document, 'mouseup', this.handleUp);this.onMoveEnd.emit()}onTriggerMouseDown(e) {this.initOffset = this.isHorizontal ? e.pageX : e.pageY;this.oldOffset = this.value;this.isMoving = true;on(document, 'mousemove', this.handleMove);on(document, 'mouseup', this.handleUp);this.onMoveStart.emit()}constructor(private cdr: ChangeDetectorRef) { }ngOnInit(): void {console.log("ngOnInit");}ngAfterViewInit(): void {console.log("ngAfterViewInit");this.computeOffset()}ngAfterContentInit() {this.templates?.forEach((item) => {switch (item.getType()) {case 'left':this.leftTemplate = item.template;break;case 'right':this.rightTemplate = item.template;break;case 'top':this.topTemplate = item.template;break;case 'bottom':this.bottomTemplate = item.template;break;default:this.leftTemplate = item.template;break;}});}// 输入框数据变化时onChange: (value: any) => void = () => null;onTouched: () => void = () => null;writeValue(val: number | string): void {if (val !== this.value) {this.value = valthis.computeOffset();this.cdr.markForCheck();}}// UI界面值发生更改,调用注册的回调函数registerOnChange(fn: any): void {this.onChange = fn;}// 在blur(等失效事件),调用注册的回调函数registerOnTouched(fn: any): void {this.onTouched = fn;}// 设置禁用状态setDisabledState?(isDisabled: boolean): void {}
}

TlTemplateDirective指令实现

import { Directive, Input, TemplateRef, ViewContainerRef } from "@angular/core";
import { NzSafeAny } from "topdsm-lib/core/types";@Directive({selector: '[tlTemplate]'
})
export class TlTemplateDirective {@Input('tlTemplate')name: string = "default"// @Input()// type: string = ""constructor(private viewContainer: ViewContainerRef, public template: TemplateRef<NzSafeAny>) {//this.template = templateRef;}ngOnInit(): void {this.viewContainer.createEmbeddedView(this.template)}getType() {return this.name;}
}

事件绑定、解绑

export const on = (function() {if (document.addEventListener) {return function(element, event, handler) {if (element && event && handler) {element.addEventListener(event, handler, false);}};} else {return function(element, event, handler) {if (element && event && handler) {element.attachEvent('on' + event, handler);}};}
})();export const off = (function() {if (document.removeEventListener) {return function(element, event, handler) {if (element && event) {element.removeEventListener(event, handler, false);}};} else {return function(element, event, handler) {if (element && event) {element.detachEvent('on' + event, handler);}};}
})();

component.html

<div [ngClass]="prefix + '-wrapper'" #outerWrapper><div [ngClass]="prefix + '-horizontal'" *ngIf="isHorizontal; else verticalSlot"><div class="left-pane" [ngStyle]="{right: anotherOffset + '%'}" [ngClass]="paneClasses"><ng-container *ngTemplateOutlet="leftTemplate"></ng-container></div><div [ngClass]="prefix + '-trigger-con'" [ngStyle]="{left: offset + '%'}" (mousedown)="onTriggerMouseDown($event)"><div ngClass="tl-shrink-splitter-trigger tl-shrink-splitter-trigger-vertical" ><!-- <span class="tl-shrink-splitter-trigger-bar-con vertical" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" [tTooltip]="tooltipContent" [tooltipPosition]="tooltipPosition" *ngIf="tlShowExpandIcon"></span> --><span class="tl-shrink-splitter-trigger-bar-con vertical" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" nz-tooltip [nzTooltipTitle]="tooltipContent" [nzTooltipPlacement]="tooltipPosition" *ngIf="tlShowExpandIcon"></span></div></div><div class="right-pane" [ngStyle]="{left: offset + '%'}" [ngClass]="paneClasses"><ng-container *ngTemplateOutlet="rightTemplate"></ng-container></div></div><ng-template #verticalSlot><div [ngClass]="prefix + '-vertical'" ><div class="top-pane" [ngStyle]="{bottom: anotherOffset + '%'}" [ngClass]="paneClasses"><ng-container *ngTemplateOutlet="topTemplate"></ng-container></div><div [ngClass]="prefix + '-trigger-con'" [ngStyle]="{top: offset + '%'}" (mousedown)="onTriggerMouseDown($event)"><div ngClass="tl-shrink-splitter-trigger tl-shrink-splitter-trigger-horizontal" ><!-- <span class="tl-shrink-splitter-trigger-bar-con horizontal" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" [tTooltip]="tooltipContent" [tooltipPosition]="tooltipPosition" *ngIf="tlShowExpandIcon"></span> --><span class="tl-shrink-splitter-trigger-bar-con horizontal" [ngClass]="triggrrClass" (mousedown)="expandChange($event)" nz-tooltip [nzTooltipTitle]="tooltipContent" [nzTooltipPlacement]="tooltipPosition" *ngIf="tlShowExpandIcon"></span></div></div><div class="bottom-pane" [ngStyle]="{top: offset + '%'}" [ngClass]="paneClasses"><ng-container *ngTemplateOutlet="bottomTemplate"></ng-container></div></div></ng-template>
</div>

component.less

@split-prefix-cls: ~"tl-shrink-splitter";
@trigger-bar-background: rgba(23, 35, 61, 0.25);
@trigger-background: #f8f8f9;
@trigger-width: 8px;
@trigger-bar-width: 4px;
@trigger-bar-offset: (@trigger-width - @trigger-bar-width) / 2;
@trigger-bar-interval: 3px;
@trigger-bar-weight: 1px;
@trigger-bar-con-height: 20px;
.tl-shrink-splitter{position: relative;height: 100%;width: 100%;
}
.tl-shrink-splitter-wrapper{position: relative;height: 100%;width: 100%;
}.@{split-prefix-cls}{background-color: #fff;border: 1px solid #dee2e6;&-pane{position: absolute;transition: all .3s ease-in;padding: 8px;&.tl-shrink-splitter-pane-moving{transition: none;}&.left-pane, &.right-pane {top: 0;bottom: 0;}&.left-pane {left: 0;}&.right-pane {right: 0;padding-left: 16px;}&.top-pane, &.bottom-pane {left: 0;right: 0;}&.top-pane {top: 0;}&.bottom-pane {bottom: 0;padding-top: 16px;}&-moving{-webkit-user-select: none;-moz-user-select: none;-ms-user-select: none;user-select: none;}}&-trigger{border: 1px solid #dcdee2;&-con {position: absolute;transform: translate(-50%, -50%);z-index: 10;}&-bar-con {position: absolute;overflow: hidden;&:hover{color: #000 !important;}&.vertical {top: 50%;left: -6px;width: 20px;height: @trigger-bar-con-height;background-color: #fff;border: 1px solid #ccc;border-radius: 50%;display: flex;align-items: center;justify-content: center;color: #b2b2b2;font-size: 14px;cursor: pointer;}&.horizontal {left: 50%;top: -4px;width: @trigger-bar-con-height;height: 20px;//transform: translate(-50%, 0);background-color: #fff;border: 1px solid #ccc;border-radius: 50%;display: flex;align-items: center;justify-content: center;color: #b2b2b2;font-size: 14px;cursor: pointer;}}&-vertical {width: @trigger-width;height: 100%;background: @trigger-background;border-top: none;border-bottom: none;cursor: col-resize;.@{split-prefix-cls}-trigger-bar {width: @trigger-bar-width;height: 1px;background: @trigger-bar-background;float: left;margin-top: @trigger-bar-interval;}}&-horizontal {height: @trigger-width;width: 100%;background: @trigger-background;border-left: none;border-right: none;cursor: row-resize;.@{split-prefix-cls}-trigger-bar {height: @trigger-bar-width;width: 1px;background: @trigger-bar-background;float: left;margin-right: @trigger-bar-interval;}}}&-horizontal {.@{split-prefix-cls}-trigger-con {top: 50%;height: 100%;width: 0;}}&-vertical {.@{split-prefix-cls}-trigger-con {left: 50%;height: 0;width: 100%;}}
}.tl-shrink-splitter.contract{.tl-shrink-splitter-trigger-vertical{width: 0;padding-left: 0;}.tl-shrink-splitter-trigger-horizontal{height: 0;padding-top: 0;}.tl-shrink-splitter-trigger{border: 0;}&.contract-left{.tl-shrink-splitter-pane.left-pane{width: 0;padding: 0;overflow: hidden;}.right-pane{padding-left: 8px;}}.tl-shrink-splitter-trigger-bar-con{&.vertical{left: -6px;}}&.contract-right{.tl-shrink-splitter-trigger-bar-con{&.vertical{left: -16px;}}}&.contract-top{.tl-shrink-splitter-pane.top-pane{overflow: hidden;height: 0;padding: 0;}.bottom-pane{padding-top: 8px;}.tl-shrink-splitter-trigger-bar-con.horizontal{transform: rotate(90deg);}}&.contract-bottom{.tl-shrink-splitter-trigger-bar-con{&.horizontal{top: -16px;}}.tl-shrink-splitter-pane.bottom-pane{overflow: hidden;height: 0;padding: 0;}.top-pane{padding-top: 8px;}.tl-shrink-splitter-trigger-bar-con.horizontal{transform: rotate(90deg);}}
}
.tl-shrink-splitter.expand{.tl-shrink-splitter-trigger-bar-con{&.vertical{left: -8px;}}&.contract-top{.tl-shrink-splitter-trigger-bar-con.horizontal{transform: rotate(90deg);}}&.contract-bottom{.tl-shrink-splitter-trigger-bar-con.horizontal{transform: rotate(90deg);}}
}

页面效果

左右容器和上下容器
image.png

demo

import { Component } from '@angular/core';@Component({selector: 'tl-demo-shrink-splitter-basic',template: `<button tButton type="button" label="切换伸缩状态" class="ui-plusWidth ui-button-primary" style="margin-right: 8px" (click)="expandChange()"></button><div class="split-box"><tl-shrink-splitter [(tlExpand)]="expand" [(ngModel)]="value" (onMoving)="triggerMoveHnadle($event)"><ng-template tlTemplate="left"><div>左侧区域自定义</div></ng-template><ng-template tlTemplate="right"><div>右侧区域自定义</div></ng-template></tl-shrink-splitter>  </div>`,styles: [`.split-box{height: 200px;display: flex;position: relative;overflow: hidden;}.split-right{margin-left: 10px;border: 1px solid #e3e3e3;flex:1;}`]
})
export class TlDemoShrinkSplitterBasicComponent {expand = truevalue = 0.3expandChange(){this.expand = !this.expand}triggerMoveHnadle(e){console.log(e);console.log(this.value);  }
}

image.png

image.png

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

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

相关文章

Docker容器引擎(2)

目录 一.批量删除镜像&#xff0c;容器 二.Docker 网络实现原理 随机映射端口&#xff08;从32768开始&#xff09; 访问自己&#xff1a; 在10服务器上配置路由转发&#xff1a; 指定映射端口&#xff1a; 查看容器的输出和日志信息&#xff1a; 将宿主机目标|文件挂载…

RabbitMQ中交换机的应用及原理,案例的实现

目录 一、介绍 1. 概述 2. 作用及优势 3. 工作原理 二、交换机Exchange 1. Direct 2. Topic 3. Fanout 三、代码案例 消费者代码 1. 直连direct 生产者代码 测试 2. 主题topic 生产者代码 测试 3. 扇形fanout 生产者代码 测试 每篇一获 一、介绍 1. …

Vue的生命周期方法

beforeCreate 在实例初始化之后&#xff0c;数据观测&#xff08;data observe&#xff09;和 event/watcher 事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问。 created 实例已经创建完成之后被调用。在这一步&#xff0c;实…

【JavaEE进阶】 MyBatis使用注解实现增删改查

文章目录 &#x1f343;前言&#x1f334;传递参数&#x1f38b;增(Insert)&#x1f6a9;返回主键 &#x1f384;删(Delete)&#x1f332;改(Update)&#x1f333;查(Select)&#x1f6a9;起别名&#x1f6a9;结果映射&#x1f6a9;开启驼峰命名(推荐使用) ⭕总结 &#x1f343…

[UI5 常用控件] 01.Text

文章目录 前言1. 普通文本2. 长文本&#xff1a;3. 设置最大显示行数 ( maxLines3 )4. 单行显示 ( wrappingfalse )5. 显示空白符 ( renderWhitespacetrue )6. 使用 - 连接单词:只适用于英文 ( wrappingTypeHyphenated )7. 空白时使用 - 代替 ( emptyIndicatorModeOn )8. JSON数…

2024年需要重点关注的15种计算机病毒

2024年&#xff0c;计算机病毒威胁变得愈发多元化和复杂化。涉及勒索病毒、二维码病毒、挖矿木马等15种类型&#xff0c;这些病毒从数据勒索到系统入侵&#xff0c;对全球网络安全构成严峻挑战。 2024年&#xff0c;计算机病毒威胁变得愈发多元化和复杂化。涉及勒索病毒、二维码…

【网络安全】常见的网络威胁有哪些?

随着互联网的快速发展&#xff0c;网络安全问题日益凸显。常见的网络威胁包括病毒、木马、恶意软件等。这些威胁不仅会影响计算机的安全运行&#xff0c;还会窃取用户的个人信息&#xff0c;造成巨大的损失。因此&#xff0c;我们需要采取一些措施来保护自己的网络安全。 常见的…

android 图片添加水印

android 图片添加水印 本文主要讲下android 中如何给图片添加水印. 在Android中给图片添加水印可以使用Bitmap、Matrix和Canvas类的方法来实现. private Bitmap addWater() {// 加载原始图片Bitmap sourceBitmap BitmapFactory.decodeResource(getResources(), R.mipmap.sou…

Vue+OpenLayers7:OpenLayers7地图初始化时如何设置默认缩放级别、设置默认地图中心点、最大缩放级别和最小缩放级别以及默认坐标系

返回《Vue+OpenLayers7》专栏目录:Vue+OpenLayers7 本章讲解OpenLayers7地图初始化时如何设置默认缩放级别、设置默认地图中心点、最大缩放级别和最小缩放级别以及默认坐标系等配置。 前言 OpenLayers7地图初始化时如何设置默认缩放级别、初始化时设置默认地图中心点、设置…

启动mitmproxy报错 ImportError: cannot import name ‘url_quote‘ from ‘werkzeug.urls‘

报错截图 ImportError: cannot import name url_quote from werkzeug.urls (d:\soft\python\python38\lib\site-packages\werkzeug\urls.py) 原因是Werkzeug版本不兼容导致 解决方法 pip install Werkzeug2.2.2

大数据学习之Flink算子、了解(Source)源算子(基础篇二)

Source源算子&#xff08;基础篇二&#xff09; 目录 Source源算子&#xff08;基础篇二&#xff09; 二、源算子&#xff08;source&#xff09; 1. 准备工作 2.从集合中读取数据 可以使用代码中的fromCollection()方法直接读取列表 也可以使用代码中的fromElements()方…

北斗短报文DTU 北斗通信DTU无线数传终端

北斗是我国自主建设的卫星导航系统&#xff0c;被广泛应用于全球定位、导航和时间同步等领域。随着物联网的迅猛发展&#xff0c;北斗短报文DTU作为物联网连接的关键技术&#xff0c;成为了各行各业的热门话题。 ** 一、北斗短报文DTU的概念与特点 **   北斗短报文DTU(Data…

二叉树知识

提示&#xff1a;文章 文章目录 前言一、背景二、 2.1 2.2 总结 前言 前期疑问&#xff1a; 本文目标&#xff1a; 一、背景 二、二叉树初始知识 题目 源于做的一道牛课题 若一颗完全二叉树中某节点无左孩子&#xff0c;则该节点是 A、高度为1的节点 B、高度为2的节点…

Redis持久化和集群架构

目录 Redis持久化 RDB快照&#xff08;snapshot&#xff09; RDB优点 RDB缺点 RDB的触发机制 AOF持久化 AOF文件重写 AOF触发机制 混合模式 Redis主从架构 Redis哨兵高可用架构 Redis Cluster架构 槽位定位算法 跳转重定位 Redis集群节点间的通信机制 Redis持久化…

在 MATLAB 中注释多行

使用 MATLAB 中的注释块注释多行代码 要注释一行或两行代码&#xff0c;我们可以使用%字符来完成。但是&#xff0c;如果我们必须注释多行代码&#xff0c;则此方法将花费大量时间。我们可以使用注释块来注释多行代码&#xff0c;而不是使用%来注释多行代码。写在该块中的任何…

【数学建模】综合评价方法

文章目录 综合评价的基本理论和数据预处理综合评价的基本概念综合评价体系的构建综合指标的预处理方法评价指标预处理示例 常用的综合评价数学模型线性加权综合评价模型TOPSIS法灰色关联度分析熵值法秩和比&#xff08;RSR&#xff09;法综合评价示例 综合评价的基本理论和数据…

【学网攻】 第(3)节 -- 交换机配置聚合端口

文章目录 【学网攻】 第(1)节 -- 认识网络【学网攻】 第(2)节 -- 交换机认识及使用 前言 网络已经成为了我们生活中不可或缺的一部分&#xff0c;它连接了世界各地的人们&#xff0c;让信息和资源得以自由流动。随着互联网的发展&#xff0c;我们可以通过网络学习、工作、娱乐…

精品基于Uniapp+springboot自习室预约系统App教室阅览室

《[含文档PPT源码等]精品基于Uniappspringboot自习室预约系统App》该项目含有源码、文档、PPT、配套开发软件、软件安装教程、项目发布教程、包运行成功&#xff01; 软件开发环境及开发工具&#xff1a; 开发语言&#xff1a;Java 后台框架&#xff1a;springboot、ssm 安…

【数据库连接】连接sqlite3报错:go-sqlite3 requires cgo to work. This is a stub

报错信息 register db Ping default, Binary was compiled with ‘CGO_ENABLED0’, go-sqlite3 requires cgo to work. This is a stubWindows解决办法 新建环境变量 新报错 Failed to build the application: # runtime/cgo cgo: C compiler “gcc” not found: exec: “gc…

IaC基础设施即代码:Terraform 连接 tencentcloud COS 实现多资源管理

目录 一、实验 1.环境 2.Terraform 连接 tencentcloud 腾讯云COS 3.申请VPC专有网络资源 4.申请安全组资源 5.申请CVM资源 6.申请CLB资源 7.申请DNS资源 8.销毁资源 二、问题 1. Terraform申请安全组资源失败 2.Terraform验证云主机资源报错 3. A记录和CNAME的区别 …