vue 后管项目实现跨目录拖拽功能
我使用的是vuedraggable插件。参考文档:https://www.itxst.com/vue-draggable/fueijmfy.html
安装:npm i -S vuedraggable
引入:import draggable from 'vuedraggable'
<template> <div class="classification_configuration"> <a-card> <div class="content" v-for="(items, indexs) in classificationList" :key="indexs"> <div class="content_title"> <h4 v-show="items.moduleTypeShow">{{items.moduleType}}</h4> <div v-show="!items.moduleTypeShow" class="content_edit"> <a-input v-model="dataTitle" placeholder="请输入内容" /> </div> <img v-show="items.moduleTypeShow" @click="clickEdit(items, indexs)" class="image_edit" src="../../assets/img/edit.png" /> <img v-show="items.moduleTypeShow && items.menuSubList.length === 0 " @click="clickDelete(indexs)" src="../../assets/img/delete.png" /> <span v-show="!items.moduleTypeShow" @click="clickSave(indexs)" style="margin-left: 16px; margin-right: 8px;">保存</span> <span v-show="!items.moduleTypeShow" @click="clickSaveCancel(indexs)">取消</span> </div> <draggable v-model="items.menuSubList" group="site" animation="300" dragClass="dragClass" ghostClass="ghostClass" chosenClass="chosenClass" @start="onStart" @end="onEnd"> <transition-group class="content_con"> <div class="content_card" v-for="item in items.menuSubList" :key="item.id"><img class="content_image" :src="item.imgUrl" :alt="item.appName">{{item.appName}}</div> </transition-group> </draggable> <!-- <div class="content_title"> <h4 v-show="items.moduleTypeShow">{{items.moduleType}}</h4> <div v-show="!items.moduleTypeShow" class="content_edit"><a-input :value="dataTitle" placeholder="请输入内容" /></div> <img v-show="items.moduleTypeShow" @click="clickEdit(items, indexs)" class="image_edit" src="../../assets/img/edit.png" /> <img v-show="items.moduleTypeShow && items.menuSubList.length === 0 " @click="clickDelete(indexs)" src="../../assets/img/delete.png" /> <span v-show="!items.moduleTypeShow" @click="clickSave(indexs)" style="margin-left: 16px; margin-right: 8px;">保存</span> <span v-show="!items.moduleTypeShow" @click="clickSaveCancel(indexs)">取消</span> </div> <div class="content_con"> <div class="content_card" v-for="(item, index) in items.menuSubList" :key="index" :draggable="true" @dragstart="dragStart(items, item)" @dragenter="dragEnter(items, item,$event)" @dragend="dragEnd(items, item, $event)" @dragover="dragOver($event)"> <img class="content_image" :src="item.imgUrl" :alt="item.appName"> {{item.appName}} </div> </div> --> </div> <div class="add_list" @click="clickAddList()"><img src="../../assets/img/add.png" />新增应用分类</div> </a-card> <div class="content_bottom"> <div></div> <div> <a-button @click="clickCancel()">取消</a-button> <a-button @click="clickPreview()" class="button_class">预览</a-button> <a-button @click="clickRelease()" class="button_class" type="primary">发布</a-button> </div> </div> <div class="messge" v-show="showPreview"> <div class="messge_con test"> <div class="card"> <div v-for="(elements, indexs) in classificationList" :key="indexs"> <h4>{{elements.moduleType}}<span class="card_shadow"></span></h4> <div class="content"> <div class="content_card" v-for="(element, index) in elements.menuSubList" :key="index"> <img :src="element.imgUrl" /> <div>{{element.appName}}</div> </div> </div> </div> </div> </div> <div class="messge_but" @click="showPreview = false"><img class="que" src="../../assets/img/close.png" /></div> </div> </div> </template> <script> import { mapGetters } from 'vuex'; import draggable from 'vuedraggable' import { buttonPermission } from '../../util/util'; export default { components: { draggable, }, // computed: { // ...mapGetters(['classificationList']), // }, data() { return { drag: false, classificationList: [], moduleTypeShow: true, dataTitle: '', // oldData: null, // newData: null, oldData: { module: null, menuSubList: [], }, newData: { module: null, menuSubList: [], }, dataList: [ { id:1,appName:'测试一号' }, { id:2,appName:'测试二号' }, { id:3,appName:'测试三号' }, { id:4,appName:'测试四号' }, ], showPreview: false, }; }, created() { this.$store.commit('SET_MENU', '/classification'); this.init(); }, methods: { init() { this.$store.dispatch('getClassificationListAction').then(res => { this.classificationList = res.data.menuList.map(({ module, moduleType, menuSubList, }) => { return { module, moduleType, menuSubList, moduleTypeShow: true, } }); }).catch(err => { console.error(err); }); }, //开始拖拽事件 onStart() { this.drag = true; }, //拖拽结束事件 onEnd() { this.drag = false; }, clickEdit(items, indexs) { this.dataTitle = items.moduleType; this.classificationList[indexs].moduleTypeShow = false; }, clickDelete(indexs) { this.classificationList.splice(indexs, 1); }, clickSave(indexs) { this.classificationList[indexs].moduleType = this.dataTitle; this.classificationList[indexs].moduleTypeShow = true; }, clickSaveCancel(indexs) { if (this.dataTitle === '') { this.classificationList.splice(indexs, 1); } else { this.classificationList[indexs].moduleTypeShow = true; this.dataTitle = ''; } }, dragStart(items, value) { // this.oldData = value; this.oldData.module = items.module; this.oldData.menuSubList[0] = value; }, dragEnter(items, value, e) { // this.newData = value; this.newData.module = items.module; this.newData.menuSubList[0] = value; e.preventDefault(); }, dragEnd(items, item, e) { if (this.oldData !== this.newData) { // let oldIndex = this.dataList.indexOf(this.oldData); // let newIndex = this.dataList.indexOf(this.newData); // let newItems = [...this.dataList]; // // 删除老的节点 // newItems.splice(oldIndex, 1); // // 在列表中目标位置增加新的节点 // newItems.splice(newIndex, 0, this.oldData); // this.dataList = [...newItems]; this.classificationList = [...newItems]; } }, dragOver(e) { e.preventDefault(); }, clickAddList() { this.dataTitle = ''; this.classificationList.push({ moduleType: '', menuSubList: [], moduleTypeShow: false, }); }, clickCancel() { const that = this; this.$confirm({ title: '确认取消吗?', okText: '确认', cancelText: '取消', onOk() { that.init(); }, }); }, clickPreview() { this.showPreview = true; }, clickRelease() { let dataMessage = ''; this.classificationList.forEach(item => { if (!item.moduleTypeShow) { dataMessage = '您还未保存更改名称!' } }) if (dataMessage === '') { this.$store.dispatch('giveReleaseAction', { menuList: this.classificationList }).then(res => { if (res.resultCode === '1') { this.$message.success('发布成功!'); } else { this.$message.error(res.message); } }).catch(err => { console.error(err); }) } else { debugger this.$message.error(dataMessage); } }, }, }; </script> <style lang="less" scoped> .classification_configuration { font-size: 14px; font-family: PingFangSC-Regular, PingFang SC; font-weight: 400; color: rgba(0, 0, 0, 0.85); line-height: 14px; .content { .content_title{ font-size: 16px; font-family: PingFangSC-Medium, PingFang SC; font-weight: 500; line-height: 16px; display: flex; align-items: center; .content_edit{ width: 120px; display: inline-block; } img{ width: 18px; height: 18px; cursor: pointer; } .image_edit{ margin-left: 15px; margin-right: 12px; } span{ cursor: pointer; font-size: 14px; font-family: PingFangSC-Regular, PingFang SC; font-weight: 400; color: #1890FF; line-height: 24px; } } .content_con{ margin: 8px 0 28px 0; display: flex; flex-wrap: wrap; .content_card{ cursor: pointer; width: 23.45%; //254px height: 50px; background: #F8F8F8; border-radius: 2px; line-height: 50px; padding-left: 8px; display: flex; align-items: center; margin-right: 24px; margin-top: 12px; .content_image{ width: 24px; height: 23px; margin-right: 9px; } } } } .add_list{ cursor: pointer; height: 32px; // line-height: 2; border: 1px dashed #e8e8e8; // text-align: center; display: flex; justify-content: center; align-items: center; img{ width: 12px; margin-right: 7px; } } .content_bottom { height: 56px; width: calc(~"100% - 250px"); width: -moz-calc(~"100% - 250px"); width: -webkit-calc(~"100% - 250px"); background: #ffffff; position: fixed; bottom: 0; box-shadow: 0px -1px 2px 0px rgba(0, 0, 0, 0.03); filter: blur(0px); display: flex; align-items: center; justify-content: space-between; padding: 0 24px 0 48px; .ant-btn-link { color: rgba(0, 0, 0, 0.45); } .button_class { margin-left: 8px; } } .messge{ position: fixed; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); width: -webkit-fill-available; height: -webkit-fill-available; -webkit-backface-visibility: hidden; backface-visibility: hidden; -webkit-transition: .2s; transition: .2s; background-color: rgba(36, 36, 36, 0.5); z-index: 999; color: #2B333F; line-height: 16px; .messge_con{ position: fixed; top: 50%; left: 50%; transform: translate3d(-50%, -50%, 0); width: 87%; background-color: #FFFFFF; border-radius: 4px; // width: 30%; // height: 90%; width: 391px; height: 667px; overflow: auto; .card{ padding: 16px 15px 26px 18px; h4{ font-size: 16px; font-family: PingFangSC-Medium, PingFang SC; font-weight: bold; color: #333333; line-height: 22px; } .card_shadow{ display: inline-block; width: 36px; height: 16px; background: linear-gradient(180deg, #FECF00 0%, #F9BD0A 100%); box-shadow: 0px 4px 8px 0px rgba(253, 202, 4, 0.45); border-radius: 8px; opacity: 0.25; position: relative; top: 10px; left: -20px; } .content{ margin-left: 7px; display: flex; flex-wrap: wrap; margin-top: 16px; margin-bottom: 24px; .content_card{ width: 22.1%; margin-right: 10px; margin-bottom: 8px; text-align: center; } img{ width: 36px; height: 36px; } } } } .test::-webkit-scrollbar {/*滚动条整体样式*/ width: 6px; height: 50px; background: #595959; // border-radius: 3px; // opacity: 0.64; } .test::-webkit-scrollbar-thumb {/*滚动条整体样式*/ // width: 6px; // height: 10px; background: #595959; border-radius: 3px; } .test::-webkit-scrollbar-track {/*滚动条里面轨道*/ background: #FFFFFF; border-radius: 4px; } .messge_but{ cursor: pointer; position: fixed; top: 88%; left: 50%; .que{ width: 36px; } } } } </style>
效果: