| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- <template>
- <view class="review-view-page">
- <view v-if="pageLoading" class="review-view-state">
- <u-loading-icon mode="circle" />
- </view>
- <view v-else-if="pageError" class="review-view-state">
- <u-empty mode="list" :text="pageError" icon-size="80" />
- </view>
- <template v-else-if="review">
- <view class="review-view-card">
- <text class="review-view-card__label">评分</text>
- <view class="review-stars">
- <text
- v-for="n in scoreMax"
- :key="n"
- class="review-stars__item"
- :class="{ 'review-stars__item--on': n <= review.score }"
- >★</text>
- <text class="review-stars__text">{{ review.score }} 分</text>
- </view>
- </view>
- <view class="review-view-card">
- <text class="review-view-card__label">评价内容</text>
- <text class="review-view-content">{{ review.content || '(无文字评价)' }}</text>
- <view v-if="review.pics && review.pics.length" class="review-view-pics">
- <image
- v-for="(pic, idx) in review.pics"
- :key="idx"
- class="review-view-pic"
- :src="pic"
- mode="aspectFill"
- @click="previewPics(idx)"
- />
- </view>
- <text v-if="review.createTime" class="review-view-time">评价时间:{{ review.createTime }}</text>
- </view>
- <view v-if="review.replyContent" class="review-view-card review-view-reply">
- <text class="review-view-card__label">商家回复</text>
- <text class="review-view-content">{{ review.replyContent }}</text>
- <text v-if="review.replyTime" class="review-view-time">回复时间:{{ review.replyTime }}</text>
- </view>
- <view v-if="goodsItem" class="review-view-card review-view-goods">
- <text class="review-view-card__label">评价商品</text>
- <order-goods-row :item="goodsItem" />
- </view>
- </template>
- </view>
- </template>
- <script setup>
- import { ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import { getOrderDetail } from '@/api/order'
- import { getOrderReview } from '@/api/orderReview'
- import { mapOrderDetail, mapOrderReview } from '@/utils/orderDisplay'
- import { ensureApiToken } from '@/utils/apiAuth'
- import { REVIEW_SCORE_MAX } from '@/constants/order'
- import OrderGoodsRow from '@/components/order/OrderGoodsRow.vue'
- const orderId = ref('')
- const orderItemId = ref('')
- const review = ref(null)
- const goodsItem = ref(null)
- const pageLoading = ref(false)
- const pageError = ref('')
- const scoreMax = REVIEW_SCORE_MAX
- async function loadReview() {
- if (!orderId.value || !orderItemId.value) {
- pageError.value = '评价信息缺失'
- return
- }
- pageLoading.value = true
- pageError.value = ''
- try {
- const [reviewRes, orderRes] = await Promise.all([
- getOrderReview(orderId.value, orderItemId.value),
- getOrderDetail(orderId.value)
- ])
- review.value = mapOrderReview(reviewRes.data)
- if (!review.value) {
- pageError.value = '暂无评价内容'
- return
- }
- const detail = mapOrderDetail(orderRes.data)
- const targetId = String(orderItemId.value)
- goodsItem.value =
- (detail?.items || []).find((row) => String(row.orderItemId) === targetId) || null
- } catch (e) {
- pageError.value = '加载失败'
- review.value = null
- goodsItem.value = null
- } finally {
- pageLoading.value = false
- }
- }
- function previewPics(index) {
- const urls = review.value?.pics || []
- uni.previewImage({ urls, current: urls[index] })
- }
- onLoad((options) => {
- if (!ensureApiToken(true)) return
- orderId.value = options.orderId || ''
- orderItemId.value = options.orderItemId || ''
- if (!orderId.value || !orderItemId.value) {
- pageError.value = '参数错误'
- return
- }
- loadReview()
- })
- </script>
- <style lang="scss" scoped>
- .review-view-page {
- min-height: 100vh;
- padding: 24rpx;
- background: #f5f6f8;
- box-sizing: border-box;
- }
- .review-view-state {
- padding: 120rpx 0;
- display: flex;
- justify-content: center;
- }
- .review-view-card {
- margin-bottom: 16rpx;
- padding: 24rpx;
- background: #fff;
- border-radius: 12rpx;
- }
- .review-view-card__label {
- display: block;
- margin-bottom: 16rpx;
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- }
- .review-stars {
- display: flex;
- align-items: center;
- gap: 8rpx;
- }
- .review-stars__item {
- font-size: 40rpx;
- color: #ddd;
- }
- .review-stars__item--on {
- color: #ffb300;
- }
- .review-stars__text {
- margin-left: 8rpx;
- font-size: 26rpx;
- color: #666;
- }
- .review-view-content {
- font-size: 28rpx;
- color: #444;
- line-height: 1.6;
- white-space: pre-wrap;
- word-break: break-word;
- }
- .review-view-pics {
- margin-top: 16rpx;
- display: flex;
- flex-wrap: wrap;
- gap: 12rpx;
- }
- .review-view-pic {
- width: 160rpx;
- height: 160rpx;
- border-radius: 8rpx;
- background: #eee;
- }
- .review-view-time {
- display: block;
- margin-top: 16rpx;
- font-size: 24rpx;
- color: #999;
- }
- .review-view-reply {
- background: #f9fff9;
- }
- .review-view-goods :deep(.order-goods-row) {
- padding-top: 0;
- }
- </style>
|