技术:
vue2.X
ant design vue 1.7.8UI框架
sortablejs
less
效果图:
动态效果图:
vue拖拽
1.安装插件
npm install [email protected]
//或通过yarn安装
yarn add [email protected]
2.代码
template代码块
<div class="page" onselectstart="return false">
<div class="addClass">
<div>编辑标签</div>
</div>
<div class="body">
<div class="bodyRight">
<div class="bodyRightTitle">
<div><span>标签池</span></div>
</div>
<div class="bodyRightLabel" ref="addEditTabsRef">
<a-spin :spinning="isLoading">
<div>
<a-tag v-for="tabItem in tagsList" :key="tabItem.id" @close="deleteTag(tabItem)"
style="margin-bottom: 5px" @dblclick="handleDblClick(tabItem)" class="tabs">
<span class="tagname" v-if="!tabItem.isEditName">
<div> {
{ tabItem.tagName }}</div>
<div title="删除"><a-icon type="close" @click.stop="removeLabel(tabItem)" /></div>
</span>
<a-input v-else class="noFilter" :ref="'input_' + tabItem.id" v-model="tabItem.tagName"
:maxLength="22" @click="inputFocus(tabItem.id)" @pressEnter="sumbitLabel(tabItem)"
placeholder="请输入标签名称" @blur="sumbitLabel(tabItem)"></a-input>
</a-tag>
<a-tag class="addTabs noFilter" @click="addTabs()" v-if="isAddShow && isEditDataId === null">
<a-icon type="plus" />
</a-tag>
</div>
</a-spin>
</div>
</div>
</div>
</div>
script代码块
import Sortable from 'sortablejs'
export default {
name: 'LabelManage',
data() {
return {
isAddShow: true,
tagsList: [],
isLoading: false,
isEditDataId: null,
}
},
created() {
//初始化调用查询接口
this._mallQueryTagByGroupCodeApi()
},
methods: {
//拖拽 此方法是重点
rowDrop() {
const tbody = this.$refs.addEditTabsRef.querySelectorAll('.ant-spin-nested-loading .ant-spin-container div') // 元素选择器名称根据实际内容替换
const _this = this
Sortable.create(tbody[0], {
sort: true,
handle: '.tabs.ant-tag', //指定哪个类名的可以拖动
animation: 300, // ms, number 单位:ms,定义排序动画的时间
filter: '.noFilter', // 过滤器,不需要进行拖动的元素
preventOnFilter: true, // 在触发过滤器`filter`的时候调用`event.preventDefault()`
chosenClass: 'dropTabs',
group: {
name: 'name', pull: false, put: false },
// 开始拖拽的时候
onMove: function (/**Event*/evt, originalEvent) {
//如果拖拽到新增后面,则禁止拖拽
if (evt.related.getAttribute('class') === 'addTabs ant-tag') {
return false
}
},
onEnd(evt) {
var navIndex = Object.assign(_this.tagsList)
navIndex.splice(evt.newIndex, 0, navIndex.splice(evt.oldIndex, 1)[0])
_this.tagsList = navIndex
for (const index in _this.tagsList) {
_this.tagsList[index].tagSort = parseInt(index) + 1
}
for (const index in _this.tagsList) {
_this.tagsList[index].tagSort = parseInt(index) + 1
}
_this.isLoading = true
const data = []
_this.tagsList.forEach((item) => {
data.push({
id: item.id,
tagSort: item.tagSort,
})
})
//此处调调换顺序的接口
_this.$forceUpdate()
_this.$message.success('更换顺序成功!')
_this.isLoading = false
},
})
},
//新增标签
addTabs() {
if (this.isEditDataId !== null) {
this.$message.warning('有标签未保存成功!')
this.inputFocus(this.isEditDataId)
return
}
const index = parseInt(this.tagsList.length + 1)
const value = 99999
const data = {
id: value,
tagName: '',
//是否编辑
isEditName: true,
//排序
tagSort: index,
}
this.tagsList.splice(index, 0, data)
this.isEditDataId = value
this.inputFocus(value)
},
//删除标签
removeLabel(e) {
const _that = this
this.$confirm({
title: '请确认是否删除“' + e.tagName + '”标签,该删除标签后,已关联该标签的模型失去该标签,请确认是否删除?”',
//自定义按钮内容
okText: '是',
centered: true,
cancelText: '否',
okType: 'danger',
onOk() {
_that.isLoading = true
//此处调删除接口
_that.$message.success('删除成功')
_that.isLoading = false
},
onCancel() {
},
})
},
//标签修改后提交
sumbitLabel(item) {
//如果是新增的没有值则删除该新增行
if (item.id === 99999 && (item.tagName ?? '') === '') {
this.tagsList = this.tagsList.filter((e) => e.id !== 99999)
this.isEditDataId = null
return
}
if ((item.tagName ?? '') === '') {
this.$message.warning('请输入标签名称!')
this.inputFocus(item.id)
return
}
this.isLoading = true
if (item.id === 99999) {
//此处调新增接口
this.isEditDataId = null
item.isEditName = false
this.$message.success('新增标签成功!')
this.isLoading = false
} else {
//此处调修改接口
item.isEditName = false
this.isEditDataId = null
this.$message.success('标签名称修改成功!')
this.isLoading = false
}
},
//触发双击事件
handleDblClick(item) {
if (this.isEditDataId !== null) {
this.inputFocus(this.isEditDataId)
return
}
item.isEditName = true
this.$set(this.tagsList, this.tagsList.indexOf(item), item)
this.isEditDataId = item.id
this.inputFocus(item.id)
},
//焦点
inputFocus(id) {
this.$nextTick(() => {
this.$refs['input_' + id][0].focus()
})
},
//删除标签
deleteTag(item) {
console.log(item)
},
//查询标签
_mallQueryTagByGroupCodeApi() {
//拖拽 此处是重点
this.$nextTick(() => {
this.rowDrop()
})
for (let index = 0; index < 50; index++) {
this.tagsList.push({
"id": index,
"tagName": parseInt(index) + 1 + "级钢铁",
"tagSort": parseInt(index) + 1
})
}
},
},
}
style代码块
<style lang="less" scoped>
.page {
display: flex;
flex-direction: column;
.addClass {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0px 20px 10px 20px;
div {
font-weight: bold;
}
border-bottom: 1px solid #d9d9d9;
}
span {
font-size: 14px;
}
.body {
height: calc(100vh - 210px);
width: 100%;
padding: 34px 24px;
display: flex;
justify-content: space-around;
.bodyRight {
width: 80%;
height: 100%;
.bodyRightTitle {
height: 40px;
background: #f7f8fa;
font-size: 14px;
padding-left: 14px;
border-radius: 4px 4px 0 0;
display: flex;
align-items: center;
border-top: 1px solid #d9d9d9;
border-left: 1px solid #d9d9d9;
border-right: 1px solid #d9d9d9;
}
.bodyRightLabel {
width: 100%;
height: 90%;
overflow-y: auto;
border-bottom: 1px solid #d9d9d9;
border-left: 1px solid #d9d9d9;
border-right: 1px solid #d9d9d9;
border-radius: 0 0 4px 4px;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
align-content: flex-start;
/deep/ .ant-spin-container {
div:nth-child(1) {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
display: flex;
flex-direction: row;
}
/deep/ .ant-tag {
background: none;
}
.addTabs {
margin: 10px 16px 0 10px;
width: 30px;
height: 28px;
border: 1px solid #1d953f;
border-radius: 34px;
font-size: 18px;
align-items: center;
display: flex;
justify-content: center;
line-height: 22px;
cursor: pointer;
color: #1d953f;
}
.dropTabs {
border: 1px solid #1890ff !important;
color: #1890ff;
}
.tabs {
margin: 10px 16px 0 10px;
width: 100px;
height: 28px;
border: 1px solid #e5e6eb;
border-radius: 34px;
font-size: 14px;
align-items: center;
display: flex;
line-height: 22px;
cursor: move;
.tagname {
width: 100%;
display: flex;
justify-content: space-between;
}
i {
display: none;
}
&:hover {
i {
font-size: 16px;
color: red;
display: inline-block;
}
}
}
.chosen {
.tabs {
border: 1px solid #1890ff;
background-color: #fff !important;
color: #1890ff;
cursor: move;
}
}
}
}
}
}
</style>
3.总结:
1.sortablejs中文官网:http://www.sortablejs.com/options.html
2.需要在$nextTick里调用拖拽的方法