咱們公司管理後臺項目是使用Element-ui組件,此次需求產品要求上傳的圖片組容許拖拽排序,我就想用vue-draggable插件了,可是相信Element-ui的el-upload組件封裝的很好,我這種菜鳥級別的前端哪裏敢動,因此我就想着上傳依然用el-upload,可是把上傳組件的展現圖片隱藏,本身根據組件的上傳以後拿到的url連接本身造成圖片數組,而後展現層由咱們本身來寫UI和vue-draggable的拖拽,話很少說,上代碼,直接莽!前端
emmm...先來兩張效果圖吧vue
<template> <div class="com-image-drag"> <div class="button-list"> <el-button @click="openDrag" v-if="!drag_open" :disabled="banner_list.length <= 1" type="text" size="small" class="operation-success" ></el-button> <el-button @click="save" v-if="drag_open" type="text" size="small" class="operation-success" ></el-button> <el-button @click="cancle" v-if="drag_open" type="text" size="small" class="operation-error" ></el-button> </div> <div class="image-list"> <!-- 拖拽層 --> <div class="list-wrap" v-show="drag_open"> <draggable v-model="banner_list" :options="{ animation: 150, ghostClass: 'sortable-ghost', chosenClass: 'chosenClass', scroll: true, scrollSensitivity: 200 }" > <div class="image-item" v-for="($item, $index) in banner_list" :key="$index" :style="{ backgroundImage: `url(${$item.url})` }" ></div> </draggable> </div> <!-- 展現層 --> <div class="list-wrap" v-show="!drag_open"> <div class="image-item" v-for="($item, $index) in banner_list" :key="$index" :style="{ backgroundImage: `url(${$item.url})` }" @mouseover.prevent="$item.is_hover = true" @mouseleave.prevent="$item.is_hover = false" > <div class="label" v-show="!$item.is_hover"> <i class="el-icon-upload-success el-icon-check icon-success"></i> </div> <div class="mask" v-show="$item.is_hover"> <i class="el-icon-delete bin" @click="deleteImage($index)"></i> </div> </div> <el-upload v-show="limit == 0 || banner_list.length < limit" list-type="picture-card" name="file" class="upload-machine" :disabled="drag_open" :action="action()" :on-error="onError" :on-success="onSuccess" :before-upload="beforeUpload" :show-file-list="false" :multiple="multiple" enctype="multipart/form-data" ></el-upload> </div> </div> </div> </template> <script> /** * @author LeeYunxiang * @description 爲了方便上傳圖片組件可拖拽排序,不改變餓了麼插件的邏輯,只作視圖層的展現 * @param {Array} list 圖片數組 * @param {Number} limit 最多可上傳幾張圖片 * @param {Function} action 上傳接口地址 * @param {Boolean} multiple 是否批量上傳 * @param {Function} beforeUpload 上傳以前的回調,用於校驗 * @param {Function} onSuccess 上傳成功的回調函數 * @param {Function} onError 上傳失敗的回調函數 */ import draggable from "vuedraggable"; export default { name: "ComImageShow", components: { draggable }, props: { list: { type: Array }, limit: { type: Number, default: 0 }, multiple: { type: Boolean, default: false }, action: { type: Function, default: () => {} }, beforeUpload: { type: Function, default: () => {} }, onError: { type: Function, default: () => {} }, onSuccess: { type: Function, default: () => {} } }, data() { return { banner_list: [], //拖拽插件不建議直接改變父組件的傳值,因此另建一個新數組 file_list: [], //保存開啓拖拽以前排序的數組 drag_open: false //拖拽開啓開關 }; }, methods: { // 刪除圖片 deleteImage(i) { this.banner_list.splice(i, 1); this.$emit("update", this.banner_list.map(item => item.url)); }, // 開啓拖拽 openDrag() { this.file_list = JSON.parse(JSON.stringify(this.banner_list)); //數組深拷貝 this.drag_open = true; }, // 取消拖拽 cancle() { this.banner_list = this.file_list; this.drag_open = false; }, // 拖拽保存 save() { this.$emit("update", this.banner_list.map(item => item.url)); this.drag_open = false; } }, mounted() { // 初始數組拷貝 this.banner_list = this.list.map(url => { let obj = { url: url, is_hover: false }; return obj; }); }, watch: { // 監聽父組件傳值改變 list(arr) { if (arr.length > this.limit && this.limit != 0) { this.$message.warning(`當前最多可上傳${this.limit}張圖片`); return false; } this.banner_list = arr.map(url => { let obj = { url: url, is_hover: false }; return obj; }); } } }; </script> <style lang="sass" scoped> .com-image-drag &:after display: block clear: both content: "" .image-list float: left &:after display: block clear: both content: "" .list-wrap float: left .image-item width: 148px height: 148px position: relative margin-right: 10px margin-bottom: 10px border: 1px solid #c0ccda background-size: 100% 100% border-radius: 6px float: left overflow: hidden cursor: pointer .label width: 46px height: 26px background-color: #13ce66 color: #FFFFFF transform: rotate(45deg) text-align: center position: absolute right: -17px top: -7px .icon-success transform: rotate(-45deg) .mask width: 100% height: 100% border-radius: 6px background-color: rgba(0, 0, 0, 0.5) position: relative .bin color: #FFFFFF font-size: 20px position: absolute left: 45% top: 43% .upload-machine float: left </style>
調用例子element-ui
<template> <image-drag :list="file_list" :multiple="true" :action="uploadUrl" :on-error="uploadError" :on-success="bannerPicSuccess" :before-upload="beforeAvatarUpload" @update="updateFile"> </image-drag> </template> <script> import ImageDrag from "@/components/common/ComImageDrag"; import { Loading } from "element-ui"; export default { components: { ImageDrag }, data() { return { banner_list: [], //ele用的 file_list: [], //本身用的 bargain: { share_image: "" }, number: "" }; }, methods: { goBack() { this.$router.go(-1); }, // 上傳圖片路徑 uploadUrl() { return `${process.env.VUE_APP_API_ROOT}upload`; }, // 圖片長傳-以前 beforeAvatarUpload(file) { let self = this; let type_arr = ["image/jpeg", "image/png"]; let type = file.type; if (!type_arr.includes(type)) { this.$message.error("圖片格式不正確,只支持jpg和png類型圖片"); return false; } const is_size = new Promise((resolve, reject) => { let width = 400; let height = 320; let img = new Image(); img.src = window.URL.createObjectURL(file); img.onload = () => { let valid = img.width === width && img.height === height; if (valid) { Loading.service({ fullscreen: true, text: "圖片上傳中,請稍後" }); resolve(file); } else { self.$message.error("請上傳400*320px大小的圖片!"); reject(); } }; }); return is_size; }, // Banner圖-成功 bannerPicSuccess(res) { this.bargain.share_image = res.data; Loading.service({ fullscreen: true }).close(); this.file_list.push(res.data); }, // Banner圖片上傳報錯 uploadError() { this.$message.error("上傳失敗,請從新上傳"); Loading.service({ fullscreen: true }).close(); }, updateFile(val) { this.file_list = val; console.log(this.file_list); } } }; </script>