1、安装vuedraggable依赖
npm i vuedraggable
2、在页面中引入
import Draggable from 'vuedraggable'
components: {Draggable
},
下面为实图使用,代码直接参考下面的,拷贝即可使用
//主页面
<template><div class="ctnr"><div class="station-area"><div class="text-area"><el-button v-if="disabledDrag && section.length < 4" type="text" @click="changeAdd">新建</el-button><div class="edit-area"><el-button v-if="disabledDrag" type="text" @click="changeDrag">编辑</el-button><div v-else><el-button type="text" @click="changeDrag('updateList')">完成</el-button><el-button type="text" @click="changeDrag('clearList')">取消</el-button></div></div></div></div><draggable :disabled="disabledDrag" class="box" v-model="section" @start="onStartBlock" @end="onEndBlock"animation="300"><transition-group><div class="section" v-for="(item,index) in section" :key="index"><div class="section-card"><h1 class="card-title"><el-input class="title-inp" v-if="!disabledDrag" v-model="item.name"></el-input><span v-else>{{item.name}}</span><i v-if="!disabledDrag" class="el-icon-circle-plus i-plus"@click="bindAddSuit(item.id,item.suites.length)"></i></h1><draggable :disabled="disabledDrag" class="card-box" v-model="item.suites" @start="onStartSuite(item.id)"@end="onEndSuite(item.id)" animation="300"><transition-group><div class="item" v-for="(item2,index2) in item.suites" :key="index2"><div class="item-card" :style="'background:'+item2.color" :class="{'href-type':disabledDrag}"@click="goPreview(item2)"><div class="op-box" v-if="!disabledDrag"><i class="el-icon-edit" @click="bindEditSuit(item2,item.id)"></i><i class="el-icon-delete" @click="bindRemoveSuit(item2.id)"></i></div><span class="card-name">{{item2.name}}</span></div></div></transition-group></draggable></div></div></transition-group></draggable><suit-box ref="SuitBox" @refreshList="refreshList"></suit-box></div>
</template><script>import Draggable from 'vuedraggable'import SuitBox from '@/components/CommonForm/SuitBox'export default {data() {return {disabledDrag: true,oSection: [],section: [],isUpdateBlock: false,isUpdateSuit: [],};},components: {Draggable,SuitBox},watch: {section: {handler(newVal, oldVal) {oldVal.length > 0 && (this.isUpdateBlock = true)},deep: true}},computed: {},mounted() {this.getBlockList()},created() {},methods: {getBlockList() {var data = [{"create_time": "2022-04-01 15:12:33","id": 2,"modify_time": "2022-06-29 19:44:23","name": "测试区域2133213122355","order_num": 1,"suites": [{"color": "#A47373","create_time": "2022-04-01 17:48:32","fillable": false,"id": 4,"modify_time": "2022-07-29 17:55:52","name": "日常签到","order_num": 1,"template_id": 423},{"color": "#9013FE","create_time": "2022-10-09 11:01:02","fillable": true,"id": 22,"modify_time": "2022-10-09 11:01:02","name": "接口","order_num": 2,"template_id": 369}]},{"create_time": "2022-04-01 15:12:41","id": 3,"modify_time": "2022-07-29 17:38:01","name": "防冻液防冻液111","order_num": 2,"suites": []},{"create_time": "2022-06-28 15:16:56","id": 41,"modify_time": "2022-07-27 14:06:52","name": "额3v","order_num": 3,"suites": [{"color": "#F9F115","create_time": "2022-07-27 14:07:02","fillable": false,"id": 18,"modify_time": "2022-07-27 14:07:02","name": "43关闭","order_num": 1,"template_id": 407}]},{"create_time": "2022-06-28 15:16:52","id": 36,"modify_time": "2022-09-05 11:31:14","name": "供电局","order_num": 4,"suites": [{"color": "#BD10E0","create_time": "2022-07-27 14:08:20","fillable": true,"id": 19,"modify_time": "2022-07-27 14:08:20","name": "看给客户","order_num": 1,"template_id": 369}]},{"create_time": "2022-06-28 15:16:56","id": 42,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 5,"suites": []},{"create_time": "2022-06-28 15:16:52","id": 39,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 6,"suites": []},{"create_time": "2022-06-28 15:16:56","id": 40,"modify_time": "2022-08-25 17:05:46","name": "saca","order_num": 7,"suites": [{"color": "#CB2929","create_time": "2022-08-25 17:05:52","fillable": false,"id": 21,"modify_time": "2022-08-25 17:05:52","name": "sacas","order_num": 1,"template_id": 423}]},{"create_time": "2022-06-28 15:16:52","id": 37,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 8,"suites": []},{"create_time": "2022-04-01 15:12:25","id": 1,"modify_time": "2022-06-29 19:44:23","name": "时分为非就不能用鼠标全选","order_num": 9,"suites": [{"color": "#F8E71C","create_time": "2022-04-01 16:35:25","fillable": false,"id": 2,"modify_time": "2022-04-01 17:59:12","name": "套件2","order_num": 2,"template_id": 363},{"color": "#B33434","create_time": "2022-04-01 18:42:04","fillable": true,"id": 6,"modify_time": "2022-04-01 18:42:04","name": "卡片222","order_num": 2,"template_id": 298},{"color": "#DC17DC","create_time": "2022-04-22 14:21:48","fillable": false,"id": 10,"modify_time": "2022-04-24 18:27:38","name": "vr","order_num": 3,"template_id": 372}]},{"create_time": "2022-06-28 15:16:56","id": 43,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 10,"suites": []},{"create_time": "2022-06-28 15:16:52","id": 38,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 11,"suites": []},{"create_time": "2022-04-01 15:12:48","id": 4,"modify_time": "2022-06-29 19:44:23","name": "测试区域4","order_num": 12,"suites": [{"color": "#994545","create_time": "2022-04-02 10:19:00","fillable": true,"id": 8,"modify_time": "2022-04-02 10:19:00","name": "车控室","order_num": 1,"template_id": 350}]}
]this.section = datathis.oSection = JSON.parse(JSON.stringify(data))},refreshList() {this.getBlockList()},onStartBlock() {},onEndBlock(e) {console.log(e)this.isUpdateBlock = true},onStartSuite() {},onEndSuite(blockId) {// 对那个块中的卡片进行了挪动!this.isUpdateSuit.includes(blockId) && this.isUpdateSuit.push(blockId)},changeAdd() {for (var i = 0; i < 4; i++) {let obj = {name: '',order_num: i + 1,station_id: this.station_id}blockAdd(obj).then(res => {})}this.getBlockList();},changeDrag(type) {this.disabledDrag = !this.disabledDragswitch (type) {case 'clearList':this.section = JSON.parse(JSON.stringify(this.oSection))break;case 'updateList':if (this.isUpdateBlock) {this.updateBlock()}if (this.isUpdateSuit.length > 0) {this.updateSuit()}break;}},bindAddSuit(id, orderNum) {if (this.isUpdateBlock || this.isUpdateSuit.length > 0) {this.$confirm("区域或卡片有更新是否保存后再新建卡片?", "提示", {confirmButtonText: "确认",cancelButtonText: "取消",type: "warning"}).then(() => {this.isUpdateBlock && this.updateBlock()this.isUpdateSuit.length > 0 && this.updateSuit()this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})}).catch(() => {this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})})} else {this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})}},bindEditSuit(info, id) {this.$refs.SuitBox.showModal(Object.assign({block_id: id}, info), 'edit')},updateBlock() {let data = []for (const [index, item] of this.section.entries()) {const obj = {id: item.id,name: item.name,order_num: index + 1}data.push(obj)}blockModify(data).then(res => {this.$message.success('区域更新成功')this.isUpdateBlock = false})},updateSuit() {const data = []for (const item of this.isUpdateSuit) {const nowBlock = this.section.filter(res => res.id === item)[0]for (const [index2, item2] of nowBlock.suites.entries()) {const obj = Object.assign(item2, {block_id: item,order_num: index2 + 1})delete obj.create_timedelete obj.modify_timedata.push(obj)}}suiteUpdate(data).then(res => {this.$message.success('卡片更新成功')this.isUpdateSuit = []})console.log(data)},bindRemoveSuit(id) {this.$confirm("是否删除此卡片?", "提示", {confirmButtonText: "确认",cancelButtonText: "取消",type: "warning"}).then(() => {suiteDelete({id}).then(res => {this.$message.success('卡片删除成功')this.getBlockList()})}).catch(() => {})},async goPreview(item) {if (!this.disabledDrag) returnconst detail = await templateDetail({id: item.template_id}).then(res => {return res.data})localStorage.previewForm = JSON.stringify(detail.content)let url = this.$router.resolve({path: `/preview?type=${item.fillable?'fill':''}&template_id=${item.template_id}`});window.open(url.href, '_blank');}}}</script>
<style lang='scss' scoped>.ctnr {height: calc(100vh - 50px);background: #f6f7f9;margin: -40px -50px;padding: 20px 0 20px 20px;overflow-y: scroll;}.station-area {padding: 0 20px;display: flex;align-items: center;.text-area {display: flex;align-items: center;margin-left: auto;.edit-area {margin-left: 15px;}}}/deep/ .el-form-item--small.el-form-item {margin-bottom: 0 !important;}.box {width: 100%;height: 100%;}.section {width: 50%;height: 50%;display: inline-block;padding: 20px 20px 0 0;&-card {background: #fff;border-radius: 14px;height: 100%;padding: 20px;display: flex;flex-wrap: wrap;// padding: ;.card-title {font-size: 16px;margin: 0;position: relative;width: 100%;color: #606266;.title-inp {width: 50%;}.i-plus {color: #448ef7;position: absolute;right: 0;cursor: pointer;font-size: 25px;}}.card-box {width: 100%;height: calc(100% - 30px);}}.item {width: 33.3%;padding: 10px 10px 0 0;height: 33.3%;display: inline-block;&-card {width: 100%;height: 100%;background: #eee;border-radius: 11px;position: relative;display: flex;align-items: center;justify-content: center;user-select: none;&.href-type {cursor: pointer;}.op-box {position: absolute;top: 4px;right: 5px;color: #fff}.card-name {color: #fff;font-size: 16px;width: 80%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;text-align: center;}}}}</style>
//组件
<template><div><el-dialog @click.native="bindClosePicker" :title="type==='edit'?'编辑卡片':'添加卡片'" :visible.sync="dialogFormVisible":before-close="bindClose"><el-form :model="form" ref="ruleForm" size="small" :rules="rules" class="form-style"><el-form-item label="卡片名称" :label-width="formLabelWidth" prop="name"><el-input v-model="form.name" autocomplete="off"></el-input></el-form-item><el-form-item label="选择模板" :label-width="formLabelWidth" prop="template_id"><el-select v-model="form.template_id" placeholder="请选择"><el-option v-for="item in templateData" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="选择背景色" :label-width="formLabelWidth" prop="color"><div class="picker-input" style="position:relative"><div @click.stop="bindShowPicker" class="color-tip" :style="'background:'+form.color"></div><el-input v-model="form.color" autocomplete="off"></el-input></div><div class="picker-box" v-if="showPicker" tabindex="0" @click.stop=""><sketch-picker v-model="pickerColor" @input="updatePickerColor"></sketch-picker></div></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="bindClose(()=>dialogFormVisible = false)" size="small">取 消</el-button><el-button :loading="postLoading" type="primary" @click="bindPost" size="small">确 定</el-button></div></el-dialog><!-- :disableAlpha="true" --></div>
</template><script>import {Sketch} from 'vue-color'import {suiteUpdate} from '@/api/commonForm'import {listTemplate} from '@/api/form/form'import {number} from '@/utils/validate'export default {data() {const validateNumber = (rule, value, callback) => {console.log(rule, value, callback)if (value && !number(value)) {callback(new Error('排序值输入错误'));} else {callback()}};const validateColor = (rule, value, callback) => {console.log(rule, value, callback)const reg = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/if (!value) {callback(new Error('请选择色值'));} else if (value && !reg.test(value)) {callback(new Error('色值输入错误'));} else {callback()}};return {pickerColor: '',dialogFormVisible: false,formLabelWidth: '100px',form: {},type: 'add',postLoading: false,templateData: [],showPicker: false,rules: {name: [{required: true,message: '请输入卡片名称',trigger: 'blur'}],template_id: [{required: true,message: '请选择模板',trigger: 'change'}],color: [{required: true,validator: validateColor,trigger: 'blur'}],order_num: [{validator: validateNumber,trigger: 'blur'}],}};},components: {'sketch-picker': Sketch,},computed: {},watch: {},mounted() {this.getListTemplate()},methods: {async showModal(info, type) {this.type = type || 'add'if (info) {const {name,id,template_id,order_num,block_id,color} = infothis.form = {name,template_id,id,order_num,block_id,color: !color ? '' : color}} else {this.form = {}}this.dialogFormVisible = true},getListTemplate() {listTemplate({page: 1,page_size: 100000}).then(res => {this.templateData = res.data})},bindPost() {console.log(this.form)this.postLoading = truethis.$refs.ruleForm.validate(async (valid) => {if (valid) {this.form.order_num && (this.form.order_num = parseInt(this.form.order_num))await suiteUpdate([this.form]).then(res => {})this.postLoading = falsethis.$message.success(this.type === 'edit' ? '编辑成功' : '添加成功')this.bindClose(() => this.dialogFormVisible = false)this.$emit('refreshList')} else {this.postLoading = falsereturn false}});},bindClose(done) {this.pickerColor = ''this.$refs.ruleForm.resetFields()done()},bindShowPicker() {this.showPicker = true},bindClosePicker() {this.showPicker = false},updatePickerColor(e) {console.log(e)this.$set(this.form, 'color', e.hex)}}}</script>
<style lang='scss' scoped>/deep/.dialog-footer {text-align: center;.el-button {width: 120px;}.el-button+.el-button {margin-left: 70px;}}.form-style {width: 70%;margin: 0 auto;}/deep/ .el-select {width: 100%;}.permission-ctnr {height: 200px;overflow-y: scroll;border: 1px solid #DCDFE6;border-radius: 4px;}.picker-box {position: absolute;top: 40px;left: -25%;}/deep/.picker-input {.color-tip {width: 15px;height: 15px;position: absolute;z-index: 1;top: 9px;left: 10px;border: 1px solid #eeeff2;border-radius: 3px;}.el-input__inner {padding-left: 30px;}}</style>