Files
blind-select/frontend-app/pages/blind/review.vue
T
admin 06488f0237 Initial commit: 帮我选盲选应用
功能:
- Go后端 (Gin + GORM + PostgreSQL)
- UniApp用户端 (iOS/Android/小程序)
- DaisyUI5后台管理
- JWT认证 + 微信登录
- 盲选加权算法
- 会员系统 + 优惠券
- 打分评价 + 偏好学习
2026-06-08 20:18:31 +00:00

323 lines
7.3 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-review">
<view class="review-header">
<text class="review-title"> 打分评价</text>
<text class="review-subtitle">你对这次盲选体验满意吗</text>
</view>
<!-- 商家名 -->
<view class="merchant-info card">
<text class="merchant-name">{{ packageName }}</text>
<text class="merchant-shop">{{ shopName }}</text>
</view>
<!-- 总体评分 -->
<view class="overall-rating card">
<text class="section-label">你打几分</text>
<view class="overall-stars">
<text
v-for="i in 5" :key="i"
class="star"
:class="{ active: overallRating >= i }"
@click="overallRating = i"
></text>
</view>
<text class="rating-label" v-if="overallRating">{{ ratingLabels[overallRating - 1] }}</text>
</view>
<!-- 维度评分 -->
<view class="dimensions card">
<text class="section-label">维度评分</text>
<view class="dimension-row" v-for="dim in dimensions" :key="dim.key">
<text class="dim-icon">{{ dim.icon }}</text>
<text class="dim-label">{{ dim.label }}</text>
<view class="dim-stars">
<text
v-for="i in 5" :key="i"
class="star small"
:class="{ active: dimensionScores[dim.key] >= i }"
@click="setDimScore(dim.key, i)"
></text>
</view>
</view>
</view>
<!-- 标签选择 -->
<view class="tags-section card">
<text class="section-label">怎么形容这次体验</text>
<view class="tags">
<view
class="tag-btn"
:class="{ active: selectedTags.includes(tag) }"
v-for="tag in allTags"
:key="tag"
@click="toggleTag(tag)"
>
<text>{{ tag }}</text>
</view>
</view>
</view>
<!-- 备注 -->
<view class="note-section card">
<text class="section-label">备注选填</text>
<textarea
class="note-textarea"
placeholder="写点什么..."
v-model="noteText"
maxlength="200"
></textarea>
</view>
<!-- 再次前往 -->
<view class="repeat-section card">
<view class="repeat-option" @click="isRepeat = !isRepeat">
<text class="repeat-icon">{{ isRepeat ? '🔁' : '○' }}</text>
<text class="repeat-text">我还会再来</text>
<text class="repeat-hint">这个选项对你的推荐权重影响很大</text>
</view>
</view>
<!-- 提交 -->
<view class="submit-area">
<button class="submit-btn" @click="submitReview" :disabled="overallRating === 0">
提交评价
</button>
</view>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { useRoute } from '@dcloudio/uni-app'
import { reviewApi } from '@/api/index.js'
const route = useRoute()
const sessionId = ref(parseInt(route.query.sessionId) || 1)
const packageId = ref(parseInt(route.query.packageId) || 1)
const packageName = ref('神秘套餐')
const shopName = ref('某某商家')
const overallRating = ref(0)
const dimensionScores = ref({ taste: 0, value: 0, distance: 0, match: 0 })
const selectedTags = ref([])
const noteText = ref('')
const isRepeat = ref(false)
const ratingLabels = ['太失望了', '不太好', '还行', '还不错', '非常满意!']
const dimensions = [
{ key: 'taste', label: '口味', icon: '🍽️' },
{ key: 'value', label: '性价比', icon: '💰' },
{ key: 'distance', label: '距离', icon: '📍' },
{ key: 'match', label: '符合预期', icon: '🎯' },
]
const allTags = [
'口味赞', '环境好', '性价比高', '值得再来', '服务棒',
'踩雷', '太贵', '难吃', '远', '分量少', '排队久', '适合约会',
]
function setDimScore(key, score) {
dimensionScores.value[key] = score
}
function toggleTag(tag) {
const idx = selectedTags.value.indexOf(tag)
if (idx >= 0) {
selectedTags.value.splice(idx, 1)
} else {
selectedTags.value.push(tag)
}
}
async function submitReview() {
if (overallRating.value === 0) {
uni.showToast({ title: '请先打分', icon: 'none' })
return
}
try {
await reviewApi.submit({
session_id: sessionId.value,
package_id: packageId.value,
rating: overallRating.value,
taste: dimensionScores.value.taste,
value: dimensionScores.value.value,
distance: dimensionScores.value.distance,
match: dimensionScores.value.match,
tags: selectedTags.value,
text: noteText.value,
is_repeat: isRepeat.value,
})
uni.showToast({ title: '评价已提交!这会影响下次推荐哦~', icon: 'success' })
setTimeout(() => uni.navigateBack(), 1500)
} catch (e) {
uni.showToast({ title: '提交失败', icon: 'none' })
}
}
</script>
<style lang="scss" scoped>
.page-review {
min-height: 100vh;
background: #f5f5f5;
padding-bottom: 100rpx;
}
.review-header {
background: linear-gradient(135deg, #FFD700, #FFA500);
padding: 60rpx 30rpx 40rpx;
color: #fff;
text-align: center;
.review-title {
font-size: 36rpx;
font-weight: 700;
display: block;
}
.review-subtitle {
font-size: 24rpx;
opacity: 0.9;
display: block;
margin-top: 8rpx;
}
}
.card {
background: #fff;
border-radius: 20rpx;
padding: 24rpx;
margin: 20rpx 24rpx;
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.06);
}
.merchant-info {
text-align: center;
.merchant-name {
font-size: 32rpx;
font-weight: 700;
display: block;
}
.merchant-shop {
font-size: 24rpx;
color: #999;
display: block;
margin-top: 4rpx;
}
}
.section-label {
font-size: 28rpx;
font-weight: 600;
display: block;
margin-bottom: 16rpx;
}
.overall-stars {
display: flex;
justify-content: center;
gap: 16rpx;
}
.star {
font-size: 64rpx;
color: #ddd;
transition: color 0.2s;
&.active { color: #FFD700; }
&.small {
font-size: 36rpx;
}
}
.rating-label {
display: block;
text-align: center;
font-size: 24rpx;
color: #999;
margin-top: 12rpx;
}
.dimension-row {
display: flex;
align-items: center;
padding: 16rpx 0;
border-bottom: 1rpx solid #f0f0f0;
&:last-child { border: none; }
.dim-icon { font-size: 32rpx; width: 40rpx; }
.dim-label {
flex: 1;
font-size: 26rpx;
margin-left: 12rpx;
}
.dim-stars {
display: flex;
gap: 4rpx;
}
}
.tags {
display: flex;
flex-wrap: wrap;
gap: 12rpx;
}
.tag-btn {
padding: 10rpx 24rpx;
border: 2rpx solid #eee;
border-radius: 30rpx;
font-size: 24rpx;
color: #666;
&.active {
border-color: #FF6B35;
background: #FFF3E0;
color: #FF6B35;
}
}
.note-textarea {
width: 100%;
min-height: 160rpx;
font-size: 26rpx;
padding: 16rpx;
background: #f8f8f8;
border-radius: 12rpx;
box-sizing: border-box;
}
.repeat-option {
display: flex;
align-items: center;
gap: 16rpx;
padding: 10rpx 0;
.repeat-icon { font-size: 36rpx; }
.repeat-text {
font-size: 28rpx;
font-weight: 600;
color: #FF6B35;
}
.repeat-hint {
font-size: 20rpx;
color: #999;
display: block;
}
}
.submit-area {
padding: 0 40rpx;
}
.submit-btn {
background: linear-gradient(135deg, #FF6B35, #FF8C42);
color: #fff;
border: none;
border-radius: 40rpx;
font-size: 32rpx;
font-weight: 600;
box-shadow: 0 8rpx 30rpx rgba(255,107,53,0.3);
&:disabled {
opacity: 0.5;
}
}
</style>