0 问题待解决
- import { } 与否
- 为什么要封装
- 对齐问题
- 每次重启项目,sitemap就会消失
- 动态修改标题失效
- 图片问题多多 :高度自适应 改成image固定高度,mode:aspectFill
- 微信小程序文档
- b站视频链接
- 后端接口文档
一、知识点
- uni-app 是一个使用 Vue.js语法来开发所有前端应用的框架
- 项目技术栈:js+vue+微信小程序+uni-app
- 750rpx = 屏幕宽度(微信小程序)
- 100vw = 屏幕宽度(H5)
- 100vh = 屏幕高度(H5)
- 微信小程序中不支持
*
通配符 - 出现奇怪的报错后,可以npm重启项目
- 原生的微信小程序不支持promise
- uni-api支持promise,请求返回值是个数组,无法实现项目“等待中”效果,因此要自行封装异步请求
二、基本语法
1. 数据展示
- 在 js 的 data 中定义数据
- 在 template中通过 {{ 数据 }} 来显示
- 在标签的属性(自定义属性)上通过 :data-index='数据’来使用
2. 数据循环、条件编译、计算属性同vue语法
3. 事件传参(传入$event获取自定义属性)
思考:target
和 currentTarget
的区别
<text data-id="myId" @click="showId($event)">点我显示id</text>
showId(event) {console.log(event.currentTarget.dataset.id)
}
4. 组件
1. 注册组件
- 创建组件页面
.vue
文件(组件样式也在这里修改) - import组件(注意不能用
-
,js中会当成减号) - 注册到vue实例上,
components: {}
<template>
中短横线形式,使用组件
2. 组件传参
1)父传子
- props属性和data同级,并不是在data里面!
- 为了父组件复用子组件
父组件
父组件中自定义属性mysrc
(体现传值),右边内容若为变量,则须在mysrc
前添加冒号
<my-yuyan :mysrc="xuanyan" mytitle="渲言"></my-yuyan><my-yuyan :mysrc="yuyan" mytitle="喻言"></my-yuyan>
data() {return {xuanyan: "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591198770665&di=3bdaf80f7bd15f03dfea55bf88e391a0&imgtype=0&src=http%3A%2F%2Fimglf5.nosdn0.126.net%2Fimg%2FbjZaRmVpWExkaE1ScFc3VzRjUW1INUJhcTcxVGNTcVF0VGpFYkxXelNiZWZnMW1SYjAzUFJBPT0.jpg%3FimageView%26thumbnail%3D500x0%26quality%3D96%26stripmeta%3D0%26type%3Djpg",yuyan: "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591200770069&di=a90535dc0ff79617758b4ec32484e1bd&imgtype=0&src=http%3A%2F%2Fwx2.sinaimg.cn%2Flarge%2F59853be1ly1gff7y14skvj20c80c8dna.jpg"};},
子组件
(父组件所传值)需要在子组件中声明:props
中当与父组件的自定义属性相同
<view><view>{{mytitle}}</view><image class="yuyan" :src="mysrc"></image></view>
export default {props:['mysrc', 'mytitle']
}
2)子传父
- 子组件
<template>
绑定事件 - 子组件
methods
部分触发自定义事件 - 父组件
<template>
在子组件上监听自定义事件 - 父组件
methods
部分处理子组件传来的参数
子组件
父组件
3)全局共享数据
*1. 通过 Vue的原型共享数据
main.js
中定义
Vue.prototype.theme ="the 9"
.vue
组件中使用
onLoad() {console.log(this.theme)}
*2. 通过 globalData 共享数据
App.vue
文件定义
globalData: {date: '2020-05-30'
}
获取
getApp().globalData.date
3. 组件插槽slo - 父向子传递标签
在父组件中使用到了子组件,在该页面的子组件内部插入标签,但是不知道具体的放置位置在哪,所以要在子组件定义的页面里用slot
标签来占位。
父组件
- 这个标签放在子组件内部!
子组件 - 使用
<slot>
占位,制定父组件传来的标签出现在哪个位置
效果,救命啊~~~
5. 生命周期
-
uni-app框架的生命周期结合了 vue 和 微信小程序的生命周期
-
全局的APP中 使用 onLaunch 表示应用启动时
-
页面中 使用 onLoad 或者 onShow 分别表示 页面加载完毕时 和页面 显示时
-
组件中使用 mounted 组件挂载完毕时
三、项目
(一)项目开始
0. 接口文档
接口文档
1. 起步
项目名称:my-project → 默认模板 → 注意项目目录(C:\try\uniapp\my-project\dist\dev\mp-weixin
)→ AppId
可暂时忽略
全局安装
npm install -g @vue/cli
创建项目
vue create -p dcloudio/uni-preset-vue my-project
启动项目(微信小程序)
npm run dev:mp-weixin
微信小程序开发者工具导入项目
2. 引入saas (在启动项目前安装)
安装依赖
npm install sass-loader node-sass
vue组件中,添加属性 <style lang='scss'>
注意: 这里需要在vscode里编辑C:\try\uniapp\my-project
路径下的项目
3. 页面
app.js
文件 (微信小程序中)
"pages": ["pages/index2/index","pages/index/index"],
pages
数组中第一项表示应用启动页
pages.json
(vscode中)
"pages": [ {"path": "pages/index/index","style": {"navigationBarTitleText": "uni-app"}},{"path": "pages/index2/index","style": {"navigationBarTitleText": "uni-app"}}]
4. 报错 sitemap配置
官网配置
VM303:1 sitemap.json
Error: 未找到入口 sitemap.json 文件,或者文件读取失败,请检查后重新编译。
在vscode的微信小程序的根目录下新建sitemap.json
{"rules":[{"action": "allow","page": "*"}]
}
5. uni-api
uni-api
-
原生的微信小程序的api都是不支持promise
-
uni-app对大部分的小程序的原生api做了封装,使之支持promise
使用方式 :
wx.request(原生微信小程序)修改为uni.request (uni-api的方式 )
微信小程序:
wx.request({url: '',success(res) {console.log(res)}
})
uni-api:
uni.request({url: ''
})
.then(res=> {console.log(res)
})
6. uni-ui
uni-ui
uni-ui的使用方式
- 安装uni-ui
- 局部引入组件
- 注册组件
- 使用组件
cnpm install @dcloudio/uni-ui
import {uniBadge} from '@dcloudio/uni-ui'
export default {components: {uniBadge}
}
<uni-badge text="1"></uni-badge>
<uni-badge text="2" type="success" @click="bindClick"></uni-badge>
<uni-badge text="3" type="primary" :inverted="true"></uni-badge>
7. 全局引入字体图标、样式
styles文件夹放入src路径下
在App.vue
文件中引入,注意不能使用@
<style>@import "./styles/iconfont.wxss";
</style>
使用
<text class="iconfont iconvideocamera"></text>
8. 小程序导航栏标题、样式修改
pages.json
"globalStyle": {"navigationBarTextStyle": "white","navigationBarTitleText": "青春有你","navigationBarBackgroundColor": "#000"}
(二)首页模块
1. 组件分段器-tab栏/标签页
tab栏:推荐、分类、最新、专辑
1. 组件引入
分段器的使用
文档中以下部分要修改:
- 引入路径
import { uniSegmentedControl } from "@dcloudio/uni-ui";
- 参数e
onClickItem(e) {if (this.current !== e.currentIndex) {this.current = e.currentIndex;}}
- 样式从
button
改为text
<uni-segmented-controlstyle-type="text"></uni-segmented-control>
2. 结构分析
上下结构
- 上:左右结构
2.【专题】 图片详情 - 第48课
1. 封装组件
从首页-推荐-热门
中的图片跳转到到图片详情
页面
- 将热门中的图片标签封装为超链接组件(js方式跳转)/ 图片详情组件
- 缓存(图片详情页面需要的)
图片数组
和图片索引
(组件传值,存储到全局数据中) - 图片详情页面要使用到插槽
父组件:
<view class="img_wrap" v-for="(item, index) in hots" :key="item.id"><go-detail :list="months.items" :index="index"><image :src="item.thumb" mode="widthFix"></image></go-detail>
</view>
子组件
<template><view @click="clickHotImg"> 图片详情组件<slot></slot></view>
</template><script>
export default {props:['list', 'index'],methods:{clickHotImg() {getApp().globalData.imgList = this.listgetApp().globalData.imgIndex = this.indexuni.navigateTo({url: '../../pages/imgDetail/index' -- 也可以用根路径/pages的形式,见图片分类})}}};
</script>
图片详情页面,需要在pages.json
中配置
<template><view>图片详情页面</view>
</template><script>
export default {onLoad() {console.log('缓存的数据', getApp().globalData)}
};
</script>
2. 项目中所有需要跳转的表情替换为组件
- 首页-推荐-热门
- 首页-推荐-月份
- 专辑-专辑详情-wallpaper
3. 【问题】:组件不渲染
- 将slot前的文字去除,自定义组件不渲染了?
- 修改slot前的文字,影响了组件的宽度?
给自定义组件加样式,继承宽高,这时原来scallToFill的模式图片不显示了,原因未知(高度为0),只好用回widthFix,再给外部标签加overflow:hidden
<view class="wallpaper_image" v-for="(item, index) in wallpaper" :key="item.id"><!-- :src="item.thumb+item.rule.replace('$<Height>',360)" --><go-detail :list="wallpaper" :index="index" class="wallpaperDetail"> <image :src="item.thumb+item.rule.replace('$<Height>',360)" mode="widthFix" ></image></go-detail>
</view>
.wallpaper {display: flex;flex-wrap: wrap;.wallpaper_image {width: 33.3%;height: 130rpx;border: 1rpx solid #fff;.wallpaperDetail {display: flex;justify-content: center;align-items: center;height: 100%;width: 100%;overflow: hidden;}}
}
4. 约定
图片详情页面包括:专辑图像+专辑相关+最热评论+最新评论
5. 判断图片src存在后再渲染
本地图片未加载成功的情况 Failed to load local image resource /pages/XXX/处理
<image v-if="imgList[imgIndex].thumb+imgList[imgIndex].rule.replace('$<Height>',360)" :src="imgList[imgIndex].thumb+imgList[imgIndex].rule.replace('$<Height>',360)">
</image>
6. 用全局数据初始化
这样写太土了
data() {return {imgList: getApp().globalData.imgList,imgIndex: getApp().globalData.imgIndex}
}```go
export default {onLoad() {const{imgList, imgIndex} = getApp().globalDatathis.imgDetail = imgList[imgIndex]console.log('缓存的数据', getApp().globalData)},data() {return {imgDetail: {}}}
};
7. moment.js处理时间戳为几天前
fromNow()
实现显示为XX天/月/年前moment.locale('zh-cn')
使用中文语言(写在js里)
this.commemtTime = moment(this.imgDetail.atime*1000).fromNow()
3. 【专题】封装手势滑动组件 - P66
- 手指在容器上产生了移动
-
手指按下屏幕事件 touchstart
-
手指离开屏幕事件 touchend
-
手指在屏幕上的坐标 event.changedTouches[0].clientX 和 clientY
- 手指在容器上滑动的时间不能太长 - Date.now()时间戳
-
记录按下屏幕的时间
-
记录离开屏幕的时间
- 根据坐标判断滑动的方向
-
手指离开屏幕的时候根据坐标判断滑动的方向
子组件
<template><view @touchstart="handleTouchStart" @touchend="handleTouchEnd"><slot></slot></view>
</template><script>
export default {data() {return {//按下的时间startTime: 0,startX: 0,startY: 0};},methods: {handleTouchStart(event) {this.startTime = Date.now()this.startX = event.changedTouches[0].clientX this.startY = event.changedTouches[0].clientY },handleTouchEnd(event) {const endTime = Date.now()const endX = event.changedTouches[0].clientXconst endY = event.changedTouches[0].clientYif (endTime - this.startTime > 2000) {return}let direction = ''if (Math.abs(endX - this.startX) > 10) {direction = endX -this.startX > 0 ? 'right': 'left'} else {return}this.$emit('swipeAction', {direction})}}
};
</script>
父组件
<swipe-action @swipeAction="handleSwipeAction"><image v-if="imgUrl" :src="imgUrl"mode="scaleToFill"></image>
</swipe-action>
handleSwipeAction (e) {console.log(e)
}
*. 解决bug:垂直距离滑动造成的翻页
if (Math.abs(endX - this.startX) > 10 && Math.abs(endY - this.startY) < 10) {direction = endX -this.startX > 0 ? 'right': 'left'} else {return}
4. 【专题】下载图片
downloadFile
下载远程文件到小程序的内存中saveImageToPhotosAlbum
将 图片从内存中下载到本地
async handleClick() {await uni.showLoading({title: '下载中'})const res1 = await uni.downloadFile({url: this.imgDetail.img})const {tempFilePath} = res1[1]const res2 = await uni.saveImageToPhotosAlbum({ filePath: tempFilePath})uni.hideLoading()await uni.showToast({title: '下载成功'})
}
(三)推荐模块
1. 封装异步请求
- 基于原生的promise来封装
- 挂载到Vue的原型上
- 通过 this.request 的方式来使用
2. for循环
注意:在uni-app框架里,仍用v-for
指令
<view class="recommend_item" v-for="item in recommends" :key="item.id"><image :src="item.thumb"></image>
</view
3. 理解flex:wrap
flex-wrap: nowrap | wrap | wrap-reverse;
两栏布局常见:
(限定了每一循环项的宽度为一半,并且换行)
width: 50%;
flex-wrap: wrap;
4. 插件CssTree
快速根据html结构,生成对应sass结构(选中代码,ctrl
+shift
+P
)
5. sass的变量使用,定义主题颜色
uni.scss
文件中定义:
在样式中使用:
6. 对齐问题
这种结构很不好啊,避免这种结构
块级元素和行内元素同时存在的情况,如何基线对齐
7. moment.js第三方库处理时间
moment.js官方文档
- cnpm install moment --save
优化:
由于月份是请求来的数据,防止在一开始渲染的时候,出现undefined
,需要改造组件:
<!-- 月份列表 开始 -->
<view class="months_wrap" v-if="Object.keys(months).length!==0" >
或者在最外层标签加:
<scroll-view v-if="recommends.length > 0" scroll-y class="recommend_view" @scrolltolower="handlerToLower" >
8. 【专题】翻页效果:使用scroll-view改造容器
-
原先使用
view
标签的情况(template
内第一个最外层的view
),不能冻结头部tab栏,因此替换为scroll-view
标签 -
添加
scroll-y
属性 -
计算高度
height: calc(100vh - 45px);
(屏幕的高减去头部固定高度) -
绑定滚动条触底事件,@scrolltolower,当到底时,①
skip+=limit
,②再次发起请求,③且hots做数据叠加,达到分页效果 -
如何叠加?使用es6语法
this.hots = [...this.hots, ...res.res.vertical]
-
hasMore判断是否还有更多,当返回值.length为0,将hasMore置为false
-
头部的轮播图无须多次请求,当this.xx.length为0,才初始化
methods: {getList() {this.request({url: "http://157.122.54.189:9088/image/v3/homepage/vertical",data: this.params}).then((res)=> {if(res.res.vertical.length === 0) {uni.showToast({title: '到底了宝贝儿:)',icon: 'none'})this.hasMore = falsereturn} if(this.recommends.length === 0) {// 首次请求this.recommends = res.res.homepage[1].itemsthis.months = res.res.homepage[2]this.months.MM = moment(this.months.stime).format("MM")this.months.DD = moment(this.months.stime).format("DD")}this.hots = [...this.hots, ...res.res.vertical]})},handlerToLower() {if(this.hasMore) {this.params.skip+=this.params.limitthis.getList()} else {uni.showToast({title: '到底了:)',icon: 'none'})}}}
9. 【专题】写分页容易错的地方
加了分页代码后,小程序报错Cannot read property 'replace' of undefined
这可不是小程序的bug,定位到使用replace方法的地方
<image :src="item.thumb+item.rule.replace('$<Height>',360)"></image>
因此意味着item.rule可能是null/undefined,那么直接将src赋值为 item.thumb看看效果:
法宣循环多出了一个空白的image
,并且this.wallpaper
的首项是一个空数组,那一定是在分页请求,做数据叠加除了问题,定位到代码:
this.wallpaper = [this.wallpaper, ...res.res.wallpaper]
竟然是没有写解构,惊了(ಥ﹏ಥ)(ಥ﹏ಥ)(ಥ﹏ಥ),吸取教训,遇到问题冷静排查。
(三)专辑模块
1. 轮播图组件swiper
indicator-dots
面板指示圆点
autoplay
自动播放
circular
连续播放
<view class="album_swiper"><swiper indicator-dots autoplay circular> <swiper-item v-for="item in banner" :key="item.id"> <image :src="item.thumb" mode="widthFix"></image></swiper-item></swiper>
注意屏幕尺寸变化时,轮播图、图片应该随之变化:
在不设置的情况下,调整为大屏,图片显示不全。
swiper
组件默认高度150px,不能由内容撑开,宽度750rpx- 图片默认宽320px,默认高240px,由于项目里base.wxss重置了样式,图片宽为100%
- 应当重新赋值给
swiper
的高,使之与图片宽高等比 - 检查元素,看图片实际宽640px、高284px
- swiper的高为 750/(640/284),图片高度设置为100%
- 注意
calc
不要再写错了!不要有多余的样式,否者可能会不生效
swiper {height: calc(750rpx / (640/284));image {height: 100%;}}
2. 组件中动态修改页面标题
在每个组件的mounted
钩子中添加如下代码:
mounted() {uni.setNavigationBarTitle({title: '分类'})}
3. 【专题】左右布局
外容器:flex布局
左:flex: 1
→ 内image固定宽高
image:height+width: 200rpx; mode=“aspectFill”
右:flex: 2
当红框中内容是子绝父相定位到底部时,由于绝对定位宽度由内容撑开,因此子需要加width: 100%
使之占满整行,这样做justify-content: space-between
时才能两端对齐。
.bgInfo {display: flex;height: 80rpx;align-items: center;padding: 0 20rpx;justify-content: space-between;position: absolute;width: 100%;bottom: 10rpx;color: #fff;.albumName {font-size: 40rpx;}.followBtn {width: 152rpx;height: 60rpx;display: flex;justify-content: center;align-items: center;border-radius: 10rpx;padding: 5 10rpx;font-size: 30rpx;background-color: $themeColor;}}
图片填满格子:
mode=“scaleToFill”
.wallpaper_image {width: 33.3%;height: 170rpx;border: 3rpx solid #fff;display: flex;justify-content: center;align-items: center;image {width: 100%;height: 100%;}}
4. 【专题】文字一行显示,超出显示省略号
右:flex: 2
+ overflow: hidden
(否则会被文字撑开)
text-overflow: ellipsis;overflow: hidden;white-space: nowrap;
5. 点击推荐页面,跳转到专辑详情,并获取id参数
改造成navigator
组件,注意绑定的url是在" "内再使用模板字符串
<navigator :url="`/pages/album/index?id=${item.target}`" class="recommend_item" v-for="item in recommends" :key="item.id"><image :src="item.thumb" mode="widthFix"></image>
</navigator>
在方法里的url又不用带双引号" "
getAlbumDetail() {this.request({url: `http://157.122.54.189:9088/image/v1/wallpaper/album/${this.id}/wallpaper`,data: this.params}).then(res => {console.log('专辑详情', res)}) }
在跳转到的页面打印来源页面所携带的参数(接收参数id):
export default {onLoad(options) {console.log(options)}
}
6. 小程序设置默认tabs页
设置分段器tab栏自带的current
属性
而pages.json文件中,pages数组的首项为项目启动首页
"pages": [ {"path": "pages/album/index"},{"path": "pages/home/index"}
7. 后端返回代码有换行符,前端如何处理
在view
标签里包裹一层text
即可
<view class="author_info"><text>{{album.desc}}</text></view>
后端返回:
"desc":"急先锋是一种精神,他们急流勇进,先锋夺人!\n急先锋是一份责任,他们上天入地,护你周全!"
效果:
8. onReachBottom上拉加载事件
和scroll-view
组件分页的区别:保持头部tab栏不动
专辑详情页面上拉加载:整个页面的加载更多分页
(四)分类模块
1. css3渐变效果
.imgName {background-image: linear-gradient(to right top, rgba(0,0,0,.2),rgba(0,0,0,0));
}
2. 点击图片跳转详情
<navigator class="img" v-for="item in category" :key="item.id" url='/pages/imgCategory/index'><image :src="item.cover" mode="aspectFill"></image><div class="imgName">{{item.name}}</div>
</navigator>
这里用url='/pages/imgCategory/index'
根路径/
3. 图片分类-最新/热门修改分段器
由于最新和热门的数据渲染只需改变请求参数,因此修改分段器,将参数order
写入
items: [{ title: "最新", order:"new"}, { title: "热门", order:"hot"}]
同时分段器绑定的values
也要返回新的数组
<uni-segmented-control:current="current":values="items.map(e=>e.title)"@clickItem="onClickItem"style-type="text"active-color="#d4237a"
></uni-segmented-control>
同时,最新/热门只使用一个组件
<view class="category_tab_content"><view class="cate_item" v-for="item in vertical" :key="item.id"><image :src="item.thumb" mode="aspectFill"></image></view>
</view>
4. 根据tabs请求数据
- 点击tab栏,更新请求参数
- 重新发请求
5. scroll-view标签使用flex布局的注意点
若未进行设置 enable-flex 属性以使 flexbox 布局生效
设置,flex效果失效
<scroll-view scroll-y enable-flex class="category_tab_content"><view class="cate_item" v-for="item in vertical" :key="item.id"><image :src="item.thumb" mode="aspectFill"></image></view>
</scroll-view>
6. 下拉事件加载更多的注意点(数据重置)
- bug1:点击tab栏时skip应被初始化为0
- bug2:vertical的数组不断被叠加,切换tab栏时还显示之前的数据
- 优化:当重复点击“最新”或“热门”时,后续代码不必重复执行
onClickItem(e) {if (this.current !== e.currentIndex) {this.current = e.currentIndexthis.params.skip = 0 this.vertical = []this.params.order = this.items[e.currentIndex].orderthis.getCategoryDetail()} else {return}
}
(五)精美视频
1. 组件选择
- 每一个子页面用到的接口地址或接口参数都不一样
- 将每一个页面的接口地址或接口参数都封装到标题数组中
- 点击标题时,也传递对应的接口路径和参数给内容组件(使用
watch
监听参数变化) - 内容组件接收参数,发送请求渲染页面
接口:
- 推荐:
http://157.122.54.189:9088/videoimg/v1/videowp/featured
- 娱乐:
http://157.122.54.189:9088/videoimg/v1/videowp/category/59b25abbe7bce76bc834198a
- 最新:
http://157.122.54.189:9088/videoimg/v1/videowp/videowp
(改变参数的order
) - 热门:
http://157.122.54.189:9088/videoimg/v1/videowp/videowp
(改变参数的order
) - 分类:
http://157.122.54.189:9088/videoimg/v1/videowp/category
<view class="video_tab_content"><view v-if="current < 4"><video-main :urlobj="{url: items[current].url, params:items[current].params}"></video-main></view><view v-if="current === 4"><video-category></video-category></view>
</view>
具体哪个组件需要做分页,就在子组件内部用scroll-view
标签
2. watch监听urlobj
- 重新发请求
- 在点击tab栏时重置参数(去除数组叠加)
watch: {urlobj() {this.getVideoList()this.content = []}},
3. 跳转视频详情
- 在子组件缓存视频信息到全局
- 页面跳转
- 在详情页面的
onLoad
里拿到数据
子组件
<view class="video_item" v-for="item in content" :key="item.id" @click="goVideo(item)"><image :src="item.img" mode="aspectFill"></image>
<view>
goVideo(item){getApp().globalData.video = itemuni.navigateTo({url: '/pages/videoPlay/index'})
}
详情页面
onLoad() {console.log(getApp().globalData.video)
}
4. 视频播放页面
1. 让video标签铺满容器
<video :src="video.video" objectFit="fill"></video>
2. 静音功能
video标签的muted
属性控制是否静音false/true
<video :src="video.video" objectFit="fill" :muted="isMuted"></video>
<view class="iconfont iconjingyin" @click="handleMute"></view>
3. 转发功能
button标签的open-type
属性设置为share
<view class="iconfont iconzhuanfa"><button open-type="share" class="share"></button>
</view>
.iconzhuanfa {position: relative;.share {position: absolute;right: 0;bottom: 0;height: 80rpx;width: 80rpx;opacity: 0;}
}
4. 下载视频
- 先观察返回值
async downloadVideo(){const res = await uni.downloadFile({ url: this.video.video })console.log('下载临时路径', res)
}
async downloadVideo(){await uni.showLoading({title: '下载中'})const { tempFilePath } = (await uni.downloadFile({ url: this.video.video }))[1]await uni.saveVideoToPhotosAlbum({filePath: tempFilePath})uni.hideLoading()await uni.showToast({title: '下载成功'})
}
5. 背景图片使用滤镜效果
.bgImg {position: absolute;height: 100vh;width: 100vw;filter: blur(20px);z-index: -1;
}