前言
大家好,我是橙海LZ。(好久没发文章了- -)
离职也有一个多月了,最近一周在BOSS直聘和智联招聘投了几天简历,杭州互联网行情相当惨淡,主动沟通了几十家的公司,就约到寥寥几家公司的面试邀请。
在投递过程中发现,很多公司岗位JD上写需要有跨端、可视化、服务端等业务方面的开发经验,在上家公司跨端这块业务做的比较少,所以去B站上找了黑马的uni-app视频快速学习下,这篇文章基于视频内容和官方文档的知识点做一个总结。
文章一共分为两篇,阅读过程中能够了解到:
- uni-app常用开发模式
- uni-ui组件库使用
- 登录流程的基本实现
- 发布上线
- 原生App打包
- uniCloud云开发
准备工作
安装HBuilderX编辑器
安装HBuilderX,在官网下载即可。
第一次安装使用HBuilderX,需要去安装一些必要插件。
如果是使用HBuilderX进行项目开发,需要在「设置-运行设置」中去配置下微信开发者工具的安装路径(其他平台同理)。
运行项目后可能会碰到如下报错,去微信开发者工具中的「设置-安全」中开启「服务端口」,
再次运行项目,并将打包编译好的微信小程序目录导入到微信开发者工具中。
通过VScode进行uni-app开发
推荐安装的vscode插件
插件安装好后,可以悬停显示文档说明、代码提示、以及快速创建uni-app文件等功能。
刚开始创建文件,会发现创建页面以后只有index文件,没有创建同名文件夹,这里需要找到图中的扩展修改下配置。
以及修改下vscode配置,让manifest.json和pages.json文件能够编写注释。
注意只用增加这两个文件的配置即可,不用配置如*.json的内容,其他json文件依旧保持原先配置。
项目开发
拉取项目模板
git clone http://git.itcast.cn/heimaqianduan/erabbit-uni-app-vue3-ts.git heima-shop
在manifest.json文件中配置「AppID」
HBuilderX中打开
manifest.json文件,是一个可视化配置的窗口,配置同理。
uni-ui组件库
uni-ui是DCloud提供的一个跨端ui库,它是基于vue组件的、flex布局的、无dom的跨全端ui框架。
uni-ui 不包括内置组件,它是内置组件的补充。
安装
pnpm add @dcloudio/uni-ui
官方文档在介绍uni-ui时,提到了支持easycom模式,它是一种只要符合指定规范,无需import就能使用组件的方式。
注意,配置好easycom后需要重启项目
easycom模式
配置TS类型声明
安装相关类型声明,并在tsconfig.json文件中修改配置
pnpm add -D @types/wechat-miniprogram @uni-helper/uni-app-types @uni-helper/uni-ui-types
{
"extends": "@vue/tsconfig/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"sourceMap": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
},
"lib": ["esnext", "dom"],
"types": [
"@dcloudio/types",
"miniprogram-api-typings",
"@uni-helper/uni-app-types",
"@uni-helper/uni-ui-types"
]
},
"vueCompilerOptions": {
// experimentalRuntimeMode 已废弃
// "nativeTags": ["block", "component", "template", "slot"]
// 目前最新的配置方式,是通过plugins属性处理
"plugins": ["@uni-helper/uni-app-types/volar-plugin"]
},
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.tsx", "src/**/*.vue"]
}
状态管理
该项目基于Vue3进行开发,状态管理库选择pinia,同时通过pinia-plugin-persistedstate进行持久化存储,和普通web端持久化配置不同,这里需要使用图中用法,否则持久化内容不会生效。
Configuration | Pinia Plugin Persistedstate (prazdevs.github.io)
基于uni.request封装请求实例
通过uni.addInterceptor来添加拦截器,对常见的request请求和uploadFile上传文件场景做请求拦截处理参数信息。
同时在获取服务器响应内容后,针对除200状态码以外的情况做了拦截;401状态下意味着用户尚未登录,强制跳转到登录页;其余情况根据接口返回的信息来抛出提示框,如果没有信息返回默认展示为请求失败。
import { useMemberStore } from '@/stores'
const baseURL = 'https://pcapi-xiaotuxian-front-devtest.itheima.net'
const httpInterceptor = {
invoke(options: UniApp.RequestOptions) {
if (!options.url.startsWith('http')) {
options.url = baseURL + options.url
}
options.timeout = 10000
options.header = {
...options.header,
'source-client': 'miniapp',
}
const token = useMemberStore().profile?.token
if (token) {
options.header.Authorization = token
}
},
}
uni.addInterceptor('request', httpInterceptor)
uni.addInterceptor('uploadFile', httpInterceptor)
interface IData<T> {
code: string
msg: string
result: T
}
export const http = <T>(options: UniApp.RequestOptions) => {
return new Promise<IData<T>>((resolve, reject) => {
uni.request({
...options,
success(res) {
if (res.statusCode >= 200 && res.statusCode < 300) {
resolve(res.data as IData<T>)
} else if (res.statusCode === 401) {
useMemberStore().clearProfile()
uni.navigateTo({
url: '/pages/login/login',
})
reject(res)
} else {
uni.showToast({
title: (res.data as IData<T>).msg || '请求失败',
icon: 'none',
})
reject(res)
}
},
fail(err) {
uni.showToast({
title: '请求失败',
icon: 'none',
})
reject(err)
},
})
})
}
请求实例使用
正常获取到数据
uni-app更推荐使用内置的请求API(比如将前缀
wx、my等替换为uni),因为uni-app集成兼容了各大主流平台的大多数API。当然如果想使用axios这类的HTTP库,也有适配方案及配置方式,可以自行了解下。
首页模块
自定义导航栏
默认展示的导航栏如图所示
不过因为展示元素内容相对有限,可以手动实现导航栏来覆盖
<script setup lang="ts">
// 获取屏幕边界到安全区域距离
const { safeAreaInsets } = uni.getSystemInfoSync()
</script>
<template>
<view class="navbar" :style="{ paddingTop: safeAreaInsets!.top + 10 + 'px' }">
<!-- logo文字 -->
<view class="logo">
<image class="logo-image" src="@/static/images/logo.png"></image>
<text class="logo-text">新鲜 · 亲民 · 快捷</text>
</view>
<!-- 搜索条 -->
<view class="search">
<text class="icon-search">搜索商品</text>
<text class="icon-scan"></text>
</view>
</view>
</template>
<style lang="scss">
/* 自定义导航条 */
.navbar {
background-image: url(@/static/images/navigator_bg.png);
background-size: cover;
position: relative;
display: flex;
flex-direction: column;
padding-top: 20px;
.logo {
display: flex;
align-items: center;
height: 64rpx;
padding-left: 30rpx;
.logo-image {
width: 166rpx;
height: 39rpx;
}
.logo-text {
flex: 1;
line-height: 28rpx;
color: #fff;
margin: 2rpx 0 0 20rpx;
padding-left: 20rpx;
border-left: 1rpx solid #fff;
font-size: 26rpx;
}
}
.search {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 10rpx 0 26rpx;
height: 64rpx;
margin: 16rpx 20rpx;
color: #fff;
font-size: 28rpx;
border-radius: 32rpx;
background-color: rgba(255, 255, 255, 0.5);
}
.icon-search {
&::before {
margin-right: 10rpx;
}
}
.icon-scan {
font-size: 30rpx;
padding: 15rpx;
}
}
</style>
这里在使用uni.getSystemInfoSync()API时,uni可能会出现ts报错无法找到情况,如果出现安装在类型声明即可。
pnpm add @types/uni-app -D
将自定义导航栏组件引入首页使用的效果如下
注意此时引入使用后,可以发现其实是展示了两个导航栏,这里还需要去pages.json文件中配置,来覆盖默认展示的导航栏。
{
"page": {
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"navigationStyle": "custom", // 用于设置导航栏样式为默认还是自定义
"navigationBarTextStyle": "white" // 用于设置导航栏标题颜色及状态栏前景颜色,只支持白色或黑色。
}
},
}
}
看下在不同设备下的展示效果
轮播图
在根目录下的components中新建XtxSwiper.vue轮播图组件。
<script setup lang="ts">
import type { BannerItem } from '@/types/home'
import { ref } from 'vue'
const activeIndex = ref(0)
// 当 swiper 下标发生变化时触发
const onChange: UniHelper.SwiperOnChange = (ev) => {
activeIndex.value = ev.detail.current
}
// 定义 props 接收
defineProps<{
list: BannerItem[]
}>()
</script>
<template>
<view class="carousel">
<swiper :circular="true" :autoplay="false" :interval="3000" @change="onChange">
<swiper-item v-for="item in list" :key="item.id">
<navigator url="/pages/index/index" hover-class="none" class="navigator">
<image mode="aspectFill" class="image" :src="item.imgUrl"></image>
</navigator>
</swiper-item>
</swiper>
<!-- 指示点 -->
<view class="indicator">
<text
v-for="(item, index) in list"
:key="item.id"
class="dot"
:class="{ active: index === activeIndex }"
></text>
</view>
</view>
</template>
<style lang="scss">
@import './styles/XtxSwiper.scss';
</style>
在pages.json中追加匹配easycom模式。
"easycom": {
"autoscan": true,
"custom": {
"^uni-(.*)": "@dcloudio/uni-ui/lib/uni-$1/uni-$1.vue",
// 追加配置,符合Xtx开头命名的组件无需import,可以直接使用
"^Xtx(.*)": "@/components/Xtx$1.vue"
}
},
src/types/component.d.ts配置全局自定义文件的类型声明,用于正确显示自定义组件的ts类型。
import 'vue'
import XtxSwiper from '@/components/XtxSwiper.vue'
declare module 'vue' {
export interface GlobalComponents {
XtxSwiper: typeof XtxSwiper
}
}
将鼠标悬停到组件上,就会正确显示组件类型
效果如下
滚动容器scroll-view
<scroll-view>是uni-app提供的内置组件,用于区域滚动。
在首页这里,将除了顶部自定义导航栏以外的内容放置到滚动容器内,并设置flex: 1,让滚动容器填满除了自定义导航栏以外的区域。
效果如下
滚动底部事件
滚动容器可以通过监听scrolltolower事件,来完成滚动到底部去获取数据的常见交互。
<scroll-view
scroll-y
class="scroll-view"
@scrolltolower="onScrolltolower"
>
<XtxSwiper :list="bannerList" />
<CategoryPanel :list="categoryList" />
<HotPanel :list="hotList" />
<XtxGuess ref="XtxGuessRef" />
</scroll-view>
const XtxGuessRef = ref<XtxGuessInstance>()
const onScrolltolower = () => {
XtxGuessRef.value?.getMore()
}
// types/component.d.ts
import 'vue'
import XtxSwiper from '@/components/XtxSwiper.vue'
import XtxGuess from '@/components/XtxGuess.vue'
declare module 'vue' {
export interface GlobalComponents {
XtxSwiper: typeof XtxSwiper
XtxGuess: typeof XtxGuess
}
}
export type XtxGuessInstance = InstanceType<typeof XtxGuess>
getMore方法是子组件通过defineExposeAPI暴露给父组件调用的获取数据方法。
效果如下
下拉刷新
<scroll-view>组件已内置了下拉刷新这部分功能。
<scroll-view
scroll-y
refresher-enabled
:refresher-triggered="isRefreshFlag"
class="scroll-view"
@scrolltolower="onScrolltolower"
@refresherrefresh="onRefresh"
>
<XtxSwiper :list="bannerList" />
<CategoryPanel :list="categoryList" />
<HotPanel :list="hotList" />
<XtxGuess ref="XtxGuessRef" />
</scroll-view>
refresher-enabled:开启自定义下拉刷新
refresher-triggered:设置当前下拉刷新状态,true 表示下拉刷新已经被触发,false 表示下拉刷新未被触发
refresherrefresh:自定义下拉刷新被触发的事件
const onRefresh = async () => {
// 标记开始刷新,并开启动画
isRefreshFlag.value = true
// 业务逻辑
XtxGuessRef.value?.reset()
await getData()
await XtxGuessRef.value?.getMore()
// 标记刷新结束,并关闭动画
isRefreshFlag.value = false
}
骨架屏
微信开发者工具可以快速根据当前页面结构,生成骨架屏的样式代码。
点击确认后会生成index.skeleton.wxml和index.skeleton.wxss两个文件。
接下来在VScode中,在首页index目录下,新建components文件夹并新建PageSkeleton.vue文件,将创建的骨架屏文件相关内容拷贝到该.vue文件下。
注意复制代码进来后,还需要删除调整部分代码:
- 可以删除顶部备注
- 组件属性修改为动态绑定,如
scroll-view组件上的refresh-enabled="true"需要修改为:refresh-enabled="true"- css文件内容也拷贝进来
<template>
<CustomNavbar />
<scroll-view
refresher-enabled
scroll-y
:refresher-triggered="isRefreshFlag"
class="scroll-view"
@scrolltolower="onScrolltolower"
@refresherrefresh="onRefresh"
>
<PageSkeleton v-if="isLoading" />
<template v-else>
<XtxSwiper :list="bannerList" />
<CategoryPanel :list="categoryList" />
<HotPanel :list="hotList" />
<XtxGuess ref="XtxGuessRef" />
</template>
</scroll-view>
</template>
效果如下
在uni-app中骨架屏代码的编写还是挺方便的,好评!
热门推荐模块
在首页跳转到这个热门推荐页面中会携带路径参数,uni-app可以通过props和onLoad生命周期方法中的options参数来获取路径参数。
// hot.vue
const props = defineProps<{
type: string
}>()
console.log(props, '---props')
onLoad((options) => {
console.log(options, '---options')
})
// hot.vue
// 这里在onload中根据路径传参去动态修改导航栏标题
onLoad((options) => {
currUrlMap.value = hotMap.find((item) => item.type === options!.type)!
uni.setNavigationBarTitle({
title: currUrlMap.value.title,
})
getHotRecommendData()
})
使用props的好处是能够在全局文件中的任意位置使用,但是props无论在Vue还是React中大多用于父子组件通信,当获取页面参数也通过props来获取时,感觉比较奇怪。
使用onLoad中的options参数似乎更符合正常需要,但是就只能在onLoad方法内部使用,要想在其他位置使用,要么维护一个变量存储后再在组件内使用。
大家如果有更好的思路/方法,欢迎评论区讨论!
分类模块
页面效果如下
依旧根据页面内容,通过微信开发者工具生成骨架屏内容
点击图片通过<navigator>组件跳转至商品详情页
<navigator
v-for="goods in item.goods"
:key="goods.id"
class="goods"
hover-class="none"
:url="`/pages/goods/goods?id=${goods.id}`"
>
<image class="image" :src="goods.picture"></image>
<view class="name ellipsis">{{ goods.name }}</view>
<view class="price">
<text class="symbol">¥</text>
<text class="number">{{ goods.price }}</text>
</view>
</navigator>
这里点击图片的预览效果,通过uni.previewImage实现
uni.previewImage({
current: url, // current 为当前显示图片的链接/索引值,不填或填写的值无效则为 urls 的第一张。
urls: goods.value!.mainPictures, // 需要预览的图片链接列表
})
这里使用的是uni-ui组件库中的<uni-popup>弹出层组件,将地址面板组件和服务说明组件做了包裹,让两者均以弹出层的形式展示。
<uni-popup ref="popup" type="bottom" background-color="#fff">
<AddressPanel v-if="popupName === 'address'" @close="popup?.close()" />
<ServicePanel v-if="popupName === 'service'" @close="popup?.close()" />
</uni-popup>
在地址面板组件中使用了iconfonts字体图标,在页面中通过使用icon-ring、icon-checked来快速展示。
这里通过在src/styles文件夹中的fonts.scss文件来维护,并在App.vue中引入做全局使用。
登陆模块
由于登录页不属于导航栏页面,无法直接去访问调试,所以在微信开发者工具中去添加编译模式,将需要访问的登录页设置为默认访问的页面。
给按钮增加点击事件以及open-type开放能力属性,将鼠标移入到属性上可以看到对应支持属性值。(之前安装的vscode插件提供的功能)
<button class="button phone" open-type="getPhoneNumber" @getphonenumber="onGetPhonenumber">
<text class="icon icon-phone"></text>
手机号快捷登录
</button>
const onGetPhonenumber: UniHelper.ButtonOnGetphonenumber = async (e) => {
const encryptedData = e.detail!.encryptedData
const iv = e.detail!.iv
const res = await postLoginWxMinAPI({
code: wxCode,
encryptedData,
iv,
})
console.log(res, '---res')
}
由于此处使用的是个人小程序,未进行认证,所以API调用无法成功,下面使用测试接口来模拟登录流程
<button @tap="onGetPhonenumberSimple">
<text class="icon icon-phone">模拟快捷登录</text>
</button>
const onGetPhonenumberSimple: UniHelper.ButtonOnGetphonenumber = async (e) => {
const res = await postLoginWxMinSimpleAPI('填写手机号码')
memberStore.setProfile(res.result)
uni.showToast({
icon: 'success',
title: '登录成功',
success: () => {
setTimeout(() => {
uni.switchTab({
url: '/pages/my/my',
})
}, 500)
},
})
}
点击模拟快捷登录,成功以后会跳转至「我的」页面(注意这里需要使用switchTab方法来进行导航栏页面的跳转)
我的模块
顶部处使用同样也是使用自定义导航栏
设置页
新建pagesMember目录,通过分包模式在新建「设置」页面。
分包subPackages:
访问率高的页面放在主包;
访问率低的页面放在子包按需加载;当用户点击到子包目录中的页面时,还是会有代码包下载的过程,可能会有卡顿的过程,所以子包也不建议拆太大。
可以预先配置可能会跳转到的分包,使用「分包预下载」,在进入页面后根据配置进行预下载。
个人信息页
同样通过分包模式新建该页面
该页面通过在「设置页」点击头像后跳转进入
点击头像需要唤起当前设备的上传文件窗口
此处需要通过uni-app的「条件编译」能力对API做下兼容处理
条件编译在uni-app中主要用于处理多端差异,通过
#ifdef、#ifndef的方式来实现,后续还会在很多地方使用到。
const onAvatarChange = () => {
// 调用拍照/选择图片
// 选择图片条件编译
// #ifdef H5 || APP-PLUS
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替
uni.chooseImage({
count: 1,
success: (res) => {
// 文件路径
const tempFilePaths = res.tempFilePaths
// 上传
uploadFile(tempFilePaths[0])
},
})
// #endif
// #ifdef MP-WEIXIN
// uni.chooseMedia 仅支持微信小程序端
uni.chooseMedia({
// 文件个数
count: 1,
// 文件类型
mediaType: ['image'],
success: (res) => {
// 本地路径
const { tempFilePath } = res.tempFiles[0]
// 上传
uploadFile(tempFilePath)
},
})
// #endif
}
地址管理页
使用uni-ui框架的swipe-action组件来实现左滑删除的效果
在「设置页」点击「我的收货地址」进入
效果如下
SKU模块
SKU:存货单位(Stock Keeping Unit),库存管理的最小可用单元,通常称为"单品"。
SKU模块的实现相对比较固定,在DCloud插件市场也有比较成熟的插件可以使用,这里使用现成插件进行开发。
在商品详情页使用SKU插件
<template>
<vk-data-goods-sku-popup
v-model="isShowSku"
:localdata="localdata"
:mode="mode"
add-cart-background-color="#FFA868"
buy-now-background-color="#27BA9B"
ref="skuPopupRef"
:actived-style="{
color: '#27BA9B',
borderColor: '#27BA9B',
backgroundColor: '#E9F8F5',
}"
@add-cart="onAddCart"
@buy-now="onBuyNow"
/>
</template>
效果如下:
不知道大家有没有发现一个小细节,为什么这个组件没有在当前页面中引入也能使用,难道是匹配上了easycom模式?
检查下pages.json文件
可以看到文件中也没有配置如vk-data开头的匹配规则,那是怎么触发easycom的?
原因是开启autoscan自动扫描属性。
下载复制进项目的插件匹配components/组件名称/组件名称.vue这个规则,所以可以使用easycom模式。
购物车模块
这是购物车模块的页面效果
当处于未登录状态时,购物车模块展示的内容是不一样的
在登录时,会往localStorage中存储数据,这里通过pinia获取存储个人信息数据,如果存在就代表登录,反之就是未登录
import { useMemberStore } from '@/stores'
// 获取会员Store
const memberStore = useMemberStore()
这里有一处特殊处理,就是购物车主逻辑的内容放在CartMain.vue文件中,购物车页面有两个分别是cart.vue和cart2.vue。
其中cart.vue作为底部导航栏页面,通过switchTab进行跳转,其左上角不会有返回按钮,但是在商品详情页中,也可以跳转到购物车页面,且左上角需要有返回按钮,所以这里通过两个文件进行维护,两个文件本质上也只是引用CartMain.vue的包装组件而已。
创建订单页
在购物车页点击结算后,需要跳转到「创建订单页」
新建pagesOrder目录,并通过分包模式创建create.vue文件
页面效果如下:
此处注意创建订单页顶部点击可以进入地址页,
此时如果由于点击事件在最外层容器上,当点击修改时跳转到修改页面,会冒泡到容器的事件上,导致触发对应事件。
所以需要增加阻止事件冒泡以及阻止默认行为。
商品详情页点击购买时,也需要可以跳转到「创建订单页」
订单详情页面
在pagesOrder下通过分包模式新建detail.vue订单详情页,在创建订单页点击提交订单后跳转进来。
订单详情页增加下骨架屏效果,具体是通过微信开发者工具来生成,再手动处理为.vue文件(步骤同上,如果有点模糊,可以回顾下目录中的「骨架屏」章节~)
设置过渡动画(微信小程序)
该功能是实现在微信小程序端,其他端有兴趣大家可以自行实现~
// 基于小程序的 Page 类型扩展 uni-app 的 Page
type PageInstance = Page.PageInstance & WechatMiniprogram.Page.InstanceMethods<any>
// #ifdef MP-WEIXIN
// 获取当前页面实例,数组最后一项
const pageInstance = pages.at(-1) as PageInstance
// 页面渲染完毕,绑定动画效果
onReady(() => {
// 动画效果,导航栏背景色
pageInstance.animate(
'.navbar',
[{ backgroundColor: 'transparent' }, { backgroundColor: '#f8f8f8' }],
1000,
{
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
},
)
// // 动画效果,导航栏标题
pageInstance.animate('.navbar .title', [{ color: 'transparent' }, { color: '#000' }], 1000, {
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
})
// // 动画效果,导航栏返回按钮
pageInstance.animate('.navbar .back', [{ color: '#fff' }, { color: '#000' }], 1000, {
scrollSource: '#scroller',
timeRange: 1000,
startScrollOffset: 0,
endScrollOffset: 50,
})
})
// #endif
这里一共设置了三种过渡动画,分别介绍下:
第一个动画,控制.navbar这个class的容器,默认背景色透明,当 id 为 scroller 的容器滚动 50 的距离时,背景色变为#f8f8f8,动画持续 1s。
第二个动画
控制导航栏的.title这个class的容器,默认字体颜色透明,当 id 为 scroller 的容器滚动 50 的距离时,字体颜色变为黑色,动画持续 1s。
第三个动画 控制导航栏左上角图标所在的容器,默认字体颜色白色,当 id 为 scroller 的容器滚动 50 的距离时,字体颜色变为黑色,动画持续 1s。
支付倒计时
该效果通过uni-ui中uni-countdown组件来实现。
服务端也会返回倒计时秒数的时间,通过second属性传入后,会转为分+秒的形式
点击「去支付」后,注意需要通过redirectTo来跳转,跳转到支付结果后不可返回到当前页。
const onOrderPay = async () => {
if (import.meta.env.DEV) {
// 开发环境模拟支付
await getPayMockAPI({ orderId: query.id })
} else {
// #ifdef MP-WEIXIN
// 正式环境支付:1.获取支付订单信息,2.调用微信支付API
// const res = await getPayWxPayMiniPayAPI({ orderId: query.id })
// await wx.requestPayment(res.result)
await getPayMockAPI({ orderId: query.id })
// #endif
// #ifdef H5 || APP-PLUS
// H5端 和 App 端未开通支付-模拟支付体验
await getPayMockAPI({ orderId: query.id })
// #endif
}
// 关闭当前页,再跳转支付结果页
uni.redirectTo({ url: `/pagesOrder/payment/payment?id=${query.id}` })
}
支付结果页
通过分包模式在pagesOrder下新建payment支付结果页
页面效果:
点击「查看订单」跳转到订单详情页(同样是不可返回上个页面)
<navigator
hover-class="none"
class="button navigator"
:url="`/pagesOrder/detail/detail?id=${query.id}`"
open-type="redirect"
>
查看订单
</navigator>
支付结果页状态流转的页面展示效果:
写到最后
以上就是项目中的页面部分的所有内容了。
# 下篇文章内容大纲:
1. 发布上线
2. 打包为安卓及IOS端安装包
3. 跨端兼容
4. uniCloud 云开发
感兴趣的伙伴欢迎点个关注🌟~
感谢你看到这里,如发现文章中的问题或其他感兴趣的内容欢迎指正讨论。^ ^