LogicFlow 学习笔记——9. LogicFlow 进阶 节点

LogicFlow 进阶 节点(Node)

连线规则

在某些时候,我们可能需要控制边的连接方式,比如开始节点不能被其他节点连接、结束节点不能连接其他节点、用户节点后面必须是判断节点等,想要达到这种效果,我们需要为节点设置以下两个属性。

  • sourceRules - 当节点作为边的起始节点(source)时的校验规则
  • targetRules - 当节点作为边的目标节点(target)时的校验规则

以正方形(square)为例,在边时我们希望它的下一节点只能是圆形节点(circle),那么我们应该给square添加作为source节点的校验规则。

import { RectNode, RectNodeModel } from '@logicflow/core';
class SquareModel extends RectNodeModel {initNodeData(data) {super.initNodeData(data);const circleOnlyAsTarget = {message: "正方形节点下一个节点只能是圆形节点",validate: (sourceNode, targetNode, sourceAnchor, targetAnchor) => {return targetNode.type === "circle";},};this.sourceRules.push(circleOnlyAsTarget);}
}

在上例中,我们为modelsourceRules属性添加了一条校验规则,校验规则是一个对象,我们需要为其提供messagevalidate属性。
message属性是当不满足校验规则时所抛出的错误信息,validate则是传入规则的校验的回调函数。validate方法有两个参数,分别为边的起始节点(source)和目标节点(target),我们可以根据参数信息来决定是否通过校验,其返回值是一个布尔值。

提示
当我们在面板上进行边操作的时候,LogicFlow会校验每一条规则,只有全部通过后才能连接。
在边时,当鼠标松开后如果没有通过自定义规则(validate方法返回值为false),LogicFlow会对外抛出事件connection:not-allowed

lf.on('connection:not-allowed', (msg) => {console.log(msg)
})

下面举个例子,通过设置不同状态下节点的样式来展示连接状态
在节点model中,有个state属性,当节点连接规则校验不通过时,state属性值为5。我们可以通过这个属性来实现连线是节点的提示效果。

新建src/views/Example/LogicFlowAdvance/NodeExample/Component/HexagonNode/index.ts代码如下:

import { ConnectRule, PointTuple, PolygonNode, PolygonNodeModel } from '@logicflow/core'class CustomHexagonModel extends PolygonNodeModel {setAttributes(): void {const width = 100const height = 100const x = 50const y = 50// 计算六边形,中心点为 [50, 50],宽高均为 100const pointsList: PointTuple[] = [[x - 0.25 * width, y - 0.5 * height],[x + 0.25 * width, y - 0.5 * height],[x + 0.5 * width, y],[x + 0.25 * width, y + 0.5 * height],[x - 0.25 * width, y + 0.5 * height],[x - 0.5 * width, y]]this.points = pointsList}getConnectedSourceRules(): ConnectRule[] {const rules = super.getConnectedSourceRules()const geteWayOnlyAsTarget = {message: '下一个节点只能是 circle',validate: (source: any, target: any, sourceAnchor: any, targetAnchor: any) => {console.log('sourceAnchor, targetAnchor, source, target',sourceAnchor,targetAnchor,source,target)return target.type === 'circle'}}rules.push(geteWayOnlyAsTarget)return rules}getNodeStyle(): {[x: string]: anyfill?: string | undefinedstroke?: string | undefinedstrokeWidth?: number | undefined} {const style = super.getNodeStyle()if (this.properties.isSelected) {style.fill = 'red'}if (this.isHovered) {style.stroke = 'red'}// 如果此节点不允许被连接,节点变红if (this.state === 5) {style.fill = 'red'}if (this.state === 4) {style.fill = 'green'}return style}
}export default {type: 'HexagonNode',view: PolygonNode,model: CustomHexagonModel
}

之后新建src/views/Example/LogicFlowAdvance/NodeExample/Example01.vue代码如下:

<script setup lang="ts">
import LogicFlow, { Definition } from '@logicflow/core'
import { onMounted } from 'vue'
import HexagonNode from './Component/HexagonNode'
import '@logicflow/core/dist/style/index.css'const data = {nodes: [{id: '1',type: 'rect',x: 300,y: 100},{id: '2',type: 'circle',x: 300,y: 250},{id: '3',type: 'HexagonNode',x: 100,y: 100,text: '只能连接到圆'}],edges: []
}const SilentConfig = {stopScrollGraph: true,stopMoveGraph: true,stopZoomGraph: true
}const styleConfig: Partial<Definition> = {style: {rect: {rx: 5,ry: 5,strokeWidth: 2},circle: {fill: '#f5f5f5',stroke: '#666'},ellipse: {fill: '#dae8fc',stroke: '#6c8ebf'},polygon: {fill: '#d5e8d4',stroke: '#82b366'},diamond: {fill: '#ffe6cc',stroke: '#d79b00'},text: {color: '#b85450',fontSize: 12}}
}onMounted(() => {const lf = new LogicFlow({container: document.getElementById('container')!,grid: true,...SilentConfig,...styleConfig})lf.register(HexagonNode)lf.setTheme({nodeText: {color: '#000000',overflowMode: 'ellipsis',lineHeight: 1.2,fontSize: 12}})lf.render(data)lf.translateCenter()lf.on('connection:not-allowed', (error) => {alert(error.msg)})
})
</script>
<template><h3>Example Node (Advance) - 01</h3><div id="container"></div>
</template>
<style>
#container {/* 定义容器的宽度和高度 */width: 100%;height: 500px;
}
</style>

运行后效果如下:
在这里插入图片描述

移动

有些时候,我们需要更加细粒度的控制节点什么时候可以移动,什么时候不可以移动,比如在实现分组插件时,需要控制分组节点子节点不允许移动出分组。和连线规则类似,我们可以给节点的moveRules添加规则函数。

class MovableNodeModel extends RectNodeModel {initNodeData(data) {super.initNodeData(data);this.moveRules.push((model, deltaX, deltaY) => {// 需要处理的内容});}
}

graphModel中支持添加全局移动规则,例如在移动A节点的时候,期望把B节点也一起移动了。

lf.graphModel.addNodeMoveRules((model, deltaX, deltaY) => {// 如果移动的是分组,那么分组的子节点也跟着移动。if (model.isGroup && model.children) {lf.graphModel.moveNodes(model.children, deltaX, deltaY, true);}return true;
});

新建src/views/Example/LogicFlowAdvance/NodeExample/Component/CustomNode/index.ts代码如下:

import { RectNode, RectNodeModel } from '@logicflow/core'
class CustomNode extends RectNode {// 禁止节点点击后被显示到所有元素前面toFront() {return false}
}class CustomNodeModel extends RectNodeModel {initNodeData(data: any) {if (!data.text || typeof data.text === 'string') {data.text = {value: data.text || '',x: data.x - 230,y: data.y}}super.initNodeData(data)this.width = 500this.height = 200this.isGroup = truethis.zIndex = -1this.children = data.children}getTextStyle() {const style = super.getTextStyle()style.overflowMode = 'autoWrap'style.width = 15return style}
}export default {type: 'custom-node',view: CustomNode,model: CustomNodeModel
}

新建src/views/Example/LogicFlowAdvance/NodeExample/Component/MovableNode/index.ts,代码如下:

import { RectNode, RectNodeModel } from '@logicflow/core'
class MovableNode extends RectNode {}class MovableNodeModel extends RectNodeModel {initNodeData(data: any) {super.initNodeData(data)this.moveRules.push((model, deltaX, deltaY) => {// 不允许移动到坐标为负值的地方if (model.x + deltaX - this.width / 2 < 0 || model.y + deltaY - this.height / 2 < 0) {return false}return true})console.log(data)this.children = data.childrenif (this.children) {this.isGroup = true}}
}export default {type: 'movable-node',view: MovableNode,model: MovableNodeModel
}

新建src/views/Example/LogicFlowAdvance/NodeExample/Example02.vue代码如下:

<script setup lang="ts">
import LogicFlow from '@logicflow/core'
import { onMounted } from 'vue'
import '@logicflow/core/dist/style/index.css'
import CustomNode from './Component/CustomNode'
import MovableNode from './Component/MovableNode'const data = {nodes: [{id: 'node-1',type: 'custom-node',x: 300,y: 250,text: '你好',children: ['circle-1']},{type: 'movable-node',x: 100,y: 70,text: '你好',children: ['node-1']},{id: 'circle-1',type: 'circle',x: 300,y: 250,text: 'hello world'}],edges: []
}const SilentConfig = {stopScrollGraph: true,stopMoveGraph: true,stopZoomGraph: true
}onMounted(() => {const lf = new LogicFlow({container: document.getElementById('container')!,grid: true,...SilentConfig})lf.register(CustomNode)lf.register(MovableNode)lf.graphModel.addNodeMoveRules((model, deltaX, deltaY) => {console.log(model)if (model.isGroup && model.children) {// 如果移动的是分组,那么分组的子节点也跟着移动。lf.graphModel.moveNodes(model.children, deltaX, deltaY, true)}return true})lf.render(data)lf.translateCenter()
})
</script>
<template><h3>Example Node (Advance) - 02</h3><div id="container"></div>
</template>
<style>
#container {/* 定义容器的宽度和高度 */width: 100%;height: 500px;
}
</style>

运行后效果如下:
在这里插入图片描述

锚点

对于各种基础类型节点,LogicFlow都内置了默认锚点。LogicFlow支持通过重写获取锚点的方法来实现自定义节点的锚点。
新建src/views/Example/LogicFlowAdvance/NodeExample/Component/SqlEdge/index.ts代码如下:

import { PolylineEdge, PolylineEdgeModel } from '@logicflow/core'// 自定义边模型类,继承自 BezierEdgeModel
class CustomEdgeModel2 extends PolylineEdgeModel {/*** 重写 getEdgeStyle 方法,定义边的样式*/getEdgeStyle() {const style = super.getEdgeStyle() // 调用父类方法获取默认的边样式style.strokeWidth = 1 // 设置边的线条宽度为1style.stroke = '#ababac' // 设置边的颜色为淡灰色return style // 返回自定义的边样式}/*** 重写 getData 方法,增加锚点数据的保存*/getData() {const data: any = super.getData() // 调用父类方法获取默认的边数据// 添加锚点ID到数据中,以便保存和后续使用data.sourceAnchorId = this.sourceAnchorId // 保存源锚点IDdata.targetAnchorId = this.targetAnchorId // 保存目标锚点IDreturn data // 返回包含锚点信息的边数据}/*** 自定义方法,基于锚点的位置更新边的路径*/updatePathByAnchor() {// 获取源节点模型const sourceNodeModel = this.graphModel.getNodeModelById(this.sourceNodeId)// 从源节点的默认锚点中查找指定的锚点const sourceAnchor = sourceNodeModel.getDefaultAnchor().find((anchor) => anchor.id === this.sourceAnchorId)// 获取目标节点模型const targetNodeModel = this.graphModel.getNodeModelById(this.targetNodeId)// 从目标节点的默认锚点中查找指定的锚点const targetAnchor = targetNodeModel.getDefaultAnchor().find((anchor) => anchor.id === this.targetAnchorId)// 如果找到源锚点,则更新边的起始点if (sourceAnchor) {const startPoint = {x: sourceAnchor.x,y: sourceAnchor.y}this.updateStartPoint(startPoint)}// 如果找到目标锚点,则更新边的终点if (targetAnchor) {const endPoint = {x: targetAnchor.x,y: targetAnchor.y}this.updateEndPoint(endPoint)}// 清空当前边的控制点列表,以便贝塞尔曲线重新计算控制点this.pointsList = []this.initPoints()}
}// 导出自定义边配置
export default {type: 'sql-edge', // 自定义边的类型标识view: PolylineEdge, // 使用贝塞尔曲线边的视图model: CustomEdgeModel2 // 使用自定义的边模型
}

新建src/views/Example/LogicFlowAdvance/NodeExample/Component/SqlNode/index.ts代码如下:

import { h, HtmlNode, HtmlNodeModel } from '@logicflow/core'class SqlNode extends HtmlNode {/*** 1.1.7 版本后支持在 view 中重写锚点形状*/getAnchorShape(anchorData: any) {const { x, y, type } = anchorDatareturn h('rect', {x: x - 5,y: y - 5,width: 10,height: 10,className: `custom-anchor ${type === 'left' ? 'incomming-anchor' : 'outgoing-anchor'}`})}setHtml(rootEl: HTMLElement): void {rootEl.innerHTML = ''const {properties: { fields, tableName }} = this.props.modelrootEl.setAttribute('class', 'table-container')const container = document.createElement('div')container.className = `table-node table-color-${Math.ceil(Math.random() * 4)}`const tableNameElement = document.createElement('div')tableNameElement.innerHTML = tableNametableNameElement.className = 'table-name'container.appendChild(tableNameElement)const fragment = document.createDocumentFragment()for (let i = 0; i < fields.length; i++) {const item = fields[i]const fieldElement = document.createElement('div')fieldElement.className = 'table-feild'const itemKey = document.createElement('span')itemKey.innerText = item.keyconst itemType = document.createElement('span')itemType.innerText = item.typeitemType.className = 'feild-type'fieldElement.appendChild(itemKey)fieldElement.appendChild(itemType)fragment.appendChild(fieldElement)}container.appendChild(fragment)rootEl.appendChild(container)}
}class SqlNodeModel extends HtmlNodeModel {/*** 给 model 自定义添加字段方法*/addField(item: any) {this.properties.fields.unshift(item)this.setAttributes()// 为了保持节点顶部位置不变,在节点变化后,对节点进行一个位移,位移距离为添加高度的一半this.move(0, 24 / 2)// 更新节点连接边的 paththis.incoming.edges.forEach((egde) => {// 调用自定义的更新方案egde.updatePathByAnchor()})this.outgoing.edges.forEach((edge) => {// 调用自定义的更新方案edge.updatePathByAnchor()})}getOutlineStyle() {const style = super.getOutlineStyle()style.stroke = 'none'if (style.hover) {style.hover.stroke = 'none'}return style}// 如果不用修改锚的形状,可以重写颜色相关样式getAnchorStyle(anchorInfo: any) {const style = super.getAnchorStyle(anchorInfo)if (anchorInfo.type === 'left') {style.fill = 'red'style.hover.fill = 'transparent'style.hover.stroke = 'transpanrent'style.className = 'lf-hide-default'} else {style.fill = 'green'}return style}setAttributes() {this.width = 200const {properties: { fields }} = thisthis.height = 60 + fields.length * 24const circleOnlyAsTarget = {message: '只允许从右边的锚点连出',validate: (_sourceNode: any, _targetNode: any, sourceAnchor: any) => {return sourceAnchor.type === 'right'}}this.sourceRules.push(circleOnlyAsTarget)this.targetRules.push({message: '只允许连接左边的锚点',validate: (_sourceNode, _targetNode, _sourceAnchor, targetAnchor: any) => {return targetAnchor.type === 'left'}})}getDefaultAnchor() {const {id,x,y,width,height,isHovered,isSelected,properties: { fields, isConnection }} = thisconst anchors: any[] = []fields.forEach((feild: any, index: any) => {// 如果是连出,就不显示左边的锚点if (isConnection || !(isHovered || isSelected)) {anchors.push({x: x - width / 2 + 10,y: y - height / 2 + 60 + index * 24,id: `${id}_${feild.key}_left`,edgeAddable: false,type: 'left'})}if (!isConnection) {anchors.push({x: x + width / 2 - 10,y: y - height / 2 + 60 + index * 24,id: `${id}_${feild.key}_right`,type: 'right'})}})return anchors}
}export default {type: 'sql-node',model: SqlNodeModel,view: SqlNode
}

新建 src/views/Example/LogicFlowAdvance/NodeExample/Example03.vue 代码如下:

<script setup lang="ts">
import LogicFlow from '@logicflow/core'
import { onMounted, ref } from 'vue'
import '@logicflow/core/dist/style/index.css'
import SqlEdge from './Component/SqlEdge'
import SqlNode from './Component/SqlNode'
import { ElButton } from 'element-plus'const data = {nodes: [{id: 'node_id_1',type: 'sql-node',x: 100,y: 100,properties: {tableName: 'Users',fields: [{key: 'id',type: 'string'},{key: 'name',type: 'string'},{key: 'age',type: 'integer'}]}},{id: 'node_id_2',type: 'sql-node',x: 400,y: 200,properties: {tableName: 'Settings',fields: [{key: 'id',type: 'string'},{key: 'key',type: 'integer'},{key: 'value',type: 'string'}]}}],edges: []
}const SilentConfig = {stopScrollGraph: true,stopMoveGraph: true,stopZoomGraph: true
}const lfRef = ref<LogicFlow>()onMounted(() => {const lf = new LogicFlow({container: document.getElementById('container')!,grid: true,...SilentConfig})lf.register(SqlEdge)lf.register(SqlNode)lf.setDefaultEdgeType('sql-edge')lf.setTheme({bezier: {stroke: '#afafaf',strokeWidth: 1}})lf.render(data)lf.translateCenter()// 1.1.28新增,可以自定义锚点显示时机了lf.on('anchor:dragstart', ({ data, nodeModel }) => {console.log('dragstart', data)if (nodeModel.type === 'sql-node') {lf.graphModel.nodes.forEach((node) => {if (node.type === 'sql-node' && nodeModel.id !== node.id) {node.isShowAnchor = truenode.setProperties({isConnection: true})}})}})lf.on('anchor:dragend', ({ data, nodeModel }) => {console.log('dragend', data)if (nodeModel.type === 'sql-node') {lf.graphModel.nodes.forEach((node) => {if (node.type === 'sql-node' && nodeModel.id !== node.id) {node.isShowAnchor = falself.deleteProperty(node.id, 'isConnection')}})}})lfRef.value = lf
})const addField = () => {lfRef.value?.getNodeModelById('node_id_1').addField({key: Math.random().toString(36).substring(2, 7),type: ['integer', 'long', 'string', 'boolean'][Math.floor(Math.random() * 4)]})
}
</script>
<template><h3>Example Node (Advance) - 02</h3><ElButton @click="addField()" style="margin-bottom: 10px">Add Field</ElButton><div id="container" class="sql"></div>
</template>
<style>
#container {/* 定义容器的宽度和高度 */width: 100%;height: 500px;
}
.sql {.table-container {box-sizing: border-box;padding: 10px;}.table-node {width: 100%;height: 100%;overflow: hidden;background: #fff;border-radius: 4px;box-shadow: 0 1px 3px rgb(0 0 0 / 30%);}.table-node::before {display: block;width: 100%;height: 8px;background: #d79b00;content: '';}.table-node.table-color-1::before {background: #9673a6;}.table-node.table-color-2::before {background: #dae8fc;}.table-node.table-color-3::before {background: #82b366;}.table-node.table-color-4::before {background: #f8cecc;}.table-name {height: 28px;font-size: 14px;line-height: 28px;text-align: center;background: #f5f5f5;}.table-feild {display: flex;justify-content: space-between;height: 24px;padding: 0 10px;font-size: 12px;line-height: 24px;}.feild-type {color: #9f9c9f;}/* 自定义锚点样式 */.custom-anchor {cursor: crosshair;fill: #d9d9d9;stroke: #999;stroke-width: 1;/* rx: 3; *//* ry: 3; */}.custom-anchor:hover {fill: #ff7f0e;stroke: #ff7f0e;}.lf-node-not-allow .custom-anchor:hover {cursor: not-allowed;fill: #d9d9d9;stroke: #999;}.incomming-anchor {stroke: #d79b00;}.outgoing-anchor {stroke: #82b366;}
}
</style>

启动后效果如下:
在这里插入图片描述
上面的示例中,我们自定义锚点的时候,不仅可以定义锚点的数量和位置,还可以给锚点加上任意属性。有了这些属性,我们可以再做很多额外的事情。例如,我们增加一个校验规则,只允许节点从右边连出,从左边连入;或者加个id,在获取数据的时候保存当前连线从哪个锚点连接到哪个锚点。

注意
一定要确保锚点id唯一,否则可能会出现在连线规则校验不准确的问题。在实际开发中,存在隐藏锚点的需求,可以参考 github issue 如何隐藏锚点?

更新

HTML 节点目前通过修改 properties 触发节点更新

 /*** @overridable 支持重写* 和react的shouldComponentUpdate类似,都是为了避免出发不必要的render.* 但是这里不一样的地方在于,setHtml方法,我们只在properties发生变化了后再触发。* 而x,y等这些坐标相关的方法发生了变化,不会再重新触发setHtml.*/shouldUpdate() {if (this.preProperties && this.preProperties === this.currentProperties) return;this.preProperties = this.currentProperties;return true;}componentDidMount() {if (this.shouldUpdate()) {this.setHtml(this.rootEl);}}componentDidUpdate() {if (this.shouldUpdate()) {this.setHtml(this.rootEl);}}

如果期望其他内容的修改可以触发节点更新,可以重写shouldUpdate(相关issue: #1208)

shouldUpdate() {if (this.preProperties &&this.preProperties === this.currentProperties &&this.preText === this.props.model.text.value) return;this.preProperties = this.currentProperties;this.preText = this.props.model.text.valuereturn true;
}

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

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

相关文章

Typora—适用于 Mac 和 Win 系统的优秀 Markdown 文本编辑器

Typora 是一款适用于 Mac 和 Win 系统的优秀 Markdown 文本编辑器&#xff0c;它以其简洁易用的界面和强大的功能受到了众多用户的喜爱。 首先&#xff0c;Typora 的界面设计非常简洁直观&#xff0c;没有过多繁杂的菜单和按钮&#xff0c;让用户能够专注于写作本身。它采用实时…

LVS – NAT 模式集群构建

目录 1 环境准备 1.1 准备四台服务器 1.2 IP与网关均按照下图配置 1.3 网卡配置 1.4 real server 安装 web服务 1.5 安装ipvsadm 管理工具 2 使用ipvsadm管理LVS 2.1 创建集群指定使用的算法 2.2 添加真实服务器指定工作原理 2.3 查看是否正确创建集群 2.4 开启FORWARD路由转发…

​一个高清影像下载插件

数据是GIS的血液&#xff01; 虽然我们在水经微图&#xff08;简称“微图”&#xff09;中可以下载各种各样丰富的地图数据&#xff0c;但相信大家对数据的追求是无止境的。 我们现在就来分享一下&#xff0c;如何在QGIS中下载高清卫星影像的方法。 如果你需要最新版本的QGI…

最新下载:Hype 4 mac版【软件附加安装教程】

Hype是一款强大的Mac OS平台 HTML5 创作工具&#xff0c;它可以在网页上做出赏心悦目的动画效果&#xff0c;创建丰富的网页交互动画&#xff0c;支持层、时间轴等编辑方式&#xff0c;并能很好的导出HTML5/CSS3/JavaScript&#xff0c;在台式机&#xff0c;智能手机和iPad上流…

裁剪图片的最简单方法?这四种裁剪方法真的超级简单!

裁剪图片的最简单方法&#xff1f;在丰富多彩的现代生活中&#xff0c;图片成为了我们表达、沟通甚至展示身份的重要媒介&#xff0c;然而&#xff0c;无论是出于个人审美还是专业需求&#xff0c;图片的格式和尺寸往往成为了我们不得不面对的问题&#xff0c;特别是那些未经雕…

flink1.12.0学习笔记(一)-部署与入门

flink1.12.0学习笔记&#xff08;1&#xff09;-部署与入门 1-1-Flink概述 Flink诞生 Flink 诞生于欧洲的一个大数据研究项目 StratoSphere。该项目是柏林工业大学的一个研究性项目。早期&#xff0c; Flink 是做 Batch 计算的&#xff0c;但在 2014 年&#xff0c; StratoS…

mathtype7.6官方无需激活版安装包下载

大家好&#xff0c;今天我要和大家分享的是一款非常实用的数学工具——mathtype7.6最新版本&#xff01;&#x1f4d0; 作为一个数学专业的学生&#xff0c;我经常需要处理大量的数学公式和符号。以前我都是用手写或者电脑自带的公式编辑器&#xff0c;但是效率低下而且容易出错…

课设--学生成绩管理系统(二)

欢迎来到 Papicatch的博客 目录 &#x1f40b;引言 &#x1f988;编写目的 &#x1f988;项目说明 &#x1f40b;产品介绍 &#x1f988;产品概要说明 &#x1f988;产品用户定位 &#x1f988;产品中的角色 &#x1f40b; 产品总体业务流程图 &#x1f40b; 产品功…

装机必备-WinRAR安装教程

软件介绍&#xff1a;WinRAR 是一款功能强大的压缩包管理器&#xff0c;可用于备份数据&#xff0c;缩减电子邮件附件的大小&#xff0c;解压缩从 Internet 上下载的RAR、ZIP及其它类型文件&#xff0c;新建 RAR 及 ZIP 格式等的压缩类文件。这是我们新电脑或重装系统后必须安装…

adb常用命令详解--提升开发效率利器

文章目录 文件管理截屏与录屏logcat 使用dumpsys 相关ps 相关am 相关pm 相关wm 相关setprop 设置属性input 相关adb connect 远程无线调试其它 本文首发地址 https://h89.cn/archives/281.html 最新更新地址 https://gitee.com/chenjim/chenjimblog 博主 Linux 使用较多&#x…

04 翼型和机翼、尾翼几何选择

04 翼型和机翼、尾翼几何选择 4 -1 引言4-2 翼型的选择4-2-1 翼型的几何4-2-2 翼型的升力和阻力4-2-3 翼型选择与设计4-2-4 设计升力系数4-2-5 失速4-2-6 翼型厚度比4-2-7 关于翼型其他方面的考虑 4-3 机翼几何外形4-3-1 展弦比4-2-3 机翼后掠角4-3-3 机翼稍根比4-3-4 机翼扭转…

团结的力量:友情、互助与感恩

时间如白驹过隙&#xff0c;半载光阴转瞬即逝。回首过去的六个月&#xff0c;在CSDN平台上&#xff0c;我经历了无数的挑战和成长。在大厂和阿豪的帮助下&#xff0c;我的粉丝数终于突破了万大关。这不仅是我个人的成就&#xff0c;更是我们团结、互助和感恩精神的见证。 初识…

aardio实战篇) 下载微信公众号文章为pdf和html

首发地址&#xff1a; https://mp.weixin.qq.com/s/w6v3RhqN0hJlWYlqTzGCxA 前言 之前在PC微信逆向) 定位微信浏览器打开链接的call提过要写一个保存公众号历史文章的工具。这篇文章先写一个将文章保存成pdf和html的工具&#xff0c;后面再补充一个采集历史的工具&#xff0c…

HTTP协议版本历程

HTTP协议的发展历程 版本推出年份当前状态HTTP/0.91991年已过时HTTP/1.01996年已过时HTTP/1.11997年标准HTTP/2.02015年标准HTTP/3.02022年标准 HTTP/0.9 HTTP/0.9非常简单&#xff0c;并不涉及数据包传输&#xff0c;通过请求和响应的交换达成通信&#xff0c;请求由单行指…

SmartEDA、Multisim、Proteus大比拼:电路设计王者之争?

在电路设计领域&#xff0c;SmartEDA、Multisim和Proteus无疑是三款备受瞩目的软件工具。它们各自拥有独特的功能和优势&#xff0c;但在这场电路设计王者的竞争中&#xff0c;谁才是真正的领跑者&#xff1f;让我们深入探究这三款软件的异同&#xff0c;揭示它们各自的魅力所在…

图像处理与视觉感知复习--图像特征描述图像生成

文章目录 角点&#xff08;关键点&#xff09;的特点图像分类的流程梯度方向直方图&#xff08;HOG&#xff09;流程平移、旋转和尺度特征&#xff08;SIFT&#xff09;流程常用的图像生成模型GAN的原理Diffusion Model的原理mAP计算方法 角点&#xff08;关键点&#xff09;的…

Vue48-ref属性

一、需求&#xff1a;操作DOM元素 1-1、使用原生的id属性 不太好&#xff01; 1-2、使用 ref属性 原生HTML中&#xff0c;用id属性给元素打标识&#xff0c;vue里面用ref属性。 给哪个元素加了ref属性&#xff0c;vc实例对象就收集哪个元素&#xff01;&#xff01;&#xff0…

HTML初体验

可参考jd.com官网&#xff0c;ctrlu查看当前页面源代码 找到你的项目&#xff0c;在项目中创建html类型的网页文件 标准的HTML正确书写格式 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>Title&…

仅靠独立网站也能赚到100万,真的太牛了

你听说过 Photopea 吗&#xff1f;这是一个免费的类似 Photoshop 的图像编辑器。 这个项目&#xff1a; 每月1300万访问量每月150万用户使用小时每月10万美元的广告收入 Photopea 项目的天才创造者是 Ivan Kutskir。 令人惊讶的是&#xff0c;他独自处理了每日50万用户&…

Tomcat配置详解

文章目录 一、配置文件介绍配置文件日志文件 二、组件组件分层和分类核心组件Tomcat处理请求过程URL对应关系 三、部署java程序手动部署搭建博客状态页 四、常见配置详解tomcat端口号安全配置管理虚拟主机配置Context配置 四、Tomcat Nginx动静分离 一、配置文件介绍 配置好环…