讲讲项目里的仪表盘编辑器(一)
需求
要做一个仪表盘系统,要求有:
① 设计功能(包括布局、大小、排列)
② 预览功能
③ 运行功能

布局选择
做编辑器,肯定要先选择布局。
前端有几种常用布局。
静态布局
也叫文档布局。默认的网页形式是文档流的形式,一个网页就像是一条条从左流向右的河流。在文档中有两种元素,内联元素(display:inline)和块级元素(display:block)。内联元素默认从左到右流,遇到阻碍或者宽度不够自动换行,继续按照从左到右的方式布局。而块级元素则会独占一行,按照从上往下的方式布局。内联元素的宽度和高度默认都被内容撑开。
这肯定不适合用来做编辑器。
浮动布局
浮动元素会脱离文档流并浮动到左侧或右侧。这时候其他的周围内容就会在这个被设置浮动 (float) 的元素周围环绕。

这种肯定也不行。因为元素只能向左向右浮动。并且周围元素会围绕浮动元素旋转,并不会脱离文档流,也就是无法自定义其位置。
定位布局
通过调整position这个CSS属性的值来实现(absolute/relative/fixed/static(绝对/相对/固定/静态(默认))四个值)。
static为静态布局,遵循默认文档流。这没什么好说的。
absolute绝对定位:元素会脱离文档流,通过TBLR( top,bottom,left,right) 定位,会选取最近的一个有定位设置的父级对象(非static)进行绝对定位,如果没有设置定位属性的父级对象,则将以body坐标原点进行定位。绝对定位的元素不会占有空间,也不会影响别的元素。
relative相对定位:对象不可层叠、不脱离文档流,参考自身静态位置通过 TBLR定位。设置了TBLR之后,元素位置会发生偏移,但仍然占有原来的位置,且不会影响别的元素,而是覆盖在上方。相对定位与绝对定位的区别就是相对定位是只占有原来的空间,而绝对定位不占有空间。
fixed固定定位:顾名思义,固定定位就是固定在一个位置,不会随着页面滚动而改变位置的定位方式,像常见的页面上的小广告,或者右下角的返回页面顶部等等。
如果要做编辑器,fixed(只有单页了)和static肯定不行。relative不脱离文档流,也不行。那么只能以页面为定位的absolute绝对定位了。这么做有几个缺点:
第一,每个元素都需要精准的TBLR值。这是很麻烦的。甚至需要去计算。
第二:拖拽放大缩小元素的时候,会改变其自身的TBLR值。并且可能会盖到其他元素上面去。我们需要的效果是像果冻一样挤压其他元素。使其挪到后面或下面。
瀑布布局
视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部。

瀑布流的实现方法决定了它的元素排序,具体请看我这篇推文。
瀑布流布局的实现_AI3D_WebEngineer的博客-CSDN博客瀑布流布局视觉表现为参差不齐的多栏布局,随着页面滚动条向下滚动,这种布局还会不断加载数据块并附加至当前尾部 瀑布流的实现方法决定了它的元素排序,具体请看我这篇推文。https://blog.csdn.net/weixin_42274805/article/details/132981042

flex布局

弹性盒模型。也是纯CSS布局。具体可以看廖雪峰的Flex 布局教程:语法篇 - 阮一峰的网络日志
https://www.ruanyifeng.com/blog/2015/07/flex-grammar.html
但是做编辑器是不能选择flex布局的,为什么?请看下面Grid布局的介绍
Grid布局
网络布局,目前唯一一种 CSS 二维布局。擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。
与flex布局的区别:flex 布局是一维布局,Grid 布局是二维布局。flex 布局一次只能处理一个维度上的元素布局,一行或者一列。Grid 布局是将容器划分成了“行”和“列”,产生了一个个的网格,我们可以将网格元素放在与这些行和列相关的位置上,从而达到我们布局的目的。

布局实现
<body><div class="container"><div class="item"><img src="img/bg1.png" alt="" /></div><div class="item"><img src="img/bg2.png" alt="" /></div><div class="item"><img src="img/bg3.png" alt="" /></div><div class="item"><img src="img/bg4.png" alt="" /></div><div class="item"><img src="img/bg5.png" alt="" /></div><div class="item"><img src="img/bg6.png" alt="" /></div></div>
</body>
此时我们只需要对container这个父容器添加grid属性就好:
<style>.container {/* 声明一个容器 */display: grid;/* 声明列的宽度 */grid-template-columns: repeat(3, 200px);/* 声明行间距和列间距 */grid-gap: 20px;/* 声明行的高度 */grid-template-rows: 100px 200px;}.item img {width: 100%;height: 100%;}
</style>
grid-template-columns: repeat(3, 200px);
这里的2是分为3列的意思,200px是列宽

容器
我们通过在元素上声明 display:grid 或 display:inline-grid 来创建一个网格容器。一旦我们这样做,这个元素的所有直系子元素将成为网格项目。比如上面 .container所在的元素为一个网格容器,其直系子元素将成为网格项目。
网格轨道
grid-template-columns 和 grid-template-rows 属性来定义网格中的行和列。容器内部的水平区域称为行,垂直区域称为列。

网格单元
一个网格单元是在一个网格元素中最小的单位
1 2 3 4 5 6 各是一个网络单元
网格线
划分网格的线,称为"网格线"。这对我们来说是不可见的。Grid 会为我们创建编号的网格线来让我们来定位每一个网格元素。m 列有 m + 1 根垂直的网格线,n 行有 n + 1 跟水平网格线。
详细的语法请看:
最强大的 CSS 布局 —— Grid 布局 - 掘金Grid 布局即网格布局,是一种新的 CSS 布局模型,比较擅长将一个页面划分为几个主要区域,以及定义这些区域的大小、位置、层次等关系。号称是最强大的的 CSS 布局方案,是目前唯一一种 CSS 二维布局。利用 Grid 布局,我们可以轻松实现类似下图布局,演示地址 讲到布局,…
https://juejin.cn/post/6854573220306255880
我的选择
Grid布局是最佳选择。因为二维布局提供的网格概念,使得元素的大小和位置可以被合理配置。但是Grid布局的兼容性很一般。考虑到有拖拽和放大缩小功能,且是基于Vue2的项目,我选择了一个网格(栅格)拖拽布局库vue-grid-layout 。他可以完美地实现自定义布局功能。
编辑器页面

这是一个完整的编辑器页面。除开上部的公共导航栏之外。编辑器由左边的组件栏和右边的设计器组成。

组件的设计器设计为弹窗模式。

拖拽、移动的浮窗效果。

dashboard-design.vue:编辑器外壳,通过插槽来插入页面模块
// dashboard-design.vue
<template><div :class="$style.design"><global-header :class="$style.header"><template slot="center"><slot name="header-center" /></template></global-header><slot /></div>
</template>
index.vue:仪表盘编辑器文件
// index.vue<template><dashboard-design-layout:id="$route.params.id">// 头部<dashboard-design-header-centerslot="header-center"/><template><divid="dashboardContent">// 左边的组件列表<a-card:class="$style.controls"><control-list :dragType.sync="dragType" @add="handleClickAddField" /></a-card>// 右边设计器区域<a-card :class="$style.main">....</a-card></div></dashboard-design-layout>
</template>
此时一个简单的编辑机页面结构就出来了,紧接着我们需要给它添加CSS样式

设计器概览
<a-card :class="$style.main">// 按钮<div :class="$style.action"><div :class="$style.actionLeft"><a-button type="link" @click="handlePreview"><x-icon type="tc-icon-search-square" /><span>预览</span></a-button></div><div :class="$style.actionRight"><async-button type="primary" :click="validateAndSave">保存</async-button></div></div>// 设计器<drag-containerref="container":fields="fields":dragType.sync="dragType":selectedField="selectedField":dashboard="form":initFieldStyle="initFieldStyle"@add="handleAdd"@edit="handleEdit"@delete="handleDelete"@select="selectField"/>
</a-card>

我们关注drag-container.vue是怎么样的实现,它一定会有的两个功能:
① 能展示现有的设计布局
② 能自适应拖拽进来的元素,并允许用户自定义它的位置和大小
让我们看看它的实现:
// drag-container.vue
<template><divid="dashboardLayout":style="styleCSSVariable"><background :background="themeBackground"><grid-layoutref="layout":class="$style.layout":layout.sync="layout":colNum="60":rowHeight="15"@dragover.native="handleDrag"@dragleave.native="handleDragLeave"@drop.native="handleDrop">....</grid-layout></background></div>
</template>
可以看到,根元素这里使用了一个名为styleCSSVariable的CSS集。这里的实现是:
get styleCSSVariable() {return createDashboardCSS(this.dashboard.setting.style);
}
这里可以根据当前仪表盘的用户设置风格(如“暗黑”、“科技”、“酷炫”)进行css样式管理。return了诸如这些设置:
return {// 文字颜色'--font': '#cccccc','--font-sub': '#999999','--font-info': '#666666',// 辅助文字'--font-color-secondary': '#aaaaaa',// 提示性文字'--font-color-info': '#ff2d2d','--font-active-light': '#ffffff',// 删除按钮/文字'--delete': '#fe5959',// 警示色'--warning': '#ff9900',// 操作成功'--success': '#19be6b'....
}

可以看到设计器的元素style样式上绑定了一堆颜色定义。这其实就是CSS中的var()函数用法。详情可以看我这篇推文。
CSS的var()函数用法与JS获取css函数变量值的方法_AI3D_WebEngineer的博客-CSDN博客
https://blog.csdn.net/weixin_42274805/article/details/133135688?csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22133135688%22%2C%22source%22%3A%22weixin_42274805%22%7D 往下看,这里面套了个background的背景板。紧接着是我们的主角。来自于vue-grid-layout插件的grid-layout画布组件。
colNum:画布总共设计为60列
rowHeight:每行高度
:layout.sync="layout" 这里传入的是当前的布局信息
@dragover.native 这里响应的是原生的dragover 事件。拖拉到当前节点上方时,在当前节点上持续触发(相隔几百毫秒)
@dragleave.native
拖拉操作离开当前节点范围时,在当前节点上触发@drop.native drag 被拖拉的节点或选中的文本,释放到目标节点时,在目标节点上触发
这里为什么要设置三个事件呢?drop响应的是用户放手的事件,dragleave是用户手势离开设计器的时候响应,dragover是用户的手势在设计器里持续响应(刷新状态)。而我们知道,drag原生事件并不只三个:
dragstart:用户开始拖拉时,在被拖拉的节点上触发。
dragend:拖拉结束时(释放鼠标键或按下 ESC 键)在被拖拉的节点上触发。
dragenter:拖拉进入当前节点时,在当前节点上触发一次,该事件的target属性是当前节点。
但这些对于我们设计器来说并无作用。
设计器拖拽部分(一)
大概明白了设计器的拖拽设计,紧接着我们来看最关键的数据结构。 
其实很简单,我们只需要传入一个layout-item数组到layout-grid,它就可以正常响应渲染。哪怕数组是乱序的。也就是说layout-grid将严格按照每个layout-item的x/y/w/h进行计算布局

layout-grid是不关注item的max/min-h/w属性的。因为这个事layout-item自己的事情。也就是说layout-grid甚至只是个容器。它接受的item数组是什么样,就会展示什么样。同时它响应上面的拖拽事件,方便我们生成每个layout-item数据。
直接先看@drop.native="handleDrop"部分
/** @name 拖动放置时 **/async handleDrop() {if (this.isInChildCom) return; // 进入子元素范围则无需触发try {let field = createDashboardField(this.dragType);field.widget.layout = pick(this.dragLayout, 'x', 'y', 'w', 'h');this.layout.splice(this.dragLayoutIndex, 1, {...field.widget.layout,i: field.pkId,});this.$emit('add', field);} catch (e) {this.layout.splice(this.dragLayoutIndex, 1);throw e;}}/** @name 当前拖拽元素的layout **/dragLayout = null;/** @name 当前拖拽元素的index **/get dragLayoutIndex() {return this.layout.indexOf(this.dragLayout);}
我把代码精简一下,大概是先创建一个拖拽组件的实例(谨慎地用try方法进行尝试,只要中途有抛错就把这个拖拽进来的元素切割掉)。紧接着更新layout。并把新增的组件实例抛出去(做数据存储)。
请注意,this.layout仅仅用于当前页面layout-grid的渲染。不参与数据存储。
编辑器概览

结合一下代码来看看:
// index.vue
<a-card><!-- 左侧组件库--><control-list :dragType.sync="dragType" @add="handleClickAddField" />
</a-card>
<a-card><!-- 左侧设计器--><drag-container:dragType.sync="dragType":selectedField="selectedField"/>
</a-card><script>..../** @name 左边栏拖动时的组件类型 **/dragType = null;
</script>
这里用了sync传值的语法糖。让子组件能够通过emit修改父组件的值。
// control-list.vue<template><div :class="$style.controlList"><divv-for="control in group.list":key="control.type"@dragstart="handleDragStart(control.type, control.draggable)"@dragend="handleDragEnd(control.draggable)"><x-icon :type="control.icon" /><span>{{control.name}}</span></div></div>
</template>...@Prop() dragType;handleDragStart(type, draggable = true) {if (draggable) {this.$emit('update:dragType', type);}
}handleDragEnd(draggable = true) {if (draggable) {this.$emit('update:dragType', null);}
}
control-list.vue这里触发拖拽开始的事件就会emit一个有效的类型值给index.vue,如果拖拽停止就会emit一个空类型值给index。index再通过pro传参给drag-container,这里drag-container为什么也要用sync呢?因为当拖拽放置完成之后,drag-container会把类型值清空,以恢复起始的模式。
// drag-container.vue<template><grid-layout@dragover.native="handleDrag"@dragleave.native="handleDragLeave"@drop.native="handleDrop"><grid-itemv-for="layoutItem in layout":key="layoutItem.i"v-bind="getLayoutProps(layoutItem)"><dash-layout-item@inChildComponent="inChildComponent"><component:is="getComponent(layoutItem)"/><dash-layout-item>/></grid-item></grid-layout>
</template>@Prop() dragType;
我们来看看这个dragType的作用是什么?
/** @name 拖动放置时 **/
async handleDrop() {...try {let field = createDashboardField(this.dragType);} catch {...} finally {this.$emit('update:dragType', null);}
}
第一个作用,在拖拽放置的时候将组件类型传给创建方法。如果为null,则被捕获错误,回滚拖拽操作。不管成功与否,最后finally必须把父组件的dragType置为空以结束流程。
/** @name 左边栏拖动触发 **/@Watch('dragType')handleDragTypeChange(type) {this.isInChildCom = false; // 重新拖动,重置是否在现有组件内部的判断if (type) {// 生成默认的布局item属性this.dragLayout = {i: 'drag',...getDashboardLayoutByType(type),};} else {this.dragLayout = null;}}
第二个作用,观察dragType值。并根据传入的组件类型为其生成默认的布局属性(x,y,w,h)之类。
再来看看
<grid-itemv-for="layoutItem in layout":key="layoutItem.i"v-bind="getLayoutProps(layoutItem)"
><dash-layout-item@inChildComponent="inChildComponent"><component :is="getComponent(layoutItem)"/><dash-layout-item>/>
</grid-item>
这里为什么要套个dash-layout-item,就是为了监控拖拽元素是否已经进入到现有元素。
// dash-layout-item
<template><div@dragenter="dragenter"@dragleave="dragleave"><slot /></div>
</template><script>/** @name 进入-有效目标 **/
dragenter() {this.$emit('inChildComponent', true);
}/** @name 离开-有效目标 **/
dragleave(e) {...this.$emit('inChildComponent', false);
}</script>
相关文章:
讲讲项目里的仪表盘编辑器(一)
需求 要做一个仪表盘系统,要求有: ① 设计功能(包括布局、大小、排列) ② 预览功能 ③ 运行功能 布局选择 做编辑器,肯定要先选择布局。 前端有几种常用布局。 静态布局 也叫文档布局。默认的网页形式…...
解决方案 | 如何构建市政综合管廊安全运行监测系统?
如何构建市政综合管廊安全运行监测系统?WITBEE万宾城市生命线智能监测仪器,5年免维护设计,集成10多项结构与气体健康监测指标,毫秒级快速响应,时刻感知综合管廊运行态势...
JCEF中js与java交互、js与java相互调用
jcef中js与java相互调用,java与js相互调用,chrome与java相互调用,java与chrome相互调用、jcef与java相互调用 前提:https://blog.csdn.net/weixin_44480167/article/details/133170970(java内嵌浏览器CEF-JAVA、jcef、…...
9.20 校招 实习 内推 面经
绿泡*泡: neituijunsir 交流裙 ,内推/实习/校招汇总表格 1、校招丨智行者2024年校园招聘正式启动啦 校招丨智行者2024年校园招聘正式启动啦 2、校招 | 乐动机器人2024校园招聘 校招 | 乐动机器人2024校园招聘 3、校招丨小天才2024届秋季校园招聘 …...
基于JAVA+SpringBoot+Vue+协同过滤算法+爬虫的前后端分离的租房系统
✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式🍅 一、项目背景介绍: 随着城市化进程的加快…...
【Android Framework系列】第16章 存储访问框架 (SAF)
1 概述 Android 4.4(API 级别 19)引入了存储访问框架 (Storage Access Framework)。SAF让用户能够在其所有首选文档存储提供程序中方便地浏览并打开文档、图像以及其他文件。 用户可以通过易用的标准 UI,以统一方式在所有应用和提供程序中浏…...
Antdesign 4中让分页组件居中显示的方法
在Ant Design 4中分页组件默认是最右边显示的,而这个没有设置位置的属性的 解决办法: 在pagination的属性中增加: style: {textAlign: "center"} 在Ant Design 5中可以让pagination使用align: center来实现分页组件居中...
【笔记】ubuntu 20.04 + mongodb 4.4.14定时增量备份脚本
环境 ubuntu 20.04mongodb 4.4.14还没实际使用(20230922)后续到10月底如果有问题会修改 原理 只会在有新增数据时生成新的备份日期目录备份恢复时,如果恢复的数据库未删除,则会覆盖数据 准备 准备一个文件夹,用于…...
c++实现的一个定时器实例
/* * author: hjjdebug * date : 2023年 09月 23日 星期六 11:52:29 CST * description: 用std::thread 实现了一个定时器,深刻理解一下定时器是怎样工作的. * 参考Timer.h, Timer.cpp */ $ cat main.cpp #include "Timer.h" #include <unis…...
Python线程和进程
1、深度解析Python线程和进程 一篇文章带你深度解析Python线程和进程 - 知乎使用Python中的线程模块,能够同时运行程序的不同部分,并简化设计。如果你已经入门Python,并且想用线程来提升程序运行速度的话,希望这篇教程会对你有所帮…...
算法 寻找峰值-(二分查找+反向双指针)
牛客网: BM19 题目: 寻找数组峰值,可能多个返回任一个,每个值满足nums[i] ! nums[i 1] 思路: 双指针 left 0, right n-1, 相向而行,取中间位置mid, nums[mid]与nums[mid1]比较,如果nums[mid] < nums[mid1],说明…...
【数据结构】—交换排序之快速排序究极详解,手把手带你从简单的冒泡排序升级到排序的难点{快速排序}(含C语言实现)
食用指南:本文在有C基础的情况下食用更佳 🔥这就不得不推荐此专栏了:C语言 ♈️今日夜电波:靴の花火—ヨルシカ 0:28━━━━━━️💟──────── 5:03 …...
【c#-Nuget 包“在此源中不可用”】 Nuget package “Not available in this source“
标题c#-Nuget 包“在此源中不可用”…但 VS 仍然知道它吗? (c# - Nuget package “Not available in this source”… but VS still knows about it?) 背景: 今日从公司svn 上拉取很久很久以前的代码,拉取下来200报错,进一步发…...
【数据结构】二叉树之堆的实现
🔥博客主页:小王又困了 📚系列专栏:数据结构 🌟人之为学,不日近则日退 ❤️感谢大家点赞👍收藏⭐评论✍️ 目录 一、二叉树的顺序结构 📒1.1顺序存储 📒1.2堆的性质…...
电工-三极管输入输出特性曲线讲解
三极管特性曲线是反映三极管各电极电压和电流之间相互关系的曲线,是用来描述晶体三极管工作特性曲线,常用的特性曲线有输入特性曲线和输出特性曲线。这里以下图所示的共发射极电路来分析三极管的特性曲线。 输入特性曲线 该曲线表示当e极与c极之间的电…...
深入解析容器与虚拟化:技术、对比与生态
深入解析容器与虚拟化:技术、对比与生态 文章目录 深入解析容器与虚拟化:技术、对比与生态容器和虚拟化的基本概念和原理容器的定义和特点虚拟化的定义和特点 容器使用场景容器和虚拟机的对比虚拟化技术的四个特点容器实现虚拟化的原理常见容器引擎和容器…...
制作游戏demo的心得
制作这个游戏demo出来的心得 https://www.bilibili.com/video/BV1cF411m7Dh/ 制作游戏demo的心得 制作游戏demo,主要是为了表现自己的技术,那就一门心思想着如何提高表现力就行了,在整体的画面渲染风格方面或许没有什么可选择的,…...
Web Tour Server窗口闪现
1.打开该文件所在位置 2.右击选择编辑,在最后一行加上pause,保存后重新打开Server窗口 3.重新打开后,若出现以下情况: 以管理员身份打开cmd命令行,输入命令netstat -aon|findstr “1080”,查看1080端口占用…...
Linux下的基本指令
目录 01. ls 指令 02. pwd命令 03. cd 指令 04. touch指令 05.mkdir指令(重要): 06.rmdir指令 && rm 指令(重要): 07.man指令(重要): 08mv指令ÿ…...
随机数生成器代码HTML5
代码如下 <!DOCTYPE html> <html> <head> <title>随机数生成器</title> <meta name"viewport" content"widthdevice-width, initial-scale1.0"> <style> body { text-align: center; bac…...
浅谈 React Hooks
React Hooks 是 React 16.8 引入的一组 API,用于在函数组件中使用 state 和其他 React 特性(例如生命周期方法、context 等)。Hooks 通过简洁的函数接口,解决了状态与 UI 的高度解耦,通过函数式编程范式实现更灵活 Rea…...
java调用dll出现unsatisfiedLinkError以及JNA和JNI的区别
UnsatisfiedLinkError 在对接硬件设备中,我们会遇到使用 java 调用 dll文件 的情况,此时大概率出现UnsatisfiedLinkError链接错误,原因可能有如下几种 类名错误包名错误方法名参数错误使用 JNI 协议调用,结果 dll 未实现 JNI 协…...
汇编常见指令
汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX(不访问内存)XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...
图表类系列各种样式PPT模版分享
图标图表系列PPT模版,柱状图PPT模版,线状图PPT模版,折线图PPT模版,饼状图PPT模版,雷达图PPT模版,树状图PPT模版 图表类系列各种样式PPT模版分享:图表系列PPT模板https://pan.quark.cn/s/20d40aa…...
JAVA后端开发——多租户
数据隔离是多租户系统中的核心概念,确保一个租户(在这个系统中可能是一个公司或一个独立的客户)的数据对其他租户是不可见的。在 RuoYi 框架(您当前项目所使用的基础框架)中,这通常是通过在数据表中增加一个…...
Elastic 获得 AWS 教育 ISV 合作伙伴资质,进一步增强教育解决方案产品组合
作者:来自 Elastic Udayasimha Theepireddy (Uday), Brian Bergholm, Marianna Jonsdottir 通过搜索 AI 和云创新推动教育领域的数字化转型。 我们非常高兴地宣布,Elastic 已获得 AWS 教育 ISV 合作伙伴资质。这一重要认证表明,Elastic 作为 …...
aardio 自动识别验证码输入
技术尝试 上周在发学习日志时有网友提议“在网页上识别验证码”,于是尝试整合图像识别与网页自动化技术,完成了这套模拟登录流程。核心思路是:截图验证码→OCR识别→自动填充表单→提交并验证结果。 代码在这里 import soImage; import we…...
游戏开发中常见的战斗数值英文缩写对照表
游戏开发中常见的战斗数值英文缩写对照表 基础属性(Basic Attributes) 缩写英文全称中文释义常见使用场景HPHit Points / Health Points生命值角色生存状态MPMana Points / Magic Points魔法值技能释放资源SPStamina Points体力值动作消耗资源APAction…...
简约商务通用宣传年终总结12套PPT模版分享
IOS风格企业宣传PPT模版,年终工作总结PPT模版,简约精致扁平化商务通用动画PPT模版,素雅商务PPT模版 简约商务通用宣传年终总结12套PPT模版分享:商务通用年终总结类PPT模版https://pan.quark.cn/s/ece1e252d7df...
mcts蒙特卡洛模拟树思想
您这个观察非常敏锐,而且在很大程度上是正确的!您已经洞察到了MCTS算法在不同阶段的两种不同行为模式。我们来把这个关系理得更清楚一些,您的理解其实离真相只有一步之遥。 您说的“select是在二次选择的时候起作用”,这个观察非…...
