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

这里创建项目就不多说了

安装element-plus

npm install element-plus

安装vuedraggable

npm install vuedraggable

安装ali-oss

npm install ali-oss

这里是封装一下:在components创建文件夹jc-upload>jc-upload.vue

在封装的过程中遇到了一个问题就是draggable和el-upload上传按钮独占一行,显然不是我们需要的效果,先看问题

百度了一下,没有找到什么解决办法,这里通过一行css解决以上问题,如有大佬有更好的方案可以分享一下

.draggable-container {
     display: contents;
 }

 完整代码

<template>
    <div class="upload-container">
      <draggable class="draggable-container" v-model="newsFileList" itemKey="url" ghost-class="ghost" animation="300">
        <template #item="{ element }">
          <ul class="el-upload-list el-upload-list--picture-card">
            <li :key="element.url" class="el-upload-list__item is-success animated">
              <img class="el-upload-list__item-thumbnail" :src="element.url" alt="">
              <label class="el-upload-list__item-status-label">
                  <i class="el-icon el-icon--upload-success el-icon--check">
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                      <path fill="currentColor"
                          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">
                      </path>
                    </svg>
                  </i>
              </label>
              <i class="el-icon-close"></i>
              <span class="el-upload-list__item-actions">
                  <!-- 预览 -->
                  <span class="el-upload-list__item-preview"
                      @click="handlePictureCardPreview(element)">
                      <i class="el-icon el-icon--zoom-in">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                          <path fill="currentColor"
                              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">
                          </path>
                        </svg>
                      </i>
                  </span>
                  <!-- 删除 -->
                  <span class="el-upload-list__item-delete" @click="handleRemove(element)">
                      <i class="el-icon el-icon--zoom-in">
                        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
                          <path fill="currentColor"
                              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">
                          </path>
                        </svg>
                      </i>
                  </span>
              </span>
            </li>
          </ul>
        </template>
      </draggable>
      <el-upload 
        ref="uploadRef" 
        :show-file-list="false" 
        :class="newsFileList.length >= limit ? 'upload-hide' : ''"
        :action="action" 
        :accept="accept" 
        list-type="picture-card" 
        :file-list="newsFileList" 
        :on-remove="handleRemove" 
        :on-success="onSuccess" 
        :limit="limit"
        :disabled="disabled"
        with-credentials 
        :multiple="limit > 1? true : false"
        drag>
        <el-icon><Plus /></el-icon>
      </el-upload>

      <el-dialog v-model="dialogVisible">
        <img class="uy-w-p-100" w-full :src="dialogImageUrl" alt="Preview Image" />
      </el-dialog>
    </div>
</template>

<script lang="ts" setup>
import { reactive, ref, watch } from 'vue'
import { ElNotification, type UploadFile } from 'element-plus'
import OSS from 'ali-oss';
import draggable from 'vuedraggable';

const $emit = defineEmits(['success'])

/**
 * 组件props
 */
const props = defineProps({
  action: {
    type: String,
    // default: `${import.meta.env.VITE_SERVE}/api/Osstoken/getToken`
    default: `/api/api/Osstoken/getToken`
  },
  fileList: {
    type: Array as () => UploadFile[],
    default: () => []
  },
  limit: {
    type: Number,
    default: 1
  },
  accept: {
    type: String,
    default: 'image/*'
  },
  disabled: {
    type: Boolean,
    default: false
  }
})

// data数据
const uploadRef = ref()
const dialogImageUrl = ref('')
const dialogVisible = ref(false)
const disabled = ref(false)
let newsFileList = ref(props.fileList);

// methods方法
const handleRemove = (file: UploadFile) => {
  const index = newsFileList.value.findIndex(item => item.url === file.url);
  newsFileList.value.splice(index, 1);
  $emit('success', newsFileList)
};

const handlePictureCardPreview = (file: UploadFile) => {
  dialogImageUrl.value = file.url!
  dialogVisible.value = true
};

const uploadAliyun = async (res: any, file: UploadFile, fileList: UploadFile[]) => {
  if (res.code === 0) {
    let date = new Date();
    let year = date.getFullYear();
    let month = date.getMonth() + 1;
    let day = date.getDate();
    let formattedMonth = month.toString().padStart(2, '0');
    let formattedDay = day.toString().padStart(2, '0');
    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()}`
    let client = new OSS({
      accessKeyId: res.data.AccessKeyId, //OSS的用户名
      accessKeySecret: res.data.AccessKeySecret, //OSS密钥
      region: res.data.region, //域名是创建Buckiet,时选择的服务器地址 比如选择 华南3(广州):oss-cn-guangzhou
      bucket: res.data.bucket,  //创建Buckiet的名称
      stsToken: res.data.SecurityToken //临时Token
    })
    const put = async () => {
      try {
        const result = await client.put(filePath, file.raw!);
        if(result.res.status === 200) {
          if(props.accept === 'video/*') {
            file.url = (result.res as any).requestUrls[0] + '?x-oss-process=video/snapshot,t_0,f_jpg';
          }
          newsFileList.value.push({
            url: (result.res as any).requestUrls[0],
            name: '',
            status: 'success',
            uid: 0
          })
          $emit('success', newsFileList)
        }
      } catch (err) {
        ElNotification.success({ message: '上传失败~' });
      }
    }
    put();
  } else {
    ElNotification.success({ message: res.msg });
  }
};

const onSuccess = (res: any, file: UploadFile, fileList: UploadFile[]) => {
  uploadAliyun(res, file, fileList)
};

watch(() => props.fileList, (newVal) => {
  newsFileList.value = newVal
}, { deep: true })

watch(() => newsFileList.value, (newVal) => {
  if(JSON.stringify(newVal) !== JSON.stringify(props.fileList)) {
    $emit('success', newVal)
  }
}, { deep: true })
</script>

<style lang="scss" scoped>
:deep(.el-upload-dragger) {
  padding: 56px 0;
  background-color: transparent;
  border: none;
}

.upload-hide {
    position: relative;

    :deep(.el-upload--picture-card) {
        display: none;
    }
}

.upload-container {
  display: flex;
  flex-wrap: wrap;

  .draggable-container {
    display: contents;
  }
}
</style>

对以上代码解释一下

重点解决el-upload上传按钮独占一行问题

.upload-container {
  display: flex;
  flex-wrap: wrap;

  .draggable-container {
    display: contents;
  }
}

主要对limit最大限制对上传按钮隐藏 

.upload-hide {
    position: relative;

    :deep(.el-upload--picture-card) {
        display: none;
    }
}

action主要获取OSS上传凭证这里用了代理,通过前端请求后端接口会拒绝访问,线上不需要代理

这段代码主要为了更新排序后的数组

watch(() => newsFileList.value, (newVal) => {
  if(JSON.stringify(newVal) !== JSON.stringify(props.fileList)) {
    $emit('success', newVal)
  }
}, { deep: true })

这里为什么选用ref并不是为了省事ref一把梭,刚开始我用的 reactive但是在排序的时候一直回弹,具体原因没有深究,换成ref就没问题了

let newsFileList = ref(props.fileList);

就解释这么多吧,有什么疑问或不明白的地方可以留言,有什么更好的方案请大佬们赐教,写的不好请多多担待

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/952020.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何在 Linux系统用中挂载和管理磁盘分区

在 Linux 系统中&#xff0c;挂载和管理磁盘分区是系统管理的基本任务之一。以下是详细步骤&#xff0c;帮助你完成这一过程。 1. 查看现有磁盘和分区 首先&#xff0c;使用以下命令来查看系统中的磁盘和分区&#xff1a; bash 复制 lsblk或者使用&#xff1a; bash 复制…

Opencv图片的旋转和图片的模板匹配

图片的旋转和图片的模板匹配 目录 图片的旋转和图片的模板匹配1 图片的旋转1.1 numpy旋转1.1.1 函数1.1.2 测试 1.2 opencv旋转1.2.1 函数1.2.2 测试 2 图片的模板匹配2.1 函数2.2 实际测试 1 图片的旋转 1.1 numpy旋转 1.1.1 函数 np.rot90(kl,k1)&#xff0c;k1逆时针旋转9…

【YOLOv8杂草作物目标检测】

YOLOv8杂草目标检测 算法介绍模型和数据集下载 算法介绍 YOLOv8在禾本科杂草目标检测方面有显著的应用和效果。以下是一些关键信息的总结&#xff1a; 农作物幼苗与杂草检测系统&#xff1a;基于YOLOv8深度学习框架&#xff0c;通过2822张图片训练了一个目标检测模型&#xff…

vue3 react使用高德离线地图,最新解决内网情况首次不能加载离线地图2025年1月10日

下载离线资源 下载地址 https://download.csdn.net/download/u010843503/90234612 2、部署私有化瓦片资源 ngxin中配置如下 server{listen 18082;server_name localhost;location / {root D:/GisMap/_alllayers;#try_files $uri $uri/ /index.html;#index index.html;} }下载…

Hbuilder ios 离线打包sdk版本4.36,HbuilderX 4.36生成打包资源 问题记录

1、打包文档地址https://nativesupport.dcloud.net.cn/AppDocs/usesdk/ios.html#%E9%85%8D%E7%BD%AE%E5%BA%94%E7%94%A8%E7%89%88%E6%9C%AC%E5%8F%B7 2、配置应用图标 如果没有appicon文件&#xff0c;此时找到 Assets.xcassets 或者 Images.xcassets(看你sdk引入的启动文件中…

Unity中 Xlua使用整理(二)

1.Xlua的配置应用 xLua所有的配置都支持三种方式&#xff1a;打标签&#xff1b;静态列表&#xff1b;动态列表。配置要求&#xff1a; 列表方式均必须是static的字段/属性 列表方式均必须放到一个static类 建议不用标签方式 建议列表方式配置放Editor目录&#xff08;如果是H…

【计算机网络】课程 实验二 交换机基本配置和VLAN 间路由实现

实验二 交换机基本配置和VLAN 间路由实现 一、实验目的 1&#xff0e;了解交换机的管理方式。 2&#xff0e;掌握通过Console接口对交换机进行配置的方法。 3&#xff0e;掌握交换机命令行各种模式的区别&#xff0c;能够使用各种帮助信息以及命令进行基本的配置。 4&…

【数据结构:前缀树Trie】

目录 前言前缀树介绍和应用一、前缀树的定义前缀树的问题和思考前缀树的映射思想前缀树三大性质 二.前缀树节点结构三. 前缀树接口介绍和实现四个接口API1. insert(String word)2. search(String word)3. startsWith(String pre)4. delete(String word) API实现1. 查询操作sear…

Jenkins触发器--在其他项目执行后构建

前言&#xff1a; jenkins中有多种触发器可用&#xff0c;可以方便的控制构建的启动 这里简单介绍下项目后构建的配置方法 1. 解释&#xff1a; Build after other projects are built Set up a trigger so that when some other projects finish building, a new build is…

Linux(18)——提高命令行运行效率

目录 一、创建和执行 shell 脚本&#xff1a; 1、命令解释器&#xff1a; 2、执行 Bash Shell 脚本&#xff1a; 3、从 shell 脚本提供输出&#xff1a; 二、对特殊字符加引号&#xff1a; 1、反斜杠 &#xff08;\&#xff09;&#xff1a; 2、单引号 &#xff08; &…

软件系统安全逆向分析-混淆对抗

1. 概述 在一般的软件中&#xff0c;我们逆向分析时候通常都不能直接看到软件的明文源代码&#xff0c;或多或少存在着混淆对抗的操作。下面&#xff0c;我会实践操作一个例子从无从下手到攻破目标。 花指令对抗虚函数表RC4 2. 实战-donntyousee 题目载体为具有漏洞的小型软…

计算机网络 (33)传输控制协议TCP概述

一、定义与基本概念 TCP是一种面向连接的、可靠的、基于字节流的传输层通信协议。它工作在OSI模型的第四层&#xff0c;即传输层&#xff0c;为用户提供可靠的、有序的和无差错的数据传输服务。TCP协议与UDP协议是传输层的两大主要协议&#xff0c;但两者在设计上有明显的不同&…

【从0带做】基于Springboot3+Vue3的高校食堂点餐系统

大家好&#xff0c;我是武哥&#xff0c;最近给大家手撸了一个基于SpringBoot3Vue3的高校食堂点餐系统&#xff0c;可用于毕业设计、课程设计、练手学习&#xff0c;系统全部原创&#xff0c;如有遇到网上抄袭站长的&#xff0c;欢迎联系博主~ 详细介绍 https://www.javaxm.c…

一文说清dockerfile编写

docker用的时间比较久了&#xff0c;关于怎样把jar打成镜像&#xff0c;怎样基于已有mysql镜像添加额外初始化后封装成新的镜像&#xff0c;进行简单的说明。 1.jar封装镜像 from centos # 设置本地为中文&#xff0c;解决中文乱码问题 RUN localedef -i zh_CN -f UTF-8 zh_CN…

基于Python实现的通用小规模搜索引擎

基于Python实现的通用小规模搜索引擎 1.项目简介 1.1背景 《信息内容安全》网络信息内容获取技术课程项目设计 一个至少能支持10个以上网站的爬虫程序&#xff0c;且支持增量式数据采集;并至少采集10000个实际网页;针对采集回来的网页内容&#xff0c; 能够实现网页文本的分…

ssm旅游攻略网站设计+jsp

系统包含&#xff1a;源码论文 所用技术&#xff1a;SpringBootVueSSMMybatisMysql 需要源码或者定制看文章最下面或看我的主页 目 录 目 录 III 1 绪论 1 1.1 研究背景 1 1.2 目的和意义 1 1.3 论文结构安排 2 2 相关技术 3 2.1 SSM框架介绍 3 2.2 B/S结构介绍 3 …

算法提高 图形输出

时间限制&#xff1a;C/C 1000MS&#xff0c;其他语言 2000MS 内存限制&#xff1a;C/C 512MB&#xff0c;其他语言 1024MB 难度&#xff1a;困难 分数&#xff1a;100 OI排行榜得分&#xff1a;14(0.1*分数2*难度) 描述 编写一程序&#xff0c;在屏幕上输出如下内容&#xff1…

[程序设计]—代理模式

[程序设计]—代理模式&#x1f473; 本文章记录学习于——52.面向切面&#xff1a;AOP-场景模拟_哔哩哔哩_bilibili 最近闲来无事&#xff0c;在学习Spring的源码&#xff1a; 后面慢慢更新源码系列blog&#xff0c;希望多多关注&#x1f64f;&#x1f64f; 目前已经总结的b…

ue5玩家角色添加武器。切换武器位置,手上武器放到背上。演示一下人体插槽和武器的连接。仅仅演示,实际项目不是这么用的

把第一人称资源包导进来 这就是我们枪的骨骼网格体 我们找到这个骨骼 右手添加插槽 取个名字 因为武器上也有动画&#xff0c;所有武器单独写个蓝图类 新建一个蓝图类 BP_Weapon 把枪的蓝图拖到人的静态网格体下&#xff0c;成为一个部分 选中BP_Weapon的父类套接字…

如何选择适合的证件照制作软件,让您的照片制作更轻松

在当今数字化的时代&#xff0c;制作证件照不再需要专门前往照相馆。选择一款合适的证件照制作软件&#xff0c;您可以在家中轻松完成标准证件照的拍摄与制作。然而&#xff0c;面对市面上琳琅满目的软件&#xff0c;找到最适合您需求的软件并不简单。本文将为您详细介绍选择证…