| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198 |
- <template>
- <view class="review-edit-page">
- <view v-if="pageLoading" class="review-edit-state">
- <u-loading-icon mode="circle" />
- </view>
- <template v-else>
- <view v-if="goodsItem" class="review-edit-card review-edit-goods">
- <text class="review-edit-card__label">评价商品</text>
- <order-goods-row :item="goodsItem" />
- </view>
- <view class="review-edit-card">
- <text class="review-edit-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 <= score }"
- @click="score = n"
- >★</text>
- </view>
- </view>
- <view class="review-edit-card">
- <text class="review-edit-card__label">评价内容</text>
- <textarea
- class="review-edit-textarea"
- v-model="content"
- :maxlength="contentMax"
- placeholder="选填,分享您的购物体验"
- />
- <text class="review-edit-count">{{ content.length }}/{{ contentMax }}</text>
- </view>
- <view class="review-edit-card">
- <image-upload-grid v-model="pics" label="上传图片" :max="picMax" />
- </view>
- <button class="review-submit-btn" :disabled="submitting || score < 1" @click="onSubmit">
- {{ submitting ? '提交中...' : '提交评价' }}
- </button>
- </template>
- </view>
- </template>
- <script setup>
- import { ref } from 'vue'
- import { onLoad } from '@dcloudio/uni-app'
- import { getOrderDetail } from '@/api/order'
- import { submitReview } from '@/api/orderReview'
- import { mapOrderDetail } from '@/utils/orderDisplay'
- import { ensureApiToken } from '@/utils/apiAuth'
- import { REVIEW_SCORE_MAX, REVIEW_PIC_MAX, REVIEW_CONTENT_MAX } from '@/constants/order'
- import { useActionGuard } from '@/utils/actionGuard'
- import ImageUploadGrid from '@/components/order/ImageUploadGrid.vue'
- import OrderGoodsRow from '@/components/order/OrderGoodsRow.vue'
- const submitGuard = useActionGuard()
- const orderId = ref('')
- const orderItemId = ref('')
- const goodsItem = ref(null)
- const score = ref(5)
- const content = ref('')
- const pics = ref([])
- const submitting = submitGuard.locked
- const pageLoading = ref(false)
- const scoreMax = REVIEW_SCORE_MAX
- const picMax = REVIEW_PIC_MAX
- const contentMax = REVIEW_CONTENT_MAX
- function onSubmit() {
- submitGuard.run(async () => {
- if (score.value < 1) {
- uni.showToast({ title: '请选择星级', icon: 'none' })
- return
- }
- const payload = {
- score: score.value,
- content: content.value.trim(),
- pics: pics.value
- }
- if (orderItemId.value) {
- payload.orderItemId = Number(orderItemId.value) || orderItemId.value
- }
- await submitReview(orderId.value, payload)
- uni.showToast({ title: '评价成功', icon: 'success' })
- setTimeout(() => {
- uni.navigateBack()
- }, 500)
- })
- }
- async function loadGoods() {
- pageLoading.value = true
- try {
- const res = await getOrderDetail(orderId.value)
- const detail = mapOrderDetail(res.data)
- const targetId = String(orderItemId.value)
- goodsItem.value =
- (detail?.items || []).find((row) => String(row.orderItemId) === targetId) || null
- if (!goodsItem.value) {
- uni.showToast({ title: '商品信息不存在', icon: 'none' })
- setTimeout(() => uni.navigateBack(), 800)
- }
- } catch (e) {
- uni.showToast({ title: '加载失败', icon: 'none' })
- setTimeout(() => uni.navigateBack(), 800)
- } finally {
- pageLoading.value = false
- }
- }
- onLoad((options) => {
- if (!ensureApiToken(true)) return
- orderId.value = options.orderId || ''
- orderItemId.value = options.orderItemId || ''
- if (!orderId.value || !orderItemId.value) {
- uni.showToast({ title: '订单信息缺失', icon: 'none' })
- setTimeout(() => uni.navigateBack(), 800)
- return
- }
- loadGoods()
- })
- </script>
- <style lang="scss" scoped>
- .review-edit-page {
- min-height: 100vh;
- padding: 24rpx;
- background: #f5f6f8;
- box-sizing: border-box;
- }
- .review-edit-state {
- padding: 120rpx 0;
- display: flex;
- justify-content: center;
- }
- .review-edit-card {
- margin-bottom: 16rpx;
- padding: 24rpx;
- background: #fff;
- border-radius: 12rpx;
- }
- .review-edit-goods :deep(.order-goods-row) {
- padding-top: 0;
- }
- .review-edit-card__label {
- display: block;
- margin-bottom: 16rpx;
- font-size: 28rpx;
- color: #333;
- font-weight: 500;
- }
- .review-stars {
- display: flex;
- gap: 12rpx;
- }
- .review-stars__item {
- font-size: 48rpx;
- color: #ddd;
- }
- .review-stars__item--on {
- color: #ffb300;
- }
- .review-edit-textarea {
- width: 100%;
- min-height: 200rpx;
- padding: 16rpx;
- font-size: 28rpx;
- background: #f9f9f9;
- border-radius: 8rpx;
- box-sizing: border-box;
- }
- .review-edit-count {
- display: block;
- margin-top: 8rpx;
- text-align: right;
- font-size: 24rpx;
- color: #999;
- }
- .review-submit-btn {
- margin-top: 32rpx;
- height: 88rpx;
- line-height: 88rpx;
- background: linear-gradient(135deg, #43a047 0%, #2e7d32 100%);
- color: #fff;
- font-size: 30rpx;
- border-radius: 44rpx;
- border: none;
- }
- .review-submit-btn[disabled] {
- opacity: 0.5;
- }
- </style>
|