引用
PascalCase
(首字母大写命名)组件,<my-component-name>
和 <MyComponentName>
都是可接受
直接在 DOM
(即非字符串的模板) 中使用时只有 kebab-case
是有效的,比如 text/x-template
场景下,只能用后者
知识点
class
样式对象写法,CSS伪元素 ::before
用法vue
指令v-model
,内联模板x-template
- 事件
dblclick blur
, 防止事件传播修饰符 stop
- 引用父实例属性
$parent
, 全局方法 Vue.set Vue.delete
,子组件类型校验props
树形curd
// 树形原始数据结构
var data = {
name: 'home',
children: [
{ name: 'hello.js' },
{
name: 'child folder',
children: [
{
name: 'code folder',
children: [
{ name: 'php' },
{ name: 'golang' }
]
},
]
},
{ name: 'wat.py' }
]
}
// 注册全局子组件
Vue.component('item', {
template: '#item-template',
// 类型校验,接收父组件传递过来的数据(只读)
props: {
model: Object,
},
// 当前子组件私有的响应数据
data() {
return {
open: false, // 展开标志
name: this.model.name, // 显示文本
show: true // 是否使用编辑
}
},
computed: {
// 子节点判断标志
isFolder() {
return this.model.children && this.model.children.length
}
},
methods: {
toggle() {
if (this.isFolder) {
this.open = !this.open
}
},
changeType() {
// 将文件变为文件夹
if (!this.isFolder) {
// 向响应式对象中添加一个新属性,需确保这个新属性同样是响应式的,以触发视图更新
Vue.set(this.model, 'children', [])
this.addChild()
this.open = true
}
},
addChild() {
this.model.children.push({
name: 'new file'
})
},
// 清除文件夹下子节点
delChild() {
if (this.isFolder) {
Vue.delete(this.model, 'children')
}
},
delFile() {
// 通过父节点删除当前节点
if (this.$parent.model === undefined) {
console.log('根节点不能删除!!!')
this.name = '根节点不能删除!'
return
}
var parent = this.$parent.model.children
if (parent && Array.isArray(parent) && parent.length) {
// 清除当前节点
parent.splice(parent.indexOf(this.model), 1)
}
},
// 编辑开关
editToggle() {
this.show = !this.show
},
doneEdit(name) {
this.old = name
this.name = name
this.show = false
},
cancelEdit() {
this.name = this.old
this.show = false
}
}
})
var demo = new Vue({
el: '#demo',
data: {
treeData: data
}
})
视图
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>vuejs树形结构curd</title>
<style>
body {
font-family: Menlo, Consolas, monospace;
color: #444;
}
.item {
cursor: pointer;
}
.bold {
font-weight: bold;
}
ul {
padding-left: 1em;
line-height: 1.5em;
list-style-type: dot;
}
.folderc::before {
content: '? '
}
.foldero::before {
content: '? '
}
.file::before {
content: '? '
}
p {
line-height: 7px;
font-size:7px;
}
</style>
<script src="https://unpkg.com/vue/dist/vue.min.js"></script>
</head>
<body>
<script type="text/x-template" id="item-template">
<li>
<div :class="{foldero:isFolder && open ,folderc: !(isFolder && open),file:!isFolder}"
@click="toggle" @dblclick="changeType">
<span v-show="show">{{ name }}</span>
<input type="text" class="edit" v-model="name"
@blur="doneEdit(name)" @keyup.enter="doneEdit(name)"
@keyup.esc="cancelEdit()" v-show="!show" @click.stop @dblclick.stop >
<span @click.stop='editToggle'>[e]</span>
<span @click.stop='delChild' v-show="isFolder">[c]</span>
<span @click.stop='delFile' v-show="!isFolder">[x]</span>
<span v-if="isFolder">[{{ open ? '-':'+'}}]</span>
</div>
<ul v-show="open" v-if="isFolder">
<item class="item" v-for="(treeData,index) in this.model.children" :model="treeData" :key="index"/>
<li><span class="add" @click.stop="addChild">[append] </span></li>
</ul>
</li>
</script>
<p>[+]展开 [-]折叠 [e]编辑 </p>
<p>[x]删除文件 [c]清空文件夹</p>
<p>[append]追加节点文件</p>
<p>文件夹递归删除转为文件</p>
<p>双击文件转为含新文件的文件夹</p>
<ul id="demo">
<item class="item" :model="treeData" />
</ul>
<script src="tree.js"></script>
</body>
</html>