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

news2025/1/11 11:07:58

这里创建项目就不多说了

安装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);

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

理解Unity脚本编译过程:程序集

https://docs.unity3d.com/Manual/script-compilation.html 关于Unity C#脚本编译的细节&#xff0c;其中一个比较重要的知识点就是如何自定义Assembly。 预定义的assembly 默认情况下&#xff0c;Unity会按照这个规则进行编译。 PhaseAssembly nameScript files1Assembly-…

设计模式 行为型 责任链模式(Chain of Responsibility Pattern)与 常见技术框架应用 解析

责任链模式&#xff08;Chain of Responsibility Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许将请求沿着处理者链进行发送。每个处理者对象都有机会处理该请求&#xff0c;直到某个处理者决定处理该请求为止。这种模式的主要目的是避免请求的发送者和接收者之间…

VS2022如何修改我们新建工程打开新建文件中,默认输入我们的main函数和宏定义

1.右击我们的VS环境&#xff0c;选择【打开文件位置】 2. 进入C:\Program Files\Microsoft Visual Studio\2022\Community\Common7\IDE 目录 进入【VC】文件夹 进入【VCProjectItems】文件夹 3. 修改newcfile.cpp文件 右击选择【打开方式】选择【记事本】 添加如下内容 #defi…

2025-1-10-sklearn学习(36、37) 数据集转换-无监督降维+随机投影 沙上并禽池上暝。云破月来花弄影。

文章目录 sklearn学习(36、37) 数据集转换-无监督降维随机投影sklearn学习(36) 数据集转换-无监督降维36.1 PCA: 主成份分析36.2 随机投影36.3 特征聚集 sklearn学习(37) 数据集转换-随机投影37.1 Johnson-Lindenstrauss 辅助定理37.2 高斯随机投影37.3 稀疏随机矩阵 sklearn学…

openssl编译

关于windows下&#xff0c;openssl编译 环境准备 安装 perl:https://djvniu.jb51.net/200906/tools/ActivePerl5_64.rar安装nasm&#xff1a;https://www.nasm.us/pub/nasm/releasebuilds/2.13.01/win64/nasm-2.13.01-installer-x64.exe下载opensll源码&#xff1a;https://o…

2025-1-9 QT 使用 QXlsx库 读取 .xlsx 文件 —— 导入 QXlsx库以及读取 .xlsx 的源码 实践出真知,你我共勉

文章目录 1. 导入QXlsx库2. 使用 QXlsx库 读取 .xlsx 文件小结 网上有很多教程&#xff0c;但太费劲了&#xff0c;这里有个非常简便的好方法&#xff0c;分享给大家。 1. 导入QXlsx库 转载链接 &#xff1a;https://github.com/QtExcel/QXlsx/blob/master/HowToSetProject.md…

先辑芯片HPM5300系列之SEI多摩川协议命令表问题研究

多摩川协议有9条命令&#xff0c;但是先辑SEI的命令表只有8张。0-6是可用的&#xff0c;第7张是黑洞表&#xff0c;所以只有7张可用。 命令表的限制颇多&#xff0c;比如命令表只能按顺序使用 &#xff1a;例如0、1、3&#xff0c;那么命令表3是不能用的。 如果想要实现9个命令…

kotlin项目无法访问Java类的问题

使用IntelliJ创建一个Kotlin项目&#xff0c;然后在src/main/kotlin中创建一个java接口&#xff1a;Animal.java&#xff0c;然后在Main.kt中打印这个java接口&#xff0c;如下&#xff1a; fun main() {println(Animal::class.java) }代码在编辑器中并没有报错&#xff0c;但…

全栈面试(一)Basic/微服务

文章目录 项目地址一、Basic InterviewQuestions1. tell me about yourself?2. tell me about a time when you had to solve a complex code problem?3. tell me a situation that you persuade someone at work?4. tell me a about a confict with a teammate and how you…

医疗可视化大屏 UI 设计新风向

智能化交互 借助人工智能与机器学习技术&#xff0c;实现更智能的交互功能。如通过语音指令或手势控制来操作大屏&#xff0c;医护人员无需手动输入&#xff0c;可更便捷地获取和处理信息。同时&#xff0c;系统能根据用户的操作习惯和数据分析&#xff0c;自动推荐相关的医疗…

Angular由一个bug说起之十三:Cross Origin

跨域 想要了解跨域&#xff0c;首要要了解源 什么是源&#xff0c;源等于协议加域名加端口号 只有这三个都相同&#xff0c;才是同源&#xff0c;反之则是非同源。 比如下面这四个里&#xff0c;只有第4个是同源 而浏览器给服务器发送请求时&#xff0c;他们的源一样&#xff0…

【LeetCode Hot100 贪心算法】 买卖股票的最佳时机、跳跃游戏、划分字母区间

贪心算法 买卖股票的最佳时机买卖股票的最佳时机II跳跃游戏跳跃游戏II划分字母区间 买卖股票的最佳时机 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一个不同的…

MCANet: 基于多模态字幕感知的大语言模型训练无关视频异常检测

目录 摘要01 引言02 相关工作2.1 视频异常检测2.2 基于视频的大语言模型&#xff08;VLLMs&#xff09; 03 方法论3.1 问题定义3.2 MCANet3.3 图像字幕分支3.4 音频字幕分支3.5 基于LLM的异常评分3.6 视频-文本分数优化 04 实验4.1 数据集和评估指标4.2 实现细节4.3 定性结果4.…

为深度学习引入张量

为深度学习引入张量 什么是张量&#xff1f; 神经网络中的输入、输出和转换都是使用张量表示的&#xff0c;因此&#xff0c;神经网络编程大量使用张量。 张量是神经网络使用的主要数据结构。 张量的概念是其他更具体概念的数学概括。让我们看看一些张量的具体实例。 张量…

Taro+Vue实现图片裁剪组件

cropper-image-taro-vue3 组件库 介绍 cropper-image-taro-vue3 是一个基于 Vue 3 和 Taro 开发的裁剪工具组件&#xff0c;支持图片裁剪、裁剪框拖动、缩放和输出裁剪后的图片。该组件适用于 Vue 3 和 Taro 环境&#xff0c;可以在网页、小程序等平台中使用。 源码 https:…

[DO374] Ansible 配置文件

[DO374] Ansible 配置文件 1. 配置文件位置2. 配置文件3. Ansible 配置4. Ansible的Ad-hoc5. Ansible 模块6. playbook段落7. 任务执行后续8. Ansible 变量8.1 ansible 变量的定义8.1.1 主机变量8.1.2 主机组变量 8.2 vars的循环 9. Ansible Collection10. Ansible-galaxy 安装…

[程序设计]—代理模式

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

OSPF - 2、3类LSA(Network-LSA、NetWork-Sunmmary-LSA)

前篇博客有对常用LSA的总结 2类LSA&#xff08;Network-LSA&#xff09; DR产生泛洪范围为本区域 作用:  描述MA网络拓扑信息和网络信息&#xff0c;拓扑信息主要描述当前MA网络中伪节点连接着哪几台路由。网络信息描述当前网络的 掩码和DR接口IP地址。 影响邻居建立中说到…

景芯SOC设计实战

终身辅导、一对一辅导&#xff0c;手把手教您完成SoC全流程设计&#xff0c;从入门到进阶&#xff0c;带您掌握SoC芯片架构、算法、设计、验证、DFT、后端及低功耗全流程&#xff01;直播视频不定期升级&#xff01;让您快速超越同龄人&#xff01; 景芯团队主打文档服务器实战…

多云架构,JuiceFS 如何实现一致性与低延迟的数据分发

随着大模型的普及&#xff0c;GPU 算力成为稀缺资源&#xff0c;单一数据中心或云区域的 GPU 资源常常难以满足用户的全面需求。同时&#xff0c;跨地域团队的协作需求也推动了企业在不同云平台之间调度数据和计算任务。多云架构正逐渐成为一种趋势&#xff0c;然而该架构下的数…