<template>
|
<view class="rating-page" :class="{ leaving: isLeaving }">
|
<guide-nav-bar title="我的评分" @back="goBack" />
|
|
<view class="score-summary">
|
<text class="summary-label">综合评分:</text>
|
<text class="summary-stars">{{ starText(summaryScore) }}</text>
|
<text class="summary-score">{{ summaryScoreText }}</text>
|
</view>
|
|
<picker mode="date" :value="selectedDate" @change="changeDate">
|
<view class="date-filter">
|
<text>筛选日期</text>
|
<image class="calendar-icon" src="/static/icons/calendar-days.svg" mode="aspectFit" />
|
</view>
|
</picker>
|
|
<scroll-view class="rating-list" scroll-y>
|
<view v-for="item in reviews" :key="item.id" class="review-row" :class="{ 'with-images': item.images.length }">
|
<view class="review-head">
|
<text class="review-date">{{ item.date }}</text>
|
<text class="review-stars">{{ starText(item.score) }}</text>
|
</view>
|
<view class="review-comment">{{ item.comment }}</view>
|
<view v-if="item.images.length" class="image-list">
|
<view
|
v-for="(image, index) in item.images"
|
:key="`${item.id}_${index}`"
|
class="review-image-wrap"
|
@tap="previewImages(item.images, index)"
|
>
|
<image class="review-image" :src="image" mode="aspectFill" />
|
</view>
|
</view>
|
</view>
|
<view v-if="!loading && !reviews.length" class="empty-state">
|
{{ selectedDate ? '该日期暂无评价' : '暂无评价' }}
|
</view>
|
</scroll-view>
|
</view>
|
</template>
|
|
<script>
|
import GuideNavBar from '../../components/guide-nav-bar/guide-nav-bar.vue'
|
import { getGuideRatings, normalizeRatingImages, resolveRatingImageUrl } from '../../services/rating'
|
import { formatRatingStars } from '../../utils/rating'
|
import { backOrHome } from '../../utils/router'
|
|
export default {
|
components: {
|
GuideNavBar
|
},
|
data() {
|
return {
|
selectedDate: '',
|
isLeaving: false,
|
summaryScore: 0,
|
reviews: [],
|
loading: false
|
}
|
},
|
computed: {
|
summaryScoreText() {
|
return Number(this.summaryScore || 0).toFixed(1)
|
}
|
},
|
onLoad() {
|
this.loadRatings()
|
},
|
methods: {
|
loadRatings() {
|
this.loading = true
|
return getGuideRatings({ date: this.selectedDate })
|
.then((data) => {
|
this.summaryScore = Number(data.score || 0)
|
this.reviews = (data.rows || []).map((item) => ({
|
id: item.id,
|
date: item.dateText || '',
|
score: Number(item.score || 0),
|
comment: item.comment || '游客未填写评价内容',
|
images: normalizeRatingImages(item).map(resolveRatingImageUrl)
|
}))
|
})
|
.catch((error) => {
|
uni.showToast({ title: error.message || '评分加载失败', icon: 'none' })
|
})
|
.finally(() => {
|
this.loading = false
|
})
|
},
|
starText(score) {
|
return formatRatingStars(score)
|
},
|
goBack() {
|
if (this.isLeaving) {
|
return
|
}
|
this.isLeaving = true
|
setTimeout(() => {
|
backOrHome()
|
}, 280)
|
},
|
changeDate(event) {
|
this.selectedDate = event.detail.value
|
this.loadRatings()
|
},
|
previewImages(images, index) {
|
uni.previewImage({
|
current: images[index],
|
urls: images
|
})
|
}
|
}
|
}
|
</script>
|
|
<style>
|
.rating-page {
|
display: flex;
|
flex-direction: column;
|
min-height: 100vh;
|
background: #ffffff;
|
animation: pageSlideIn 260ms cubic-bezier(0.22, 1, 0.36, 1) both;
|
will-change: transform, opacity;
|
}
|
|
.rating-page.leaving {
|
animation: pageSlideOut 280ms cubic-bezier(0.4, 0, 1, 1) both;
|
}
|
|
@keyframes pageSlideIn {
|
from {
|
opacity: 0.35;
|
transform: translateX(72rpx);
|
}
|
|
to {
|
opacity: 1;
|
transform: translateX(0);
|
}
|
}
|
|
@keyframes pageSlideOut {
|
from {
|
opacity: 1;
|
transform: translateX(0);
|
}
|
|
to {
|
opacity: 0.2;
|
transform: translateX(260rpx);
|
}
|
}
|
|
.score-summary {
|
display: flex;
|
align-items: center;
|
height: 174rpx;
|
padding: 0 78rpx;
|
border-bottom: 1rpx solid #d9d9d9;
|
background: #ffffff;
|
}
|
|
.summary-label {
|
color: #0f172a;
|
font-size: 27rpx;
|
font-weight: 600;
|
}
|
|
.summary-stars {
|
margin-left: 8rpx;
|
color: #ffbf00;
|
font-size: 31rpx;
|
letter-spacing: 4rpx;
|
line-height: 1;
|
}
|
|
.summary-score {
|
margin-left: 22rpx;
|
color: #0f172a;
|
font-size: 27rpx;
|
}
|
|
.date-filter {
|
display: flex;
|
align-items: center;
|
height: 84rpx;
|
padding: 0 78rpx;
|
border-bottom: 1rpx solid #d9d9d9;
|
color: #0f172a;
|
font-size: 27rpx;
|
font-weight: 600;
|
}
|
|
.calendar-icon {
|
width: 30rpx;
|
height: 30rpx;
|
margin-left: 10rpx;
|
}
|
|
.rating-list {
|
flex: 1;
|
min-height: 0;
|
background: #ffffff;
|
}
|
|
.review-row {
|
margin-left: 78rpx;
|
padding: 42rpx 24rpx 32rpx 0;
|
border-bottom: 1rpx solid #d9d9d9;
|
}
|
|
.review-row.with-images {
|
padding-bottom: 40rpx;
|
}
|
|
.review-head {
|
display: flex;
|
align-items: center;
|
justify-content: space-between;
|
}
|
|
.review-date {
|
color: #0f172a;
|
font-size: 27rpx;
|
font-weight: 600;
|
}
|
|
.review-stars {
|
color: #ffbf00;
|
font-size: 31rpx;
|
letter-spacing: 5rpx;
|
line-height: 1;
|
}
|
|
.review-comment {
|
margin-top: 24rpx;
|
color: #0f172a;
|
font-size: 27rpx;
|
line-height: 1.3;
|
}
|
|
.image-list {
|
display: flex;
|
gap: 32rpx;
|
margin-top: 32rpx;
|
}
|
|
.review-image-wrap {
|
width: 170rpx;
|
height: 126rpx;
|
overflow: hidden;
|
background: #e6e6e6;
|
}
|
|
.review-image {
|
width: 100%;
|
height: 100%;
|
}
|
|
.empty-state {
|
padding: 96rpx 32rpx;
|
color: #94a3b8;
|
font-size: 27rpx;
|
text-align: center;
|
}
|
</style>
|