432 lines
11 KiB
Vue
432 lines
11 KiB
Vue
<script setup>
|
||
/**
|
||
* 我的订单页面
|
||
* 展示用户订单列表,支持按状态筛选
|
||
* 设计稿参考蓝湖:我的订单
|
||
*/
|
||
import { ref, computed } from 'vue'
|
||
import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app'
|
||
import { useUserStore } from '@/store/user.js'
|
||
import { useAuth } from '@/composables/useAuth.js'
|
||
import { getOrderList } from '@/api/order.js'
|
||
import { retestRecord } from '@/api/assessment.js'
|
||
import Empty from '@/components/Empty/index.vue'
|
||
import Loading from '@/components/Loading/index.vue'
|
||
|
||
const userStore = useUserStore()
|
||
const { checkLogin } = useAuth()
|
||
|
||
// 筛选标签配置
|
||
const tabs = [
|
||
{ label: '全部', value: 'all' },
|
||
{ label: '已测评', value: 'completed' },
|
||
{ label: '待测评', value: 'pending' }
|
||
]
|
||
|
||
// 状态
|
||
const activeTab = ref('all')
|
||
const loading = ref(false)
|
||
const orderList = ref([])
|
||
const page = ref(1)
|
||
const pageSize = ref(10)
|
||
const total = ref(0)
|
||
const noMore = ref(false)
|
||
|
||
/**
|
||
* 获取订单的综合显示状态文本
|
||
* 优先使用后端返回的 displayStatusText,否则前端根据订单状态和测评状态计算
|
||
*/
|
||
function getDisplayStatus(order) {
|
||
if (order.displayStatusText) return order.displayStatusText
|
||
|
||
// 前端兜底计算:根据订单状态和测评记录状态
|
||
const orderStatus = order.status
|
||
const assessmentStatus = order.assessmentStatus
|
||
const bookingStatus = order.bookingStatus
|
||
|
||
// 退款/取消/待支付优先
|
||
if (orderStatus === 4) return '退款中'
|
||
if (orderStatus === 5) return '已退款'
|
||
if (orderStatus === 6) return '已取消'
|
||
if (orderStatus === 1) return '待支付'
|
||
|
||
// 规划订单,根据预约状态显示
|
||
if (order.orderType === 2 && bookingStatus) {
|
||
const bookingMap = { 1: '待联系', 2: '联系中', 3: '已完成', 4: '已取消' }
|
||
return bookingMap[bookingStatus] || order.statusText || '已支付'
|
||
}
|
||
|
||
// 已支付/已完成的订单,根据测评记录状态显示
|
||
if (assessmentStatus) {
|
||
const map = { 1: '待测评', 2: '测评中', 3: '测评生成中', 4: '已测评', 5: '生成失败', 7: '需重测' }
|
||
return map[assessmentStatus] || order.statusText || '已支付'
|
||
}
|
||
|
||
return order.statusText || '已支付'
|
||
}
|
||
|
||
/**
|
||
* 根据当前tab过滤订单列表
|
||
*/
|
||
const filteredList = computed(() => {
|
||
// 过滤掉已退款和退款中的订单
|
||
const visibleList = orderList.value.filter(order => {
|
||
const dst = getDisplayStatus(order)
|
||
return dst !== '已退款' && dst !== '退款中'
|
||
})
|
||
if (activeTab.value === 'all') return visibleList
|
||
return visibleList.filter(order => {
|
||
const dst = getDisplayStatus(order)
|
||
if (activeTab.value === 'completed') return dst === '已测评'
|
||
if (activeTab.value === 'pending') return dst === '待测评' || dst === '测评中'
|
||
return true
|
||
})
|
||
})
|
||
|
||
/**
|
||
* 获取显示状态的样式类
|
||
*/
|
||
function getStatusClass(order) {
|
||
const dst = getDisplayStatus(order)
|
||
if (dst === '已测评' || dst === '待测评' || dst === '已完成') return 'status-green'
|
||
if (dst === '测评生成中' || dst === '退款中' || dst === '已退款' || dst === '报告生成中') return 'status-red'
|
||
if (dst === '待联系' || dst === '联系中' || dst === '需重测' || dst === '生成失败') return 'status-orange'
|
||
return 'status-gray'
|
||
}
|
||
|
||
/**
|
||
* 格式化金额
|
||
*/
|
||
function formatAmount(amount) {
|
||
if (amount === undefined || amount === null) return '0.00'
|
||
return Number(amount).toFixed(2)
|
||
}
|
||
|
||
/**
|
||
* 切换标签
|
||
*/
|
||
function switchTab(tab) {
|
||
if (activeTab.value === tab.value) return
|
||
activeTab.value = tab.value
|
||
}
|
||
|
||
/**
|
||
* 加载订单列表
|
||
*/
|
||
async function loadOrderList(isRefresh = false) {
|
||
if (loading.value) return
|
||
|
||
if (isRefresh) {
|
||
page.value = 1
|
||
noMore.value = false
|
||
}
|
||
|
||
loading.value = true
|
||
|
||
try {
|
||
const res = await getOrderList({
|
||
page: page.value,
|
||
pageSize: pageSize.value
|
||
})
|
||
|
||
if (res.code === 0 && res.data) {
|
||
const list = res.data.list || []
|
||
total.value = res.data.total || 0
|
||
|
||
if (isRefresh) {
|
||
orderList.value = list
|
||
} else {
|
||
orderList.value = [...orderList.value, ...list]
|
||
}
|
||
|
||
noMore.value = orderList.value.length >= total.value
|
||
} else {
|
||
uni.showToast({ title: res.message || '获取订单失败', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
console.error('获取订单列表失败:', error)
|
||
uni.showToast({ title: '网络错误,请重试', icon: 'none' })
|
||
} finally {
|
||
loading.value = false
|
||
uni.stopPullDownRefresh()
|
||
}
|
||
}
|
||
|
||
onPullDownRefresh(() => {
|
||
loadOrderList(true)
|
||
})
|
||
|
||
onReachBottom(() => {
|
||
if (!noMore.value && !loading.value) {
|
||
page.value++
|
||
loadOrderList()
|
||
}
|
||
})
|
||
|
||
/**
|
||
* 查看测评结果
|
||
*/
|
||
function viewResult(order) {
|
||
uni.navigateTo({
|
||
url: `/pages/assessment/result/index?recordId=${order.assessmentRecordId}`
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 开始测评
|
||
*/
|
||
async function startAssessment(order) {
|
||
const dst = getDisplayStatus(order)
|
||
if (dst === '生成失败' || dst === '需重测') {
|
||
// 生成失败或需重测:调用重测接口后跳转答题页(免费重测)
|
||
try {
|
||
uni.showLoading({ title: '重置中...' })
|
||
const res = await retestRecord(order.assessmentRecordId)
|
||
uni.hideLoading()
|
||
|
||
if (res && res.code === 0) {
|
||
uni.navigateTo({
|
||
url: `/pages/assessment/questions/index?typeId=${order.productId || 1}&recordId=${order.assessmentRecordId}`
|
||
})
|
||
} else {
|
||
uni.showToast({ title: res?.message || '重置失败,请重试', icon: 'none' })
|
||
}
|
||
} catch (error) {
|
||
uni.hideLoading()
|
||
console.error('重测重置失败:', error)
|
||
uni.showToast({ title: '重置失败,请重试', icon: 'none' })
|
||
}
|
||
} else {
|
||
uni.navigateTo({
|
||
url: `/pages/assessment/questions/index?recordId=${order.assessmentRecordId}`
|
||
})
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 判断是否显示查看结果按钮
|
||
*/
|
||
function showViewResultBtn(order) {
|
||
return getDisplayStatus(order) === '已测评'
|
||
}
|
||
|
||
/**
|
||
* 判断是否显示开始测评按钮
|
||
*/
|
||
function showStartBtn(order) {
|
||
const dst = getDisplayStatus(order)
|
||
return dst === '待测评' || dst === '生成失败' || dst === '需重测'
|
||
}
|
||
|
||
onShow(() => {
|
||
userStore.restoreFromStorage()
|
||
if (checkLogin()) {
|
||
loadOrderList(true)
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="order-list-page">
|
||
<!-- 标签筛选栏 - 胶囊标签样式 -->
|
||
<view class="tab-bar">
|
||
<view
|
||
class="tab-item"
|
||
:class="{ active: activeTab === tab.value }"
|
||
v-for="tab in tabs"
|
||
:key="tab.value"
|
||
@click="switchTab(tab)"
|
||
>
|
||
{{ tab.label }}
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 页面加载中 -->
|
||
<Loading type="page" :loading="loading && orderList.length === 0" />
|
||
|
||
<!-- 订单列表 -->
|
||
<view class="order-list" v-if="filteredList.length > 0">
|
||
<view
|
||
class="order-card"
|
||
v-for="order in filteredList"
|
||
:key="order.id"
|
||
>
|
||
<!-- 订单日期 -->
|
||
<view class="info-row">
|
||
<text class="info-label">订单日期:</text>
|
||
<text class="info-value">{{ order.createTime }}</text>
|
||
</view>
|
||
<!-- 订单编号 -->
|
||
<view class="info-row">
|
||
<text class="info-label">订单编号</text>
|
||
<text class="info-value">{{ order.orderNo || '--' }}</text>
|
||
</view>
|
||
<!-- 测评项目 -->
|
||
<view class="info-row">
|
||
<text class="info-label">测评项目</text>
|
||
<text class="info-value">{{ order.productName || '--' }}</text>
|
||
</view>
|
||
<!-- 测评金额 -->
|
||
<view class="info-row">
|
||
<text class="info-label">测评金额</text>
|
||
<text class="info-value amount">¥{{ formatAmount(order.amount) }}</text>
|
||
</view>
|
||
<!-- 订单状态 + 操作按钮 -->
|
||
<view class="info-row status-row">
|
||
<view class="status-wrap">
|
||
<text class="info-label">订单状态</text>
|
||
<text class="status-text" :class="getStatusClass(order)">
|
||
{{ getDisplayStatus(order) }}
|
||
</text>
|
||
</view>
|
||
<view class="action-wrap">
|
||
<view
|
||
class="action-btn"
|
||
v-if="showViewResultBtn(order)"
|
||
@click="viewResult(order)"
|
||
>
|
||
查看测评结果
|
||
</view>
|
||
<view
|
||
class="action-btn"
|
||
v-if="showStartBtn(order)"
|
||
@click="startAssessment(order)"
|
||
>
|
||
开始测评
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 加载更多 -->
|
||
<Loading
|
||
type="more"
|
||
:loading="loading && orderList.length > 0"
|
||
:noMore="noMore"
|
||
noMoreText="没有更多订单了"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 空状态 -->
|
||
<Empty
|
||
v-if="!loading && filteredList.length === 0"
|
||
text="暂无订单记录"
|
||
:showButton="false"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/styles/variables.scss';
|
||
|
||
.order-list-page {
|
||
min-height: 100vh;
|
||
background-color: $bg-color;
|
||
padding-bottom: calc(#{$spacing-lg} + env(safe-area-inset-bottom));
|
||
}
|
||
|
||
// 标签筛选栏 - 胶囊标签样式
|
||
.tab-bar {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: $spacing-lg $spacing-lg $spacing-md;
|
||
background-color: $bg-white;
|
||
gap: $spacing-md;
|
||
|
||
.tab-item {
|
||
padding: 12rpx 32rpx;
|
||
font-size: $font-size-md;
|
||
color: $text-secondary;
|
||
border: 2rpx solid $border-color;
|
||
border-radius: $border-radius-round;
|
||
white-space: nowrap;
|
||
|
||
&.active {
|
||
color: #FF6B00;
|
||
border-color: #FF6B00;
|
||
background-color: rgba(255, 107, 0, 0.05);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 订单列表
|
||
.order-list {
|
||
padding: $spacing-md $spacing-lg;
|
||
|
||
.order-card {
|
||
background-color: $bg-white;
|
||
border-radius: $border-radius-lg;
|
||
padding: $spacing-lg;
|
||
margin-bottom: $spacing-lg;
|
||
|
||
.info-row {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: $spacing-xs 0;
|
||
|
||
.info-label {
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
font-weight: $font-weight-medium;
|
||
}
|
||
|
||
.info-value {
|
||
font-size: $font-size-md;
|
||
color: $text-color;
|
||
|
||
&.amount {
|
||
color: $error-color;
|
||
}
|
||
}
|
||
}
|
||
|
||
// 状态行:左侧状态 + 右侧按钮
|
||
.status-row {
|
||
margin-top: $spacing-xs;
|
||
|
||
.status-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: $spacing-sm;
|
||
|
||
.status-text {
|
||
font-size: $font-size-md;
|
||
font-weight: $font-weight-medium;
|
||
|
||
&.status-green {
|
||
color: $success-color;
|
||
}
|
||
|
||
&.status-red {
|
||
color: $error-color;
|
||
}
|
||
|
||
&.status-orange {
|
||
color: $warning-color;
|
||
}
|
||
|
||
&.status-gray {
|
||
color: $text-placeholder;
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-wrap {
|
||
.action-btn {
|
||
padding: 12rpx 32rpx;
|
||
font-size: $font-size-sm;
|
||
color: $text-white;
|
||
background-color: $success-color;
|
||
border-radius: $border-radius-md;
|
||
|
||
&:active {
|
||
opacity: 0.8;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|