mi-assessment/uniapp/pages/planner/list/index.vue
2026-02-20 14:57:43 +08:00

317 lines
6.9 KiB
Vue

<template>
<view class="planner-list-page">
<!-- 自定义导航栏 -->
<Navbar title="学业规划" :showBack="true" />
<!-- 页面内容 -->
<view class="page-content">
<!-- 加载状态 -->
<Loading v-if="pageLoading" type="page" :loading="true" />
<!-- 规划师列表 -->
<view v-else class="planner-list">
<!-- 有数据时显示列表 -->
<template v-if="plannerList.length > 0">
<view
class="planner-card"
v-for="(item, index) in plannerList"
:key="item.id || index"
@click="handlePlannerClick(item)"
>
<!-- 规划师头像 -->
<view class="planner-avatar">
<image
:src="item.avatar || item.photo || '/static/ic_empty.png'"
mode="aspectFill"
class="avatar-image"
@error="handleAvatarError(index)"
/>
</view>
<!-- 规划师信息 -->
<view class="planner-info">
<!-- 姓名 -->
<view class="planner-name">{{ item.name || '规划师' }}</view>
<!-- 介绍 -->
<view class="planner-intro">{{ item.intro || item.introduction || '暂无介绍' }}</view>
<!-- 价格 -->
<view class="planner-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ formatPrice(item.price) }}</text>
<text class="price-unit">/次</text>
</view>
</view>
<!-- 预约按钮 -->
<view class="planner-action">
<view class="book-btn">预约</view>
</view>
</view>
</template>
<!-- 无数据时显示空状态 -->
<view v-else class="empty-state">
<image
src="/static/ic_empty.png"
mode="aspectFit"
class="empty-icon"
/>
<text class="empty-text">暂无规划师</text>
</view>
</view>
<!-- 底部安全区域 -->
<view class="safe-bottom"></view>
</view>
</view>
</template>
<script setup>
/**
* 规划师列表页面
* 展示规划师卡片:照片、姓名、介绍、价格
*/
import { ref, onMounted } from 'vue'
import { onPullDownRefresh } from '@dcloudio/uni-app'
import { getPlannerList } from '@/api/planner.js'
import Navbar from '@/components/Navbar/index.vue'
import Loading from '@/components/Loading/index.vue'
// 页面状态
const pageLoading = ref(true)
const plannerList = ref([])
/**
* 加载规划师列表
*/
async function loadPlannerList() {
pageLoading.value = true
try {
const res = await getPlannerList()
if (res && res.code === 0 && res.data) {
// 支持多种数据格式
if (Array.isArray(res.data)) {
plannerList.value = res.data
} else if (res.data.list && Array.isArray(res.data.list)) {
plannerList.value = res.data.list
} else {
plannerList.value = []
}
}
} catch (error) {
console.error('加载规划师列表失败:', error)
uni.showToast({
title: '加载失败,请稍后重试',
icon: 'none'
})
} finally {
pageLoading.value = false
}
}
/**
* 格式化价格
* @param {number|string} price - 价格
* @returns {string} 格式化后的价格
*/
function formatPrice(price) {
if (price === undefined || price === null) return '0'
const num = Number(price)
if (isNaN(num)) return '0'
// 如果是整数,不显示小数点
if (Number.isInteger(num)) return num.toString()
// 否则保留两位小数
return num.toFixed(2)
}
/**
* 处理头像加载失败
*/
function handleAvatarError(index) {
console.error(`规划师头像 ${index + 1} 加载失败`)
}
/**
* 处理规划师点击
*/
function handlePlannerClick(item) {
// 跳转到预约页面
uni.navigateTo({
url: `/pages/planner/book/index?plannerId=${item.id}&plannerName=${encodeURIComponent(item.name || '')}`
})
}
/**
* 下拉刷新
*/
onPullDownRefresh(async () => {
await loadPlannerList()
uni.stopPullDownRefresh()
})
/**
* 页面加载
*/
onMounted(() => {
loadPlannerList()
})
</script>
<style lang="scss" scoped>
@import '@/styles/variables.scss';
.planner-list-page {
min-height: 100vh;
background-color: $bg-color;
}
// 页面内容
.page-content {
padding: $spacing-lg;
padding-bottom: env(safe-area-inset-bottom);
}
// 规划师列表
.planner-list {
display: flex;
flex-direction: column;
gap: $spacing-md;
}
// 规划师卡片
.planner-card {
display: flex;
align-items: center;
background-color: $bg-white;
border-radius: $border-radius-lg;
padding: $spacing-lg;
box-shadow: $shadow-sm;
&:active {
opacity: 0.9;
}
}
// 规划师头像
.planner-avatar {
flex-shrink: 0;
width: 160rpx;
height: 160rpx;
border-radius: $border-radius-md;
overflow: hidden;
margin-right: $spacing-md;
.avatar-image {
width: 100%;
height: 100%;
}
}
// 规划师信息
.planner-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: $spacing-xs;
}
// 规划师姓名
.planner-name {
font-size: $font-size-lg;
font-weight: $font-weight-medium;
color: $text-color;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
// 规划师介绍
.planner-intro {
font-size: $font-size-sm;
color: $text-secondary;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
line-height: 1.5;
}
// 规划师价格
.planner-price {
display: flex;
align-items: baseline;
margin-top: $spacing-xs;
.price-symbol {
font-size: $font-size-sm;
color: $error-color;
font-weight: $font-weight-medium;
}
.price-value {
font-size: $font-size-xl;
color: $error-color;
font-weight: $font-weight-bold;
margin-left: 2rpx;
}
.price-unit {
font-size: $font-size-xs;
color: $text-placeholder;
margin-left: 4rpx;
}
}
// 预约按钮区域
.planner-action {
flex-shrink: 0;
margin-left: $spacing-md;
.book-btn {
display: flex;
align-items: center;
justify-content: center;
width: 120rpx;
height: 64rpx;
background-color: $primary-color;
color: $text-white;
font-size: $font-size-md;
font-weight: $font-weight-medium;
border-radius: $border-radius-round;
&:active {
background-color: $primary-dark;
}
}
}
// 空状态
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 200rpx 0;
.empty-icon {
width: 200rpx;
height: 200rpx;
margin-bottom: $spacing-lg;
}
.empty-text {
font-size: $font-size-md;
color: $text-placeholder;
}
}
// 底部安全区域
.safe-bottom {
height: 40rpx;
padding-bottom: env(safe-area-inset-bottom);
}
</style>