订单模块
创建订单
静态结构
src\pages\order\create\index.vue
<template>
<view class="order-create">
<scroll-view scroll-y class="viewport">
<!-- 收货地址 -->
<navigator
class="shipment"
hover-class="none"
url="/pages/my/address/index?from=order"
>
<view class="user"> {{ "收货人" }} 13xxxxxxxxxx </view>
<view class="address">
{{ "广东省广州市天河区" }}{{ "吉山街道黑马程序员" }}
</view>
<text class="icon icon-right"></text>
</navigator>
<!-- 收货地址 - 无地址 -->
<navigator
class="shipment"
hover-class="none"
url="/pages/my/address/index?from=order"
>
<view class="address"> 请选择收货地址 </view>
<text class="icon icon-right"></text>
</navigator>
<!-- 商品信息 -->
<view class="goods">
<navigator
v-for="item in []"
:key="item.skuId"
:url="`/pages/goods/index?id=${item.id}`"
class="item"
hover-class="none"
>
<image class="cover" :src="item.picture"></image>
<view class="meta">
<view class="name ellipsis">
{{ item.name }}
</view>
<view class="type">{{ item.attrsText }}</view>
<view class="price">
<view class="actual">
<text class="symbol">¥</text>{{ item.payPrice }}
</view>
<view class="original">
<text class="symbol">¥</text>{{ item.price }}
</view>
</view>
<view class="quantity">x{{ item.count }}</view>
</view>
</navigator>
</view>
<!-- 配送及支付方式 -->
<view class="related">
<view class="item">
<text class="text">配送时间</text>
<text class="picker icon-fonts">{{ "时间不限(周一至周五)" }}</text>
</view>
<view class="item">
<text class="text">支付方式</text>
<text class="picker icon-fonts">{{ "在线支付" }}</text>
</view>
<view class="item">
<text class="text">买家备注</text>
<input v-model="buyerMessage" cursor-spacing="30" placeholder="建议留言前先与商家沟通确认" />
</view>
</view>
<!-- 支付金额 -->
<view class="settlement">
<view class="item">
<text class="text">商品总价: </text>
<text class="number">
<text class="symbol">¥</text>
{{ "782.00" }}
</text>
</view>
<view class="item">
<text class="text">运费: </text>
<text class="number">
<text class="symbol">¥</text>
{{ "12.00" }}
</text>
</view>
<!-- 如果有折扣,渲染折扣金额,没有折扣就不渲染了,免得用户伤心 -->
<view class="item" v-if="true">
<text class="text">折扣: </text>
<text class="number danger">
<text class="symbol">-¥</text>
{{ "12.00" }}
</text>
</view>
</view>
</scroll-view>
<!-- 底部工具 -->
<view class="toolbar">
<view class="amount">
<text class="symbol">¥</text>
<text class="number">{{ 782.0 }}</text>
</view>
<view class="button">提交订单</view>
</view>
</view>
</template>
<script>
export default {
data(){
return {
// 买家留言
buyerMessage: "",
}
}
};
</script>
<style lang="scss">
.safe-area-bottom {
background-color: #fff;
}
.order-create {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
background-color: #f4f4f4;
}
.viewport {
padding-top: 20rpx;
}
.shipment {
padding: 30rpx 30rpx 25rpx 84rpx;
margin: 0 20rpx;
font-size: 26rpx;
border-radius: 10rpx;
background: url(https://static.botue.com/erabbit/static/images/locate.png)
20rpx center / 50rpx no-repeat #fff;
position: relative;
}
.shipment .icon {
font-size: 36rpx;
color: #333;
transform: translateY(-50%);
position: absolute;
top: 50%;
right: 20rpx;
}
.shipment .user {
color: #333;
margin-bottom: 5rpx;
}
.shipment .address {
color: #666;
}
.link {
margin: 20rpx;
text-align: center;
line-height: 72rpx;
font-size: 26rpx;
color: #fff;
border-radius: 72rpx;
background-color: #27ba9b;
}
.popup-root {
.list {
padding: 20rpx 0 40rpx 10rpx !important;
}
.list .item {
padding: 30rpx 60rpx 30rpx 10rpx;
position: relative;
}
.list .item .icon {
color: #999;
font-size: 40rpx;
transform: translateY(-50%);
position: absolute;
top: 50%;
right: 10rpx;
}
.list .item .icon-checked {
color: #27ba9b;
}
.list .item .text {
font-size: 28rpx;
color: #444;
}
}
.goods {
margin: 20rpx 20rpx 0;
padding: 0 20rpx;
border-radius: 10rpx;
background-color: #fff;
}
.goods .item {
display: flex;
padding: 30rpx 0;
border-top: 1rpx solid #eee;
}
.goods .item:first-child {
border-top: none;
}
.goods .item .cover {
width: 170rpx;
height: 170rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.goods .item .meta {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.goods .item .name {
height: 80rpx;
font-size: 26rpx;
color: #444;
}
.goods .item .type {
line-height: 1.8;
padding: 0 15rpx;
margin-top: 6rpx;
font-size: 24rpx;
align-self: flex-start;
border-radius: 4rpx;
color: #888;
background-color: #f7f7f8;
}
.goods .item .price {
display: flex;
align-items: baseline;
margin-top: 6rpx;
font-size: 28rpx;
}
.goods .item .symbol {
font-size: 20rpx;
}
.goods .item .original {
font-size: 24rpx;
color: #999;
text-decoration: line-through;
}
.goods .item .actual {
margin-right: 10rpx;
color: #cf4444;
}
.goods .item .quantity {
position: absolute;
bottom: 0;
right: 0;
font-size: 26rpx;
color: #444;
}
.related {
padding: 0 20rpx;
margin: 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #fff;
}
.related .item {
display: flex;
justify-content: space-between;
align-items: center;
min-height: 80rpx;
font-size: 26rpx;
color: #333;
}
.related input {
flex: 1;
text-align: right;
margin: 20rpx 0;
padding-right: 20rpx;
font-size: 26rpx;
color: #999;
}
.related .item .text {
width: 125rpx;
}
.related .picker {
color: #666;
}
.related .picker::after {
content: "\e6c2";
}
/* 结算清单 */
.settlement {
padding: 0 20rpx;
margin: 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #fff;
}
.settlement .item {
display: flex;
align-items: center;
justify-content: space-between;
height: 80rpx;
font-size: 26rpx;
color: #333;
}
.settlement .symbol {
font-size: 80%;
}
.settlement .danger {
color: #cf4444;
}
.gap {
height: 20rpx;
}
.toolbar {
height: 120rpx;
padding: 0 40rpx;
border-top: 1rpx solid #eaeaea;
background-color: #fff;
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar .amount {
font-size: 40rpx;
color: #cf4444;
}
.toolbar .amount .symbol,
.toolbar .amount .decimal {
font-size: 75%;
}
.toolbar .button {
width: 220rpx;
text-align: center;
line-height: 72rpx;
font-size: 26rpx;
color: #fff;
border-radius: 72rpx;
background-color: #27ba9b;
}
</style>
请求数据、渲染界面
1、封装API:src/api/cart.js
import http from "@/utils/http";
/**
* 获取预付订单(填写订单)
*/
export const getPreOrderAPI = () => {
return http({
url: "/member/order/pre",
});
};
2、封装请求函数
3、onShow触发请求
4、声明变量保存数据
src/pages/cart/index.vue
import { getPreOrderAPI } from "@/api/order";
/* 学习目标:请求数据、保存数据、渲染界面 */
export default {
data() {
return {
buyerMessage: "",
preOrder: null,
};
},
methods: {
async loadData() {
const { result } = await getPreOrderAPI();
this.preOrder = result;
},
},
onShow() {
this.loadData();
},
};
5、渲染界面
改造地址管理
目标:支持点击事件
src/pages/my/address/index.vue
<template>
<view class="viewport">
<!-- 地址列表 -->
<view
class="item"
+ @click="onSelect(item)"
>
<view class="user">
{{ item.receiver }}
<text>{{ item.contact }}</text>
<text class="badge" v-if="item.isDefault"> 默认 </text>
</view>
<view class="locate">
{{ item.fullLocation }} {{ item.address }}
</view>
<!-- 🐛 添加阻止冒泡 -->
<navigator
+ @click.stop
:url="`./form?id=${item.id}`"
class="edit"
hover-class="none"
>
修改
</navigator>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
methods: {
+ onSelect(item) {
+ if (item) {
+ console.log("item -----> ", item);
+ // TODO: 将item存入redux中
+ }
+ },
+ onLoad({ from }) {
+ this.from = from;
+ },
};
地址管理-选中数据存入vuex
1、addres模块中声明state、mutations
src/store/address.js
export default {
// 命名空间
namespaced: true,
state: {
+ selectedAddress: null,
},
mutations: {
+ onSelectAddress(state, payload) {
+ state.selectedAddress = payload;
+ },
},
};
2、注册模块
src/store/index.js
+import address from "./address";
const store = new Vuex.Store({
modules: {
user,
+ address,
},
});
3、地址管理模块、选中事件、数据存入vuex中,并返回上一页
src/pages/my/address/index.vue
methods: {
+ ...mapMutations("address", ["onSelectAddress"]),
onSelect(item) {
if (item) {
// TODO: 将item存入redux中
+ this.onSelectAddress(item);
+ uni.navigateBack();
}
},
4、订单模块:根据vuex中是否有数据、条件渲染
src/pages/order/create/index.vue
computed: {
...mapState("address", ["selectedAddress"]),
},
<!-- 收货地址 -->
<navigator
class="shipment"
hover-class="none"
url="/pages/my/address/index?from=order"
+ v-if="selectedAddress"
>
<view class="user">
+ {{ selectedAddress.receiver }} {{ selectedAddress.contact }}
</view>
<view class="address">
+ {{ selectedAddress.fullLocation }}{{ selectedAddress.address }}
</view>
<text class="icon icon-right"></text>
</navigator>
<!-- 收货地址 - 无地址 -->
<navigator
+ v-else
class="shipment"
hover-class="none"
url="/pages/my/address/index?from=order"
>
<view class="address"> 请选择收货地址 </view>
<text class="icon icon-right"></text>
</navigator>
获取支付id
1、封装API
src/api/order.js
/**
* 提交订单
*/
export const createOrderAPI = (data) => {
return http({
url: "/member/order",
method: "POST",
data,
});
};
2、绑定提交事件
3、调用API
4、跳转到订单详情页面
async onSubmit() {
if (!this.selectedAddress) {
return uni.showToast({
title: "请先选择收货地址",
});
}
const { result } = await createOrderAPI({
goods: this.preOrder.goods,
addressId: this.selectedAddress.id,
payType: 1,
payChannel: 2,
buyMessage: "尽快发货",
deliveryTimeType: 1,
});
uni.showToast({ title: "创建订单成功" });
setTimeout(() => {
uni.navigateTo({ url: `/pages/order/detail?orderId=${result.id}` });
}, 1000);
},
订单详情
订单相关常量
src\pages\order\OrderConstance.js
// 订单状态
// 订单状态,0为全部 1为待付款、2为待发货、3为待收货、4为待评价、5为已完成、6为已取消
export const OrderState = {
QuanBu: 0,
DaiFuKuan: 1,
DaiFaHuo: 2,
DaiShouHuo: 3,
DaiPingJia: 4,
YiWanCheng: 5,
YiQuXiao: 6,
};
// 订单状态描述
export const OrderStateOptions = {
[OrderState.DaiFuKuan]: "待付款",
[OrderState.DaiFaHuo]: "待发货",
[OrderState.DaiShouHuo]: "待收货",
[OrderState.DaiPingJia]: "待评价",
[OrderState.YiWanCheng]: "已完成",
[OrderState.YiQuXiao]: "已取消",
};
静态结构
src\pages\order\detail.vue
<template>
<view class="order-detail">
<!-- 内容 -->
<scroll-view class="viewport" scroll-y>
<!-- 顶部导航栏 -->
<view class="navbar" :style="{ paddingTop: safeArea.top + 'px' }">
<view class="wrap">
<navigator
open-type="navigateBack"
class="back icon-left"
></navigator>
<view :class="['title', platform]"></view>
</view>
</view>
<!-- 订单状态 -->
<view class="overview" :style="{ paddingTop: safeArea.top + 40 + 'px' }">
<template v-if="detail.orderState === OrderState.DaiFuKuan">
<view class="status icon-clock">等待付款</view>
<view class="tips">
<text>应付金额: ¥{{ 0.01 }}</text>
<text class="countdown"> 支付剩余 </text>
<uni-countdown
:minute="15"
:second="00"
:showDay="false"
start
color="#fff"
/>
</view>
<view class="button">去支付</view>
</template>
<template v-else>
<view class="status icon-clock">
{{ OrderStateOptions[detail.orderState] }}
</view>
</template>
</view>
<!-- 商品信息 -->
<view class="goods">
<view class="item">
<navigator
v-for="item in detail.skus"
:key="item.id"
:url="`/pages/goods/index?id=${item.spuId}`"
hover-class="none"
>
<image class="cover" :src="item.image"></image>
<view class="meta">
<view class="name ellipsis">{{ item.name }}</view>
<view class="type">{{ item.attrsText }}</view>
<view class="price">
<view class="original">
<text class="symbol">¥</text>
<text>{{ item.curPrice }}</text>
</view>
<view class="actual">
<text class="text">实付: </text>
<text class="symbol">¥</text>
<text>{{ item.realPay }}</text>
</view>
</view>
<view class="quantity">x{{ item.quantity }}</view>
</view>
</navigator>
<view
class="action"
v-if="detail.orderState === OrderState.YiWanCheng"
>
<view class="button primary">申请售后</view>
<navigator url="/pages/comments/publish/index" class="button">
去评价
</navigator>
</view>
</view>
<!-- 合计 -->
<view class="total">
<view class="row">
<view class="text">商品总价: </view>
<view class="symbol">{{ detail.totalMoney }}</view>
</view>
<view class="row">
<view class="text">运费: </view>
<view class="symbol">{{ detail.postFee }}</view>
</view>
<view class="row paid">
<view class="text">实付: </view>
<view class="symbol primary">{{ detail.payMoney }}</view>
</view>
</view>
</view>
<!-- 订单信息 -->
<view class="detail">
<view class="title">订单信息</view>
<view class="row">
<view>订单编号: {{ detail.id }}</view>
<view>下单时间: {{ detail.createTime }}</view>
<view >
支付方式: 在线支付
</view>
<view >
支付渠道: 微信支付
</view>
</view>
</view>
</scroll-view>
<!-- 底部工具 -->
<view class="toobar" v-if="detail.orderState === OrderState.DaiFuKuan">
<view @tap="orderPay" class="primary">去支付</view>
<view class="default">取消订单</view>
</view>
</view>
</template>
<script>
import { OrderStateOptions, OrderState } from "./OrderConstance";
import { mapState } from "vuex";
export default {
computed: {
...mapState(["safeArea", "platform"]),
},
data(){
return {
detail: null,
OrderStateOptions,
OrderState
}
}
};
</script>
<style lang="scss">
.order-detail {
width: 100vw;
height: 100vh;
display: flex;
flex-direction: column;
overflow: hidden;
}
.navbar {
width: 750rpx;
color: #fff;
background-image: url(https://static.botue.com/erabbit/static/images/order_bg.png);
background-size: cover;
position: fixed;
top: 0;
left: 0;
z-index: 9;
.title {
height: 40px;
line-height: 30px;
text-align: center;
font-size: 17px;
font-weight: 500;
}
.android {
text-align: left;
padding-left: 42px;
}
.wrap {
position: relative;
}
.back {
position: absolute;
left: 10px;
top: 4px;
line-height: 1;
font-size: 23px;
z-index: 9;
}
}
.viewport {
background-color: #f7f7f8;
}
.overview {
display: flex;
flex-direction: column;
align-items: center;
line-height: 1;
padding-bottom: 30rpx;
color: #fff;
background-image: url(https://static.botue.com/erabbit/static/images/order_bg.png);
background-size: cover;
.status {
font-size: 36rpx;
&::before {
margin-right: 6rpx;
font-weight: 500;
}
}
.tips {
margin-top: 30rpx;
font-size: 24rpx;
width: 100%;
text-align: center;
display: flex;
justify-content: center;
align-items: center;
.countdown {
margin-left: 10rpx;
}
}
.button {
width: 260rpx;
height: 64rpx;
text-align: center;
line-height: 64rpx;
margin-top: 30rpx;
font-size: 28rpx;
color: #27ba9b;
border-radius: 68rpx;
background-color: #fff;
}
}
.shipment {
line-height: 1.4;
padding: 0 20rpx;
margin: 20rpx 20rpx 0;
border-radius: 10rpx;
background-color: #fff;
.locate {
background-image: url(https://static.botue.com/erabbit/static/images/locate.png);
.user {
font-size: 26rpx;
color: #444;
}
.address {
font-size: 24rpx;
color: #666;
}
}
.logistics {
background-image: url(https://static.botue.com/erabbit/static/images/car.png);
border-bottom: 1rpx solid #eee;
position: relative;
&::after {
position: absolute;
right: 10rpx;
top: 50%;
transform: translateY(-50%);
content: "\e6c2";
font-family: "erabbit" !important;
font-size: 32rpx;
color: #666;
}
.message {
font-size: 26rpx;
color: #444;
}
.date {
font-size: 24rpx;
color: #666;
}
}
}
.shipment .locate,
.shipment .logistics {
min-height: 120rpx;
padding: 30rpx 30rpx 25rpx 75rpx;
background-size: 50rpx;
background-repeat: no-repeat;
background-position: 6rpx center;
}
.goods {
margin: 20rpx 20rpx 0;
padding: 0 20rpx;
border-radius: 10rpx;
background-color: #fff;
.item {
padding: 30rpx 0;
border-bottom: 1rpx solid #eee;
navigator {
display: flex;
}
.cover {
width: 170rpx;
height: 170rpx;
border-radius: 10rpx;
margin-right: 20rpx;
}
.meta {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
position: relative;
}
.name {
height: 80rpx;
font-size: 26rpx;
color: #444;
}
.type {
line-height: 1.8;
padding: 0 15rpx;
margin-top: 6rpx;
font-size: 24rpx;
align-self: flex-start;
border-radius: 4rpx;
color: #888;
background-color: #f7f7f8;
}
.price {
display: flex;
margin-top: 6rpx;
font-size: 24rpx;
}
.symbol {
font-size: 20rpx;
}
.original {
color: #999;
text-decoration: line-through;
}
.actual {
margin-left: 10rpx;
color: #444;
}
.text {
font-size: 22rpx;
}
.quantity {
position: absolute;
bottom: 0;
right: 0;
font-size: 24rpx;
color: #444;
}
.action {
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
padding: 30rpx 0 0;
}
}
.action {
.button {
width: 200rpx;
height: 60rpx;
text-align: center;
justify-content: center;
line-height: 60rpx;
margin-left: 20rpx;
border-radius: 60rpx;
border: 1rpx solid #ccc;
font-size: 26rpx;
color: #444;
}
.primary {
color: #27ba9b;
border-color: #27ba9b;
}
}
.total {
line-height: 1;
font-size: 26rpx;
padding: 20rpx 0;
color: #666;
.row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 0;
}
.symbol {
&::before {
content: "¥";
font-size: 20rpx;
}
}
.paid {
font-size: 30rpx;
color: #444;
}
.primary {
color: #cf4444;
}
}
}
.detail {
line-height: 1;
padding: 30rpx 20rpx 0;
margin: 20rpx 20rpx 0;
font-size: 26rpx;
color: #666;
border-radius: 10rpx;
background-color: #fff;
.title {
font-size: 30rpx;
color: #444;
}
.row {
padding: 20rpx 0;
& > view {
display: block;
padding: 10rpx 0;
}
}
}
.toobar {
display: flex;
flex-direction: row-reverse;
justify-content: flex-start;
padding: 30rpx 20rpx;
background-color: #fff;
box-shadow: 0 -4rpx 6rpx rgba(240, 240, 240, 0.6);
position: relative;
z-index: 9;
& > view {
width: 200rpx;
height: 72rpx;
text-align: center;
line-height: 72rpx;
margin-left: 15rpx;
font-size: 26rpx;
border-radius: 72rpx;
}
.default {
color: #444;
border: 1rpx solid #ccc;
}
.primary {
color: #fff;
background-color: #27ba9b;
}
}
.popup-root {
padding: 30rpx 30rpx 0;
border-radius: 10rpx 10rpx 0 0;
overflow: hidden;
.title {
font-size: 30rpx;
text-align: center;
margin-bottom: 30rpx;
}
.description {
font-size: 28rpx;
padding: 0 20rpx;
.tips {
color: #444;
margin-bottom: 12rpx;
}
.cell {
display: flex;
justify-content: space-between;
align-items: center;
line-height: 1;
padding: 15rpx 0;
margin-bottom: 4rpx;
color: #666;
}
.icon-ring {
font-size: 38rpx;
color: #999;
}
.icon-checked {
font-size: 38rpx;
color: #27ba9b;
}
}
.footer {
display: flex;
justify-content: space-between;
padding: 30rpx 0 40rpx;
font-size: 28rpx;
color: #444;
.button {
flex: 1;
height: 72rpx;
text-align: center;
line-height: 72rpx;
margin: 0 20rpx;
color: #444;
border-radius: 72rpx;
border: 1rpx solid #ccc;
}
.primary {
color: #fff;
background-color: #27ba9b;
border: none;
}
}
}
</style>
请求订单详情、渲染界面
1、封装API:src/api/order.js
/**
* 获取订单详情
*/
export const geOrderDetailAPI = (id) => {
return http({
url: `/member/order/${id}`,
});
};
2、封装请求函数
3、onShow触发请求
4、保存数据
src/pages/cart/index.vue
import { geOrderDetailAPI } from "@/api/order";
export default {
data() {
return {
detail: null,
};
},
async onLoad({ id }) {
console.log("id -----> ", id);
// 💥💥 先写死一个id,方便调试, 注意使用字符串, 不要使用数字
id = "1634489003572989954";
const { result } = await geOrderDetailAPI(id);
this.detail = result;
},
};
5、渲染界面
渲染倒计时
1、根据dayjs,定义计算属性countdown
computed: {
...mapState(["safeArea", "platform"]),
+ countdown() {
+ if (this.detail) {
+ const res = dayjs.unix(this.detail.countdown).format("mm ss");
+ return res.split(" ");
+ } else {
+ return [];
+ }
+ },
},
2、绑定给倒计时组件
<uni-countdown
+ :minute="countdown[0]"
+ :second="countdonw[1]"
:showDay="false"
start
color="#fff"
/>
实现支付
文档:微信支付
注意:
💥💥必须使用企业开发者账号登录
💥💥 删除测试id
前置步骤:
- 改造登录方法,token存入vuex中
async onGetPhone(e) {
const { iv, encryptedData } = e.detail;
const [err, res] = await uni.login();
if (!err) {
const {result} = await loginByPhoneAPI({
iv,
encryptedData,
code: res.code,
});
+ this.$store.commit("user/saveProfile", result);
uni.showToast({
title: "登录成功",
icon: "success",
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
步骤:
- 封装API,获取微信支付所需参数
- 调用
uni.requestPayment
调起微信支付 - 支付成功,弹出提示,跳转到支付成功页面
/pages/order/payment
代码:
/**
* 支付-微信-小程序
*/
export const getPayWxPayMiniPay = (orderId) => {
return http({
url: `/pay/wxPay/miniPay`,
data: { orderId },
});
};
async orderPay() {
const res = await getPayWxPayMiniPay(this.order.id);
console.log("res -----> ", res);
const [err, result] = await uni.requestPayment(res.result);
if (!err) {
uni.showToast({ title: "支付成功", icon: "success" });
setTimeout(() => {
u
})
} else {
uni.showToast({ title: "支付失败", icon: "error" });
}
},
支付成功
src\pages\order\payment.vue
支付成功代码(静态)
只需要实现
- 返回首页
- 跳转到订单页面
<template>
<view class="payment">
<view class="navbar" :style="{ paddingTop: safeArea.top + 'px' }">
<view class="wrap">
<navigator class="back icon-left" @click="goBack"></navigator>
<view :class="['title', platform]">支付成功</view>
</view>
</view>
<scroll-view
class="viewport"
id="scrollView"
enhanced
scroll-y
:show-scrollbar="false"
>
<!-- 订单状态 -->
<view
class="overview"
:style="{ paddingTop: safeArea.top + 40 + 'px' }"
>
<view class="status icon-checked">支付成功</view>
<view class="buttons">
<navigator
hover-class="none"
class="button"
open-type="switchTab"
url="/pages/index/index"
>
返回首页
</navigator>
<navigator hover-class="none" url="/pages/order/index">
查看订单
</navigator>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import { mapState } from "vuex";
export default {
computed: {
...mapState(["safeArea"]),
},
data() {
return {};
},
};
</script>
<style lang="scss">
.payment {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.navbar {
width: 750rpx;
color: #fff;
background-color: #27ba9b;
position: fixed;
top: 0;
left: 0;
z-index: 9;
}
.navbar .title {
height: 40px;
line-height: 30px;
text-align: center;
font-size: 17px;
font-weight: 500;
opacity: 0;
}
.navbar .android {
text-align: left;
padding-left: 42px;
}
.navbar .wrap {
position: relative;
}
.navbar .back {
position: absolute;
left: 10px;
top: 4px;
line-height: 1;
font-size: 23px;
z-index: 9;
}
.viewport {
background-color: #f7f7f8;
}
.overview {
line-height: 1;
padding-bottom: 70rpx;
color: #fff;
background-color: #27ba9b;
}
.overview .status {
font-size: 36rpx;
font-weight: 500;
text-align: center;
}
.overview .status::before {
display: block;
font-size: 110rpx;
margin-bottom: 20rpx;
}
.overview .buttons {
height: 60rpx;
line-height: 60rpx;
display: flex;
justify-content: center;
align-items: center;
margin-top: 60rpx;
}
.overview navigator {
text-align: center;
margin: 0 10rpx;
font-size: 28rpx;
color: #fff;
&:first-child {
width: 200rpx;
border-radius: 64rpx;
border: 1rpx solid #fff;
}
}
</style>
拓展-uni-app 多端开发
条件编译
条件编译是用特殊的注释作为标记,在编译时根据这些特殊的注释,将注释里面的代码编译到不同平台。
**写法:**以 #ifdef 或 #ifndef 加 %PLATFORM% 开头,以 #endif 结尾。
- #ifdef:if defined 仅在某平台存在
- #ifndef:if not defined 除了某平台均存在
- %PLATFORM%:平台名称
条件编译写法 | 说明 |
---|---|
#ifdef APP-PLUS 需条件编译的代码 #endif | 仅出现在 App 平台下的代码 |
#ifndef H5 需条件编译的代码 #endif | 除了 H5 平台,其它平台均存在的代码 |
#ifdef H5 || MP-WEIXIN 需条件编译的代码 #endif | 在 H5 平台或微信小程序平台存在的代码(这里只有||,不可能出现&&,因为没有交集) |
%PLATFORM% 可取值如下:
值 | 平台 |
---|---|
APP-PLUS | App |
APP-PLUS-NVUE | App nvue |
H5 | H5 |
MP-WEIXIN | 微信小程序 |
MP-ALIPAY | 支付宝小程序 |
MP-BAIDU | 百度小程序 |
MP-TOUTIAO | 字节跳动小程序 |
MP-QQ | QQ小程序 |
MP-360 | 360小程序 |
MP | 微信小程序/支付宝小程序/百度小程序/字节跳动小程序/QQ小程序/360小程序 |
quickapp-webview | 快应用通用(包含联盟、华为) |
quickapp-webview-union | 快应用联盟 |
quickapp-webview-huawei | 快应用华为 |
支持的文件
- .vue
- .js
- .css
- pages.json
- 各预编译语言文件,如:.scss、.less、.stylus、.ts、.pug
注意: 条件编译是利用注释实现的,在不同语法里注释写法不一样,js使用 // 注释
、css 使用 /* 注释 */
、vue/nvue 模板里使用 <!-- 注释 -->
;
API 的条件编译
// #ifdef %PLATFORM%
平台特有的API实现
// #endif
示例,如下代码仅在 App 下出现:
示例,如下代码不会在 H5 平台上出现:
除了支持单个平台的条件编译外,还支持多平台同时编译,使用 || 来分隔平台名称。
示例,如下代码会在 App 和 H5 平台上出现:
组件的条件编译
<!-- #ifdef %PLATFORM% -->
平台特有的组件
<!-- #endif -->
示例,如下公众号关注组件仅会在微信小程序中出现:
<view>
<view>微信公众号关注组件</view>
<view>
<!-- uni-app未封装,但可直接使用微信原生的official-account组件-->
<!-- #ifdef MP-WEIXIN -->
<official-account></official-account>
<!-- #endif -->
</view>
</view>
样式的条件编译
/* #ifdef %PLATFORM% */
平台特有样式
/* #endif */
注意: 样式的条件编译,无论是 css 还是 sass/scss/less/stylus 等预编译语言中,必须使用 /*注释*/
的写法。
正确写法
错误写法
pages.json 的条件编译
下面的页面,只有运行至 App 时才会编译进去。