广告
返回顶部
首页 > 资讯 > 前端开发 > html >vue 中怎么利用webuploader 实现文件上传功能
  • 462
分享到

vue 中怎么利用webuploader 实现文件上传功能

2024-04-02 19:04:59 462人浏览 独家记忆
摘要

今天就跟大家聊聊有关Vue 中怎么利用WEBuploader 实现文件上传功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、封装组件引入百度提供

今天就跟大家聊聊有关Vue 中怎么利用WEBuploader 实现文件上传功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

一、封装组件

引入百度提供的webuploader.js、Uploader.swf

CSS样式就直接写在组件里面了 

<template>
 <div>
  <div id="list" class="uploader-list"></div>
  <div id="wrapper">
   <div class="uploader-container">
    <div :id="id" limitSize="1" :ext="ext"></div>
    <el-button  size="small" :loading="uploadLoading" type="success" @click="start">上传到服务器</el-button>
    <el-button  :disabled="stopBtn" size="small" type="danger" @click="stop">暂停上传</el-button>
   </div>
  </div>
  <div class="el-upload__tip">{{tip}}</div>
  <div class="file-list">
   <ul class="el-upload-list el-upload-list&#45;&#45;text">
    <li v-for="file in fileList" :class="['el-upload-list__item', 'is-' + file.status]" :key="file">
     <a class="el-upload-list__item-name">
      <i class="el-icon-document"></i>{{file.name}}
     </a>
     <label class="el-upload-list__item-status-label">
      <i :class="{'el-icon-upload-success': true,'el-icon-circle-check': listType === 'text',
     'el-icon-check': ['picture-card', 'picture'].indexOf(listType) > -1}"></i>
     </label>
     <i class="el-icon-close" @click="removeFile(file)"></i>
     <el-progress
      v-if="file.status === 'uploading'"
      :type="listType === 'picture-card' ? 'circle' : 'line'"
      :stroke-width="listType === 'picture-card' ? 6 : 2"
      :percentage="file.percentage">
     </el-progress>
    </li>
   </ul>
  </div>
 </div>
</template>
<script>
 import '../js/Jquery.js'
 import '../js/webuploader.js'
 import { Base64 } from 'js-base64'
 import CryptoJS from 'crypto-js';

 export default{
  name: 'fileUpload',
  props: {
   id: {
    type: String,
    default: function(){
     return "filePicker";
    }
   },
   //上传提示
   tip: {
    type: String,
    default: function(){
     return "";
    }
   },
   //文件后缀名限制
   ext: {
    type: String,
    default: function(){
     return "jpg,jpeg,png,pdf,mp4,avi.mp3";
    }
   },
   //分片大小设置
   chunkSize: {
    type: Number,
    default: function(){
     return 2097152;
    }
   },
   //分片上传重试次数
   chunkRetry: {
    type: Number,
    default: function(){
     return 1;
    }
   },
   //是否自动上传
   auto: {
    type: Boolean,
    default: function(){
     return false;
    }
   },
   //上传文件大小限制
   sizeLimit: {
    type: Number,
    default: function(){
     return 209715200;
    }
   },
   //上传文件数量限制
   countLimit: {
    type: Number,
    default: function(){
     return 5;
    }
   }
  },
  data(){
   return{
    appId: AppConfig.appId,
    securityKey: AppConfig.securityKey,
    checkUrl: AppConfig.checkUrl,
    uploadUrl: AppConfig.uploadUrl,
    mergeUrl: AppConfig.mergeUrl,
    previewName: '选择文件',
    wul_fileMd5: '',
    wul_size: 0,
    wul_fileName: '',
    wul_chunk: 0,
    wul_uploader: '',
    fileList: [],
    listType: 'text',
    percentage: 0,
    fileObject: {
     uid: '',
     name: '',
     ext: '',
     type: '',
     status: '',
     percentage: 0,
     url: ''
    },
    uploadLoading: false,
    stopBtn: true
   }
  },
  methods: {
   
   getFileList: function(){
    return this.fileList;
   },
   //绑定事件
   wul_init: function() {
    //提示只能选择一个文件
    this.wul_uploader.on('filesQueued', function (files) {
     if (files.length > 1) {
      this.$message({
       message: '请选择一张图片',
       type: 'error'
      });
      for (var i = 0; i < files.length; i++) {
       this.wul_uploader.cancelFile(files[i]);
      }
      this.wul_uploader.reset();
      this.wul_fileMd5 = "";
      this.wul_size = 0;
      this.wul_fileName = "";
      this.wul_chunk = 0;  //当前切片数
     }else{
      if( this.fileList.length == this.countLimit ){
       this.$message({
        message: '已经达到上传文件限制数量',
        type: 'error'
       });
      }else{
       //此时往需要上传的文件列表中添加文件
       let file = {
        uid: Date.now() + this.tempIndex++,
        name: files[0].name,
        type: files[0].type,
        ext: files[0].ext,
        status: "ready",
        percentage: 0
       }
       this.fileObject = file;
       this.fileList.push(this.fileObject);
      }
     }
    }.bind(this));

    //文件校验格式和大小
    this.wul_uploader.on('error', function (type) {
      debugger
     if (type == 'Q_EXCEED_SIZE_LIMIT') {
      this.$message({
       message: '文件超过指定大小',
       type: 'error'
      });
     }
     if (type == 'Q_TYPE_DENIED') {
      this.$message({
       message: '文件格式错误,请选择文件',
       type: 'error'
      });
     }
     if (type == 'F_EXCEED_SIZE') {
      this.$message({
       message: "文件超过" + this.sizeLimit / 1024 / 1024 + "M",
       type: 'error'
      });
     }
    }.bind(this));

    //上传进度
    this.wul_uploader.on('uploadProgress', function (file, percentage) {
     this.percentage = percentage * 100;
     this.fileObject.status = "uploading";
     this.fileObject.percentage = this.percentage;
     console.log(this.fileObject.percentage);
    }.bind(this));

    //每次切片上传完成之后的判断
    this.wul_uploader.on('uploadAccept', function (object, ret) {
     if (ret.responseCode != 0) {
      this.wul_uploader.cancelFile(this.wul_uploader.getFiles()[0].id);
     }
    });

    this.wul_uploader.on('uploadBeforeSend', function(object, data, headers) {
     console.log(data);
    });
   },

   option: function(key, val) {
    this.wul_uploader.option(key, val);
    var options = this.wul_uploader.options;
    this.wul_uploader.destroy();  //注销uploader
    this.wul_uploader = WebUploader.create(options);
    this.wul_init();
   },
   start: function(){
    if(this.wul_uploader.getFiles()[0] != null) {
     this.wul_uploader.upload(this.wul_uploader.getFiles()[0].id);
     this.uploadLoading = true;
     this.stopBtn = false;
    } else {
     this.$message({
      message: "请选择上传文件",
      type: 'error'
     });
    }
   },
   stop: function(){
    this.wul_uploader.cancelFile(this.wul_uploader.getFiles()[0].id);
   },
   removeFile: function(file){
    this.fileList.splice(this.fileList.indexOf(file), 1);
   },
   change: function(){
    this.option('accept', {
     title: 'Images',
     extensions: 'gif,jpg,jpeg,bmp,png'
    });
   }
  },
  mounted(){
   WebUploader.Uploader.reGISter({
    "before-send-file": "beforeSendFile",
    "before-send": "beforeSend",
    "after-send-file": "afterSendFile",
   }, {
    beforeSendFile: function (file) {
     var deferred = WebUploader.Deferred();
     this.wul_uploader.md5File(file).then(function (val) {
      this.wul_fileMd5 = val;
      this.wul_size = file.size;
      this.wul_fileName = file.name;
      var timestamp = Date.parse(new Date()) / 1000;
      var signParam = "{chunkSize=" + this.chunkSize + ", fileMd5=" + this.wul_fileMd5 + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}";
      var sign = Base64.encode(CryptoJS.HMacSHA1(signParam, this.securityKey));
      // 获取断点续传位置
      jQuery.ajax({
       type: "POST",
       // 测试
       url: this.checkUrl,
       data: {
        // 文件大小
        size: this.wul_size,
        // 文件唯一标记
        fileMd5: this.wul_fileMd5,
        // 切片大小
        chunkSize: this.chunkSize,
        // 签名
        sign: sign,
        // 应用分配id
        appId: this.appId,
        // 当前时间戳
        timestamp: timestamp

       },
       dataType: "JSON",
       // 上传失败
       error: function (XMLHttpRequest, textStatus, errorThrown) {
        this.$message({
         message: "上传失败...",
         type: 'error'
        });
        this.uploadLoading = false;
        this.stopBtn = true;
       }.bind(this),
       success: function (response) {
        if (response.responseCode == 0) { // 切片获取成功
         this.wul_chunk = response.chunk;
         deferred.resolve();
        } else { // 切片获取失败,请求成功
         this.wul_uploader.cancelFile(file);  //取消文件上传
         this.$message({
          message: "切片检查失败,请联系管理员",
          type: 'error'
         });
         deferred.resolve();
         this.uploadLoading = false;
         this.stopBtn = true;
        }
       }.bind(this)
      });
      return deferred.promise();
     }.bind(this));
     return deferred.promise();
    }.bind(this),
    beforeSend: function (block) {
     var deferred = WebUploader.Deferred();
     if (block.chunk < this.wul_chunk) {
      return deferred.reject();
     }
     this.wul_uploader.md5File(block.blob).then(function (chunkMd5) {
      var timestamp = Date.parse(new Date()) / 1000;
      var signParam = '{chunk=' + block.chunk + ', chunkMd5=' + chunkMd5 + ', chunkSize=' + this.chunkSize + ', fileMd5=' + this.wul_fileMd5 + ', size=' + this.wul_size + ', timestamp=' + timestamp + '}';
      var signTemp = CryptoJS.HmacSHA1(signParam, this.securityKey);
      var sign = Base64.encode(signTemp);  //获取sign值
      this.wul_uploader.options.fORMData = {
       'timestamp': timestamp,
       'appId': this.appId,
       'chunk': block.chunk,
       'chunkSize': this.chunkSize,
       'fileMd5': this.wul_fileMd5,
       'chunkMd5': chunkMd5,
       'size': this.wul_size,
       'sign': sign
      };
      deferred.resolve();
     }.bind(this))
     return deferred.promise();
    }.bind(this),
    afterSendFile: function (file) {
     var timestamp = Date.parse(new Date()) / 1000;
     var signParam = "{chunkSize=" + this.chunkSize + ", fileMd5=" + this.wul_fileMd5 + ", fileName=" + file.name + ", size=" + this.wul_size + ", timestamp=" + timestamp + "}";
     var sign = Base64.encode(CryptoJS.HmacSHA1(signParam, this.securityKey));
     // 如果分块上传成功,则通知后台合并分块
     jQuery.ajax({
      type: "POST",
      url: this.mergeUrl,
      data: {
       appId: this.appId,
       fileMd5: this.wul_fileMd5,
       fileName: file.name,
       chunkSize: this.chunkSize,
       sign: sign,
       size: this.wul_size,
       timestamp: timestamp
      },
      success: function (response) {
       if (response.responseCode == 0) {
        this.fileObject.status = "success";
        this.fileObject.percentage = 100;
        this.fileObject.url = response.filePath;
       } else {
        this.fileObject.status = "exception";
        this.$message({
         message: "上传失败,失败原因:" + response.responseMsg,
         type: 'error'
        });
       }
       this.uploadLoading = false;
       this.stopBtn = true;
       this.wul_uploader.reset();
       this.wul_fileMd5 = "";
       this.wul_size = 0;
       this.wul_fileName = "";
       this.wul_chunk = 0;  //当前切片数
      }.bind(this)
     });
    }.bind(this)
   });
   this.wul_uploader = WebUploader.create({
    // swf文件路径
    swf: '../js/Uploader.swf',
    // 文件接收服务端。
    server: this.uploadUrl,
    // 定义选择按钮
    pick: {
     "id": "#" + this.id,
     "innerhtml": this.previewName
    },
    // 自动上传
    auto: this.auto,
    // 禁止浏览器打开文件
    disableGlobalDnd: true,
    // 添加截图功能
    paste: '#wrapper',
    // 定义拖动面板
    dnd: '#wrapper',
    // 分片上传
    chunked: true,
    // 分片大小为2M
    chunkSize: this.chunkSize,
    // 分片上传失败重试次数
    chunkRetry: this.chunkRetry,
    // 图片不做压缩
    compress: false,
    // 队列设置10个,为了选择多个文件的时候能提示
    fileNumLimit: 10,
    // 提前准备好下一个文件
    prepareNextFile: true,
    // 限制单个文件大小
    fileSingleSizeLimit: this.sizeLimit,
    //线程数
    threads : 1,
    // 限制格式
    accept: {
     title: "access",
     extensions: this.ext
    }
   });
   this.wul_init();
  }
 }
</script>
<style>
 
 html, body, div, span, applet, object, iframe, h2, h3, h4, h5, h6, h7, p, blockquote, pre,
 a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp,
 small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li,
 fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td,
 article, aside, canvas, details, fiGCaption, figure, footer, header, hgroup, menu, nav, section, summary,
 time, mark, audio, video, input {
  margin: 0;
  padding: 0;
  border: none;
  outline: 0;
  font-size: 100%;
  font: inherit;
  vertical-align: baseline;
 }

 html, body, form, fieldset, p, div, h2, h3, h4, h5, h6, h7 {
  -webkit-text-size-adjust: none;
 }

 article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {
  display: block;
 }

 body {
  font-family: arial, sans-serif;
 }

 ol, ul {
  list-style: none;
 }

 blockquote, q {
  quotes: none;
 }

 blockquote:before, blockquote:after, q:before, q:after {
  content: '';
  content: none;
 }

 ins {
  text-decoration: none;
 }

 del {
  text-decoration: line-through;
 }

 table {
  border-collapse: collapse;
  border-spacing: 0;
 }

 
 #wrapper {
  width: 100%;
  margin: 0 auto;
  height: 35px;
 }

 .img-preview {
  width: 160px;
  height: 90px;
  margin-top: 1em;
  border: 1px solid #ccc;
 }

 .cropper-wraper {
  position: relative;
 }

 .upload-btn {
  background: #ffffff;
  border: 1px solid #cfcfcf;
  color: #565656;
  padding: 10px 18px;
  display: inline-block;
  border-radius: 3px;
  margin-left: 10px;
  cursor: pointer;
  font-size: 14px;

  position: absolute;
  right: 1em;
  bottom: 2em;
 }
 .upload-btn:hover {
  background: #f0f0f0;
 }
 .uploader-container{
  width: 100%;
  font-size: 10px;
 }

 .webuploader-container {
  position: relative;
  width: 100px;
  height: 21px;
  float: left;
 }
 .webuploader-element-invisible {
  position: absolute !important;
  clip: rect(1px 1px 1px 1px); 
  clip: rect(1px,1px,1px,1px);
 }
 .webuploader-pick {
  position: relative;
  display: inline-block;
  cursor: pointer;
  background: #00b7ee;
  padding: 6px 15px;

  color: #fff;
  text-align: center;
  border-radius: 3px;
  overflow: hidden;
 }
 .webuploader-pick-hover {
  background: #00a2d4;
 }

 .webuploader-pick-disable {
  opacity: 0.6;
  pointer-events:none;
 }
 .file-list{
  width: 100%;
 }
</style>

二、导出组件

var fileUpload = require('./src/file_upload.vue');

module.exports = {
 fileUpload
}

三、demo  引用方式

<template>
 <div>
  <el-card class="box-card">
   <fileUpload ref="fileUpload" :ext="ext" :countLimit="5" :tip="tip">aaa</fileUpload>
  </el-card>
 </div>
</template>
<script>
 import {fileUpload} from '@/components/fileUpload/index.js'
 export default{
  name: 'hello',
  components: {fileUpload},
  data(){
   return{
    fileList: [],
    ext: 'png,jpg,jpeg,mp3,mp4,pdf',
    tip: '可上传png/jpg/jpeg/mp3/mp4/pdf,大小不超过200M'
   }
  },
  created(){

  },
  methods: {
   getFileList: function(){
    this.fileList = this.$refs.fileUpload.getFileList();
    console.log(this.fileList);
   }
  }
 }
</script>

四、运行效果图

vue 中怎么利用webuploader 实现文件上传功能

vue 中怎么利用webuploader 实现文件上传功能

vue 中怎么利用webuploader 实现文件上传功能

看完上述内容,你们对vue 中怎么利用webuploader 实现文件上传功能有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程网html频道,感谢大家的支持。

--结束END--

本文标题: vue 中怎么利用webuploader 实现文件上传功能

本文链接: https://www.lsjlt.com/news/72216.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • vue 中怎么利用webuploader 实现文件上传功能
    今天就跟大家聊聊有关vue 中怎么利用webuploader 实现文件上传功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。一、封装组件引入百度提供...
    99+
    2022-10-19
  • WebUploader+SpringMVC实现文件上传功能
    WebUploader是由Baidu团队开发的一个简单的以HTML5为主,FLASH为辅的现代文件上传组件。在现代的浏览器里面能充分发挥html5的优势,同时又不摒弃主流IE浏览器,沿用原来的FLASH运行时,兼容IE6+,iOS 6+, ...
    99+
    2023-05-31
    webuploader springmvc 文件上传
  • 在Java项目中使用WebUploader如何实现一个文件上传功能
    本篇文章为大家展示了在Java项目中使用WebUploader如何实现一个文件上传功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。之前自己写小项目的时候也碰到过文件上传的问题,没有找到很好的解决方...
    99+
    2023-05-31
    java webuploader 文件上传
  • 利用servlet怎么实现一个文件上传功能
    本篇文章为大家展示了利用servlet怎么实现一个文件上传功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。1.servlet上传文件  servlet上传文件就是将客户端的文件上传到服务器端。  ...
    99+
    2023-05-31
    servlet 一个文件
  • 利用hadoop怎么实现一个文件上传功能
    本篇文章给大家分享的是有关利用hadoop怎么实现一个文件上传功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。hdfs上的文件是手动执行命令从本地linux上传至hdfs的。...
    99+
    2023-05-31
    hadoop
  • 利用Struts怎么实现一个文件上传功能
    这期内容当中小编将会给大家带来有关利用Struts怎么实现一个文件上传功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。1.在jsp文件中进行定义<form action="/Struts...
    99+
    2023-05-31
    struts st
  • JavaScript中如何使用webuploader实现上传视频功能
    这篇文章主要为大家展示了“JavaScript中如何使用webuploader实现上传视频功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“JavaScript...
    99+
    2022-10-19
  • nodejs中怎么利用express实现一个文件上传功能
    这期内容当中小编将会给大家带来有关nodejs中怎么利用express实现一个文件上传功能,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。步骤:(1)使用express创建...
    99+
    2022-10-19
  • vue怎么使用el-upload实现文件上传功能
    这篇文章主要介绍了vue怎么使用el-upload实现文件上传功能的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue怎么使用el-upload实现文件上传功能文章都会有所收获,下面我们一起来看看吧。uploa...
    99+
    2023-06-30
  • Vue Element UI + OSS怎么实现上传文件功能
    这篇文章主要为大家展示了“Vue Element UI + OSS怎么实现上传文件功能”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Vue Element UI...
    99+
    2022-10-19
  • 利用Spring MVC怎么实现一个文件上传功能
    本篇文章给大家分享的是有关利用Spring MVC怎么实现一个文件上传功能,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。使用Spring MVC 上传文件必须配置文件解析器,如...
    99+
    2023-05-31
    springmvc 一个文件
  • vue+axios+java实现文件上传功能
    本文实例为大家分享了vue+axios+java实现文件上传的具体代码,供大家参考,具体内容如下 背景 vue.js + axios + element前端,Java后台实现的文件上...
    99+
    2022-11-13
  • Jeeplus-vue 实现文件的上传功能
    前端 一、uploadList.vue ① 首先在页面中添加一个放置图片的位置,来展示图片 <el-table-column prop="upload" labe...
    99+
    2022-11-13
  • HTML5中怎么实现文件上传功能
    HTML5中怎么实现文件上传功能,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。用的技术主要是:ajaxFileReaderFo...
    99+
    2022-10-19
  • Flex2.0中怎么实现文件上传功能
    Flex2.0中怎么实现文件上传功能,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。Flex2.0从零开始实现文件上传以前在Flex1.5的时候也做过,不过当初使...
    99+
    2023-06-17
  • vue中实现图片和文件上传功能
    本篇文章为大家展示了vue中实现图片和文件上传功能,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。html页面<input type="fi...
    99+
    2022-10-19
  • 利用servlet怎么实现一个文件上传下载功能
    利用servlet怎么实现一个文件上传下载功能?相信很多没有经验的人对此束手无策,为此本文总结了问题出现的原因和解决方法,通过这篇文章希望你能解决这个问题。一、准备工作:  1.1 文件上传插件:uploadify;  1.2 文件上传所需...
    99+
    2023-05-31
    servlet 一个文件
  • Springboot文件上传功能怎么实现
    这篇文章主要介绍了Springboot文件上传功能怎么实现的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Springboot文件上传功能怎么实现文章都会有所收获,下面我们一起来看看吧。1.新建文件上传页面在st...
    99+
    2023-07-06
  • Ajax怎么实现文件上传功能
    这篇文章主要介绍“Ajax怎么实现文件上传功能”,在日常操作中,相信很多人在Ajax怎么实现文件上传功能问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Ajax怎么实现文件上传...
    99+
    2022-10-19
  • javaweb怎么实现文件上传功能
    本文小编为大家详细介绍“javaweb怎么实现文件上传功能”,内容详细,步骤清晰,细节处理妥当,希望这篇“javaweb怎么实现文件上传功能”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。创建文件上传页面<%...
    99+
    2023-07-02
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作