1. App.vue 启动时立即预加载 uiConfig,杜绝首屏网络延时导致瞬间闪现付费会员入口 2. 会员中心 (member/index.vue):在提审防审模式下直接展示纯净【便民服务指南】卡片,杜绝任何 VIP 皇冠、会员标价、折扣与充值入口 3. 个人中心 (mine/index.vue):提审防审模式下彻底隐藏会员卡片与我的余额,紧凑上移卡片布局 4. 订单明细、充值页与消息中心增加防审合规兼容,避免误触发任何虚拟支付诱导
363 行
8.5 KiB
Vue
363 行
8.5 KiB
Vue
<template>
|
||
<view class="orders fc-page">
|
||
<!-- 规范化顶部导航 -->
|
||
<fc-navbar title="我的订单" theme="gradient" :show-back="true" />
|
||
<view v-if="!isAudit" class="orders__tabs">
|
||
<view v-for="t in tabs" :key="t.value" class="orders__tab"
|
||
:class="{ 'orders__tab--active': type === t.value }" @click="type = t.value">
|
||
<text>{{ t.label }}</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 未登录友好引导 -->
|
||
<view v-if="!isLogin" class="orders__unlogin-box">
|
||
<fc-empty icon="receipt" text="登录后查看我的订单记录" />
|
||
<button class="orders__unlogin-btn" @click="goLogin">
|
||
<text class="orders__unlogin-btn-txt">立即登录</text>
|
||
</button>
|
||
</view>
|
||
|
||
<fc-empty v-else-if="!list.length" icon="receipt" text="暂无订单记录" />
|
||
|
||
<view v-else class="orders__list">
|
||
<view v-for="o in list" :key="o.id" class="orders__item" @click="detail(o)">
|
||
<view class="orders__item-head">
|
||
<text class="orders__no">订单号:{{ o.orderNo }}</text>
|
||
<text class="orders__status" :class="`orders__status--${o.status}`">{{ statusText(o.status) }}</text>
|
||
</view>
|
||
<view class="orders__item-body">
|
||
<view class="orders__icon">
|
||
<image class="fc-icon fc-icon--lg" :src="iconUrl(orderIconKey(o.type))" mode="aspectFit" />
|
||
</view>
|
||
<view class="orders__info">
|
||
<text class="orders__title">{{ o.title }}</text>
|
||
<text class="orders__time">{{ timeText(o.createdAt) }}</text>
|
||
</view>
|
||
<view class="fc-price">
|
||
<text class="fc-price__symbol">¥</text>
|
||
<text class="orders__amount">{{ o.amount }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="orders__item-foot" @click.stop>
|
||
<text class="orders__pay-type">{{ payText(o.payType) }}</text>
|
||
<view class="orders__actions">
|
||
<template v-if="o.status === 'pending'">
|
||
<view class="orders__btn orders__btn--cancel" @click="cancelOrder(o)"><text>取消订单</text></view>
|
||
<view class="orders__btn orders__btn--pay" @click="payOrder(o)"><text>去支付</text></view>
|
||
</template>
|
||
<view v-else class="orders__btn orders__btn--ghost" @click="detail(o)"><text>查看详情</text></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import FcNavbar from '@/components/fc-navbar/fc-navbar.vue'
|
||
import FcEmpty from '@/components/fc-empty/fc-empty.vue'
|
||
import memberApi from '@/common/api/member.js'
|
||
import orderApi from '@/common/api/order.js'
|
||
import { ORDER_TEXT, ORDER_ICON_KEY } from '@/common/constants/ui.js'
|
||
import { formatTime } from '@/common/utils/format.js'
|
||
import { iconUrl, loadUiConfig, uiState } from '@/common/store/ui.js'
|
||
import { pay } from '@/common/utils/pay.js'
|
||
|
||
export default {
|
||
components: { FcNavbar, FcEmpty },
|
||
data() {
|
||
return {
|
||
type: 'all',
|
||
all: [],
|
||
|
||
}
|
||
},
|
||
computed: {
|
||
/** 提审防审模式开关 */
|
||
isAudit() {
|
||
// #ifdef MP-WEIXIN
|
||
return uiState.virtualPayAudit !== false
|
||
// #endif
|
||
return false
|
||
},
|
||
/** 是否已登录 */
|
||
isLogin() {
|
||
const token = uni.getStorageSync('token') || uni.getStorageSync('fc_token') || ''
|
||
return !!token
|
||
},
|
||
/** 动态选项卡:提审模式彻底隐藏会员与充值订单标签 */
|
||
tabs() {
|
||
if (this.isAudit) {
|
||
return [
|
||
{ label: '全部订单', value: 'all' },
|
||
{ label: '发布订单', value: 'post' },
|
||
]
|
||
}
|
||
return [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '发布订单', value: 'post' },
|
||
{ label: '会员订单', value: 'member' },
|
||
{ label: '充值订单', value: 'recharge' },
|
||
]
|
||
},
|
||
/** 订单列表(提审模式过滤掉虚拟支付相关历史记录) */
|
||
list() {
|
||
let rows = this.all || []
|
||
if (this.isAudit) {
|
||
rows = rows.filter(o => o.type !== 'member' && o.type !== 'recharge')
|
||
}
|
||
if (this.type === 'all') return rows
|
||
return rows.filter((o) => o.type === this.type)
|
||
},
|
||
},
|
||
onLoad() {
|
||
loadUiConfig()
|
||
},
|
||
onShow() {
|
||
this.fetch()
|
||
},
|
||
methods: {
|
||
iconUrl,
|
||
async fetch() {
|
||
if (!this.isLogin) {
|
||
this.all = []
|
||
return
|
||
}
|
||
try {
|
||
const res = await memberApi.orders({ page: 1, size: 50 })
|
||
this.all = (res && res.list) || []
|
||
} catch (e) {
|
||
// 静默置空,杜绝未登录或网络波动时弹出生硬 Toast
|
||
this.all = []
|
||
}
|
||
},
|
||
goLogin() {
|
||
uni.navigateTo({ url: '/pages/auth/login' })
|
||
},
|
||
statusText(s) {
|
||
return ORDER_TEXT[s] || s
|
||
},
|
||
payText(t) {
|
||
return t === 'balance' ? '余额支付' : '微信支付'
|
||
},
|
||
timeText(t) {
|
||
return formatTime(t)
|
||
},
|
||
orderIconKey(t) {
|
||
return ORDER_ICON_KEY[t] || 'order-other'
|
||
},
|
||
/**
|
||
* 待支付订单去支付
|
||
* @param {Object} o 订单对象
|
||
* @returns {Promise<void>}
|
||
*/
|
||
async payOrder(o) {
|
||
if (!o || !o.orderNo) return
|
||
try {
|
||
uni.showLoading({ title: '正在调起支付…' })
|
||
await pay({ orderNo: o.orderNo, payType: o.payType || 'wxpay' })
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '支付成功', icon: 'success' })
|
||
await this.fetch()
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: e.message || '支付未完成', icon: 'none' })
|
||
}
|
||
},
|
||
/**
|
||
* 主动取消待支付订单
|
||
* @param {Object} o 订单对象
|
||
*/
|
||
cancelOrder(o) {
|
||
if (!o || !o.orderNo) return
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定要取消此订单吗?',
|
||
confirmColor: '#00E070',
|
||
cancelColor: '#666666',
|
||
success: async (res) => {
|
||
if (res.confirm) {
|
||
try {
|
||
uni.showLoading({ title: '正在取消…' })
|
||
await orderApi.cancel({ orderNo: o.orderNo })
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '订单已取消', icon: 'success' })
|
||
await this.fetch()
|
||
} catch (e) {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: e.message || '取消失败', icon: 'none' })
|
||
}
|
||
}
|
||
},
|
||
})
|
||
},
|
||
/**
|
||
* 跳转订单详情页面
|
||
* @param {Object} o 订单对象
|
||
*/
|
||
detail(o) {
|
||
if (!o || !o.orderNo) return
|
||
uni.navigateTo({
|
||
url: `/pages/order/detail?orderNo=${o.orderNo}`,
|
||
})
|
||
},
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.orders__tabs {
|
||
display: flex;
|
||
background: #ffffff;
|
||
border-bottom: 2rpx solid $fc-border;
|
||
}
|
||
.orders__tab {
|
||
flex: 1;
|
||
height: 88rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: $fc-font-sm;
|
||
color: $fc-text-secondary;
|
||
&--active {
|
||
color: $fc-primary-text;
|
||
font-weight: 700;
|
||
position: relative;
|
||
}
|
||
}
|
||
.orders__list {
|
||
padding: 20rpx 0;
|
||
}
|
||
.orders__item {
|
||
margin: 0 24rpx 20rpx;
|
||
padding: 24rpx;
|
||
background: #ffffff;
|
||
border-radius: $fc-radius-lg;
|
||
box-shadow: $fc-shadow;
|
||
}
|
||
.orders__item-head {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
.orders__no {
|
||
font-size: $fc-font-xs;
|
||
color: $fc-text-placeholder;
|
||
}
|
||
.orders__status {
|
||
font-size: $fc-font-sm;
|
||
font-weight: 600;
|
||
color: $fc-primary-text;
|
||
&--paid {
|
||
color: $fc-success;
|
||
}
|
||
&--pending {
|
||
color: $fc-warning;
|
||
}
|
||
&--refunded {
|
||
color: $fc-danger;
|
||
}
|
||
&--closed {
|
||
color: #999999;
|
||
}
|
||
}
|
||
.orders__item-body {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 20rpx;
|
||
}
|
||
.orders__icon {
|
||
width: 96rpx;
|
||
height: 96rpx;
|
||
flex: 0 0 96rpx;
|
||
border-radius: 20rpx;
|
||
background: $fc-gradient-soft;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
.orders__info {
|
||
flex: 1;
|
||
margin-left: 20rpx;
|
||
}
|
||
.orders__title {
|
||
display: block;
|
||
font-size: $fc-font;
|
||
font-weight: 600;
|
||
color: $fc-text;
|
||
}
|
||
.orders__time {
|
||
display: block;
|
||
margin-top: 8rpx;
|
||
font-size: $fc-font-xs;
|
||
color: $fc-text-placeholder;
|
||
}
|
||
.orders__amount {
|
||
font-size: 36rpx;
|
||
}
|
||
.orders__item-foot {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 20rpx;
|
||
padding-top: 20rpx;
|
||
border-top: 2rpx solid $fc-border;
|
||
}
|
||
.orders__pay-type {
|
||
font-size: $fc-font-xs;
|
||
color: $fc-text-secondary;
|
||
}
|
||
.orders__actions {
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
.orders__btn {
|
||
padding: 12rpx 28rpx;
|
||
border-radius: $fc-radius-pill;
|
||
font-size: $fc-font-sm;
|
||
font-weight: 600;
|
||
margin-left: 16rpx;
|
||
|
||
&--pay {
|
||
background: #00E070;
|
||
color: #04301E;
|
||
}
|
||
|
||
&--cancel {
|
||
background: #ffffff;
|
||
border: 2rpx solid #D8E2DC;
|
||
color: #666666;
|
||
}
|
||
|
||
&--ghost {
|
||
background: #ffffff;
|
||
border: 2rpx solid $fc-primary-text;
|
||
color: $fc-primary-text;
|
||
}
|
||
}
|
||
/* 未登录空状态引导 */
|
||
.orders__unlogin-box {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
padding: 40rpx 32rpx;
|
||
}
|
||
.orders__unlogin-btn {
|
||
margin-top: -20rpx;
|
||
height: 72rpx;
|
||
padding: 0 48rpx;
|
||
border-radius: 36rpx;
|
||
background: #00D26A;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
border: none;
|
||
box-shadow: 0 4rpx 16rpx rgba(0, 210, 106, 0.25);
|
||
}
|
||
.orders__unlogin-btn-txt {
|
||
font-size: 26rpx;
|
||
font-weight: 700;
|
||
color: #ffffff;
|
||
}
|
||
</style>
|