一、vue 项目使用
文档地址: https://www.itxst.com/sortablejs/neuinffi.html
1、安装依赖
npm i -S vuedraggable
2、.vue 文件引入组件
import draggable from "vuedraggable";
components: { draggable },
3、.使用
查看文档中的示例即可:https://debug.itxst.com/js/ivv3eivm
我们使用的 npm 安装,不需要其他东西,只需要下方标注的主要代码部分,其中的css为演示展示用,无实际用处
二、进阶案例演示代码(UMD版)
1、采用技术
- vue2
- element-ui2
- avue : 基于vue2 + element-ui2 二次封装组件库
- Sortable : 拖拽
- vuedraggable : vue二次封装的拖拽,基于Sortable
2、展示图
3、源码
原为npm 版, 抽取成 UMD 版便于大家学习参考
1、本地新建 .html文件
2、复制下方代码到 .html
3、打开htm 即得到上方 展示效果中 相同效果
<!DOCTYPE html>
<html>
<head><meta charset="utf-8"><title></title>
</head>
<!-- 引入样式文件 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@smallwei/avue/lib/index.css"/>
<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css"/>
<!-- 引入相关JS 文件 -->
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script src="https://unpkg.com/element-ui/lib/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@smallwei/avue/lib/avue.min.js"></script><!-- 图片拖拽排序 -->
<script src="https://cdn.staticfile.org/Sortable/1.10.0-rc2/Sortable.min.js"></script>
<!-- 已经加载过了 -->
<!--<script src="https://www.itxst.com/package/vue/vue.min.js"></script>-->
<!--<script src="https://www.itxst.com/package/sortable/Sortable.min.js"></script>-->
<script src="https://www.itxst.com/package/vuedraggable/vuedraggable.umd.min.js"></script><div id="app">{{ message }}<div class="bt-article-all"><el-row><el-col :span="24"><el-card class="box-card"><div slot="header" class="clearfix"><span>文章信息区</span><el-button style="float: right; padding: 3px 0" type="text">保存</el-button></div><avue-form ref="form" v-model="obj" :option="option"@reset-change="emptytChange"@submit="submit"><template slot-scope="{row}" slot="content"><TinymceEditor v-if="initSuccess" :content.sync="obj.content"/></template></avue-form></el-card></el-col></el-row><el-row><!-- 左侧区 --><el-col :span="12"><el-card class="box-card"><div slot="header" class="clearfix"><span>内容编辑区</span><!-- <el-button style="float: right; padding: 3px 0" type="text">保存</el-button>--></div><div><!-- group="itxst" --><draggable v-model="contentItems" chosen-class="chosen" force-fallback="true" group="itxst" :disabled="disabledDrag" animation="1000" @start="onStart" @end="onEnd"><transition-group><div id="contentBox" class="bt-card-box" v-for="(item,index) in contentItems" :key="index" style="padding-top: 2%"><el-card class="box-card bt-card-box"><div slot="header" class="clearfix singlePerson"><span>{{index+1}} : </span><span>{{item.lableName}} </span><el-button style="float: right; padding: 3px 0" type="text" @click="delItemRow(item)">删除</el-button></div><div v-if="item.lable == 'H1' || item.lable == 'H2' || item.lable == 'H3'"><el-input type="input" placeholder="请输入内容" v-model="item.value"></el-input></div><div v-if="item.lable == 'P' ">富文本组件<!-- 富文本组件 当前单页无法加载 --><!-- <TinymceEditor v-if="drag==false" :content.sync="item.value"/>--></div><div v-if="item.lable == 'IMAGE'"><el-image style="width: 80px; height: 80px" :src="item.value" fit="cover"></el-image></div><div v-if="item.lable == 'VIDEO'"><el-input type="input" placeholder="请输入内容" v-model="item.value"></el-input><!-- <el-image style="width: 80px; height: 80px" :src="item.value" fit="cover"></el-image>--></div><div v-if="item.lable == 'ARRAY'"><!-- <avue-form :option="{column: [{label:'数组框',prop:'array', type:'array', value:[0,1]}]}"></avue-form>--><avue-array v-model="item.value" :option="{dataType:'string'}" placeholder="请输入内容"></avue-array></div></el-card></div></transition-group></draggable></div></el-card></el-col><!-- 右侧区 --><el-col :span="12"><!-- 媒体区 --><div class="bt-card-box"><el-card class="box-card"><div slot="header" class="clearfix"><span>媒体资源区</span></div><div><el-row><draggable v-model="imageItems" chosen-class="chosen" force-fallback="true" :options="{group:{name: 'itxst',pull:'clone'}, sort: true}" animation="1000" @start="onStartImages" @end="onEndImages"><transition-group><el-col :span="4" v-for="(item,index) in imageItems" :key="index"><div style="padding: 5%"><el-card class="box-card"><el-imagestyle="width: 80px; height: 80px":src="item.value"fit="cover"></el-image><span style="text-align: center;display:block;">{{ item.alt }}</span></el-card></div></el-col></transition-group></draggable></el-row></div></el-card></div><!-- 内容预览区 --><div class="bt-card-box" style="padding-top: 2%"><el-card class="box-card"><div slot="header" class="clearfix"><span>内容预览区</span></div><div v-for="(item,index) in contentItems" :key="index"><h1 v-if="item.lable == 'H1'">{{item.value}}</h1><h2 v-if="item.lable == 'H2'">{{item.value}}</h2><h3 v-if="item.lable == 'H3'">{{item.value}}</h3><span v-if="item.lable == 'P'" v-html="item.value"></span><span v-if="item.lable == 'IMAGE'"><img style="width: 50%" :src="item.value" alt="item.alt"></span><div v-if="item.lable == 'VIDEO'"><video width="50%" controls :autoplay="false"><source :src="item.value" type="video/mp4"></video></div><div v-if="item.lable == 'ARRAY'"><li v-for="(item,index) in item.value">{{index+1}}、{{item}}</li></div></div></el-card></div></el-col></el-row>{{contentItems}}<div>{{drag?'拖拽中':'拖拽停止'}}</div></div>
</div><body>
<script>//import draggable from "vuedraggable";var vm = new Vue({// 绑定 id="app" 的元素el: "#app",// components: {// draggable// },// 定义数据data: {message: "这是一个拖拽示例demo",obj: {},initSuccess: false,defaultData: {name: null,alias: null,author: "测试",categoryIds: null,coverUrl: "http://xijia-sz.oss-cn-shenzhen.aliyuncs.com/oss/file/file/gc/08769453-1(6).jpeg",lables: null,content: null,state: 1,describe: "-",auth: 1,sort: 0,seoTitle: null,seoKeyword: null,seoDescription: null,},categoryTree: [],disabledDrag: false, //默认开启拖拽drag: false,dragImages: false,contentItems: [{lable: 'H1', lableName: "一级标题", value: ''},{lable: 'H2', lableName: "二级标题", value: ''},{lable: 'H3', lableName: "三级标题", value: ''},{lable: 'P', lableName: "段落", value: ''},{lable: 'IMAGE', lableName: "图片", value: 'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'},{lable: 'VIDEO', lableName: "视频", value: 'http://127.0.0.1:10006/upload/video/swagger-ui.html/20221109-0315-11、恭喜你发现宝藏!!!.mp4'},{lable: 'ARRAY', lableName: "有序列表", value: [0, 1]},],// "lable": "H1", "name": "一级标题", "sort": 1, "value": "-"imageItems: [//fits: ['fill', 'contain', 'cover', 'none', 'fill'],//fits: ['fill', 'contain', 'cover', 'none', 'scale-down'],{lable: "IMAGE", lableName: "图片", alt: "a1", value: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"},{lable: "IMAGE", lableName: "图片", alt: "a2", value: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"},{lable: "IMAGE", lableName: "图片", alt: "a3", value: "https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"},{lable: "IMAGE", lableName: "图片", alt: "a4", value: "http://xijia-sz.oss-cn-shenzhen.aliyuncs.com/oss/file/file/gc/08769453-1(6).jpeg"},{lable: "IMAGE", lableName: "图片", alt: "a5", value: "http://xijia-sz.oss-cn-shenzhen.aliyuncs.com/oss/file/file/gc/08769453-1(6).jpeg"},{lable: "IMAGE", lableName: "图片", alt: "a6", value: "http://xijia-sz.oss-cn-shenzhen.aliyuncs.com/oss/file/file/gc/08769453-1(6).jpeg"},],},props: {closeDialog: [],uri: {},},computed: {option() {return {submitBtn: false,emptyBtn: false,submitText: '提交',emptyText: "关闭",group: [{// icon: 'el-icon-info',label: '展开/收缩文章信息',collapse: false,prop: 'group1',column: [{label: '文章名',prop: 'name',maxlength: 64,showWordLimit: true,span: 10,rules: [{required: true,message: "请输入 文章名",trigger: "blur"}]},{label: '别名',prop: 'alias',maxlength: 64,showWordLimit: true,span: 10,rules: [{required: true,message: "请输入 别名",trigger: "blur"}]},{label: '作者',prop: 'author',maxlength: 32,showWordLimit: true,span: 10,rules: [{required: true,message: "请输入 作者",trigger: "blur"}]},{label: '分类',prop: 'categoryIds',span: 10,type: "cascader",dataType: 'string',filterable: true,dicData: this.categoryTree, // 自行替换字典数据props: {value: "id",label: "name",children: "categorys"},rules: [{required: true,message: "请选择 分类ids ",trigger: "blur"}]},{label: '封面图',prop: 'coverUrl',span: 10,rules: [{required: true,message: "请上传 文章封面图url ",trigger: "blur"}],dataType: 'string',accept: 'image/png, image/jpeg, image/jpg, image/gif',type: 'upload',listType: 'picture-img',action: '128.0.0.1/update/image/cover/', // 上传地址 + 文件保存上传地址(详见接口描叙)multiple: true, // 文件多选drag: true, // 拖拽排序limit: 1, // 上传数量 1 个//fileSize: 500, // 上传大小 500 kb内tip: '只能上传 jpg/png/gif 格式的图片',loadText: '上传中...',propsHttp: {res: 'data'},uploadBefore: (file, done) => {// 文件上传前处理done(file)},uploadAfter: (res, done) => {this.$message.success('上传成功');done()},uploadError(error, column) {// 上传失败this.$message.error(error);},uploadExceed(limit, files, fileList, column) {// 文件数量验证this.$message.warning(`当前限制文件数量为 $1, 当前共 ${files.length + fileList.length} `);},},// {// label: '标签',// prop: 'lables',// type: 'array',// dataType: 'string',// limit: 10,// span: 10,// rules: [{// required: false,// message: "请添加 标签集",// trigger: "blur"// }]// },{label: '文章描述',prop: 'describe',type: 'textarea',maxlength: 256,showWordLimit: true,span: 10,rules: [{required: true,message: "请输入 文章描述",trigger: "blur"}]},// {// label: '文章内容 ',// prop: 'content',// maxlength: 0,// showWordLimit: true,// span: 10,// rules: [{// required: true,// message: "请输入 文章内容 ",// trigger: "blur"// }]// },// {// label: '状态 ',// prop: 'state',// type: 'radio',// //dicData: this.dict.get('ARTICLE_STATE'),// span: 10,// rules: [{// required: true,// message: "请选择 状态 ",// trigger: "blur"// }]// },// {// label: '访问权限 ',// prop: 'auth',// type: 'radio',// //dicData: this.dict.get('ARTICLE_AUTH'),// span: 10,// rules: [{// required: true,// message: "请选择 访问权限 ",// trigger: "blur"// }]// },// {// label: '排序',// prop: 'sort',// maxlength: 11,// showWordLimit: true,// span: 10,// rules: [{// required: true,// message: "请输入 排序",// trigger: "blur"// }]// },{label: 'seo: Title',prop: 'seoTitle',maxlength: 128,showWordLimit: true,span: 20,labelWidth: 130,rules: [{required: false,message: "请输入 seo优化字段 Title",trigger: "blur"}]},{label: 'seo: Keyword',prop: 'seoKeyword',maxlength: 256,showWordLimit: true,span: 20,labelWidth: 130,rules: [{required: false,message: "请输入 seo优化字段 Keyword",trigger: "blur"}]},{label: 'seo: Description',prop: 'seoDescription',maxlength: 256,showWordLimit: true,span: 20,labelWidth: 130,rules: [{required: false,message: "请输入 seo优化字段 Description",trigger: "blur"}]}]}],}}},created() {this.obj = this.defaultData;//this.findCategorTree();this.initSuccess = true;},// 实例被挂载后调用mounted: function () {console.log("mounted=实例已被挂载")// 输入内容时禁止拖拽var inputs = document.getElementsByClassName('el-input__inner');for (let item of inputs) {item.addEventListener('blur', event => {console.log("inputting!!111");this.disabledDrag = false;});item.addEventListener('focus', event => {console.log("inputting!!222");this.disabledDrag = true;});}},methods: {emptytChange() {this.closeDialog(false);},submit(form, done) {this.crud.post(this.uri.info, this.obj).then((res) => {console.debug(res);if (res.data.code == 200) {this.closeDialog(true);}done(form);}).catch((err) => {console.error(err);done(form);})},/*** 查询分类数据*/async findCategorTree() {let res = await this.crud.get(this.uri.findCategoryTree);this.categoryTree = res.data.data;},/*** 删除行数据* @param element*/delItemRow(item) {if (this.contentItems.length <= 1) {this.$message.error('最后一条数据不能删除');return}this.contentItems.splice(this.contentItems.indexOf(item), 1)},// 内容区拖动onStart() {this.drag = true;},onEnd() {// this.resetImageSort();this.drag = false;},// 图片区拖动onStartImages() {this.dragImages = true;},onEndImages() {this.dragImages = false;},}});// app.use(AVUE);</script><style scoped>/* 主宽度 */.bt-article-all {width: 96%;padding-left: 2%;}/* 卡片上间距 */.bt-card-box {padding-left: 1%;}/* 卡片默认样式 */.text {font-size: 14px;}.clearfix:before,.clearfix:after {display: table;content: "";}.clearfix:after {clear: both}.box-card {width: 100%;/*padding: 1%;*/padding-top: 1%;}
</style></body>
</html>
-
个人开源项目(通用后台管理系统)–> https://gitee.com/wslxm/xijia-plus , 喜欢的可以看看
-
本文到此结束,如果觉得有用,动动小手点赞或关注一下呗,将不定时持续更新更多的内容…,感谢大家的观看!