mi-assessment/uniapp/pages/order/list/index.vue
zpc ef186007c4
All checks were successful
continuous-integration/drone/push Build is passing
21
2026-04-08 19:33:23 +08:00

432 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>