【sgDragUploadFolder】自定义组件:自定义拖拽文件夹上传组件,支持上传单个或多个文件/文件夹,右下角上传托盘出现(后续更新...)

特性:

  1. 支持在拖拽上传单个文件、多个文件、单个文件夹、多个文件夹
  2. 可自定义headers
  3. 可自定义过滤上传格式
  4. 可自定义上传API接口
  5. 支持显示/隐藏右下角上传队列托盘

sgDragUploadFolder源码

<template>
    <div :class="$options.name" :dragenter="isDragenter">
      <!-- 上传按钮_________________________________________________________ -->
      <!-- 上传文件 -->
      <el-upload
        ref="uploadFile"
        :show-file-list="false"
        :headers="headers"
        :accept="accept.toString()"
        :action="actionUrl"
        :data="actionData"
        :before-upload="beforeUpload"
        :on-success="uploadSuccess"
        :on-error="uploadError"
        multiple
        :name="name"
        :auto-upload="autoUpload"
      >
      </el-upload>
  
      <!-- 上传文件夹 -->
      <el-upload
        ref="uploadFolder"
        :show-file-list="false"
        :headers="headers"
        :action="actionUrl"
        :data="actionData"
        :before-upload="beforeUpload"
        :on-success="uploadSuccess"
        :on-error="uploadError"
        :on-exceed="exceed"
        multiple
        :drag="(drag === '' || drag) && !__checkDisabledDrag()"
        :name="name"
        :auto-upload="autoUpload"
      >
      </el-upload>
      <!-- _________________________________________________________ -->
  
      <!-- 上传托盘(右下角) -->
      <sgUploadTray
        v-model="showUploadTray"
        :data="uploadList"
        @stopUpload="stopUpload"
        @dragStart="$emit(`dragUploadTrayStart`, true)"
        @dragEnd="$emit(`dragUploadTrayEnd`, true)"
        v-if="!(hideUploadTray === '' || hideUploadTray) && !sgUploadTray"
        resizeable
      />
    </div>
  </template>
  <script>
  import sgUploadTray from "@/vue/components/admin/sgUploadTray";
  export default {
    name: "sgDragUploadFolder",
    components: { sgUploadTray },
    data() {
      return {
        // 上传----------------------------------------
        name: "file", //上传的文件字段名
        headers: { kkToken: localStorage.token }, //获取token(注意仔细看后端接受token的字段名是不是叫做“token”)
        accept: `.${["png", "jpg", "jpeg", "bmp", "gif", "svg"].join(",.")}`, //默认只支持图片格式上传
        // actionUrl: `${this.$d.API_ROOT_URL}/customer/importCustomerData`,
        actionUrl: `#`,
        actionData: {},
        dur: 100,
        percent: 100,
        uploadList: [],
        showUploadTray: false,
        uploadFileBtn: null, //上传文件
        uploadFolderBtn: null, //上传文件夹
        isDragenter: false, //是否拖入
        leaveEvents: [
          "mouseenter",
          "mouseover",
          "mousemove",
          "mouseout",
          "blur",
          "visibilitychange",
        ],
        dragAreaDom: null, //拖拽放入区域
        isDragTrigger: false, //是否为拖拽触发上传
        maxSize: null, //支持最大上传文件大小
        autoUpload: true, //是否在选取文件后立即进行上传
        // ----------------------------------------
      };
    },
    props: [
      "data", //上传可选参数
      "hideUploadTray", //不显示上传托盘
      "hideUploadTrayWhenDrag", //拖拽上传的时候隐藏上传托盘(默认显示)
      "drag", //是否支持拖拽文件、文件夹或多个文件
      "disabledWhenShowSels", //当出现对应['sel','sel','sel',...]的时候,屏蔽拖拽上传(譬如出现element的v-modal)
      "sgUploadTray", //引用外部公共托盘组件dom(这种方式主要是为了解决同一个页面有多个上传托盘组件导致冲突的问题)
    ],
    watch: {
      data: {
        handler(d) {
          if (d) {
            d.name && (this.name = d.name);
            d.headers && (this.headers = d.headers);
            d.accept && (this.accept = d.accept);
            d.actionUrl && (this.actionUrl = d.actionUrl);
            d.actionData && (this.actionData = d.actionData);
            d.maxSize && (this.maxSize = d.maxSize);
            typeof d.autoUpload !== "undefined" && (this.autoUpload = d.autoUpload);
          }
        },
        deep: true,
        immediate: true,
      },
      drag: {
        handler(d) {
          if (d === "" || d) {
            this.addEvents();
          } else {
            this.removeEvents();
          }
        },
        deep: true,
        immediate: true,
      },
      showUploadTray(newValue, oldValue) {
        this.sgUploadTray && (this.sgUploadTray.show = newValue);
      },
      uploadList: {
        handler(d) {
          if (this.sgUploadTray) {
            this.sgUploadTray.uploadList || (this.sgUploadTray.uploadList = []);
            let uploadList = this.sgUploadTray.uploadList;
            d.forEach((newVal) => {
              // 避免重复添加到上传列表
              if (!uploadList.some((oldVal) => oldVal.uid == newVal.uid)) {
                uploadList.push(newVal);
                if (this.hideUploadTrayWhenDrag === "" || this.hideUploadTrayWhenDrag) {
                  this.sgUploadTray.show = false; //不显示右下角上传托盘
                } else {
                  this.sgUploadTray.show = true; //显示右下角上传托盘
                }
              }
            });
          }
        },
        deep: true,
        // immediate: true,
      },
    },
    mounted() {
      this.$nextTick(() => {
        this.uploadFileBtn = this.$refs.uploadFile.$children[0].$refs.input;
        this.uploadFolderBtn = this.$refs.uploadFolder.$children[0].$refs.input;
        this.uploadFolderBtn && (this.uploadFolderBtn.webkitdirectory = true); //让el-upload支持上传文件夹
  
        this.dragAreaDom = this.$refs.uploadFolder.$el.querySelector(`.el-upload-dragger`);
        this.dragAreaDom && this.dragAreaDom.addEventListener("drop", this.drop);
      });
    },
    destroyed() {
      this.removeEvents();
    },
    methods: {
      __checkDisabledDrag(d) {
        let aa = this.disabledWhenShowSels || [];
        aa && (Array.isArray(aa) || (aa = [aa]));
        let r = [];
        for (let i = 0, len = aa.length; i < len; i++) {
          let a = aa[i];
          let dom = document.querySelector(a);
          if (dom) {
            r.push(dom);
            return true;
          }
        }
        return r.length !== 0;
      },
      // 监听----------------------------------------
      addEvents(d) {
        this.removeEvents();
        addEventListener("dragenter", this.dragenter);
        this.leaveEvents.forEach((v) => addEventListener(v, this.leave));
        this.dragAreaDom && this.dragAreaDom.addEventListener("drop", this.drop);
      },
      removeEvents(d) {
        removeEventListener("dragenter", this.dragenter);
        this.leaveEvents.forEach((v) => removeEventListener(v, this.leave));
        this.dragAreaDom && this.dragAreaDom.removeEventListener("drop", this.drop);
      },
      dragenter(d) {
        this.isDragTrigger = true;
        this.isDragenter = true;
      },
      leave(d) {
        this.isDragenter = false;
      },
      drop(d) {
        // 触发拖拽上传
        this.uploadDragFiles(
          d,
          (file) => {
            file.isDragFile = true;
            this.beforeUpload(file);
          },
          (files) => {}
        );
      },
      // 循环获取拖拽过来的file----------------------------------------
      uploadDragFiles(e, uploadFunc, completeFunc) {
        let files = [],
          items = [].slice.call(e.dataTransfer.items);
        items.forEach((v, i) => {
          const webkitGetAsEntry = v.webkitGetAsEntry();
          eval(webkitGetAsEntry.isDirectory ? "setfolder" : "setfile")(webkitGetAsEntry);
        });
  
        // 处理文件夹
        function setfolder(webkitGetAsEntry) {
          webkitGetAsEntry
            .createReader()
            .readEntries((entries) =>
              entries.forEach((item) => (item.isFile ? setfile(item) : setfolder(item)))
            );
        }
  
        // 处理文件
        function setfile(webkitGetAsEntry) {
          webkitGetAsEntry.file((file) => {
            uploadFunc && uploadFunc(file);
            files.push(file);
          });
        }
        completeFunc && completeFunc(files);
      },
      // 上传按钮触发----------------------------------------
      triggerUploadFile(d) {
        this.isDragTrigger = false;
        this.uploadFileBtn && this.uploadFileBtn.click();
      },
      triggerUploadFolder(d) {
        this.isDragTrigger = false;
        this.uploadFolderBtn && this.uploadFolderBtn.click();
      },
      // 判断是否相同uid
      same_uid_lastModified(uid, file) {
        return (
          uid == file.uid ||
          uid == file.lastModified ||
          uid == (file.raw || {}).lastModified
        );
      },
      // 获取uid
      get_uid(file) {
        return file.uid || file.lastModified || (file.raw || {}).lastModified;
      },
      // 上传文件----------------------------------------------------------------
      showFakeLoading(file) {
        this.$emit(`showFakeLoading`, file);
        file.raw && (file = file.raw);
        file = this.uploadList.find((v) => this.same_uid_lastModified(v.uid, file)); //拖拽上传的时候没有uid
        clearInterval(file.interval);
        file.percent = 0;
        file.interval = setInterval(() => {
          file.percent >= 99 ? this.hideFakeLoading(file) : file.percent++;
        }, this.dur);
      },
      hideFakeLoading(file, { type, tip, color } = {}) {
        this.$emit(`hideFakeLoading`, file);
        // file.raw && (file = file.raw);//不需要2023.12.18
        file = this.uploadList.find((v) => this.same_uid_lastModified(v.uid, file)); //拖拽上传的时候没有uid
        clearInterval(file.interval);
        switch (type) {
          case "error":
            file.percent = 0;
            break;
          case "success":
          default:
            file.percent = 100;
        }
        type && (file.type = type);
        tip && (file.tip = tip);
        color && (file.color = color);
      },
      exceed(file, fileList) {
        this.$message.error("上传文件数量太大,分散上传吧!");
      },
      stopUpload(d) {
        this.$refs.uploadFolder.abort();
      },
      //文件上传之前
      beforeUpload(file) {
        if ((this.drag === "" || this.drag) && this.isDragTrigger) {
          if (!file.isDragFile) return; //拖拽模式下,如果不是原生js捕获到的拖拽文件,就不进入上传队列(区别开两个el-upload组件避免重复上传)
        }
        this.uploadList.unshift({
          interval: null,
          uid: this.get_uid(file), //拖拽上传的时候没有uid
          percent: 0, //加载进度
          name: file.name,
          size: file.size,
          type: file.type,
          webkitRelativePath: file.webkitRelativePath,
          type: "",
          tip: "",
          color: "",
        });
        if (this.hideUploadTrayWhenDrag === "" || this.hideUploadTrayWhenDrag) {
          this.showUploadTray = false; //不显示右下角上传托盘
        } else {
          this.showUploadTray = true; //显示右下角上传托盘
        }
        // 判断是不是特定的格式________________________
        let isFile =
          this.accept === "*"
            ? true
            : this.accept.includes(file.name.toLocaleLowerCase().split(".").pop());
        const minSize = 0.001; //限制文件最小1KB
        const isAllowMinSize = file.size / 1024 / 1024 > minSize;
        isAllowMinSize ||
          this.$message.error("上传文件大小不能小于" + minSize * 1000 + "KB");
        const maxSize = this.maxSize || 500; //限制大小
        const isAllowSize = file.size / 1024 / 1024 <= maxSize;
        isFile || this.$message.error("上传文件只能是" + this.accept + "格式");
        isAllowSize || this.$message.error("上传文件大小不能超过" + maxSize + "MB");
        let allowUpload = isFile && isAllowSize && isAllowMinSize;
        if (allowUpload) {
          this.showFakeLoading(file);
          this.$nextTick(() => {
            this.$g.file2Base64Image(file, (d) => this.$emit(`resultBase64Image`, d));
            this.$emit(`beforeUpload`, file);
          });
        } else {
          this.hideFakeLoading(file, { type: "error", tip: "上传失败", color: "red" });
        }
        return allowUpload; //若返回false则停止上传
      },
      //上传成功
      uploadSuccess(response, file, fileList) {
        /* if ((this.drag === "" || this.drag) && this.isDragTrigger) {
          if (!file.isDragFile) return; //拖拽模式下,如果不是原生js捕获到的拖拽文件,就不进入上传队列(区别开两个el-upload组件避免重复上传)
        }*/
        if (response.data && response.data.key) {
          // 下载失败原因的描述文件
          /* this.$d.customer_downloadImportCustomerExcel(
            { key: response.data.key },
            {
              s: (d) => {
                this.$emit(`error`, response, file);
                this.hideFakeLoading(file, {
                  type: "error",
                  tip: "上传失败",
                  color: "red",
                });
                this.$g.downloadFile(d, `${file.name}-上传失败原因`, ".xls");
                this.$message.error(`${file.name}-上传失败,请查看失败原因`);
              },
            }
          ); */
        } else if (response.success) {
          this.$emit(`uploadSuccess`, response, file);
          // 上传成功了
          this.hideFakeLoading(file, { type: "success", tip: "上传成功", color: "green" });
          this.$message.success(`${file.name}上传成功`);
        } else {
          this.$emit(`uploadError`, response, file);
          // 其他失败原因
          this.hideFakeLoading(file, { type: "error", tip: "上传失败", color: "red" });
        }
      },
      //上传失败
      uploadError(err, file, fileList) {
        if (this.actionUrl === "#") return;
        if ((this.drag === "" || this.drag) && this.isDragTrigger) {
          if (!file.isDragFile) return; //拖拽模式下,如果不是原生js捕获到的拖拽文件,就不进入上传队列(区别开两个el-upload组件避免重复上传)
        }
        this.$emit(`uploadError`, err, file);
        this.hideFakeLoading(file, { type: "error", tip: "上传失败", color: "red" });
        this.$message.error("上传失败");
      },
    },
  };
  </script>
  <style lang="scss">
  .sgDragUploadFolder {
    width: 0;
    height: 0;
  
    .el-upload-dragger {
      z-index: 999999; //根据情况自己拿捏
      position: absolute;
      width: 100%;
      height: 100%;
      left: 0;
      top: 0;
      display: none;
      background-color: #ffffff99;
  
      &::after {
        content: "拖拽文件到此处";
        position: absolute;
        width: 100%;
        height: 100%;
        display: flex;
        justify-content: center;
        align-items: center;
        color: #409eff;
        font-size: 18px;
        font-weight: bold;
        line-height: 1.2;
      }
    }
  
    &[dragenter] .el-upload-dragger {
      display: block;
      border-color: #409eff;
  
      &.is-dragover {
        background-color: #409eff22;
  
        &::after {
          content: "松掉鼠标上传文件";
        }
      }
    }
  }
  </style>
  

 应用

<template>
    <div>
        <div style="width:300px;height:300px;position: relative;">
            <img :src="src" style="width:100%;height:100%">
            <!-- 上传组件 -->
            <sgDragUploadFolder drag ref="sgDragUploadFolder" :data="{
                accept: `*`,
                actionUrl: `${$d.API_ROOT_URL}/customer/importCustomerData`,
            }" @resultBase64Image="resultBase64Image" @success="uploadSuccess" @error="uploadError" hideUploadTray />
        </div>
        <el-button type="primary" icon="el-icon-upload2" @click="d => $refs.sgDragUploadFolder.triggerUploadFile()">上传</el-button>

    </div>
</template>
    
<script>
import sgDragUploadFolder from "@/vue/components/admin/sgDragUploadFolder";
export default {
    components: {
        sgDragUploadFolder,
    },
    data() {
        return {
            src: '',
        }
    },
    methods: {
        resultBase64Image(d, f) {
            this.src = d;
        },
        uploadSuccess(d, f) { }, uploadError(d, f) { },
    }
};
</script>

基于sgUpload升级改版你挚爱的强哥_Vue.js,JavaScript&TypeScript,CSS2/CSS3/LESS/SASS/SCSS/HTML-CSDN博客你挚爱的强哥擅长Vue.js,JavaScript&TypeScript,CSS2/CSS3/LESS/SASS/SCSS/HTML,等方面的知识,你挚爱的强哥关注css,html5,scss,firefox,elementui,正则表达式,stylus,csrf,express,intellij-idea,ajax,tdesign,ecmascript,edge,java,postman,webpack,electron,html,sass,firebug,github,chrome,spring cloud,layui,visualstudio,less,javascript,fiddler,eclipse,angular.js,angular,前端框架,node.js,chrome devtools,css3,gulp,safari,idea,mybatis,arco design,前端,es6,vue.js,turbopack,bootstrap,webkit,postcss,jquery,anti-design-vue,intellij idea,visual studio code,react.js,webstorm,git,echarts,json,xss,typescript,sublime text,myeclipse,coffeescript,编辑器,npm,reactjs,xhtml,easyui,spring boot,vscode,yarn,view design领域.https://blog.csdn.net/qq_37860634 

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

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

相关文章

使用Gitee中的CI/CD来完成代码的自动部署与发布(使用内网穿透把本地电脑当作服务器使用)

&#x1f4da;目录 &#x1f4da;简介:⚙️ 所需工具&#xff1a;&#x1f4a8;内网穿透配置&#x1f4ad;工具介绍✨命令安装&#x1f38a;配置Cpolar&#x1f573;️关闭防火墙&#x1f95b;防火墙端口放行规则&#xff08;关闭防火墙可以忽略&#xff09;&#x1f36c;小章总…

WCF服务总结

前言 WCF,全称为Windows Communication Foundation,是一种用于构建分布式应用程序的微软框架。它提供了一种统一的编程模型,用于构建服务导向的应用程序,这些应用程序可以在本地或远程计算机上运行。WCF 支持多种传输协议和编码格式,并提供了高级安全性、可靠性和事务处理…

微软在 Perforce Helix 核心服务器中发现4个安全漏洞

微软分析师在对Perforce Helix的游戏开发工作室产品进行安全审查时&#xff0c;发现为游戏、政府、军事和技术等部门广泛使用的源代码管理平台 Perforce Helix Core Server 存在四大漏洞&#xff0c;并于今年 8 月底向 Perforce 报告了这些漏洞&#xff0c;其中一个漏洞被评为严…

路径规划之RRT *算法

系列文章目录 路径规划之Dijkstra算法 路径规划之Best-First Search算法 路径规划之A *算法 路径规划之D *算法 路径规划之PRM算法 路径规划之RRT算法 路径规划之RRT *算法 路径规划之RRT*算法 系列文章目录前言一、RRT算法1.起源2.改进2.1 重新选择父节点2.2 重新布线 3.对比…

day44代码训练|动态规划part06

完全背包和01背包问题唯一不同的地方就是&#xff0c;每种物品有无限件。 1. dp数组的含义 dp[i][j] 0-i物品&#xff0c;重量为j的容量时&#xff0c;最大的价值 2. 递推公式 dp[i][j] max(dp[i-1][j],dp[i][j-weight[i]]value[i]); 两种状态&#xff0c;不用物品i的话&…

【数论】质数

试除法判断质数 分解质因数 一个数可以被分解为质因数乘积 n &#xff0c;其中的pi都是质因数 那么怎么求pi及其指数呢&#xff1f; 我们将i一直从2~n/i循环&#xff0c;如果 n%i0&#xff0c;那么i一定是质因数&#xff0c;并且用一个while循环将n除以i&#xff0c;一直到…

蛇梯棋[中等]

一、题目 给你一个大小为n x n的整数矩阵board&#xff0c;方格按从1到n2编号&#xff0c;编号遵循 转行交替方式 &#xff0c;从左下角开始 &#xff08;即&#xff0c;从board[n - 1][0]开始&#xff09;每一行交替方向。玩家从棋盘上的方格1&#xff08;总是在最后一行、第…

礼品企业网站搭建的作用是什么

礼品一般分为企业定制礼品和零售现成礼品&#xff0c;二者都有很强的市场需求度。同时对礼品企业而言&#xff0c;一般主要以批发为主&#xff0c;客户也主要是零售商或企业。 1、拓客难 不同于零售&#xff0c;即使没有引流&#xff0c;入驻商场或街边小摊也总会有自然客户。…

【C++篇】Vector容器 Vector嵌套容器

文章目录 &#x1f354;简述vector&#x1f384;vector存放内置数据类型⭐创建一个vector容器⭐向容器里面插入数据⭐通过迭代器访问容器里面的数据⭐遍历&#x1f388;第一种遍历方式&#x1f388;第二种遍历方式&#x1f388;第三种遍历方式 &#x1f384;vector存放自定义数…

揭秘 Go 中 Goroutines 轻量级并发

理解 Goroutines、它们的效率以及同步挑战 并发是现代软件开发的一个基本概念&#xff0c;使程序能够同时执行多个任务。在 Go 编程领域&#xff0c;理解 Goroutines 是至关重要的。本文将全面概述 Goroutines&#xff0c;它们的轻量级特性&#xff0c;如何使用 go 关键字创建…

FPGA模块——以太网(1)MDIO读写

FPGA模块——以太网MDIO读写 MDIO接口介绍MDIO接口代码&#xff08;1&#xff09;MDIO接口驱动代码&#xff08;2&#xff09;使用MDIO驱动的代码 MDIO接口介绍 MDIO是串行管理接口。MAC 和 PHY 芯片有一个配置接口&#xff0c;即 MDIO 接口&#xff0c;可以配置 PHY 芯片的工…

Ubuntu 常用命令之 ifconfig 命令用法介绍

&#x1f4d1;Linux/Ubuntu 常用命令归类整理 ifconfig 是一个用于配置和显示 Linux 内核中网络接口的系统管理命令。它用于配置&#xff0c;管理和查询 TCP/IP 网络接口参数。 ifconfig 命令的参数有很多&#xff0c;以下是一些常见的参数 up&#xff1a;激活指定的网络接口…

Java学习系列(五)

1.继承 继承是java面向对象编程技术的一块基石&#xff0c;因为它允许创建分等级层次的类。 继承就是子类继承父类的特征和行为&#xff0c;使得子类对象&#xff08;实例&#xff09;具有父类的实例域和方法&#xff0c;或子类从父类继承方法&#xff0c;使得子类具有父类相…

实现单链表的基本操作(力扣、牛客刷题的基础笔试题常客)

本节来学习单链表的实现。在链表的刷题中&#xff0c;单链表占主导地位&#xff0c;很多oj题都在在单链表的背景下进行&#xff1b;而且很多链表的面试题都是以单链表为背景命题。所以&#xff0c;学好单链表的基本操作很重要 目录 一.介绍单链表 1.链表及单链表 2.定义一个…

生活中的物理2——人类迷惑行为(用笔扎手)

1实验 材料 笔、手 实验 1、先用手轻轻碰一下笔尖&#xff08;未成年人须家长监护&#xff09; 2、再用另一只手碰碰笔尾 你发现了什么&#xff1f;&#xff1f; 2发现 你会发现碰笔尖的手明显比碰笔尾的手更痛 你想想为什么 3原理 压强f/s 笔尖的面积明显比笔尾的小 …

C#文件操作(二)

一、前言 文章的续作前文是&#xff1a; C#文件操作&#xff08;一&#xff09;-CSDN博客https://blog.csdn.net/qq_71897293/article/details/135117922?spm1001.2014.3001.5501 二、流 流是序列化设备的抽象表示序列化设备可以线性方式储存数据并可按照同样的方式访问一次…

【QT】QGraphicsView和QGraphicsItem坐标转换

坐标转换 QGraphicsItem和QGraphicsView之间的坐标转换需要通过QGraphicsScene进行转换 QGraphicsView::mapToScene() - 视图 -> 场景QGraphicsView::mapFromScene() - 场景 -> 视图QGraphicsItem::mapToScene() - 图元 -> 场景QGraphicsItem::mapFromScene() - 场景 …

Java异常类分类,所有子类的父类是什么

1.异常的层次机构&#xff1a; 所有异常的父类是Throwable&#xff0c;它有两个子类&#xff0c;分别是Error和Exception。 2.Error&#xff1a; 表示系统错误&#xff0c;通常不能处理和恢复。比如StackOverFlowError或者OutOfMemoryError&#xff0c;出了问题只能结束程序…

【项目问题解决】% sql注入问题

目录 【项目问题解决】% sql注入问题 1.问题描述2.问题原因3.解决思路4.解决方案1.前端限制传入特殊字符2.后端拦截特殊字符-正则表达式3.后端拦截特殊字符-拦截器 5.总结6.参考 文章所属专区 项目问题解决 1.问题描述 在处理接口入参的一些sql注入问题&#xff0c;虽然通过M…

【matlab】绘制竖状双组渐变柱状图

【matlab】绘制竖状双组渐变柱状图