1、打开hbuildx,新建一个uniapp项目,选默认模板就可以,然后运行项目就可以选择

2、 下载安装微信/支付宝开发者工具,并且记住安装的路径!

安装好以后在hbuildx的工具-->设置-->运行配置-->开发工具路径里,将路径填上(选上),这样就可以运行项目了,这时运行微信小程序项目会在开发者工具上报错,是因为没有AppID

3、注册微信小程序

百度:微信公众平台-->账号分类-->小程序,滑到最底,注册,这里有个大坑,就是一个邮箱只能注册一个小程序,一旦注册就不能注销啦。注册好微信小程序前期就要一个AppID就好。微信公众平台扫码-->微信选择登录的小程序-->开发-->开发管理-->开发设置下就可以找到AppID了,把他复制下来到项目-->manifest.json-->微信小程序配置

4、注册支付宝小程序

百度:支付宝开放平台-->创建应用,然后支付宝小程序的AppID跟微信小程序的不一样,位置也不一样,看图,然后复制下来,到hbuildx的项目-->manifest.json-->支付宝小程序配置

配置好APPID并不是万事大吉啦。要运行到支付宝开发工具必须选对文件夹~~这块也算一个小坑啦。点击运行-->选择支付宝小程,然后等待编译,编译完成以后,你的支付宝开发工具就会自动打开,但是你的项目不会。现在需要点击右上角-->选择项目-->选择你的项目文件夹-->打开-->选择unpackage-->选择dist-->选择dev-->选择mp-alipay。这样就可以在支付宝开发工具打开项目了,不用担心,这边写了代码,支付宝开发工具上也是自动更新的

上面基本步骤就跑通了,下面就可以写代码了。

5、写代码

不分前后,想起一个更新一个

1、uni.request()在支付宝不能解析responseType(文档上说了),换成my.request,还好我把请求封装了一下,不然真是大麻烦了,下面是uniapp网络请求的封装

(1)根目录下新建utils文件夹,建index.js内容如下

const baseUrl = 'https://xxxx.com' // 全局地址import store from '../store/index.js'// 用来记录登录状态,统一封装tokenmodule.exports = (params) => {let url = params.url; // 请求地址 例如:/api/order/payRevertDevicelet method = params.method; // 请求方法let header = params.header || {}; // 请求头let data = params.data || {}; // 参数let isjson = params.isjson || false // 请求头的content-type,默认不是'application/json',如果需要就设置为TRUEif (method) {method = method.toUpperCase(); //   小写转大写}// 请求类型if (!isjson) {header = {"content-type": "application/x-www-form-urlencoded"}}else {header = {"content-type": "application/json"}}// 获取登录状态let token = store.state.tokenif (token) {header = {...header,token}}//   发起请求 加载动画if (!params.hideLoading) {uni.showLoading({title: "加载中"})}// 支付宝小程序不支持res,用my.request// #ifdef MP-ALIPAYmy.request({url: baseUrl + url,method: method || "GET",data: data,headers: header,dataType: 'json',success: res => {console.log(res,'http request res')if (res.statusCode && res.statusCode != 200) {//   api错误uni.showModal({// content: res.msgcontent: res})return;}typeof params.success == "function" && params.success(res.data);},fail: err => {uni.showModal({content: err})typeof params.fail == "function" && params.fail(err.data);},complete: (e) => {// console.log("请求完成");uni.hideLoading()typeof params.complete == "function" && params.complete(e.data);return;}})// #endif// 微信小程序的请求 // #ifdef MP-WEIXIN//  发起网络请求uni.request({url: baseUrl + url,method: method || "GET",header: header,data: data,dataType: "json",sslVerify: false, //  是否验证ssl证书success: res => {if (res.statusCode && res.statusCode != 200) {// api错误uni.showModal({content: res.msg})return;}typeof params.success == "function" && params.success(res.data);},fail: err => {uni.showModal({content: err.msg})typeof params.fail == "function" && params.fail(err.data);},complete: (e) => {// console.log("请求完成");uni.hideLoading()typeof params.complete == "function" && params.complete(e.data);return;}})//  #endif
}

(2)在main.js里挂载到vue的原型链上,这样组件就可以使用this.http()啦

import http from './utils/http.js'
Vue.prototype.http = httpimport store from './store/index.js'
//把vuex定义成全局组件
Vue.prototype.$store = store

store:这里主要是记录token

import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({state: {hasLogin: false,token: '',},mutations: {login(state, res) {state.hasLogin = truestate.token = res},},actions: {}
})
export default store

使用:以登录为例

this.http({url: '/api/user/aliLogin',method: 'POST',isjson: true,data: { // 根据接口文档填写参数,我这里目前需要一下四个code,avatar,nickname,userType: 1},success: res => {// console.log(res, 'MP-ALIPAY login res')if (res.code === 200) {// 存token 这样以后每次请求就自动带token了this.$store.commit('login', res.data)// 再进行其他操作},fail: err =>{}
})

2、支付宝小程序的大坑

以前没有接触过支付宝小程序,坑实在是太多了

(1)在支付宝开放平台注册支付宝小程序之后,它的环境有两种。一种是沙箱环境,一种是线上环境,这两个是独立的,千万不能混了,而且沙箱环境可调式的能力很少,我都是在线上环境调试的

但是,线上环境会有各种问题,首先要去支付宝公众平开,配置ip白名单,然后再在这个平台上开通各种能力,或许这样就好了,但是绝对不止这样,具体的流程忘记了,我弄了好几天才坑坑巴巴的调通了。 包括报什么ISV权限不足,然后官方文档说的什么auth_code什么什么的都是垃圾,就好好看看目前的环境,请求的地址,有没有白名单,能力有没有开通,或许就可以了

3、保存图片到本地

uni.getFileSystemManager().writeFile()、uni.saveImageToPhotosAlbum方法只能在微信小程序上使用,首先获取授权

         toSave() {uni.showLoading({title: "正在生成图片",mask: true,});uni.getSetting({success: (res) => {if (!res.authSetting["scope.writePhotosAlbum"]) {uni.authorize({scope: "scope.writePhotosAlbum",success: () => { // 授权成功// console.log('授权成功')uni.hideLoading();this.saveImg();},fail: (res) => {uni.hideLoading();// console.log('无法保存图片,请先授权')uni.showToast({title: '取消授权',icon: 'error'})},});} else { // 已经授权!// console.log('已经授权!')uni.hideLoading();this.saveImg();}},});},

 this.saveImg(),图片地址使用base64格式

saveImg() {let _this = thisconst base64Str = 'base64格式的图片地址'.slice(22), // 注意这里,截掉data:image/png;base64,buffer = uni.base64ToArrayBuffer(base64Str),filePath = wx.env.USER_DATA_PATH + "/wx.png"; // base64src.png 为保存的图片名称uni.getFileSystemManager().writeFile({filePath, // 先把文件写到临时目录里// 方式一:data: buffer,encoding: "binary",// 方式二:// data: this.imgBase64.slice(22),// encoding: "base64",success: (res) => {uni.saveImageToPhotosAlbum({filePath, // 将临时文件 保存到相册success: (res) => {uni.showToast({title: '图片保存成功',icon: 'success'})_this.$refs.popup.close()},fail: (error) => {uni.showToast({title: '图片保存失败',icon: 'error'})_this.$refs.popup.close()},});},fail: (error) => {uni.showToast({title: '图片保存失败',icon: 'error'})}});}

支付宝小程序上用my.saveImage()

toSave() {uni.showLoading({title: "正在生成图片",mask: true,});let _this = thismy.saveImage({url: '图片的网络地址',success: res => {// console.log('saveImage', res)uni.showToast({title: '图片保存成功',icon: 'success'})uni.hideLoading()_this.$refs.popup.close()},fail: err => {uni.hideLoading()uni.showToast({title: '授权失败',icon: 'error'})// console.error('saveImage err', err)_this.$refs.popup.close()}})}

4、uniapp获取用户信息并且--->微信登录微信小程序、支付宝登录支付宝小程序

这块的坑不是很多,但是登录功能是必须有的,记录一下

微信小程序

login() {let _this = thisuni.getSetting({success(res) {// console.log("授权:", res);uni.showModal({title: '授权提醒',content: '请您授权头像、昵称等信息,以便使用全部功能',cancelText: "随便逛逛",confirmText: '确认授权',success: function(showres) {if (showres.confirm) {uni.getUserProfile({desc: '获取您的昵称、头像、地区及性别', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写lang: "zh_CN",success: (infoRes) => {// console.log('授权信息', infoRes);uni.login({provider: 'weixin',success: (res1) => {_this.weixinlogin(用户信息等参数)},fail: () => {uni.showToast({title: "微信登录授权失败",icon: "none"});}})},fail: err => {// console.log(err, 'getUserProfile err')}})} else if (showres.cancel) {// console.log('用户点击取消');}}})}})},weixinlogin(参数) {this.http({url: '接口地址',method: 'POST',isjson: true,data: {登录接口需要的参数},header: {'content-type': 'application/json'},success: res => {// console.log(res, 'weixin login res')if (res.code === 200) {// store里存token,在上面的代码里写过,封装请求的时候将token封装进去this.$store.commit('login', res.data)uni.showToast({title: '登录成功!',icon: 'success'})} else {// console.log('登录失败!')uni.showToast({title: '登录失败!',icon: 'error'})}},

支付宝小程序获取用户信息登录

这里有点坑,调试的时候总是调不通,可以从一下几点找问题

1)确保代码没有问题,我是照着文档写的

2)支付宝小程序开放平台的各种配置,包括ip白名单、域名白名单、是不是这个小程序的开发者等

3)后端的秘钥,证书(都是小程序后台生成的)没有问题

4)APPID

<view class="my-beijing" @click="alilogin">// 获取用户authCodealilogin() {let _this = thismy.getAuthCode({scopes: 'auth_user',success: (res2) => {console.log(res2, 'res.authCode')// return_this.alipaylogin(res2.authCode)// console.log(res2,'res2') , userInfo.avatar, userInfo.nickName || '支付宝用户'},fail: (resfail) => {// 用户取消授权},});},// 支付宝登录alipaylogin(code) {this.http({url: 'login接口地址',method: 'POST',data: {code},success: res => {if (res.code === 200) {// 存tokenthis.$store.commit('login', res.data.token)} else {// console.log('MP-ALIPAY 登录失败!')uni.showToast({title: '登录失败!',icon: 'error'})}},fail: err => {// console.log(err, 'MP-ALIPAY login err')// console.log('登录失败!')uni.showToast({title: '登录失败!',icon: 'error'})}})},

5、获取180秒倒计时

<text v-if="!isgetcode" @click="sendMs">发送验证码</text>
<text v-else>{{content}}</text>data() {return {isgetcode: false,count: 180,canClick: false,content: ''}},methods: {// 获取倒计时daojishi() {if (!this.canClick) returnthis.canClick = falsethis.content = this.count + '秒' //这里解决60秒不见了的问题let clock = setInterval(() => {this.count--this.content = this.count + '秒'// console.log(this.content,'this.content')if (this.count <= 0) { //当倒计时小于0时清除定时器clearInterval(clock)this.content = '验证码'this.count = 180this.canClick = truethis.isgetcode = false}}, 1000)},
}

6、支付

1)uniapp的微信支付

流程就是处理页面一些列数据,然后第一步调用后端给你的创建订单接口,第二步就调用uni.requestPayment()拉起支付就好了

// 创建订单
commitorder() {// 是否同意协议if (this.agreement_radio) {let query = {创建订单时的参数}//调用创建订单接口this.http({url: '接口地址',method: 'POST',isjson: true,data: query,success: res => {// console.log(res, 'create order res')if (res.code == 200) {//调用支付this.wxpay(res.data)}},fail: err => {// console.log(err, 'create order error')}})} else {// console.log('未同意协议')this.$nextTick(() => {})}},

this.wxpay()

// 拉起微信支付wxpay(data) {let _this = this// console.log(data, 'wxpay data'),uni.requestPayment()需要的参数在创建订单的接口返回,要不然就自己对照文档计算uni.requestPayment({provider: 'wxpay',timeStamp: data.timeStamp,nonceStr: data.nonceStr,package: data.package,signType: data.signType,paySign: data.paySign,success: function(res) {// console.log('success:' + JSON.stringify(res));// 跳转到支付成功页面uni.reLaunch({url: '../Play_Ok/Play_Ok'})},fail: function(err) {// console.log('fail:' + JSON.stringify(err));// 支付失败记得删除订单}});},

2)uniapp的支付宝支付

支付宝支付用uni的方法没有成功,所以去看了支付宝支付的文档,最后选择用支付宝预授权来完成支付。步骤还是一样的先创建订单获取支付时的参数(支付宝是tradeNo),然后拉起支付

            // 支付宝支付commitorder_alipay() {// 是否同意协议if (this.agreement_radio) {// 是否选择地址if (!this.haseaddr) {uni.showToast({title:'请选择地址',icon:'error'})return}let query = {// 创建订单参数}let _this = thisthis.http({url: '创建订单接口',method: 'POST',isjson: true,data: query,success: res => {// 创建订单成功的回调,拉起支付console.log(res, 'create order res')if (res.code == 200) {//创建订单成功,接口会返回tradeNo,调支付my.tradePay({tradeNO:res.data.aliPay.tradeNo,success: (res1) => {// console.log(res1,'支付成功 res1')if(res1.resultCode == 9000){// 文档上说9000是成功,到这里就成功了}else {// 支付不成功,具体原因看返回,去对照文档}},fail: (err1) => {// 支付不成功,具体原因看返回,去对照文档}});}},fail: err => {// 创建订单失败的回调}})} else {// console.log('未同意协议')this.$nextTick(() => {})my.showToast({type: 'none',content: '请阅读并勾选协议',duration: 3000,success: () => {}})}},

7、支付宝小程序图片处理 

图片的处理看文档就好了支付宝小程序图片的官方文档

但是,这里有种情况,就是图片的宽度固定,但是高度不固定,一般来说设置image标签的mode属性为scaleToFill就好了,有的时候会出现图片被压缩的情况,这时候就直接设置mode为widthFix直接解决(文档里有)

2022-7-29更新

换了一家公司也有uniapp的项目,主要是做企微跟飞书小程序

1、企微小程序的调试

微信开发者工具上下一个企微模拟器,然后选择就好了,跟微信小程序一样的

2、视频标签video

可以先看官方文档,包括uniapp、微信、飞书的,每个平台对video的支持程度不一样。我接到的需求是接口返回视频列表,而我要让一个个视频看起来像是一个视频在播放,包括自定义进度条、倍速、开始、暂停等控件

首先这个需求在PC端已经有了实现,我的选择是copy,但是由于我太菜了,没有完成转化,就只能自己写了,当然也有借鉴

来看文件结构,在pages里新建一个页面咯,没有写成组件

全部的代码,需要注意的是,因为是CV的,所以有很多无关的代码我没时间也没能力去处理,这里只说关键点

1、看文档,找到不同平台对于video标签的支持程度。这样就可以很好的实现播放、暂停、timeupdate等

2、获取video实例,小程序里不支持操作DOM,获取video实例是通过API获取哦。这块有个大坑,一定要看好,是xx.createVideoContext方法而不是xx.createAudioContext

  onReady: function (res) {this.videoContext = wx.createVideoContext('myVideo', this)},

3、控件的实现,就是去看文档,各种开放的API都有,按照业务逻辑去调用就好了

4、拖动的进度条,这块是借鉴了之前大佬的写法,具体的代码的movePoint方法

5、终极大坑,video的层级,高,z-index不好用,那么我下面的写法在微信\企微小程序上是可以盖住video的,但是在飞书小程序就不行了,所以想了一个方法,就是将video的100% - 底下控件的高度,也算是变相解决问题了

<template><div class="live_detail" @tap="clickTouchend"><div class="video-stop" v-show="video.isPause"></div><!-- #ifdef MP-LARK --><video:show-play-btn="false"x5-playsinline="true"x5-video-player-type="h5-page"x5-video-player-fullscreen="true":show-loading="true":controls="false":autoplay="true"style="object-fit: fill;position: absolute;width: 100%;height: calc(100% - 120px);top: 0;left: 0;"id="myVideo":src="videoUrl"@pause="bindpause"@play="bindplay"@timeupdate="timeupdate"@error="videoErrorCallback"@ended="videoEnd"></video><view class="video-con flex align-center just-center"><view class="flex top"><view></view><view class="video-time flex align-center"><span class="current-time w54">{{ getVideoTime() }}</span><span class="line">/</span><span class="w54">{{ videoTotalTime() }}</span></view><view class="flex just-center progress" @touchmove.native.stop="movePoint"><view class="line"></view><viewclass="point":style="{ left: finLeft }"@touchstart.native.stop="drag = true"unselectable="on"onselectstart="return false"style="-moz-user-select: none; -webkit-user-select: none; -ms-user-select: none"></view></view></view><view class="footer flex"><img v-if="roomAbout.avatar" :src="roomAbout.avatar" alt class="head" /><img v-else :src="require('@/static/images/head.png')" alt class="head" /><view class="f_other flex just-center"><view class="title">{{ roomAbout.nickName || '暂无昵称' }}</view><view class="count"><span>粉丝数{{ roomAbout.dyfansNum || ' 暂无' }}</span><span>场观{{ roomAbout.watchTimes || ' 暂无' }}</span><span>峰值{{ roomAbout.maxOnlineNum || ' 暂无' }}</span></view></view><view></view></view></view><!-- #endif --><!-- #ifdef MP-WEIXIN --><videox5-video-player-type="h5-page":show-loading="true":controls="false":autoplay="true"object-fit="fill"style="object-fit: fill; position: absolute; width: 100%; height: 100%; top: 0; left: 0"id="myVideo":src="videoUrl"preload@pause="bindpause"@play="bindplay"@timeupdate="timeupdate"@error="videoErrorCallback"@ended="videoEnd"@canplay="canplay"@waiting="videoWaiting"@playing="videoPlaying"@videoOver="videoOver"></video><!-- 控件 --><div class="video-con flex align-center just-center"><div class="flex top"><!-- 进度条 --><div class="flex just-center progress" @touchmove.native.stop="movePoint"><div class="line"></div><divclass="point":style="{ left: finLeft }"@touchstart.native.stop="drag = true"unselectable="on"onselectstart="return false"style="-moz-user-select: none; -webkit-user-select: none; -ms-user-select: none"></div></div><!-- 时间 --><div class="video-time flex align-center"><span class="current-time w54">{{ getVideoTime() }}</span><span class="line">/</span><span class="w54">{{ videoTotalTime() || '00:00:00' }}</span></div><!-- 倍速 --><div class="ratio-play"><div class="play-text" @touchend.native.stop="showChooseRatio">{{ ratioText() }}</div><divclass="choose-ratio-list flex col"@touchend.native.stop="showChooseRatio":class="{ active: isShowChooseRatio }"><div class="choose-item" @tap="changeRatio(1.5)">1.5X</div><div class="choose-item" @tap="changeRatio(1.25)">1.25X</div><div class="choose-item" @tap="changeRatio(1)">1.0X</div><div class="choose-item" @tap="changeRatio(0.8)">0.8X</div><div class="choose-item" @tap="changeRatio(0.5)">0.5X</div></div></div></div><div class="footer flex"><img v-if="roomAbout.avatar" :src="roomAbout.avatar" alt class="head" /><img v-else :src="require('@/static/images/head.png')" alt class="head" /><div class="f_other flex just-center"><div class="title">{{ roomAbout.nickName || '暂无昵称' }}</div><div class="count"><span>粉丝数{{ roomAbout.dyfansNum || ' 暂无' }}</span><span>场观{{ roomAbout.watchTimes || ' 暂无' }}</span><span>峰值{{ roomAbout.maxOnlineNum || ' 暂无' }}</span></div></div><div></div></div></div><!-- #endif --></div>
</template>
<script>
import { getLiveRoom } from '@/api/play-record/play-record.js'export default {name: 'liveDetail',data() {return {roomAbout: {}, // 所有返回videoList: [], // 视频列表videoListTotal: 0, // 视频列表长度totalSeconds: 0, // 视频总长度videoUrl: '', // 播放地址videoCurrentIndex: 0, // 这个应该是当前播放的视频索引ratio: 1, // 视频倍速video: {//视频有关内容currentTime: 0, // 当前时间isPause: true, // 是否显示暂停按钮timeArr: [], // 存储每个视频长度,目前不知道功能isClick: false, // unknowcopyTime: 0, //unknowvolume: 1 // unknow},videoTime: 0, // 不知道作用isShowChooseRatio: false, //显示倍速left: 0, // 滑动点的位置finLeft: 0, // 滑动点的位置}},// 监听视频时间变化watch: {'video.currentTime': {handler(val) {this.computeLeft()// this.timeupdate()}}},onReady: function (res) {this.videoContext = wx.createVideoContext('myVideo', this)},onLoad(options) {let query = {getRecordUrl: 1,getRoomDetail: 1,brandId: options.brandId,roomId: options.roomId,userId: ''}this.getLive(query)},methods: {videoErrorCallback: function (e) {this.$toast(e.detail.errMsg)},bindplay() {// // #ifdef MP-LARK// this.videoContext.exitFullScreen()// //  #endifthis.video.isPause = false},bindpause() {this.video.isPause = true},computeLeft() {const total = this.videoTime + this.video.currentTimeconst left = (total / this.totalSeconds) * 360this.left = left + 'rpx'this.finLeft = (total / this.totalSeconds) * 360 + 'rpx'},movePoint(e) {let _this = thisif (this.drag) {const line = uni.createSelectorQuery().in(this).select('.progress')line.boundingClientRect(function (data) {const x = ((e.touches[0].clientX - data.left) / data.width) * _this.totalSecondsconst max = Math.max(..._this.video.timeArr)const item = _this.video.timeArr.find(i => i < x)if (item) {// 控制point如果超过最后一个视频,就不能滑动了if (x - item > max) {return}_this.calcVideo(x - item)}}).exec(function (res) {// 注意:exec方法必须执行,即便什么也不做,否则不会获取到任何数据})}},showChooseRatio() {if (this.ratioTimer) {clearTimeout(this.ratioTimer)}this.isShowChooseRatio = !this.isShowChooseRatio},// 获取视频getLive(query) {getLiveRoom(query).then(res => {if (res.code !== 0) return// console.log(res, 'res.data.liveRoomVideo')this.roomAbout = res.datathis.videoList = res.data.liveRoomVideothis.videoListTotal = res.data.liveRoomVideo.lengththis.videoList.forEach(item => {item.videoStartTime = item.videoStartTime / 1000item.videoEndTime = item.videoEndTime / 1000item.videoTime = item.videoEndTime - item.videoStartTime})let total = 0this.videoList.forEach(item => {total += item.videoTimethis.video.timeArr.push(total)})this.totalSeconds = totalif (this.videoList.length > 0) {if (this.videoList.length === 1) {this.videoUrl = this.videoList[0].videoUrlthis.videoCurrentIndex = 0} else {this.videoUrl = this.videoList[1].videoUrlthis.videoCurrentIndex = 1this.calcVideo(this.video.timeArr[0] + 1)}}})},// 处理视频获取数据calcVideo(total, isPlaying = false) {if (this.videoList.length === 1) {this.video.currentTime = totalthis.videoContext.currentTime = totalthis.video.isPause = truereturn}if (this.videoList.length === 0) {const timeText = this.formatTime(total).split(':').slice(0, 3).join(':')}let index = -1for (let i = 0; i < this.video.timeArr.length - 1; i++) {if (this.video.timeArr[0] >= total) {if (this.videoCurrentIndex !== i) {this.videoUrl = this.videoList[0].videoUrl}this.videoTime = 0this.videoCurrentIndex = 0Promise.resolve().then(res => {console.log(res, 'calcVideo promise resolve res')this.video.currentTime = totalthis.videoContext.currentTime = totalthis.video.isClick = truethis.video.copyTime = total})this.video.isPause = trueindex = 0break}if (total >= this.video.timeArr[i] && total <= this.video.timeArr[i + 1]) {index = i//   this.clickStatus = truethis.videoUrl = this.videoList[index + 1].videoUrlthis.videoCurrentIndex = index + 1this.videoTime = this.video.timeArr[i]this.video.isClick = truethis.video.copyTime = total - this.video.timeArr[i]Promise.resolve().then(() => {this.video.currentTime = total - this.video.timeArr[i]this.videoContext.currentTime = total - this.video.timeArr[i]})this.video.isPause = truebreak}if (total >= this.video.timeArr[this.video.timeArr.length - 2] &&total <= this.video.timeArr[this.video.timeArr.length - 1]) {this.videoUrl = this.videoList[this.videoList.length - 1].videoUrlthis.videoCurrentIndex = this.videoList.length - 1this.videoTime = this.video.timeArr[this.video.timeArr.length - 2]//   this.clickStatus = truethis.video.isClick = truethis.video.copyTime = total - this.video.timeArr[this.video.timeArr.length - 2]Promise.resolve().then(() => {this.video.currentTime = total - this.video.timeArr[this.video.timeArr.length - 2]this.videoContext.currentTime =total - this.video.timeArr[this.video.timeArr.length - 2]})this.video.isPause = truebreak}}if (!isPlaying) {this.videoContext && this.videoContext.pause()this.video.isPause = true} else {setTimeout(() => {this.videoContext && this.videoContext.play()}, 100)this.video.isPause = false}},// 视频播放时timeupdate(e) {// 视频在播放this.video.isPause = falseif (this.videoContext) {this.videoContext.playbackRate = this.ratio //倍速this.videoContext.volume = this.video.volumethis.video.currentTime = parseInt(e.detail.currentTime)}},canplay() {if (this.video.isClick) {this.videoContext.currentTime = this.video.copyTimethis.video.isClick = falsethis.videoContext.playbackRate = this.ratiothis.videoContext.volume = this.video.volume}},videoEnd() {if (this.videoCurrentIndex === this.videoList.length - 1) {// alert(1)this.videoUrl = this.videoList[0].videoUrlthis.videoCurrentIndex = 0this.videoTime = 0} else {this.videoTime = this.videoTime + this.video.currentTimethis.videoCurrentIndex++this.videoUrl = this.videoList[this.videoCurrentIndex].videoUrl}Promise.resolve().then(res => {console.log(res, 'videoEnd promise resolve res')this.videoContext.play()this.video.isPause = falsethis.video.iconShow = false})},videoWaiting() {if (!this.video.isPause) {this.video.isLoading = true}},videoPlaying() {this.video.isLoading = false},toggleVideoStatus() {if (this.video.isPause) {this.video.isPause = falsethis.video.coverShow = falsethis.videoContext.play()} else {this.video.isPause = truethis.videoContext.pause()}},clickTouchend() {if (this.video.isPause) {// 暂停状态this.video.isPause = falsethis.videoContext.play()} else {this.videoContext.pause()this.video.isPause = true}},changeRatio(ratio) {this.ratio = ratiothis.videoContext.playbackRate = ratiothis.$nextTick(() => {this.isShowChooseRatio = false})},// 选择倍速ratioText() {if (this.ratio === 1) {return '倍速'} else {return this.ratio + 'X'}},// 获取视频总时间videoTotalTime() {const time = this.totalSecondsif (!time) returnlet h = parseInt(time / 60 / 60)let m = Math.floor((time % 3600) / 60)let s = Math.floor(time % 60)// h >= 24 ? h - 24 : hh = h >= 10 ? h : `0${h}`m = m >= 10 ? m : `0${m}`s = s >= 10 ? s : `0${s}`return `${h}:${m}:${s}`},// 获取视频当前播放的时间getVideoTime() {const total = this.videoTime + this.video.currentTime// console.log(this.videoTime, this.video.currentTime)return this.formatTime(total)},formatTime(time) {// 格式化时分秒let h = Math.floor(time / 3600)let m = Math.floor((time % 3600) / 60)let s = Math.floor(time % 60)// h >= 24 ? h - 24 : hh = h >= 10 ? h : `0${h}`m = m >= 10 ? m : `0${m}`s = s >= 10 ? s : `0${s}`return `${h}:${m}:${s}`}}
}
</script>
<style lang="less" scoped>
.flex {display: flex;
}
.warp {flex-wrap: wrap;
}
.col {flex-direction: column;
}
.just-center {justify-content: center;
}
.just-start {justify-content: flex-start;
}
.just-end {justify-content: flex-end;
}
.just-a {justify-content: space-around;
}
.just-b {justify-content: space-between;
}
.align-center {align-items: center;
}
.align-end {align-items: flex-end;
}
.live_detail {width: 100vw;height: 100vh;position: relative;.video-stop {width: 98rpx;height: 98rpx;position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);z-index: 11;background: url(https://tagvvcloud-brandapp-1256030678.cos.ap-guangzhou.myqcloud.com/49e6cf22c7c94c7a92a61288d45d0ec8play.png)no-repeat;background-size: 100% 100%;}.video-con {// #ifdef MP-LARKposition: fixed;align-items: unset !important;// #endifz-index: 100;// #ifdef MP-WEIXINposition: absolute;// #endifleft: 0;bottom: 0;width: 100%;height: 240rpx;background: rgba(0, 0, 0, 0.5);flex-direction: column;.footer {// #ifdef MP-LARKmargin-left: 24rpx;// #endifalign-items: center;font-size: 28rpx;font-weight: 400;color: #ffffff;margin-top: 24rpx;.head {width: 96rpx;height: 96rpx;background: #d8d8d8;border-radius: 50%;}.f_other {margin-left: 24rpx;flex-direction: column;.count {margin-top: 16rpx;span {display: inline-block;margin-right: 80rpx;white-space: nowrap;}span:last-child {margin-right: unset;}}}}> div {width: calc(100% - 48rpx);}.top {justify-content: space-between !important;align-items: center;// #ifdef MP-LARKmargin-left: 24rpx;margin-right: 24rpx;// #endif}.video-time {height: 28rpx;font-size: 20rpx;font-family: PingFangSC-Regular, PingFang SC;font-weight: 400;color: #ffffff;line-height: 28rpx;opacity: 0.4;.current-time {color: #fff;}}.ratio-play {position: relative;color: rgba(255, 255, 255, 0.6);.play-text {font-size: 24rpx;width: 80rpx;height: 40rpx;line-height: 40rpx;text-align: center;background: rgba(0, 0, 0, 0.5);border-radius: 6rpx;}.choose-ratio-list {width: 80rpx;position: absolute;padding: 10rpx 0;left: 0rpx;top: -234rpx;flex-direction: column-reverse;background: rgba(0, 0, 0, 0.7);border-radius: 2rpx;display: none;transition: all 0.2s;font-size: 20rpx;&.active {display: block;}.choose-item {text-align: center;line-height: 34rpx;// #ifdef MP-LARKline-height: 42rpx;// #endifwidth: 80rpx;transition: all 0.2s;}}}.progress {width: 360rpx;height: 4rpx;line-height: 4rpx;background: #ffffff;border-radius: 2rpx;align-items: center;position: relative;.line {width: 100%;height: 4rpx;background-color: #fff;}.point {cursor: pointer;width: 24rpx;height: 16rpx;background-color: #fff;border-radius: 8rpx;position: absolute;left: v-bind(left);top: 50%;transform: translateY(-50%);}}}
}
</style>

2022-10-8 更新

实现下载视频到相册的功能

直接上代码,这里下载视频的方法uni.downloadFile返回一个对象,里面有百分比,已下载的大小等信息,还有停止下载的方法(详见官网),可以实现下载进度条,停止下载等操作

    <div class="preview_btn" @click="saveVideo(videoUrl)">保存视频到相册</div>// 下载方法,传入要保存视频的路径download(url, _this) {// 下载视频,可以返回一个 downloadTask 对象来获取保存进度(百分比),详见官网uni.downloadFile({url,success: res => {if (res.statusCode === 200) {// 保存到相册uni.saveVideoToPhotosAlbum({filePath: res.tempFilePath,success: function () {uni.showToast({title: '保存成功!',icon: 'success'})setTimeout(() => {uni.hideLoading()}, 1500);},fail: res => {console.log(res.errMsg)return uni.showToast({title: res.errMsg,icon: 'none'})},complete: res => {uni.hideLoading()}})}}})},// 将视频保存到相册,saveVideo(url) {console.log('saveVideo url-->', url)const _this = thisif (!url) returnuni.showLoading({ title: '保存中~', mask: true })//获取用户的当前设置。获取相册权限uni.getSetting({success: res => {//如果没有相册权限if (!res.authSetting['scope.writePhotosAlbum']) {//向用户发起授权请求uni.authorize({scope: 'scope.writePhotosAlbum',success: () => {//授权成功,开始下载视频_this.download(url, _this)},//授权失败fail: () => {uni.hideLoading()uni.showModal({title: '您已拒绝获取相册权限',content: '是否进入权限管理,调整授权?',success: res => {if (res.confirm) {//调起客户端小程序设置界面,返回用户设置的操作结果。(重新让用户授权)uni.openSetting({success: res => {console.log(res.authSetting)}})} else if (res.cancel) {return uni.showToast({title: '已取消!',icon: 'none'})}}})}})} else {//如果已有相册权限,开始下载视频_this.download(url, _this)}},fail: res => {}})}

uniapp微信小程序支付宝小程序的初体验,记录一些初次遇到的大坑小坑~相关推荐

  1. 微信扫描普通二维码调起体验版与已发布版的小程序

    文章转自: 微信扫描普通二维码调起体验版与已发布版的小程序_baozaobenren的博客-CSDN博客 公司有这样一个需求,就是用微信扫描二维码直接调起我们的小程序,前期不知道,直接扫描二维码,调起 ...

  2. 小程序 html编辑器,小程序富文本编辑器editor初体验

    终于,微信在5月9号的v2.7.0版本中新增了 editor富文本编辑器组件,今天有时间了准备体验一下 在5月6日的时候写了一篇小程序富文本解析的「伪需求」,从wxParse到towxml的坑,当时还 ...

  3. 飞机大战小游戏源码---飞机大战初体验

    开发环境: Windows10,pycharm,python3 源码使用教程: 打开pycharm,创建一个新的项目,文件-->新建项目 项目命名:飞机大战初体验,基本解释器选择python3版 ...

  4. 好物推荐 | 小明医声家庭健康初体验

    我自诩是一名数码爱好者,大学毕业后工作第一年购买了自己的第一台单反佳能,以我的经济能力,当然是分期啦.收到货后很是琢磨了一番,入门的单反,和我这样的新手刚刚好.每周休息日都得练练手,拍拍风景.拍拍建筑 ...

  5. 微信小程序富文本编辑器editor初体验-图片上传

    https://developers.weixin.qq.com/miniprogram/dev/component/editor.html 以前没有小程序富文本编辑器,只能输入文字,图片上传后,在服 ...

  6. 微信小程序_自定义组件_初体验

    自定义组件是微信小程序中重要的组成部分,是实现模块化开发的重要手段 个人认为,自定义组件是区分菜鸟与高手的分水岭 第一篇先介绍小程序自定义组件的基本使用 自定义组件的使用可以分为如下几个步骤 创建组件 ...

  7. layui获取select 文本_小程序富文本编辑器editor初体验

    终于,微信在5月9号的v2.7.0版本中新增了 editor富文本编辑器组件,今天有时间了准备体验一下 在5月6日的时候写了一篇小程序富文本解析的「伪需求」,从wxParse到towxml的坑,当时还 ...

  8. 微信小游戏-海盗来了打金初体验

    引子 假期在家空余时间, 发现微信小游戏突然火了, 群友推荐一款名为: 海盗来了 的小游戏 稍微体验了一把感觉很不错, 真正实现了微信对小程序的期望 -- 取代原生 app 得益于日益强大的 js 引 ...

  9. 联想小新Air13高定黑使用初体验

    联想小新Air13高定黑 前段时间老电脑一直死机,外加电池不抗用了,无奈之下,某东选购电脑,预算7k左右,挑了好久,主要是纠结于ThinkPad 翼480/小米Pro/华为matebook D/惠普 ...

  10. 小云APP移动建站初体验

    这段时间一直在研究想给自己的站做一个真正意义上的移动站,APP也好.自适应也罢,也是伤透了脑筋. 为何要做移动站? 说简单一点,就是身边的朋友已证实,移动端的流量太大了,真是非常大.随便做一个H5页面 ...

最新文章

  1. Codeforces Beta Round #14 (Div. 2) B. Young Photographer 水题
  2. 【Android 安装包优化】使用 lib7zr.so 动态库处理压缩文件 ( jni 中 main 函数声明 | 命令行处理 | jni 调用 lib7zr.so 函数库处理压缩文件完整代码 )
  3. 寻路机器人单片机程序示例_单片机精华程序代码示例:DS1302时钟程序
  4. MySQL之深入解析一条SQL的执行流程
  5. Html.Action、html.ActionLink与Url.Action的区别
  6. CCF 201512-1 数位之和
  7. xmind 模板_XMind 教程 | 如何写出让人眼前一亮的年终总结?
  8. linux远程备份视频教程,linux实现自动远程备份(scp+ssh)
  9. python 超高精度除法_Python十进制-除法,舍入,精度
  10. 2019.10.15学习总结
  11. C#中的方法(函数),委托和事件
  12. BZOJ 4503 两个串 ——FFT
  13. VAF:高级Web模糊测试工具
  14. 详解CAN总线:常用CAN连接器的使用方法
  15. ASP.NET Core 引用其他程序集项目里面的 Controller 控制器
  16. Android Sensor感应器简单使用(1)
  17. 25岁裸辞转行5G网络优化工程师:比盲目赶路更为重要的,是知道方向—分享优橙小故事
  18. 数据挖掘十大经典算法(转存)
  19. 怎样把d盘改成c盘!如何把收藏夹和桌面的路径设成D盘
  20. 【Qt象棋游戏】08_人机博弈高阶算法

热门文章

  1. Android系统通知处理流程
  2. 形同虚设:花费700美元便可突破门禁
  3. 人才招聘系统的UML图
  4. java多线程学习-网络图片下载
  5. python音频处理回声_找出音响中是否有回声
  6. 疾病负担研究(GBD)-如何优雅的展示发病率数据
  7. Pycharm启动后加载anaconda一直updating indices造成Pycharm闪退甚至电脑崩溃
  8. Mac升级后配置上的小红点困扰着我
  9. 如何建设现代化智慧档案馆【八防十防】、及相关的一些规定
  10. R语言实战-第十一章中级绘图(与ggplot2包进行对比)