巴青农资商城

review-view.vue 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. <template>
  2. <view class="review-view-page">
  3. <view v-if="pageLoading" class="review-view-state">
  4. <u-loading-icon mode="circle" />
  5. </view>
  6. <view v-else-if="pageError" class="review-view-state">
  7. <u-empty mode="list" :text="pageError" icon-size="80" />
  8. </view>
  9. <template v-else-if="review">
  10. <view class="review-view-card">
  11. <text class="review-view-card__label">评分</text>
  12. <view class="review-stars">
  13. <text
  14. v-for="n in scoreMax"
  15. :key="n"
  16. class="review-stars__item"
  17. :class="{ 'review-stars__item--on': n <= review.score }"
  18. >★</text>
  19. <text class="review-stars__text">{{ review.score }} 分</text>
  20. </view>
  21. </view>
  22. <view class="review-view-card">
  23. <text class="review-view-card__label">评价内容</text>
  24. <text class="review-view-content">{{ review.content || '(无文字评价)' }}</text>
  25. <view v-if="review.pics && review.pics.length" class="review-view-pics">
  26. <image
  27. v-for="(pic, idx) in review.pics"
  28. :key="idx"
  29. class="review-view-pic"
  30. :src="pic"
  31. mode="aspectFill"
  32. @click="previewPics(idx)"
  33. />
  34. </view>
  35. <text v-if="review.createTime" class="review-view-time">评价时间:{{ review.createTime }}</text>
  36. </view>
  37. <view v-if="review.replyContent" class="review-view-card review-view-reply">
  38. <text class="review-view-card__label">商家回复</text>
  39. <text class="review-view-content">{{ review.replyContent }}</text>
  40. <text v-if="review.replyTime" class="review-view-time">回复时间:{{ review.replyTime }}</text>
  41. </view>
  42. <view v-if="goodsItem" class="review-view-card review-view-goods">
  43. <text class="review-view-card__label">评价商品</text>
  44. <order-goods-row :item="goodsItem" />
  45. </view>
  46. </template>
  47. </view>
  48. </template>
  49. <script setup>
  50. import { ref } from 'vue'
  51. import { onLoad } from '@dcloudio/uni-app'
  52. import { getOrderDetail } from '@/api/order'
  53. import { getOrderReview } from '@/api/orderReview'
  54. import { mapOrderDetail, mapOrderReview } from '@/utils/orderDisplay'
  55. import { ensureApiToken } from '@/utils/apiAuth'
  56. import { REVIEW_SCORE_MAX } from '@/constants/order'
  57. import OrderGoodsRow from '@/components/order/OrderGoodsRow.vue'
  58. const orderId = ref('')
  59. const orderItemId = ref('')
  60. const review = ref(null)
  61. const goodsItem = ref(null)
  62. const pageLoading = ref(false)
  63. const pageError = ref('')
  64. const scoreMax = REVIEW_SCORE_MAX
  65. async function loadReview() {
  66. if (!orderId.value || !orderItemId.value) {
  67. pageError.value = '评价信息缺失'
  68. return
  69. }
  70. pageLoading.value = true
  71. pageError.value = ''
  72. try {
  73. const [reviewRes, orderRes] = await Promise.all([
  74. getOrderReview(orderId.value, orderItemId.value),
  75. getOrderDetail(orderId.value)
  76. ])
  77. review.value = mapOrderReview(reviewRes.data)
  78. if (!review.value) {
  79. pageError.value = '暂无评价内容'
  80. return
  81. }
  82. const detail = mapOrderDetail(orderRes.data)
  83. const targetId = String(orderItemId.value)
  84. goodsItem.value =
  85. (detail?.items || []).find((row) => String(row.orderItemId) === targetId) || null
  86. } catch (e) {
  87. pageError.value = '加载失败'
  88. review.value = null
  89. goodsItem.value = null
  90. } finally {
  91. pageLoading.value = false
  92. }
  93. }
  94. function previewPics(index) {
  95. const urls = review.value?.pics || []
  96. uni.previewImage({ urls, current: urls[index] })
  97. }
  98. onLoad((options) => {
  99. if (!ensureApiToken(true)) return
  100. orderId.value = options.orderId || ''
  101. orderItemId.value = options.orderItemId || ''
  102. if (!orderId.value || !orderItemId.value) {
  103. pageError.value = '参数错误'
  104. return
  105. }
  106. loadReview()
  107. })
  108. </script>
  109. <style lang="scss" scoped>
  110. .review-view-page {
  111. min-height: 100vh;
  112. padding: 24rpx;
  113. background: #f5f6f8;
  114. box-sizing: border-box;
  115. }
  116. .review-view-state {
  117. padding: 120rpx 0;
  118. display: flex;
  119. justify-content: center;
  120. }
  121. .review-view-card {
  122. margin-bottom: 16rpx;
  123. padding: 24rpx;
  124. background: #fff;
  125. border-radius: 12rpx;
  126. }
  127. .review-view-card__label {
  128. display: block;
  129. margin-bottom: 16rpx;
  130. font-size: 28rpx;
  131. color: #333;
  132. font-weight: 500;
  133. }
  134. .review-stars {
  135. display: flex;
  136. align-items: center;
  137. gap: 8rpx;
  138. }
  139. .review-stars__item {
  140. font-size: 40rpx;
  141. color: #ddd;
  142. }
  143. .review-stars__item--on {
  144. color: #ffb300;
  145. }
  146. .review-stars__text {
  147. margin-left: 8rpx;
  148. font-size: 26rpx;
  149. color: #666;
  150. }
  151. .review-view-content {
  152. font-size: 28rpx;
  153. color: #444;
  154. line-height: 1.6;
  155. white-space: pre-wrap;
  156. word-break: break-word;
  157. }
  158. .review-view-pics {
  159. margin-top: 16rpx;
  160. display: flex;
  161. flex-wrap: wrap;
  162. gap: 12rpx;
  163. }
  164. .review-view-pic {
  165. width: 160rpx;
  166. height: 160rpx;
  167. border-radius: 8rpx;
  168. background: #eee;
  169. }
  170. .review-view-time {
  171. display: block;
  172. margin-top: 16rpx;
  173. font-size: 24rpx;
  174. color: #999;
  175. }
  176. .review-view-reply {
  177. background: #f9fff9;
  178. }
  179. .review-view-goods :deep(.order-goods-row) {
  180. padding-top: 0;
  181. }
  182. </style>