-
目录树显示_vue目录树组件
2020-12-31 13:28:24I would like to commemorate last year and today, my life is full of ...单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。页面整体布局...
I would like to commemorate last year and today, my life is full of regret.一般数据类展示内容,大多采用树状结构展示内容。类似效果如下:
左侧是导航分类,可以进行新建,对单项导航分享和删除。单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。
页面整体布局页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表分为上下两块,顶部是添加按钮,下面是导航列表。
less样式。@import "../../theme/variables.less";.main { position: relative; height: 100%; overflow: hidden; .content { border: 1px solid #dcdcdc; position: relative; height: 100%; background: #f1f2f7; display: flex; border-radius: @borderRadius; .left { width: 240px; background: #fff; border-right: 1px solid rgba(220, 220, 220, 1); padding: 15px 10px; display: flex; flex-direction: column; overflow: auto; .header { width: 100%; margin-bottom: 20px; display: flex; justify-content: center; align-items: center; .btn { width: 136px; margin: 0 6px; :global { .icon { margin-right: 14px; } .customIcon { display: inline-block; transform: rotate(45deg); } } } } .treeLayout { flex: 1; .item { width: 100%; height: 32px; line-height: 32px; margin-bottom: 11px; position: relative; .link { display: flex; align-items: center; font-size: 14px; font-family: Microsoft YaHei; font-weight: 400; color: rgba(51, 51, 51, 1); padding-left: 21px; cursor: pointer; .catalogIcon { font-size: 12px; } .text { display: inline-block; flex: 1; margin-left: 12px; } .opBtn { width: 46px; display: flex; align-items: center; justify-content: space-between; } .operateIcon { display: none; } &:hover { color: #00a4ff; .opBtn { color: rgba(51, 51, 51, 1); } .operateIcon { display: block; } } } .iconBtns { position: absolute; top: 28px; right: 24px; width: 112px; background: rgba(255, 255, 255, 1); box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.13); border-radius: 4px; z-index: 10; .icon { width: 100%; height: 40px; border-radius: 2px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-family: Microsoft YaHei; font-weight: 400; color: rgba(51, 51, 51, 1); cursor: pointer; .iconName { margin-left: 18px; } &:hover { background: #e7e8e9; } } } } .itemActive { .link { color: #00a4ff; .opBtn { color: rgba(51, 51, 51, 1); } .operateIcon { display: block; } } } } } .right { flex: 1; width: 100%; overflow: hidden; } }}
这里的导航列表,新增导航,和删除都是调用相关接口。
目录树组件
页面右侧就是树状结构列表,通过路由跳转携带catalogId参数,接口查询出列表数据,后端返回的数据就是有层级的树状结构。
我认为的写一个组件,单指这里的目录树组件,组件中只需要构造出页面布局,任何的交互逻辑都不涉及,只将相关事件抛出即可。这就需要先明确好数据结构,来写样式布局了。
数据结构,有id,name,父级id,子节点数组,类型catalogType:1是目录,2是场景,3是外链场景 ... 如下:
树状结构会涉及到递归,这里为了处理方便,组件中分为两层。组件目录结构如下:
index就是对外暴露的窗口,主要目录树的布局样式是在DomNode中。先明确一下布局,目录树中单个一行,需要一个展开收起的图标,当前行类型的图标,这里业务上分三种类型,就需要以此判断显示不同图标。每项最后还会有四个操作按钮。
这里把事件简化了,只分了两个事件,一个是展开收起,一个是一系列编辑操作,传个type参数作为区分。
tabNode(node: ITree) { this.$emit("tabNode", node);},// 操作doNode(node: ITree, type: string, index: number) { this.$emit("doNode", node, type, index);},
index文件中引用DomNode,相关的接收的参数和抛出去的事件,和DomNode一致。
// index布局<div class="treeLayout"> <DomNode v-for="(item, index) in trees" :key="index" :node="item" @tabNode="tabNode" @doNode="doNode" :index="index" >
// 接收的参数
props: {
trees: {
type: Array as () => ITree[],
default: [],
},
activeId: {
type: String,
default: "",
},
},
页面右侧实现引用catalogTree组件。
前文已经提过,目录数据是后端返回的,那么treeList就是后端返回值res.data。但操作tabNode和doNode这两个方法,需要将treeList数组转换成map对象。因为需要自定义添加一些字段,这些字段只作为前端交互操作逻辑使用,所以后端返回值中不会携带。需要给每一项数据添加isOpen字段,用来判断展开收起状态。level字段,用来实现上移下移操作。先来构造这个catalogMap,定义个方法setCatalogMap,需要的参数有存放结果的treeMap,原数据treeList数组。setCatalogMap,很简单的一个递归。:trees="treeList" @tabNode="tabNode" @doNode="doNode"></catalog-tree>
拿到map对象,就可以实现tabNode和doNode这两个方法。
有认真看的话,会发现,并没有在哪里定义isOpen属性,怎么就在tabNode方法中使用了。因为我还没有写。拿到map对象,循环做个判断,用来保持isOpen状态。// 切换状态tabNode(node: ITree) { if (node.isOpen) { this.treeMap[node.catalogId].isOpen = false; } else { this.treeMap[node.catalogId].isOpen = true; }},// 编辑等一系列操作,按照类型区分doNode(node: ITree, type: string, index: number) { switch (type) { case "up": // 上移 this.doUp(node, index); break; case "down": // 下移 this.doDown(node, index); break; case "edit": // 编辑 this.doEdit(node.catalogId); break; case "delete": // 删除 this.doDelete(node); break; }},
doNode中的四个方法,编辑和删除就是调个接口,主要是上移下移操作,前端实现数据的排序,最后将最新的数据返回给后端保存,doSaveSort方法调接口保存。上代码,好好琢磨琢磨。Object.keys(treeMap).forEach((key) => { const item = treeMap[key]; if (this.treeMap[key]) { item.isOpen = this.treeMap[key].isOpen; } else { item.isOpen = true; }});
总结树状结构列表,首先需要明确数据结构,必备的字段id,name,父级id,children数组,根据数据结构,使用递归构建布局。doUp(node: ICatalogModel, index: number) { if (index === 0) { return; } const parentId: string = node.catalogParent as string; const parentItem: ICatalogModel = this.treeMap[parentId]; let dataList: ICatalogModel[] = []; // 如果为空则是顶级 if (parentItem) { if (parentItem.catalogTreeVoList) { dataList = parentItem.catalogTreeVoList; } } else { dataList = this.treeList; } const item = dataList[index]; dataList.splice(index, 1); dataList.splice(index - 1, 0, item); this.doSaveSort(dataList);},doDown(node: ICatalogModel, index: number) { const parentId: string = node.catalogParent as string; const parentItem: ICatalogModel = this.treeMap[parentId]; // 如果为空则是顶级 let dataList: ICatalogModel[] = []; if (parentItem) { if (parentItem.catalogTreeVoList) { // 最后一个不能下移 if (parentItem.catalogTreeVoList.length === (index + 1)) { return; } else { dataList = parentItem.catalogTreeVoList; } } } else { // 一级最后一个不能下移 if ( this.treeList.length === (index + 1)) { return; } dataList = this.treeList; } const item = dataList[index]; dataList.splice(index, 1); dataList.splice(index + 1, 0, item); this.doSaveSort(dataList);},
over~加班快乐 -
vue目录树组件(树状结构列表)
2020-09-14 09:56:39单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。 页面整体布局 页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表...一般数据类展示内容,大多采用树状结构展示内容。类似效果如下:
关注微信公众号,查看效果
左侧是导航分类,可以进行新建,对单项导航分享和删除。单击导航,在右侧查询出当前导航下所有目录结构,可以新建目录。新增类型分为三种,目录可以无限嵌套,当然也可以设置层级。
页面整体布局
页面分为左右两个部分。左侧列表,通过路由跳转显示右侧内容。左侧列表分为上下两块,顶部是添加按钮,下面是导航列表。
less样式。
import "../../theme/variables.less";
.main {
position: relative;
height: 100%;
overflow: hidden;
.content {
border: 1px solid #dcdcdc;
position: relative;
height: 100%;
background: #f1f2f7;
display: flex;
border-radius: @borderRadius;
.left {
width: 240px;
background: #fff;
border-right: 1px solid rgba(220, 220, 220, 1);
padding: 15px 10px;
display: flex;
flex-direction: column;
overflow: auto;
.header {
width: 100%;
margin-bottom: 20px;
display: flex;
justify-content: center;
align-items: center;
.btn {
width: 136px;
margin: 0 6px;
:global {
.icon {
margin-right: 14px;
}
.customIcon {
display: inline-block;
transform: rotate(45deg);
}
}
}
}
.treeLayout {
flex: 1;
.item {
width: 100%;
height: 32px;
line-height: 32px;
margin-bottom: 11px;
position: relative;
.link {
display: flex;
align-items: center;
font-size: 14px;
font-family: Microsoft YaHei;
font-weight: 400;
color: rgba(51, 51, 51, 1);
padding-left: 21px;
cursor: pointer;
.catalogIcon {
font-size: 12px;
}
.text {
display: inline-block;
flex: 1;
margin-left: 12px;
}
.opBtn {
width: 46px;
display: flex;
align-items: center;
justify-content: space-between;
}
.operateIcon {
display: none;
}
&:hover {
color: #00a4ff;
.opBtn {
color: rgba(51, 51, 51, 1);
}
.operateIcon {
display: block;
}
}
}
.iconBtns {
position: absolute;
top: 28px;
right: 24px;
width: 112px;
background: rgba(255, 255, 255, 1);
box-shadow: 0px 0px 6px 0px rgba(0, 0, 0, 0.13);
border-radius: 4px;
z-index: 10;
.icon {
width: 100%;
height: 40px;
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-size: 12px;
font-family: Microsoft YaHei;
font-weight: 400;
color: rgba(51, 51, 51, 1);
cursor: pointer;
.iconName {
margin-left: 18px;
}
&:hover {
background: #e7e8e9;
}
}
}
}
.itemActive {
.link {
color: #00a4ff;
.opBtn {
color: rgba(51, 51, 51, 1);
}
.operateIcon {
display: block;
}
}
}
}
}
.right {
flex: 1;
width: 100%;
overflow: hidden;
}
}
}
这里的导航列表,新增导航,和删除都是调用相关接口。
目录树组件
页面右侧就是树状结构列表,通过路由跳转携带catalogId参数,接口查询出列表数据,后端返回的数据就是有层级的树状结构。
我认为的写一个组件,单指这里的目录树组件,组件中只需要构造出页面布局,任何的交互逻辑都不涉及,只将相关事件抛出即可。这就需要先明确好数据结构,来写样式布局了。
数据结构,有id,name,父级id,子节点数组,类型catalogType:1是目录,2是场景,3是外链场景 ... 如下:
树状结构会涉及到递归,这里为了处理方便,组件中分为两层。组件目录结构如下:
index就是对外暴露的窗口,主要目录树的布局样式是在DomNode中。先明确一下布局,目录树中单个一行,需要一个展开收起的图标,当前行类型的图标,这里业务上分三种类型,就需要以此判断显示不同图标。每项最后还会有四个操作按钮。
这里把事件简化了,只分了两个事件,一个是展开收起,一个是一系列编辑操作,传个type参数作为区分。
tabNode(node: ITree) {
this.$emit("tabNode", node);
},
// 操作
doNode(node: ITree, type: string, index: number) {
this.$emit("doNode", node, type, index);
},
index文件中引用DomNode,相关的接收的参数和抛出去的事件,和DomNode一致。
// index布局
<div class="treeLayout">
<DomNode
v-for="(item, index) in trees"
:key="index"
:node="item"
@tabNode="tabNode"
@doNode="doNode"
:index="index"
></DomNode>
</div>
// 接收的参数
props: {
trees: {
type: Array as () => ITree[],
default: [],
},
activeId: {
type: String,
default: "",
},
},
页面右侧实现
引用catalogTree组件。
<catalog-tree
:trees="treeList"
@tabNode="tabNode"
@doNode="doNode"
></catalog-tree>
前文已经提过,目录数据是后端返回的,那么treeList就是后端返回值res.data。但操作tabNode和doNode这两个方法,需要将treeList数组转换成map对象。
因为需要自定义添加一些字段,这些字段只作为前端交互操作逻辑使用,所以后端返回值中不会携带。
需要给每一项数据添加isOpen字段,用来判断展开收起状态。level字段,用来实现上移下移操作。
先来构造这个catalogMap,定义个方法setCatalogMap,需要的参数有存放结果的treeMap,原数据treeList数组。
setCatalogMap,很简单的一个递归。
拿到map对象,就可以实现tabNode和doNode这两个方法。
// 切换状态
tabNode(node: ITree) {
if (node.isOpen) {
this.treeMap[node.catalogId].isOpen = false;
} else {
this.treeMap[node.catalogId].isOpen = true;
}
},
// 编辑等一系列操作,按照类型区分
doNode(node: ITree, type: string, index: number) {
switch (type) {
case "up":
// 上移
this.doUp(node, index);
break;
case "down":
// 下移
this.doDown(node, index);
break;
case "edit":
// 编辑
this.doEdit(node.catalogId);
break;
case "delete":
// 删除
this.doDelete(node);
break;
}
},
有认真看的话,会发现,并没有在哪里定义isOpen属性,怎么就在tabNode方法中使用了。
因为我还没有写。
拿到map对象,循环做个判断,用来保持isOpen状态。
Object.keys(treeMap).forEach((key) => {
const item = treeMap[key];
if (this.treeMap[key]) {
item.isOpen = this.treeMap[key].isOpen;
} else {
item.isOpen = true;
}
});
doNode中的四个方法,编辑和删除就是调个接口,主要是上移下移操作,前端实现数据的排序,最后将最新的数据返回给后端保存,doSaveSort方法调接口保存。
上代码,好好琢磨琢磨。
doUp(node: ICatalogModel, index: number) {
if (index === 0) {
return;
}
const parentId: string = node.catalogParent as string;
const parentItem: ICatalogModel = this.treeMap[parentId];
let dataList: ICatalogModel[] = [];
// 如果为空则是顶级
if (parentItem) {
if (parentItem.catalogTreeVoList) {
dataList = parentItem.catalogTreeVoList;
}
} else {
dataList = this.treeList;
}
const item = dataList[index];
dataList.splice(index, 1);
dataList.splice(index - 1, 0, item);
this.doSaveSort(dataList);
},
doDown(node: ICatalogModel, index: number) {
const parentId: string = node.catalogParent as string;
const parentItem: ICatalogModel = this.treeMap[parentId];
// 如果为空则是顶级
let dataList: ICatalogModel[] = [];
if (parentItem) {
if (parentItem.catalogTreeVoList) {
// 最后一个不能下移
if (parentItem.catalogTreeVoList.length === (index + 1)) {
return;
} else {
dataList = parentItem.catalogTreeVoList;
}
}
} else {
// 一级最后一个不能下移
if ( this.treeList.length === (index + 1)) {
return;
}
dataList = this.treeList;
}
const item = dataList[index];
dataList.splice(index, 1);
dataList.splice(index + 1, 0, item);
this.doSaveSort(dataList);
},
总结
树状结构列表,首先需要明确数据结构,必备的字段id,name,父级id,children数组,根据数据结构,使用递归构建布局。
-
vue——树形目录实现
2020-03-17 10:27:271、使用el-menu子组件递归 父级菜单menu.vue <template> <div class="main-sidebar" class="showSlide expandSide"> <el-menu class="el-menu-style" @select="" ...1、使用el-menu子组件递归
- 父级菜单menu.vue
<template> <div class="main-sidebar" class="showSlide expandSide"> <el-menu class="el-menu-style" @select="" @open="handleOpen" @close="handleClose"> <template v-for="item in fileList"> <sub-menu :param="item" ></sub-menu> </template> </el-menu> </div> </template> <script> import subMenu from "./subMenu.vue" import {fileList} from '../http/api' export default { data() { return { fileList:{}, } }, computed: { onRoutes(){ return this.$route.path; }, onRouteKeys(){ return getParentArray(this.$route.path, this.fileList); } }, created() { this.loadFile(); }, methods: { handleOpen(key, keyPath) { }, handleClose(key, keyPath) { }, loadFile(){ let projectId = this.$route.params.id fileList(projectId).then(res => { this.fileList = res.data }).catch(err => { alert("当前用例为空") }) } } } </script> <style scoped> .showSlide { animation-duration: .2s; animation-name: slideInLeft; } .hideSlide { animation-duration: .2s; animation-name: slideOutLeft; } .main-sidebar { background-color: #ffffff; position: fixed; top: 50px; left: 0; bottom: 0; height: calc(100vh - 50px); width: 44px; z-index: 810; -webkit-transition: -webkit-transform 0.3s ease-in-out, width 0.3s ease-in-out; -moz-transition: -moz-transform 0.3s ease-in-out, width 0.3s ease-in-out; -o-transition: -o-transform 0.3s ease-in-out, width 0.3s ease-in-out; transition: transform 0.3s ease-in-out, width 0.3s ease-in-out; } .expandSide { width: 280px; } .el-menu-style, .el-menu-style .el-menu{ background-color: #ffffff; } .el-menu-style .el-menu-item:hover, .el-menu-style .el-submenu__title:hover{ background-color: #eeeeee !important; } .el-menu-style .el-submenu .el-menu-item { height: 45px; line-height: 45px; } .el-menu-style .el-menu-item, .el-menu-style .el-submenu__title { height: 45px; line-height: 45px; } .main-sidebar .el-menu--collapse { width: 44px; } .main-sidebar .el-menu--collapse>.el-menu-item, .main-sidebar .el-menu--collapse>.el-submenu>.el-submenu__title { padding-left: 13px !important; } .vue-scrollbar{ background-color: #ffffff !important; height: calc(100vh - 50px) } .main-sidebar .el-scrollbar__bar.is-vertical{ display: none; } .sidebar{ min-height: 450px; } </style>
- 子组件subMenu.vue
<template> <div class="contents"> <div class="menu-list"> <el-submenu :index="item.href" v-if="item.children && item.children.length>0" class="el-menu-sub" > <template slot="title"> <div v-if="item.isShow == 2" v-contextmenu:contextmenu-file> <i class="fa fa-file"></i> <el-link :underline="false" @click="intoItem(item.id)"> <span>{{item.name}}</span> </el-link> </div> <div v-else v-contextmenu:contextmenu-folder> <i class="fa fa-folder"></i> <span>{{item.name}}</span> </div> </template> <template v-for="child in item.children"> <sub-menu v-if="child.children && child.children.length>0" :param="child"></sub-menu> <el-menu-item :index="child.href" v-else > <div v-if="child.isShow == 1" v-contextmenu:contextmenu-folder> <i class="fa fa-folder"></i> <span>{{item.name}}</span> </div> <div v-else v-contextmenu:contextmenu-file> <i class="fa fa-file"></i> <el-link :underline="false" @click="intoItem(child.id)"> <span>{{child.name}}</span> </el-link> </div> </el-menu-item> </template> </el-submenu> <el-menu-item :index="item.href" v-else class="el-menu-each"> <div v-if="item.isShow == 2" v-contextmenu:contextmenu-file> <i class="fa fa-file"></i> <el-link :underline="false" @click="intoItem(item.id)"> <span>{{item.name}}</span> </el-link> </div> <div v-else v-contextmenu:contextmenu-folder> <i class="fa fa-folder"></i> <span>{{item.name}}</span> </div> </el-menu-item> </div> </div> </template> <script> import subMenu from "./subMenu.vue" export default { name: 'subMenu', props: ['param'], data() { return { item: this.param } }, components: { subMenu, } } </script> <style scoped> .el-menu .fa { margin-right: 10px; } .box { width: 100%; } .el-dialog{ display: flex; flex-direction: column; margin:0 !important; position:absolute; top:50%; left:50%; transform:translate(-50%,-50%); /*height:600px;*/ max-height:calc(100% - 30px); max-width:calc(100% - 30px); } .el-dialog .el-dialog__body{ flex:1; overflow: auto; } </style>
2、使用开源组件v-tree
- v-tree.css
.ztree * { moz-user-select: -moz-none; -moz-user-select: none; -o-user-select:none; -khtml-user-select:none; -webkit-user-select:none; -ms-user-select:none; user-select:none; padding:0; margin:0; font-size:12px; font-family: Verdana, Arial, Helvetica, AppleGothic, sans-serif} .ztree {margin:0; padding:5px; color:#333} .ztree li{padding:0; margin:0; list-style:none; line-height:17px; text-align:left; white-space:nowrap; outline:0} .ztree li ul{ margin:0; padding:0 0 0 18px} .ztree li ul.line{ background:url(../img/line_conn.png) 0 0 repeat-y;} .ztree li a {padding-right:3px; margin:0; cursor:pointer; height:21px; color:#333; background-color: transparent; text-decoration:none; vertical-align:top; display: inline-block} .ztree li a:hover {text-decoration:underline} .ztree li a.curSelectedNode {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; opacity:0.8;} .ztree li a.curSelectedNode_Edit {padding-top:0px; background-color:#e5e5e5; color:black; height:21px; border:1px #666 solid; opacity:0.8;} .ztree li a.tmpTargetNode_inner {padding-top:0px; background-color:#aaa; color:white; height:21px; border:1px #666 solid; opacity:0.8; filter:alpha(opacity=80)} .ztree li a.tmpTargetNode_prev {} .ztree li a.tmpTargetNode_next {} .ztree li a input.rename {height:14px; width:80px; padding:0; margin:0; font-size:12px; border:1px #585956 solid; *border:0px} .ztree li span {line-height:21px; margin-right:2px} .ztree li span.button {line-height:0; margin:0; padding: 0; width:21px; height:21px; display: inline-block; vertical-align:middle; border:0 none; cursor: pointer;outline:none; background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; background-image:url("../img/v-tree.png");} .ztree li span.button.chk {width:13px; height:13px; margin:0 2px; cursor: auto} .ztree li span.button.chk.checkbox_false_full {background-position: -5px -5px;} .ztree li span.button.chk.checkbox_false_full_focus {background-position: -5px -26px;} .ztree li span.button.chk.checkbox_false_part {background-position: -5px -48px;} .ztree li span.button.chk.checkbox_false_part_focus {background-position: -5px -68px;} .ztree li span.button.chk.checkbox_false_disable {background-position: -5px -89px;} .ztree li span.button.chk.checkbox_true_full {background-position: -26px -5px;} .ztree li span.button.chk.checkbox_true_full_focus {background-position: -26px -26px;} .ztree li span.button.chk.checkbox_true_part {background-position: -26px -48px;} .ztree li span.button.chk.checkbox_true_part_focus {background-position: -26px -68px;} .ztree li span.button.chk.checkbox_true_disable {background-position: -26px -89px;} .ztree li span.button.chk.radio_false_full {background-position: -47px -5px;} .ztree li span.button.chk.radio_false_full_focus {background-position: -47px -26px;} .ztree li span.button.chk.radio_false_part {background-position: -47px -47px;} .ztree li span.button.chk.radio_false_part_focus {background-position: -47px -68px;} .ztree li span.button.chk.radio_false_disable {background-position: -47px -89px;} .ztree li span.button.chk.radio_true_full {background-position: -68px -5px;} .ztree li span.button.chk.radio_true_full_focus {background-position: -68px -26px;} .ztree li span.button.chk.radio_true_part {background-position: -68px -47px;} .ztree li span.button.chk.radio_true_part_focus {background-position: -68px -68px;} .ztree li span.button.chk.radio_true_disable {background-position: -68px -89px;} .ztree li span.button.switch {width:21px; height:21px} .ztree li span.button.root_open{background-position:-105px -63px} .ztree li span.button.root_close{background-position:-126px -63px} .ztree li span.button.roots_open{background-position: -105px 0;} .ztree li span.button.roots_close{background-position: -126px 0;} .ztree li span.button.center_open{background-position: -105px -21px;} .ztree li span.button.center_close{background-position: -126px -21px;} .ztree li span.button.bottom_open{background-position: -105px -42px;} .ztree li span.button.bottom_close{background-position: -126px -42px;} .ztree li span.button.noline_open{background-position: -105px -84px;} .ztree li span.button.noline_close{background-position: -126px -84px;} .ztree li span.button.root_docu{ background:none;} .ztree li span.button.roots_docu{background-position: -84px 0;} .ztree li span.button.center_docu{background-position: -84px -21px;} .ztree li span.button.bottom_docu{background-position: -84px -42px;} .ztree li span.button.noline_docu{ background:none;} .ztree li span.button.ico_open{margin-right:2px; background-position: -147px -21px; vertical-align:top; *vertical-align:middle} .ztree li span.button.ico_close{margin-right:2px; background-position: -147px 0; vertical-align:top; *vertical-align:middle} .ztree li span.button.ico_docu{margin-right:2px; background-position: -147px -42px; vertical-align:top; *vertical-align:middle} .ztree li span.button.edit {margin-left:2px; margin-right: -1px; background-position: -189px -21px; vertical-align:top; *vertical-align:middle} .ztree li span.button.edit:hover { background-position: -168px -21px; } .ztree li span.button.remove {margin-left:2px; margin-right: -1px; background-position: -189px -42px; vertical-align:top; *vertical-align:middle} .ztree li span.button.remove:hover { background-position: -168px -42px; } .ztree li span.button.add {margin-left:2px; margin-right: -1px; background-position: -189px 0; vertical-align:top; *vertical-align:middle} .ztree li span.button.add:hover { background-position: -168px 0; } .ztree li span.button.ico_loading{margin-right:2px; vertical-align:top; *vertical-align:middle} ul.tmpTargetzTree {background-color:#FFE6B0; opacity:0.8; filter:alpha(opacity=80)} span.tmpzTreeMove_arrow {width:16px; height:21px; display: inline-block; padding:0; margin:2px 0 0 1px; border:0 none; position:absolute; background-color:transparent; background-repeat:no-repeat; background-attachment: scroll; background-position:-168px -84px; background-image:url("../img/v-tree.png"); } ul.ztree.zTreeDragUL {margin:0; padding:0; position:absolute; width:auto; height:auto;overflow:hidden; background-color:#cfcfcf; border:1px #00B83F dotted; opacity:0.8; filter:alpha(opacity=80)} .ztreeMask {z-index:10000; background-color:#cfcfcf; opacity:0.0; filter:alpha(opacity=0); position:absolute}
- tree.vue
<template> <div> <ul class="ztree"> <vTree v-for="(node,index) in treeData" :key="index" :checkBoxType="checkBoxType" :allOpen="allOpen" :beforeClick="beforeClick" :checkBox="checkBox" :nodeTrigger="nodeTrigger" :index="index" :tree="node" :first="index===0" :last="treeData.length-1===index" :currentArray="treeData" :parentTree="node.parentTree" :rootData="treeData" :checkBoxCallInit="checkBoxCallInit" :checkBoxCall="checkBoxCall" :clickNode="clickNode" :hiddenLine="hiddenLine" :async="async" :asyncCall="asyncCall" /> </ul> </div> </template> <script> import vTree from './tree-core' export default { components: {vTree}, name: "tree", props: { treeNode: { type: Array, default: function () { return []; }, required: true, }, allOpen: { type: Boolean, default: null, required: false, }, nodeTrigger: { type: Boolean, default: false, required: false, }, checkBox: { type: Boolean, default: false, required: false, }, checkBoxType: { type: Boolean, default: true, required: false, }, beforeClick: { type: Function, default: null }, clickNode: { type: Function, }, asyncCall: { type: Function, }, hiddenLine: { type: Boolean, default: false, required: false, }, async: { type: Boolean, default: false, required: false, }, }, data() { return { treeData: [], line: '', first: true, allOpens: this.allOpen, checkedBoxCallArr: [], } }, methods: { init() { let tempList = JSON.parse(JSON.stringify(this.treeNode)); let initTree = (tree, parent) => { for (let index = 0; index < tree.length; index++) { let m = tree[index]; if (!m.hasOwnProperty("id")) { m.id = m.hasOwnProperty("id") ? m.id : null; } if (!m.hasOwnProperty("open")) { m.open = m.hasOwnProperty("open") ? m.open : false; } if (!m.hasOwnProperty("checked")) { m.checked = m.hasOwnProperty("checked") ? m.checked : false; } if (!m.hasOwnProperty("checkBox")) { m.checkBox = m.hasOwnProperty("checkBox") ? m.checkBox : false; } if (!m.hasOwnProperty("nodeTrigger")) { m.nodeTrigger = m.hasOwnProperty("nodeTrigger") ? m.nodeTrigger : false; } if (!m.hasOwnProperty("checkBoxType")) { m.checkBoxType = this.checkBoxType } if (!m.hasOwnProperty("last")) { m.last = m.hasOwnProperty("last") ? m.last : false; } if (!m.hasOwnProperty("first")) { m.first = m.hasOwnProperty("first") ? m.first : false; } if (!m.hasOwnProperty("active")) { m.active = m.hasOwnProperty("active") ? m.active : false; } if (!m.hasOwnProperty("async")) { m.async = this.async; } if (!m.hasOwnProperty("hiddenLine")) { m.hiddenLine = this.hiddenLine; } if (!m.hasOwnProperty("parentTree")) { m.parentTree = parent ? parent : null; } m.children = m.children || []; if (m.children.length > 0) initTree(m.children, m); } }; initTree(tempList, null); this.treeData = tempList; this.line = 'line'; if (this.first) this.$emit('call', this.treeData); }, changeStatus() { let changeOpen = (data) => { data.forEach(d => { d.open = this.allOpen; if (d.children) changeOpen(d.children); }); }; changeOpen(this.treeData); }, checkBoxCallInit(arr) { arr.forEach(a => { this.checkedBoxCallArr.push(a); }); }, checkBoxCall(arr, isAdd) { if (isAdd) arr.forEach(a => { this.checkedBoxCallArr.push(a); }); else { arr.forEach(a => { if (this.checkBoxCall.length === 0) return; let key = (a.id ? a.id : null) + a.index + a.name; this.checkedBoxCallArr.forEach((ss, index) => { if (((ss.id ? ss.id : null) + ss.index + ss.name) === key) { this.checkedBoxCallArr.splice(index, 1); } }); }); } this.$emit('checkBoxCall', this.checkedBoxCallArr); }, }, created() { this.init(); }, update() { }, mounted() { /*复选框回调*/ this.$emit('checkBoxCall', this.checkedBoxCallArr); }, watch: { allOpen() { this.changeStatus() } } } </script> <style scoped> @import "../../../../static/css/v-tree.css"; </style>
- tree-core.vue
<template> <li> <span v-if="vif()" title="" @click="vShow(tree)" class="button switch" :class="vTreeFirstSpan()"></span> <span v-else title="" class="button switch " :class="classes()"></span> <span v-if="checkBox" @click="selectCheckBox(tree)" class="button chk" :class="tree.checked?'checkbox_true_full':'checkbox_false_full'"></span> <a target="_blank" style="" @click="selectNode" :class="aClass()" :title="tree.name"> <span :title="tree.name" class="button" :class="iconCss()" :style="load()"></span> <span class="node_name">{{tree.name}}</span> </a> <ul :class="line"> <treeCore v-for="(tr,i) in tree.children" v-show="tree.open" :key="i" :checkBoxType="checkBoxType" :allOpen="tr.allOpen" :first="false" :checkBox="checkBox" :nodeTrigger="nodeTrigger" :index="i" :tree="tr" :currentArray="tree.children" :parentTree="tr.parentTree" :rootData="rootData" :clickNode="clickNode" @checkedBoxV="checkedBoxV" :beforeClick="beforeClick" :checkBoxCallInit="checkBoxCallInit" :checkBoxCall="checkBoxCall" :last="tree.children.length-1===i" :hiddenLine="tr.hiddenLine" :async="async" :asyncCall="asyncCall" /> </ul> </li> </template> <script> export default { name: "treeCore", props: { tree: { type: Object, required: true, }, allOpen: { type: Boolean, default: null, required: false, }, nodeTrigger: { type: Boolean, default: false, required: false, }, checkBox: { type: Boolean, default: false, required: false, }, checkBoxType: { type: Boolean, default: true, required: false, }, beforeClick: { type: Function, default: null }, last: { type: Boolean, default: null, required: false, }, first: { type: Boolean, default: true, required: false, }, currentArray: { type: Array, required: true, }, index: { type: Number, required: true, }, rootData: { type: Array, required: true, }, parentTree: { type: Object, required: false, }, checkBoxCallInit: { type: Function, }, checkBoxCall: { type: Function, }, clickNode: { type: Function, }, asyncCall: { type: Function, }, hiddenLine: { type: Boolean, default: false, required: false, }, async: { type: Boolean, default: false, required: false, }, }, data() { return { line: '', currentTree: this.tree, asyncLoading: false, } }, methods: { coreInit() { this.line = this.hiddenLine ? '' : this.currentArray.length - 1 === this.index ? '' : 'line'; if (this.tree.parentTree && this.tree.checked) { /*方案一 Scheme 1*/ let checkedBoxArr = []; checkedBoxArr.push(this.tree); let selectParent = (data) => { if (!data.checked) { data.checked = this.tree.checked; checkedBoxArr.push(data); } if (data.parentTree) { selectParent(data.parentTree); } else { this.checkBoxCallInit(checkedBoxArr); } }; selectParent(this.currentTree); /*方案二 Scheme 2*/ //this.$emit('checkedBoxV') } }, checkedBoxV() { this.tree.checked = true; this.$emit('checkedBoxV') }, pNode(tree) { return tree.open; }, vShow(tree) { /*需要有异步加载 need add async loading*/ /*默认异步加载给他按钮 当加载后才能判别时候有子节点 */ if (this.async && !tree.open && tree.children.length === 0) { this.asyncCall.call(this, tree, this.asyncBack); this.asyncLoading = true; } tree.open = !tree.open; }, asyncBack(requestDataArr) { if (typeof requestDataArr) this.addNode(requestDataArr); this.asyncLoading = false; }, selectNode() { let isClick = this.beforeClick.call(this, this.tree); if (isClick) { //清除其它 let pro = null; let clearStyle = (data) => { for (let i = 0; i < data.length; i++) { let d = data[i]; if (d.active) { pro = d; d.active = !d.active; } else if (d.children) { clearStyle(d.children); } } }; clearStyle(this.rootData); this.tree.active = true; if (this.nodeTrigger) { this.tree.open = !this.tree.open; } this.clickNode.call(this, this.tree, pro); } }, addNode(arr) { try { arr.forEach(a => { this.tree.children.push({ id: a.id ? a.id : null, name: a.name, open: a.open ? a.open : false, checked: a.checked ? a.checked : false, checkBox: a.checkBox ? a.checkBox : false, nodeTrigger: a.nodeTrigger ? a.nodeTrigger : this.nodeTrigger, checkBoxType: this.checkBoxType, last: false, first: false, active: false, async: this.async, hiddenLine: this.hiddenLine, parentTree: this.tree, children: [], }); }); } catch (e) { console.error('The asynchronous callback parameter must be an array,异步回调参数必须是数组'); } }, selectCheckBox(tree) { tree.checked = !tree.checked; let checkedBoxArr = []; checkedBoxArr.push(tree); if (this.checkBoxType) { /*级联*/ let changeChecked = (data) => { data.forEach(d => { checkedBoxArr.push(d); d.checked = tree.checked; if (d.children) changeChecked(d.children); }); }; changeChecked(tree.children); let checkChildren = (data) => { for (let i = 0; i < data.length; i++) { if (data[i].checked) return true; } }; let selectParent = (data) => { if (data.children.length > 1) { const childExistsThis = []; data.children.forEach((m, index) => { if (index !== this.index) childExistsThis.push(m); }); if (childExistsThis && data.checked && checkChildren(childExistsThis)) return; } checkedBoxArr.push(data); data.checked = tree.checked; if (data.parentTree) selectParent(data.parentTree); }; if (this.parentTree) selectParent(this.parentTree); } this.checkBoxCall(checkedBoxArr, tree.checked); } }, created() { this.coreInit(); }, computed: { vif() { return function () { return this.async ? true : this.tree.children.length > 0; } }, vTreeFirstSpan() { return function () { return this.hiddenLine ? this.tree.open ? 'noline_open treenode_switch' : 'noline_close treenode_switch' : this.currentArray.length - 1 === this.index ? this.tree.open ? 'bottom_open' : 'bottom_close' : this.tree.open ? 'roots_open' : 'roots_close'; } }, classes() { return function () { return this.hiddenLine ? 'noline_docu' : this.first ? 'roots_docu' : this.currentArray.length - 1 === this.index ? 'bottom_docu' : 'center_docu' } }, iconCss() { return function () { return this.tree.children.length > 0 ? this.tree.open ? 'ico_open' : 'ico_close' : 'ico_docu'; } }, load() { return function () { return this.asyncLoading ? 'background:url(../../static/img/loading.gif) 0 0 no-repeat' : '' } }, aClass() { return function () { return this.tree.active ? 'curSelectedNode' : ''; } } }, watch: { tree: { handler(oldV, newV) { if (newV.checked) this.tree.checked = newV.checked; }, deep: true }, } } </script> <style scoped> @import "../../../../static/css/v-tree.css"; </style>
- vtree-demo.vue
<template> <div style="height: auto"> <div class="main"> <ul> <li> <Tree :treeNode="treeNode" :clickNode="clickNode" :beforeClick="beforeClick" :hiddenLine="hiddenLine" :async="async" @call='callAsync' :asyncCall="asyncCall" :nodeTrigger="nodeTrigger" /> </li> </ul> </div> </div> </template> <script> import Tree from "./tree"; export default { components: {Tree}, data() { return { treeNode: [], nodeTrigger: false, // 所有节点 trees: [], hiddenLine: false, async: true, } }, methods: { beforeClick(node) { //可操作内部 return true; }, /*回调初始化后的数据 以后修改tree实现对节点的操作即可*/ call(data) { console.log('data', data); // this.trees = data; }, callAsync(data) { //this.trees = data; }, /*点击节点信息 上个点击节点信息*/ clickNode(data, oldData) { /*if(data.children){ data.children.push({ id: (new Date()).getTime(), name:'新增节点', children:[] }); }else{ }*/ // console.log(data); //data.open=true; }, /*异步回调函数 data 当前节点数据 call 回调函数*/ asyncCall(data, call) { setTimeout(function () { let hm = '' + (new Date()).getTime(); let addNode = [ {id: hm, name: "children node" + hm.substr(hm.length - 4, hm.length)}, ]; console.log(data.id) // call(addNode); call([]) }, 800); } }, mounted() { // this.allOpen = true; }, created() { this.treeNode = [ { id: "1", name: "父节点22 - 折叠", open: true, // 文件夹open = true/false // 文件:不显示图像 // 添加右键弹出操作 children: [ {id: "1-1", name: "叶子节点221",open: true}, {id: "1-2", name: "叶子节点222",open: true}, {id: "1-3", name: "叶子节点223",open: true}, {id: "1-4", name: "叶子节点224",open: true} ] }, { id: "2", name: "父节点23 - 折叠", children: [ {id: "2-1", name: "叶子节点231"}, {id: "2-2", name: "叶子节点232"}, {id: "2-3", name: "叶子节点233"}, {id: "2-4", name: "叶子节点234"} ] }, { open: true, id: "3", name: "父节点24 - 折叠", children: [ { id: "2-1-1", name: "叶子节点232", open: true, children: [ {id: "2-1-3", name: "叶子节点2313"}, {id: "2-2-3", name: "叶子节点2323"}, {id: "2-3-3", name: "叶子节点2333"}, {id: "2-4-3", name: "叶子节点2343"} ] }, ] }, ]; } } </script> <style scoped> .main ul li { list-style: none; float: left; width: 25%; } </style>
3、使用el-tree动态加载
-
vue tree树形结构仿文件夹目录
2020-12-24 21:40:10vue 树形结构利用vue组件自己调用自己,循环遍历生成树形结构。 组件调用自己时应绑定 name属性 拓展:给组件命名的三大作用 1.允许组件模板递归地调用自身; 2.指定 name 选项的另一个好处是便于调试。有名字的组件...vue 树形结构利用vue组件自己调用自己,循环遍历生成树形结构。
组件调用自己时应绑定 name属性拓展:给组件命名的三大作用
1.允许组件模板递归地调用自身;
2.指定 name 选项的另一个好处是便于调试。有名字的组件有更友好的警告信息。另外,当在有 vue-devtools,未命名组件将显示成 ,这很没有语义
3.可提供给 keep-alive 的 include 和 exclude 作为参数选项;
第一步:构造数据
node:{ name: '/', children: [ { name: 'Music', children: [ { name: '雨一直下.mp3' }, { name: '伍佰' } ] }, { name: 'Video', children: [ { name: 'vue教学.mp4' }, { name: 'treeBowser.mp4' } ] } ] },
**
第二步:创建组件并引入页面
初步创建组件并渲染一级目录下的文件名称(name)
这里我引用的是另一组数据,但是数据结构是一样的
**
子组件<template> <div class="tree-box"> <div class="tree-name"> <span>{{root.name}}</span> </div> </div> </template>
页面
<template> <div class="body"> <h1>Vue Tree Browser</h1> <tree-two :root="root"/> </div> </template> <script> import TreeTwo from '@/components/tree/TreeTwo' import root from '@/lib/root.js' export default { components:{ TreeTwo }, data () { return { node:{ name: '/', children: [ { name: 'Music', children: [ { name: '雨一直下.mp3' }, { name: '伍佰' } ] }, { name: 'Video', children: [ { name: 'vue教学.mp4' }, { name: 'treeBowser.mp4' } ] } ] }, root, } }, methods: { wasChildren(node){ alert(node.name) } } } </script> <style lang="less" scoped> .body{ color: #fff; width: 100%; background-color: #333; } </style>
第三步:组件调用自身
来看下渲染效果,所有数据一起渲染看起来并不清爽,加一个子文件缩进,并让文字左对齐
加一个参数:indent 默认为零,并传入回调组件
又能么点像了,但是子节点默认是不点开。加一条判断属性,点击开启或关闭。
展开关闭做好后给他们加个图标
加个计算属性判断当前root有无子节点
assets 这个文件下没有子节点 但是也可以点击。做个小优化,没有子节点不显示,或者换个图标
现在展示已经差不多了,当文件进入最后一级节点,点击时加一些触发事件
第一次做的时候碰到了问题,这里用到的是click方法与onClick事件。
用子组件的click方法触发父组件的onClick事件
子组件代码
页面代码
这是第一次写的代码。这样写只会触发根目录。因为用的是组件回调。要在回调的组件中也加入onClick事件监听,并触发自己上一级的onClick事件
或者直接在子组件调用click方法
以上内容都是观看大佬的youtube视频。放个原视频链接
https://www.youtube.com/watch?v=VdSf6SFPiFg&list=PLwXcwDw-S70k-bqhpr3xRoUJP87u5bKGq以上内容如有不足请大家多多指正,谢谢。
-
vue 递归组件实现目录树,数据处理
2020-01-10 16:51:35vue 实现目录树 递归组件 菜单展收 -
VUE递归树形目录(vue递归组件)的使用
2018-01-10 14:53:00="js/vue.js" > script > head > < body > <!-- 递归模板 --> < template id ="diguiTempTest" > < li > < div :class ="{bold: isFolder}" > {{model.data.menuName}} div > < ul ... -
vue 树形目录结构
2021-01-27 17:04:00== 200) { this.$Message.error(res.data.msg) } else { T.nodes = res.data.data T.allNodes = res.data.data } }) }, beforeDestroy() { } } script> <style lang="scss" scoped> style> 树组件 <template> ... -
vue的树形目录结构php后台该怎么组织数据?php递归遍历无线分类,输出vue要求的树形目录结构!...
2017-07-28 16:33:00需求: 需要的数据规则: 1 // demo data 2 var data = { 3 name: 'My Tree', 4 children: [ 5 { name: 'hello' }, 6 { name: 'wat' }, 7 { 8 name: 'child folder', ... 9 ... -
vue 移动端树结构功能_Vue2 实现树形菜单(多级菜单)功能模块
2020-12-20 12:40:10结构示意图├──index.html├──main.js├──router│└──index.js # 路由配置文件├──components # 组件目录│├──App.vue # 根组件│├──Home.vue # 大的框架结构组件│├──TreeView.vue│├──... -
Vue利用递归实现简单的目录树结构
2018-01-24 11:06:00日前,在工作中有一个类似chrome书签管理器的需求,在调研了iview的Tree组件,Vue-dragging,Vue-...本文着重实现树形结构的展示,拖拽功能的完善后续会完善。 Vue作为组件化的框架,原则上所有的页面元素都由数据... -
Django + Vue.js完成文件目录树的显示
2020-07-27 17:26:33最近在做一个接口自动化测试平台,需要前端展示当前服务器上的测试用例,自然而然的想到展示成一个文件目录树的形状,这样不管浏览、添加、删除等操作都可以比较方便的实现,也比较方便浏览。( BTW,你是不是觉着我... -
vue项目目录介绍
2019-09-23 17:56:58Vue项目目录 初始化项目 vue init webpack []projectname] cd [projectname] npm install vue run dev 目录树 +---build | build.js | check-versions.js | dev-client.js | dev-server.js | ... -
vue 递归组件 (树形结构目录)
2019-08-21 17:46:19在项目中 有时需要 分级目录 树形目录 这时就可以用到递归组件了 递归组件 是指就是组件中嵌套组件自身 首先我们需要一组这样的数据 [ { "name": "一级目录", cList: [ { "name": "二级目录" }, { "name": ... -
vue树形组件
2017-01-09 22:06:12vue-treevue 编写的树形菜单,小巧实用,支持vue1.0,vue2.0v1.0 功能: 1.支持多级树目录 2.支持高亮点击的节点 3.支持展开点击节点 4.支持点击收缩节点时收缩所有子目录 5.支持自定义回调函数,点击节点时回调,... -
Vue(简单的目录树组件)---附注释
2020-09-17 15:24:18<template> <el-tree ref="tree" :data="layers" #绑定数据 ...node-key="name" #key为对象的name属性 ...:default-expand-all="true" #默认全部展开 ...@check-change="checkChange" #节点选中改变事件 ... -
ant vue 树形菜单横向显示_快速实现一个简单可复用可扩展的Vue树组件
2020-12-31 05:49:25树组件的应用场景很多,比如一篇文章的目录、一个公司部门组织情况、思维导图等,其实都可以用树形结构来描述。本文讲述一下Vue中树组件的简单实现。树组件在线体验地址:http://wintc.top/laboratory/#/tr... -
antd 表格树如何展开_vue表格树状结构的实现
2020-12-28 03:15:04上一篇文章总结了vue树状目录组件,忘记的可以点击传送门回顾一下:vue目录树组件还是这个目录树,改版成表格树状结构。我这里采用的UI库是Element,库自带了一种实现方式:但是这种自带的实现方式,可扩展性很差,... -
[Vue]实现无限级导航树目录
2020-01-02 10:20:34<script src="https://cdn.bootcss.com/vue/2.6.9/vue.min.js"> <script src="https://cdn.bootcss.com/jquery/3.4.0/jquery.min.js"></script> // var TreeData = [ // { // name: 'IT互联网', // child: ... -
vue之递归组件实现树形目录
2018-12-11 17:14:00递归组件的应用===》可以通过组件命名来自己使用自己的组件 实例如下 父组件 <div class="content"> <detail-list :list="categoryList"></detail-list> </div>... ... -
Ant Design Vue Tree 树形控件实现添加子节点
2020-08-28 13:41:51这个比较简单,因为树就是从root开始的,所以直接拿到root节点的子节点数组,添加新节点对象即可。 addMenu() { const newChild = { title: '编辑菜单', key: (new Date()).getTime(), scopedSlots: {title: '... -
[Vue]实现二级导航树目录
2019-12-31 17:56:22html <!DOCTYPE html> <html> <head>...meta charset="utf-8"/>...导航目录</title> <link rel="stylesheet" type="text/css" href="css/index.css"> <link rel="...
-
物联网基础篇:快速玩转MQTT
-
激光诱导热透镜效应的一个理论模型
-
Ansible 自动化工具安装、配置和快速入门指南
-
MHA 高可用 MySQL 架构与 Altas 读写分离
-
左移运算符重载
-
场的相干和能级的近简并对共振两能级系统占据几率的影响
-
虚拟机云服务管理教程截图
-
ELF视频教程
-
python正则获取网页标签里面的内容
-
程序员必修基础套餐课
-
响应式编程入门与实战(Reactor、WebFlux、R2DBC)
-
【中国信通院】5G边缘计算安全白皮书.pdf
-
LVS + Keepalived 实现 MySQL 负载均衡与高可用
-
安全的云存储命中分布式字符串均等性检查:更高效,概念上更简单且可证明的安全性
-
用微服务spring cloud架构打造物联网云平台
-
C++MFC开发远程控制软件教程(VS2013)
-
高分辨率光谱用的染料激光振荡放大系统
-
rdr-windows.exe
-
C++中,For循环使用size()函数报错问题
-
开源一套MODBUS主机代码(带讲解分析)