<template>
<!-- 可配置化菜单demo -->
<div class="optionC">
<el-tabs v-model="activeName" @tab-click="handleClick" type="border-card">
<el-tab-pane :label="n.name" :name="n.activeName" v-for="(n, m) in syllable" :key="n.activeName">
<div v-for="(items , index) in n.children" :key="index + 'b'" style="display: flex;flex-direction: column;">
<span>{{items.title}}</span>
<draggable
class="syllable_ul"
v-model="items.children"
animation="300"
group="site"
:options="{group:'title', animation:150}"
:no-transition-on-drag="true"
@change="change"
@start="start"
@end="end"
:disabled="disabled"
:move="move"
@add="add1"
>
<!-- <transition-group type="transition"> -->
<div
@input="changeText(g.tip,g.id+index+'f',g.id+index+'s')"
contenteditable="false"
@blur="blur(g.id+index+'f', g.tip, g.id+index+'s')"
:ref="g.id+index+'f'"
@dblclick="dblclick(g.id+index+'f', g.id+idx, g, g.id+index+'s')"
class="syllable_li"
v-for="(g , idx) in items.children"
:key="g.id+idx+'f'"
>
<!-- 要用标签包裹 不然光标有问题 -->
<span :key="g.id+index+'s'" style="overflow: hidden;width: 80px;text-align: center;" :ref="g.id+index+'s'">{{g.tip}}</span>
<i :ref="g.id+idx" :key="idx+'a'" @click="deleteCard(g,n.id)" class="el-icon-circle-close i_content"></i>
<!-- <el-input :key="idx+'a'"></el-input> -->
</div>
<!-- </transition-group> -->
</draggable>
<div>
<el-button>新增
</el-button>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</template>
<script>
import draggable from 'vuedraggable'
export default {
components: {
draggable
},
data() {
return {
activeName: 'first',
disabled: false,
edit: true,
contenteditable: false,
editData: '', // 双击编辑完后的值
curEdit: '',
curEditDiv: '',
changeTex: '',
alertext: false, // 小于一个字符不让删除
syllable:[
{
name: '总部机构',
id: 'a01',
activeName: 'first',
children: [
{
title: '线上经营',
id: 'b01',
children: [
{
tip: '预约客追踪',
id: 'c01' // 拖拽后的顺序
},
{
tip: '预约宝追踪',
id: 'c02'
},
{
tip: 'c',
id: 'c03'
}
]
},
{
title: '数据中心',
id: 'b_02',
children: [
{
tip: '数据一号',
id: 'c04'
},
{
tip: '数据二号',
id: 'c05'
},
{
tip: '8',
id: 'c06'
}
]
},
]
},
{
name: '营业区',
id: 'a02',
activeName: 'second',
children: [
{
title: '线上经营',
id: 'a001',
children: [
{
tip: 'yyqa',
id: 'c00e1'
},
{
tip: 'yyqb',
id: 'c00e2'
},
{
tip: 'yyqc',
id: 'c00e3'
}
]
},
{
title: '数据中心',
id: 'b002',
children: [
{
tip: 'yyq1',
id: 'c004e'
},
{
tip: 'yyq2',
id: 'c005e'
},
{
tip: 'yyq3',
id: 'c006e'
}
]
},
]
}
],
drag: false,
}
},
methods: {
changeText(e,el,sp) {
// 去掉空格 重新定位光标
if(this.curEditDiv.innerText.indexOf(' ') > -1) {
this.curEditDiv.innerText = this.curEditDiv.innerText.trim();
this.localRange(el, sp);
}
},
// 定位光标
localRange(el, sp) {
console.log(this.$refs[sp])
if(window.getSelection) {
this.$refs[el][0].focus();
let ranges = window.getSelection();//创建range
ranges.selectAllChildren(this.$refs[sp][0]);
ranges.collapseToEnd();//光标移至最后
}else if (document.selection) { //ie10 9 8 7 6 5
var range = document.selection.createRange(); //创建选择对象
//var range = document.body.createTextRange();
range.moveToElementText(this.$refs[sp][0]); //range定位到obj
range.collapse(false); //光标移至最后
range.select();
}
},
blur(el, text, sp) {
if(this.curEditDiv.innerText.trim() === '') {
// alert('不能为空');
this.curEditDiv.innerText = text.substr(0,1);
this.localRange(el, sp);
}
this.$refs[el][0].setAttribute("contenteditable", false);
let result = [];
this.replaceAndSearch(this.syllable, this.curEdit.id, this.curEditDiv.innerText, result);
},
// 双击触发编辑
// g是当前要编辑的对象
dblclick(elInpul, i, g, sp) {
this.curEdit = g;
let el = this.$refs[elInpul][0];
this.curEditDiv = this.$refs[sp][0];
let oldHtml = this.$refs[elInpul][0].innerText;
this.$refs[elInpul][0].setAttribute("contenteditable", true);
this.localRange(elInpul, sp);
},
deleteCard(a,id) {
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
this.deleteNode(this.syllable, a.id);
this.$message({
type: 'success',
message: '删除成功!'
});
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
},
// 根据id查询/替换节点
replaceAndSearch(list, id, tip, result) {
let items = null, hasFound=false;
list.forEach(item => {
if(item.id === id) {
// debugger
item.tip = tip;
result.push(item);
hasFound = true;
} else if(item.children && item.children.length){
this.replaceAndSearch(item.children, id, tip ,result);
}
});
},
// 递归删除节点
deleteNode(data, id) {
let newDatas = data.filter(el => el.id !== id)
newDatas.forEach(el => el.children && (el.children = this.deleteNode(el.children, id)));
return newDatas
},
handleClick(tab, event) {
// console.log(tab, event);
},
//evt里面有两个值,一个evt.added 和evt.removed 可以分别知道移动元素的ID和删除元素的ID
change(evt) {
// console.log(evt , 'change...')
},
//start ,end ,add,update, sort, remove 得到的都差不多
start(evt) {
this.drag = true
// console.log(evt , 'start...')
},
end(evt) {
this.drag = false
evt.item //可以知道拖动的本身
evt.to // 可以知道拖动的目标列表
evt.from // 可以知道之前的列表
evt.oldIndex // 可以知道拖动前的位置
evt.newIndex // 可以知道拖动后的位置
console.log('end', this.syllable);
},
move(evt, originalEvent) {
// console.log(evt , 'move')
// console.log(originalEvent) //鼠标位置
},
//拖拽完成事件
add1(e) {
console.log('add1',e, this.syllable);
},
sort(e) {
console.log('sort',e);
}
},
}
</script>
<style lang="scss" scoped>
.optionC {
width: 600px;
height: 300px;
position: absolute;
left: 50%;
top: 50%;
margin-top: -150px;
margin-left: -300px;
}
.syllable_ul {
height: 100%;
}
.syllable_li {
width: 80px;
height: 30px;
background:#e1e4e8;
margin-bottom: 10px;
float: left;
list-style:none; /* 将默认的列表符号去掉 */
padding:0; /* 将默认的内边距去掉 */
margin-right:10px; /* 将默认的外边距去掉 */
border-radius: 8px;
margin-top: 5px;
// text-align: center;
font-size: 12px;
white-space: nowrap;
display: flex;
justify-content: center;
align-items: center;
&:focus{
outline:none;
// border-bottom: 0.01rem solid #80af57;
}
position: relative;
}
.sortable-ghost {
opacity: 0.4;
background-color: #e1e4e8;
}
.i_content {
position: absolute;
color: white;
margin-left: 75px;
margin-bottom: 30px;
font-size: 16px;
border-radius: 10px;
background: antiquewhite;
cursor: pointer;
left: 0;
top: -7px;
}
// .input-style {
// width: 80px;
// height: 30px !important;
// margin-bottom: 0px !important;
// }
// .syllable_li:empty::before{
// content: attr(placeholder);
// font-size: 14px;
// color: #CCC;
// line-height: 21px;
// padding-top: 10px;
// }
// div[contenteditable="true"]:empty:before{
// content: attr(placeholder);
// color: #BAB3AF;
// /*padding: 10px 0;*/
// -webkit-tap-highlight-color:transparent;
// -webkit-user-modify:read-write;
// outline:none;
// border:none;
// }
</style>
(如果有样式问题,请自行修改)
这里有几个问题需要注意下:
1. draggable标签下嵌入 transition-group 标签时拖拽会有闪动,更多draggable官网有提供。
2. 在div设置contenteditable内容下的标签里面设置overflow的问题,我上面用的span(换成其他也行),这里我写了个样式overflow: hidden,这个会影响到div处于编辑下光标会出现问题。随后我设置了width: 80px;给了宽度消除bug。具体的可以看代码(以上仅供参考)