微信小程序组件:图片、视频、语音上传

该组件封装了图片、视频、语音上传功能,也是最近开发过程中的一个收获,如图:

微信小程序组件:图片、视频、语音上传

组件相关代码

uploader.wxml

<scroll-view class="upload-file" scroll-y="true">
  <view class="audio-container" wx:if="{{voice}}">
    <view class="audio-area" bindtap="voicePlay">
      <image src="{{voiceIco}}" mode="widthFix" class="voice-play"></image>
      <text class="duration">{{durationShow}}</text>
    </view>
  </view>
  <view class="video-area" wx:if="{{video}}">
    <view class="video-container">
      <video id="myVideo" class="vd-show" src="{{video.url}}" binderror="videoErrorCallback" show-center-play-btn='true'
        show-play-btn="{{true}}" controls picture-in-picture-mode="{{['push', 'pop']}}"
        bindenterpictureinpicture='bindVideoEnterPictureInPicture'
        bindleavepictureinpicture='bindVideoLeavePictureInPicture'></video>
      <view class="del" bindtap="delVideo">
        <image src="/images/del.png" class="del-ico"></image>
      </view>
    </view>
  </view>
  <view class="image-area" wx:if="{{images.length>0}}">
    <block wx:for="{{images}}" wx:key="index">
      <view class="up-img">
        <image class="uploader-img" src="{{item.url}}" mode="aspectFill"></image>
        <view class="del" data-index="{{index}}" bindtap="delThisImg">
          <image src="/images/del.png" class="del-ico"></image>
        </view>
      </view>
    </block>

    <view class="upload-add" bindtap="uploadImage">
      <image class="uploader-img" src="../../images/upadd.png" mode="aspectFill"></image>
    </view>
  </view>
</scroll-view>
<view class="foot" wx:if="{{send}}">
  <view class="upload-area">
    <view bindtap="uploadImage" class="up-item">
      <image src="../../images/pic.png" mode="widthFix" class="up-ico"></image>
    </view>
    <view bindtap="uploadVideo" class="up-item">
      <image src="../../images/video.png" mode="widthFix" class="up-ico"></image>
    </view>
    <view bindtap="showAudioIn" class="up-item">
      <image src="../../images/icon_voice.png" mode="widthFix" class="up-ico"></image>
    </view>
    
    <slot name="at"></slot>
  </view>
  
  <slot name="btn"></slot>
</view>

<view class="audio-in" wx:if="{{audioIn}}">
  <button bindtouchstart="streamRecord" bindtouchend="streamRecordEnd">按住说话</button>
</view>

uploader.wxss

.foot {
  display: flex;
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 20rpx;
  border-top: 1px solid #eaeaea;
}

.upload-area {
  flex: 1;
}

.up-item {
  float: left;
  margin-right: 30px;
}

.up-ico {
  width: 30px;
}

.up-text {
  display: block;
  font-size: 14px;
}

.audio-in {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 40px;
  background-color: #ccc;

}

.image-area {
  width: 100%;
}

.image-area:after {
  content: "";
  display: block;
  visibility: hidden;
  clear: both;
}

.up-img {
  position: relative;
  float: left;
  margin-right: 8px;
}

.uploader-img {
  width: 84px;
  height: 84px;
}

.del {
  position: absolute;
  right: 0;
  top: 0;
  background-color: rgba(0, 0, 0, 0.8);
  width: 20px;
  height: 20px;
}

.del-ico {
  margin-top: 2px;
  margin-left: 2px;
  width: 16px;
  height: 16px;
}

.upload-add {
  float: left;
  margin-bottom: 8px;
  width: 84px;
  height: 84px;
  box-sizing: border-box;
  background-color: #EDEDEd;
  text-align: center;
  vertical-align: middle;
}

.audio-container {
  width: 100%;
  height: 50px;
}

.audio-area {
  position: relative;
  width: 200px;
  height: 36px;
  background-color: #21c38f;
  border-radius: 18px;
  margin-bottom: 10px;
}

.voice-play {
  width: 20px;
  height: 20px;
  margin-top: 8px;
  margin-left: 10px;
}

.duration {
  position: absolute;
  margin-left: 10px;
  height: 36px;
  line-height: 36px;
  font-size: 14px;
  font-family: microsoft yahei;
}

.video-container {
  width: 60%;
  position: relative;
}

.vd-show {
  width: 100%;
}

.getted {
  color: #222;
  max-width: 60%;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

.upload-file{
  height: 300px;
}

uploader.js

const $util = require('../../utils/util.js')

const plugin = requirePlugin("WechatSI")
const manager = plugin.getRecordRecognitionManager()

let app = getApp()
Component({
  lifetimes: {
    attached: function () {
      // 初始化录音
      this.initRecord()

      this.innerAudioContext = wx.createInnerAudioContext();
      this.innerAudioContext.onError(function (res) {
        wx.showToast({
          title: '语音播放失败',
          icon: 'none'
        })
      })
    }
  },
  options: {
    multipleSlots: true
  },
  /**
   * 组件的属性列表
   */
  properties: {
    images: Array,
    video: Object,
    voice: Object,
    duration: Number,
    durationShow: String
  },

  /**
   * 组件的初始数据
   */
  data: {
    info: null,
    recordStatus: false, //录音状态
    audioIn: false,
    send: true,
    images: [],
    desc: '',
    duration: 0,
    voice: null,
    video: null,
    voiceIco: '../../images/voice.png',
    durationShow: ''
  },

  /**
   * 组件的方法列表
   */
  methods: {

    /**
     * 按住按钮开始语音识别
     */
    streamRecord: function (e) {
      console.log("streamrecord", e)

      this.setData({
        //录音状态      
        recordStatus: true
      })
      //开始识别    
      manager.start({
        lang: 'zh_CN', //识别的语言,目前支持zh_CN en_US zh_HK sichuanhua
        duration: 30000, //指定录音的时长,单位ms,最大为60000。如果传入了合法的 duration ,在到达指定的 duration 后会自动停止录音
      })

    },

    /**
     * 松开按钮结束语音识别
     */
    streamRecordEnd: function (e) {
      console.log("streamRecordEnd", e)

      manager.stop()

      this.hideAudioIn()
    },

    /**
     * 初始化语音识别回调
     * 绑定语音播放开始事件
     */
    initRecord: function () {
      console.log('==语音初始化==', manager)
      let that = this
      manager.onRecognize = function (res) {
        console.log("current result", res.result)
      }
      manager.onStart = function (res) {
        console.log("成功开始录音识别", res)
      }
      manager.onStop = function (res) {
        console.log('..............结束录音')
        console.log('录音临时文件地址 -->' + res.tempFilePath);
        console.log('录音总时长 -->' + res.duration + 'ms');
        console.log('文件大小 --> ' + res.fileSize + 'B');
        console.log('语音内容 --> ' + res.result);

        if (res.result == '') {
          wx.showModal({
            title: '提示',
            content: '听不清楚,请重新说一遍!',
            showCancel: false,
            success: function (res) {}
          })
          return;
        }

        that.setData({
          desc: res.result,
          duration: res.duration,
          durationShow: $util.formatDuration(res.duration)
        })

        that.uploadFile('mp3', res.tempFilePath, 'voice')

      }
      manager.onError = function (res) {
        console.error("error msg", res.msg)
      }
    },
    // 上传图片
    uploadImage: function () {
      let that = this

      wx.chooseImage({
        success(res) {
          const tempFilePaths = res.tempFilePaths
          tempFilePaths.forEach((element, index) => {
            that.uploadFile('jpg,gif,png', element, 'image')
          });

        }
      })
    },

    showAudioIn: function () {
      this.setData({
        audioIn: true,
        send: false
      })
    },
    hideAudioIn: function () {
      this.setData({
        audioIn: false,
        send: true
      })
    },
    voicePlay: function () {
      let that = this
      this.innerAudioContext.src = this.data.voice.url; //链接到音频的地址
      this.innerAudioContext.onPlay(() => {
        that.setData({
          voiceIco: '../../images/play.gif'
        })
      }); //播放音效
      this.innerAudioContext.onEnded(() => {
        that.setData({
          voiceIco: '../../images/voice.png'
        })
      });

      this.innerAudioContext.play()

    },
    // 上传视频
    uploadVideo: function () {
      var that = this;
      wx.chooseVideo({
        maxDuration: 60,
        success: function (res) {
          var tempFilePath = res.tempFilePath;

          // 上传文件
          that.uploadFile('mp4', tempFilePath, 'video')
        },
        fail: function (e) {
          console.error(e);
        }
      })
    },
    // 上传文件到服务器
    uploadFile: function (uploadExt, path, type) {
      let that = this
      wx.uploadFile({
        url: app.globalData.domain + 'base/upload/index?type=' + uploadExt,
        filePath: path,
        name: 'file',
        formData: {},
        success(res) {
          let ret = JSON.parse(res.data)
          console.log(ret)
          if (type == 'image') {
            let images = that.data.images
            images.push(ret.data)
            that.setData({
              images: images
            })
          }
          if (type == 'video') {
            that.setData({
              video: ret.data
            })
          }
          if (type == 'voice') {
            that.setData({
              voice: ret.data,
            })
          }

          that.triggerEvent("myevent", that.data)
        }
      })
    },

    addLocation: function () {
      let that = this
      qqmapsdk.reverseGeocoder({
        success: function (res) {
          console.log(res.result);
          that.setData({
            location: res.result.address,
            lat: res.result.location.lat,
            lng: res.result.location.lng
          })
        }
      })
    },
    // 删除上传的图片
    delThisImg: function (e) {
      let that = this
      let images = that.data.images

      images.splice(e.currentTarget.dataset.index, 1);
      that.setData({
        images: images
      })

      that.triggerEvent("myevent", that.data)
    },
    delVideo: function () {
      this.setData({
        video: null
      })

      this.triggerEvent("myevent", this.data)
    }
  }
})

调用组件相关代码

json文件

{
  "usingComponents": {
    "uploader": "/components/uploader/uploader"
  }
}

wxml文件

  <uploader images="{{images}}" voice="{{voice}}" duration="{{duration}}" durationShow="{{durationShow}}"
    video="{{video}}" bindmyevent="getFiles">
    <button slot="btn" class="btn" form-type="submit" size="mini" style="width:60px;">发布</button>
  </uploader>

js文件

// 获取上传的文件
getFiles: function(e){
    this.setData({
      images: e.detail.images,
      audio: e.detail.voice,
      duration: e.detail.duration,
      video: e.detail.video
    })
  }

 

上一篇:Shell-实际业务操作03


下一篇:Educoder -Java面向对象- 封装、继承和多态(第2关:什么是继承,怎样使用继承)