mi-assessment/uniapp/pages/planner/list/index.vue
2026-02-23 21:12:09 +08:00

372 lines
8.2 KiB
Vue

<template>
<view class="planner-list-page">
<!-- 自定义导航栏 -->
<Navbar title="学业规划" :showBack="true" />
<!-- 页面内容 -->
<view class="page-content">
<!-- 提示文字 -->
<view class="page-tip">点击选择您喜欢的规划师</view>
<!-- 加载状态 -->
<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="card-top">
<!-- 头像 -->
<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-row">
<text class="planner-name">{{ item.name || '规划师' }}</text>
<text v-if="item.title" class="planner-title">|{{ item.title }}</text>
</view>
<!-- 标签列表 -->
<view class="planner-tags" v-if="parseTags(item.tags).length > 0">
<view
class="tag-item"
v-for="(tag, tagIndex) in parseTags(item.tags)"
:key="tagIndex"
>
{{ tag }}
</view>
</view>
<!-- 无标签时显示简介 -->
<view class="planner-intro" v-else-if="item.introduction">
{{ item.introduction }}
</view>
</view>
</view>
<!-- 下半部分:价格 + 按钮 -->
<view class="card-bottom">
<view class="planner-price">
<text class="price-symbol">¥</text>
<text class="price-value">{{ formatPrice(item.price) }}</text>
</view>
<view class="select-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 {string} tags - 逗号分隔的标签字符串
* @returns {string[]}
*/
function parseTags(tags) {
if (!tags) return []
return tags.split(',').map(t => t.trim()).filter(Boolean)
}
/**
* 格式化价格
* @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';
// 橙色主题色
$orange-color: #E8740E;
$orange-light: #FFF8F0;
$orange-gradient-start: #FFF5EB;
$orange-gradient-end: #FFFFFF;
.planner-list-page {
min-height: 100vh;
background-color: $bg-color;
}
.page-content {
padding: 24rpx 28rpx;
padding-bottom: env(safe-area-inset-bottom);
}
// 顶部提示文字
.page-tip {
text-align: center;
font-size: $font-size-md;
color: $text-secondary;
margin-bottom: 28rpx;
}
// 规划师列表
.planner-list {
display: flex;
flex-direction: column;
gap: 24rpx;
}
// 规划师卡片 - 顶部橙色渐变,无左侧边框
.planner-card {
background: linear-gradient(180deg, $orange-gradient-start 0%, $orange-gradient-end 40%);
border-radius: 24rpx;
padding: 28rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
&:active {
opacity: 0.9;
}
}
// 上半部分:头像 + 信息
.card-top {
display: flex;
align-items: flex-start;
}
// 头像
.planner-avatar {
flex-shrink: 0;
width: 160rpx;
height: 180rpx;
border-radius: 16rpx;
overflow: hidden;
margin-right: 24rpx;
.avatar-image {
width: 100%;
height: 100%;
}
}
// 右侧信息区
.planner-info {
flex: 1;
min-width: 0;
}
// 姓名 + 职称行
.planner-name-row {
display: flex;
align-items: baseline;
flex-wrap: wrap;
margin-bottom: 16rpx;
}
.planner-name {
font-size: 34rpx;
font-weight: $font-weight-bold;
color: $text-color;
}
.planner-title {
font-size: $font-size-sm;
color: $text-secondary;
margin-left: 4rpx;
}
// 标签列表
.planner-tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.tag-item {
display: inline-flex;
align-items: center;
padding: 8rpx 18rpx;
font-size: 22rpx;
color: $orange-color;
background-color: rgba(232, 116, 14, 0.08);
border: 1rpx solid rgba(232, 116, 14, 0.2);
border-radius: 8rpx;
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;
}
// 下半部分:价格 + 按钮
.card-bottom {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 20rpx;
}
// 价格
.planner-price {
display: flex;
align-items: baseline;
.price-symbol {
font-size: 28rpx;
color: $orange-color;
font-weight: $font-weight-bold;
}
.price-value {
font-size: 48rpx;
color: $orange-color;
font-weight: $font-weight-bold;
margin-left: 2rpx;
}
}
// 点击选择按钮
.select-btn {
display: flex;
align-items: center;
justify-content: center;
padding: 18rpx 56rpx;
background-color: $orange-color;
color: $text-white;
font-size: $font-size-md;
font-weight: $font-weight-medium;
border-radius: $border-radius-round;
&:active {
opacity: 0.85;
}
}
// 空状态
.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>