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);}}
}
页面效果
左右容器和上下容器
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); }
}
相关文章:

Angular组件(一) 分割面板ShrinkSplitter
Angular组件(一) 分割面板ShrinkSplitter 前言 分割面板在日常开发中经常使用,可将一片区域,分割为可以拖拽整宽度或高度的两部分区域。模仿iview的分割面板组件,用angular实现该功能,支持拖拽和[(ngModel)]双向绑定的方式控制区…...
抖音详情API:视频内容获取与解析技巧
一、引言 抖音是一款广受欢迎的短视频分享平台,每天都有大量的用户在抖音上分享自己的生活点滴和创意作品。对于开发者而言,如何获取并解析抖音上的视频内容,是一项极具挑战性的任务。本文将详细介绍抖音详情API,以及如何使用它来…...
SpringBoot中实现阿里云OSS对象存储
背景 在业务中我们往往需要上传文件如图片,文件上传,是指将本地图片、视频、音频等文件上传到服务器上,可以供其他用户浏览或下载的过程。文件上传在项目中应用非常广泛,我们经常发抖音、发朋友圈都用到了文件上传功能。 实现文件…...

大型语言模型 (LLM)全解读
一、大型语言模型(Large Language Model)定义 大型语言模型 是一种深度学习算法,可以执行各种自然语言处理 (NLP) 任务。 大型语言模型底层使用多个转换器模型, 底层转换器是一组神经网络。 大型语言模型是使用海量数据集进行训练…...

Unity - gamma space下还原linear space效果
文章目录 环境目的环境问题实践结果处理要点处理细节【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理【封装个简单的 *.cginc】 - shader runtime【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】【颜色参数应用前 和 颜色贴图采样后】【灯…...

Rabbitmq调用FeignClient接口失败
文章目录 一、框架及逻辑介绍1.背景服务介绍2.问题逻辑介绍 二、代码1.A服务2.B服务3.C服务 三、解决思路1.确认B调用C服务接口是否能正常调通2.确认B服务是否能正常调用A服务3.确认消息能否正常消费4.总结 四、修改代码验证1.B服务异步调用C服务接口——失败2.将消费消息放到C…...

专业120+总分400+海南大学838信号与系统考研高分经验海大电子信息与通信
今年专业838信号与系统120,总分400,顺利上岸海南大学,这一年的复习起起伏伏,但是最后还是坚持下来的,吃过的苦都是值得,总结一下自己的复习经历,希望对大家复习有帮助。首先我想先强调一下专业课…...
如何区分 html 和 html5?
HTML(超文本标记语言)和HTML5在很多方面都存在显著的区别。HTML5是HTML的最新版本,引入了许多新的特性和元素,以支持更丰富的网页内容和更复杂的交互。以下是一些区分HTML和HTML5的关键点: 新特性与元素:H…...

Ps:将文件载入堆栈
Ps菜单:文件/脚本/将文件载入堆栈 Scripts/Load Files into Stack 将文件载入堆栈 Load Files into Stack脚本命令可用于将两个及以上的文件载入到同一个 Photoshop 新文档中。 载入的每个文件都将成为独立的图层,并使用其原始文件名作为图层名。 Photos…...

【格密码基础】:补充LWE问题
目录 一. LWE问题的鲁棒性 二. LWE其他分布选择 三. 推荐文献 四. 附密码学人心中的顶会 一. LWE问题的鲁棒性 robustness,翻译为鲁棒性 已有的论文表明,及时敌手获取到部分关于秘密和error的信息,LWE问题依旧是困难的,这能…...

【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]
阅读导航 引言一、特殊类 --- 不能被拷贝的类1. C98方式:2. C11方式: 二、特殊类 --- 只能在堆上创建对象的类三、特殊类 --- 只能在栈上创建对象的类四、特殊类 --- 不能被继承的类1. C98方式2. C11方法 总结温馨提示 引言 在面向对象编程中࿰…...

VMware虚拟机部署Linux Ubuntu系统
本文介绍基于VMware Workstation Pro虚拟机软件,配置Linux Ubuntu操作系统环境的方法。 首先,我们需要进行VMware Workstation Pro虚拟机软件的下载与安装。需要注意的是,VMware Workstation Pro软件是一个收费软件,而互联网中有很…...

RFID标签:数字时代的智能身份
在数字时代,RFID标签(Radio-Frequency Identification)成为物联网(IoT)中不可或缺的一环。作为一种小巧却功能强大的设备,RFID标签在各个领域的应用不断扩展,为我们的生活和工作带来了新的可能性…...
《动手学深度学习(PyTorch版)》笔记3.2
注:书中对代码的讲解并不详细,本文对很多细节做了详细注释。另外,书上的源代码是在Jupyter Notebook上运行的,较为分散,本文将代码集中起来,并加以完善,全部用vscode在python 3.9.18下测试通过。…...

elasticsearch8.x版本docker部署说明
前提,当前部署没有涉及证书和https访问 1、环境说明,我采用三个节点,每个节点启动两个es,用端口区分 主机角色ip和端口服务器Amaster192.168.2.223:9200服务器Adata192.168.2.223:9201服务器Bdata,master192.168.2.224:9200服务器Bdata192.1…...
使用scyllaDb 或者cassandra存储聊天记录
一、使用scyllaDb的原因 目前开源的聊天软件主要还是使用mysql存储数据,数据量大的时候比较麻烦; 我打算使用scyllaDB存储用户的聊天记录,主要考虑的优点是: 1)方便后期线性扩展服务器; 2)p…...

Visual Studio如何修改成英文版
1、打开 Visual Studio Installer 2、点击修改 3、找到语言包,选择需要的语言包,而后点击修改 4、等待下载 5、 安装完成后启动Visual Studio 6、在工具-->选项-->环境-->区域设置-->English并确定 7、重启 Visual Studio,配置…...

gin中使用swagger生成接口文档
想要使用gin-swagger为你的代码自动生成接口文档,一般需要下面三个步骤: 按照swagger要求给接口代码添加声明式注释,具体参照声明式注释格式。使用swag工具扫描代码自动生成API接口文档数据使用gin-swagger渲染在线接口文档页面 第一步&…...

最新AI创作系统ChatGPT网站系统源码,Midjourney绘画V6 ALPHA绘画模型,ChatFile文档对话总结+DALL-E3文生图
一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统,支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美,那么如何搭建部署AI创作ChatGPT?小编这里写一个详细图文教程吧。已支持GPT…...
解析dapp:从底层区块链看DApp的脆弱性和挑战
每天五分钟讲解一个互联网只是,大家好我是啊浩说模式Zeropan_HH 在Web3时代,去中心化应用程序(DApps)已成为数字经济的重要组成部分。它们的同生性,即与底层区块链网络紧密相连、共存亡的特性,为DApps带来…...
DeepSeek 赋能智慧能源:微电网优化调度的智能革新路径
目录 一、智慧能源微电网优化调度概述1.1 智慧能源微电网概念1.2 优化调度的重要性1.3 目前面临的挑战 二、DeepSeek 技术探秘2.1 DeepSeek 技术原理2.2 DeepSeek 独特优势2.3 DeepSeek 在 AI 领域地位 三、DeepSeek 在微电网优化调度中的应用剖析3.1 数据处理与分析3.2 预测与…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)
2025年能源电力系统与流体力学国际会议(EPSFD 2025)将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会,EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包
文章目录 现象:mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时,可能是因为以下几个原因:1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

20个超级好用的 CSS 动画库
分享 20 个最佳 CSS 动画库。 它们中的大多数将生成纯 CSS 代码,而不需要任何外部库。 1.Animate.css 一个开箱即用型的跨浏览器动画库,可供你在项目中使用。 2.Magic Animations CSS3 一组简单的动画,可以包含在你的网页或应用项目中。 3.An…...