vue3 + ts + element-plus(el-upload + vuedraggable实现上传OSS并排序) ...

一给  金牌会员 | 2025-1-12 02:20:56 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 988|帖子 988|积分 2964

这里创建项目就不多说了
安装element-plus
  1. npm install element-plus
复制代码
安装vuedraggable
  1. npm install vuedraggable
复制代码
安装ali-oss
  1. npm install ali-oss
复制代码
这里是封装一下:在components创建文件夹jc-upload>jc-upload.vue
在封装的过程中碰到了一个问题就是draggable和el-upload上传按钮独占一行,显然不是我们需要的效果,先看问题

百度了一下,没有找到什么办理办法,这里通过一行css办理以上问题,如有大佬有更好的方案可以分享一下
  1. .draggable-container {
  2.      display: contents;
  3. }
复制代码
 完整代码
  1. <template>
  2.     <div class="upload-container">
  3.       <draggable class="draggable-container" v-model="newsFileList" itemKey="url" ghost-class="ghost" animation="300">
  4.         <template #item="{ element }">
  5.           <ul class="el-upload-list el-upload-list--picture-card">
  6.             <li :key="element.url" class="el-upload-list__item is-success animated">
  7.               <img class="el-upload-list__item-thumbnail" :src="element.url" alt="">
  8.               <label class="el-upload-list__item-status-label">
  9.                   <i class="el-icon el-icon--upload-success el-icon--check">
  10.                     <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  11.                       <path fill="currentColor"
  12.                           d="M406.656 706.944 195.84 496.256a32 32 0 1 0-45.248 45.248l256 256 512-512a32 32 0 0 0-45.248-45.248L406.592 706.944z">
  13.                       </path>
  14.                     </svg>
  15.                   </i>
  16.               </label>
  17.               <i class="el-icon-close"></i>
  18.               <span class="el-upload-list__item-actions">
  19.                   <!-- 预览 -->
  20.                   <span class="el-upload-list__item-preview"
  21.                       @click="handlePictureCardPreview(element)">
  22.                       <i class="el-icon el-icon--zoom-in">
  23.                         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  24.                           <path fill="currentColor"
  25.                               d="m795.904 750.72 124.992 124.928a32 32 0 0 1-45.248 45.248L750.656 795.904a416 416 0 1 1 45.248-45.248zM480 832a352 352 0 1 0 0-704 352 352 0 0 0 0 704zm-32-384v-96a32 32 0 0 1 64 0v96h96a32 32 0 0 1 0 64h-96v96a32 32 0 0 1-64 0v-96h-96a32 32 0 0 1 0-64h96z">
  26.                           </path>
  27.                         </svg>
  28.                       </i>
  29.                   </span>
  30.                   <!-- 删除 -->
  31.                   <span class="el-upload-list__item-delete" @click="handleRemove(element)">
  32.                       <i class="el-icon el-icon--zoom-in">
  33.                         <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
  34.                           <path fill="currentColor"
  35.                               d="M160 256H96a32 32 0 0 1 0-64h256V95.936a32 32 0 0 1 32-32h256a32 32 0 0 1 32 32V192h256a32 32 0 1 1 0 64h-64v672a32 32 0 0 1-32 32H192a32 32 0 0 1-32-32V256zm448-64v-64H416v64h192zM224 896h576V256H224v640zm192-128a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32zm192 0a32 32 0 0 1-32-32V416a32 32 0 0 1 64 0v320a32 32 0 0 1-32 32z">
  36.                           </path>
  37.                         </svg>
  38.                       </i>
  39.                   </span>
  40.               </span>
  41.             </li>
  42.           </ul>
  43.         </template>
  44.       </draggable>
  45.       <el-upload
  46.         ref="uploadRef"
  47.         :show-file-list="false"
  48.         :class="newsFileList.length >= limit ? 'upload-hide' : ''"
  49.         :action="action"
  50.         :accept="accept"
  51.         list-type="picture-card"
  52.         :file-list="newsFileList"
  53.         :on-remove="handleRemove"
  54.         :on-success="onSuccess"
  55.         :limit="limit"
  56.         :disabled="disabled"
  57.         with-credentials
  58.         :multiple="limit > 1? true : false"
  59.         drag>
  60.         <el-icon><Plus /></el-icon>
  61.       </el-upload>
  62.       <el-dialog v-model="dialogVisible">
  63.         <img class="uy-w-p-100" w-full :src="dialogImageUrl" alt="Preview Image" />
  64.       </el-dialog>
  65.     </div>
  66. </template>
  67. <script lang="ts" setup>
  68. import { reactive, ref, watch } from 'vue'
  69. import { ElNotification, type UploadFile } from 'element-plus'
  70. import OSS from 'ali-oss';
  71. import draggable from 'vuedraggable';
  72. const $emit = defineEmits(['success'])
  73. /**
  74. * 组件props
  75. */
  76. const props = defineProps({
  77.   action: {
  78.     type: String,
  79.     // default: `${import.meta.env.VITE_SERVE}/api/Osstoken/getToken`
  80.     default: `/api/api/Osstoken/getToken`
  81.   },
  82.   fileList: {
  83.     type: Array as () => UploadFile[],
  84.     default: () => []
  85.   },
  86.   limit: {
  87.     type: Number,
  88.     default: 1
  89.   },
  90.   accept: {
  91.     type: String,
  92.     default: 'image/*'
  93.   },
  94.   disabled: {
  95.     type: Boolean,
  96.     default: false
  97.   }
  98. })
  99. // data数据
  100. const uploadRef = ref()
  101. const dialogImageUrl = ref('')
  102. const dialogVisible = ref(false)
  103. const disabled = ref(false)
  104. let newsFileList = ref(props.fileList);
  105. // methods方法
  106. const handleRemove = (file: UploadFile) => {
  107.   const index = newsFileList.value.findIndex(item => item.url === file.url);
  108.   newsFileList.value.splice(index, 1);
  109.   $emit('success', newsFileList)
  110. };
  111. const handlePictureCardPreview = (file: UploadFile) => {
  112.   dialogImageUrl.value = file.url!
  113.   dialogVisible.value = true
  114. };
  115. const uploadAliyun = async (res: any, file: UploadFile, fileList: UploadFile[]) => {
  116.   if (res.code === 0) {
  117.     let date = new Date();
  118.     let year = date.getFullYear();
  119.     let month = date.getMonth() + 1;
  120.     let day = date.getDate();
  121.     let formattedMonth = month.toString().padStart(2, '0');
  122.     let formattedDay = day.toString().padStart(2, '0');
  123.     const filePath = `/uploads/${year}/${formattedMonth}/${formattedDay}/${Date.parse(date.toString()) + parseInt((Math.random() * (100000 - 10000 + 1) + 10000).toString(), 10).toString()}.${file.raw?.name.split('.').pop()}`
  124.     let client = new OSS({
  125.       accessKeyId: res.data.AccessKeyId, //OSS的用户名
  126.       accessKeySecret: res.data.AccessKeySecret, //OSS密钥
  127.       region: res.data.region, //域名是创建Buckiet,时选择的服务器地址 比如选择 华南3(广州):oss-cn-guangzhou
  128.       bucket: res.data.bucket,  //创建Buckiet的名称
  129.       stsToken: res.data.SecurityToken //临时Token
  130.     })
  131.     const put = async () => {
  132.       try {
  133.         const result = await client.put(filePath, file.raw!);
  134.         if(result.res.status === 200) {
  135.           if(props.accept === 'video/*') {
  136.             file.url = (result.res as any).requestUrls[0] + '?x-oss-process=video/snapshot,t_0,f_jpg';
  137.           }
  138.           newsFileList.value.push({
  139.             url: (result.res as any).requestUrls[0],
  140.             name: '',
  141.             status: 'success',
  142.             uid: 0
  143.           })
  144.           $emit('success', newsFileList)
  145.         }
  146.       } catch (err) {
  147.         ElNotification.success({ message: '上传失败~' });
  148.       }
  149.     }
  150.     put();
  151.   } else {
  152.     ElNotification.success({ message: res.msg });
  153.   }
  154. };
  155. const onSuccess = (res: any, file: UploadFile, fileList: UploadFile[]) => {
  156.   uploadAliyun(res, file, fileList)
  157. };
  158. watch(() => props.fileList, (newVal) => {
  159.   newsFileList.value = newVal
  160. }, { deep: true })
  161. watch(() => newsFileList.value, (newVal) => {
  162.   if(JSON.stringify(newVal) !== JSON.stringify(props.fileList)) {
  163.     $emit('success', newVal)
  164.   }
  165. }, { deep: true })
  166. </script>
  167. <style lang="scss" scoped>
  168. :deep(.el-upload-dragger) {
  169.   padding: 56px 0;
  170.   background-color: transparent;
  171.   border: none;
  172. }
  173. .upload-hide {
  174.     position: relative;
  175.     :deep(.el-upload--picture-card) {
  176.         display: none;
  177.     }
  178. }
  179. .upload-container {
  180.   display: flex;
  181.   flex-wrap: wrap;
  182.   .draggable-container {
  183.     display: contents;
  184.   }
  185. }
  186. </style>
复制代码

对以上代码解释一下
重点办理el-upload上传按钮独占一行问题
  1. .upload-container {
  2.   display: flex;
  3.   flex-wrap: wrap;
  4.   .draggable-container {
  5.     display: contents;
  6.   }
  7. }
复制代码
主要对limit最大限制对上传按钮隐藏 
  1. .upload-hide {
  2.     position: relative;
  3.     :deep(.el-upload--picture-card) {
  4.         display: none;
  5.     }
  6. }
复制代码
action主要获取OSS上传凭证这里用了代理,通过前端请求后端接口会拒绝访问,线上不需要代理
这段代码主要为了更新排序后的数组
  1. watch(() => newsFileList.value, (newVal) => {
  2.   if(JSON.stringify(newVal) !== JSON.stringify(props.fileList)) {
  3.     $emit('success', newVal)
  4.   }
  5. }, { deep: true })
复制代码
这里为什么选用ref并不是为了省事ref一把梭,刚开始我用的 reactive但是在排序的时候一直回弹,详细缘故原由没有深究,换成ref就没问题了
  1. let newsFileList = ref(props.fileList);
复制代码
就解释这么多吧,有什么疑问或不明白的地方可以留言,有什么更好的方案请大佬们见教,写的不好请多多担待

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

一给

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表