组件目录 components/Evaluation.vue
js
<template>
<view class="evaluation-container">
<!-- 综合评价 -->
<view class="evaluation-item" @tap="parentTap">
<text class="label label-1">综合评价</text>
<view class="rating-icons">
<image
v-for="(icon, index) in ratingIcons"
:key="index"
:class="['rating-icon', { active: icon.active }]"
:src="icon.active ? icon.selectedSrcDynamic : icon.unselectedSrc"
@tap.stop="setRating(index)"
/>
</view>
<text class="comment-text">
{{ getCommentText(ratingIcons.filter(i => i.active).length) }}
</text>
</view>
<!-- 描述相符 -->
<view class="evaluation-item" @tap="parentTap">
<text class="label">描述相符</text>
<view class="heart-icons">
<image
v-for="(heart, index) in descriptionHearts"
:key="index"
:class="['heart-icon', { active: heart.active }]"
:src="heart.active ? '/static/evaluate/heart-filled.png' : '/static/evaluate/heart-empty.png'"
@tap.stop="setHeart(index, 'description')"
/>
</view>
<text class="comment-text">
{{ getCommentText(descriptionHearts.filter(i => i.active).length) }}
</text>
</view>
<!-- 物流服务 -->
<view class="evaluation-item" @tap="parentTap">
<text class="label">物流服务</text>
<view class="heart-icons">
<image
v-for="(heart, index) in logisticsHearts"
:key="index"
:class="['heart-icon', { active: heart.active }]"
:src="heart.active ? '/static/evaluate/heart-filled.png' : '/static/evaluate/heart-empty.png'"
@tap.stop="setHeart(index, 'logistics')"
/>
</view>
<text class="comment-text">
{{ getCommentText(logisticsHearts.filter(i => i.active).length) }}
</text>
</view>
<!-- 服务态度 -->
<view class="evaluation-item" @tap="parentTap">
<text class="label">服务态度</text>
<view class="heart-icons">
<image
v-for="(heart, index) in serviceHearts"
:key="index"
:class="['heart-icon', { active: heart.active }]"
:src="heart.active ? '/static/evaluate/heart-filled.png' : '/static/evaluate/heart-empty.png'"
@tap.stop="setHeart(index, 'service')"
/>
</view>
<text class="comment-text">
{{ getCommentText(serviceHearts.filter(i => i.active).length) }}
</text>
</view>
</view>
</template>
<script>
export default {
name: 'Evaluation',
props: {
defaultValues: {
type: Object,
default: () => ({})
},
readonly: {
type: Boolean,
default: false
}
},
data() {
return {
ratingIcons: [
{
unselectedSrc: '/static/evaluate/face-very-bad-unselected.png',
selectedSrc: '/static/evaluate/face-very-bad.png',
selectedSrcDynamic: null,
active: false
},
{
unselectedSrc: '/static/evaluate/face-bad-unselected.png',
selectedSrc: '/static/evaluate/face-bad.png',
selectedSrcDynamic: null,
active: false
},
{
unselectedSrc: '/static/evaluate/face-neutral-unselected.png',
selectedSrc: '/static/evaluate/face-neutral.png',
selectedSrcDynamic: null,
active: false
},
{
unselectedSrc: '/static/evaluate/face-good-unselected.png',
selectedSrc: '/static/evaluate/face-good.png',
selectedSrcDynamic: null,
active: false
},
{
unselectedSrc: '/static/evaluate/face-very-good-unselected.png',
selectedSrc: '/static/evaluate/face-very-good.png',
selectedSrcDynamic: null,
active: false
}
],
descriptionHearts: Array(5).fill({ active: false }).map(h => ({ ...h })),
logisticsHearts: Array(5).fill({ active: false }).map(h => ({ ...h })),
serviceHearts: Array(5).fill({ active: false }).map(h => ({ ...h })),
commentTexts: ['很差', '不满意', '一般', '满意', '超赞']
};
},
mounted() {
this.initEvaluation(this.defaultValues);
},
methods: {
parentTap() {},
setRating(index) {
if (this.readonly) return;
const selectedSrc = this.ratingIcons[index].selectedSrc;
this.ratingIcons.forEach((icon, i) => {
icon.active = i <= index;
icon.selectedSrcDynamic = i <= index ? selectedSrc : null;
});
},
setHeart(index, type) {
if (this.readonly) return;
const hearts = this[type + 'Hearts'];
hearts.forEach((heart, i) => {
heart.active = i <= index;
});
},
getResult() {
return {
overall: this.ratingIcons.filter(i => i.active).length,
description: this.descriptionHearts.filter(i => i.active).length,
logistics: this.logisticsHearts.filter(i => i.active).length,
service: this.serviceHearts.filter(i => i.active).length
};
},
initEvaluation({ overall = 0, description = 0, logistics = 0, service = 0 }) {
const selectedSrc = this.ratingIcons[overall - 1]?.selectedSrc || null;
this.ratingIcons.forEach((icon, i) => {
icon.active = i < overall;
icon.selectedSrcDynamic = i < overall ? selectedSrc : null;
});
const setHearts = (arr, score) => {
arr.forEach((item, i) => {
item.active = i < score;
});
};
setHearts(this.descriptionHearts, description);
setHearts(this.logisticsHearts, logistics);
setHearts(this.serviceHearts, service);
},
getCommentText(count) {
if (count === 0) return '';
return this.commentTexts[count - 1];
}
}
};
</script>
<style scoped>
.evaluation-container {
/* padding: 20px; */
}
.evaluation-item {
display: flex;
align-items: center;
margin-bottom: 28rpx;
}
.label{
font-size: 28rpx;
color: #2D2D2D;
}
.label-1{
color: #0F1724;
font-weight: 600;
}
.rating-icons,
.heart-icons {
display: flex;
/* gap: 10px; */
}
.rating-icon,
.heart-icon {
width: 60rpx;
height: 60rpx;
margin-left: 30rpx;
}
/* 动画效果 */
@keyframes bounce {
0% {
transform: scale(1);
}
40% {
transform: scale(1.3);
}
100% {
transform: scale(1);
}
}
.rating-icon.active,
.heart-icon.active {
animation: bounce 0.3s ease;
}
/* 文案样式 */
.comment-text {
margin-left: 24rpx;
font-weight: 600;
font-size: 28rpx;
color: #0F1724;
white-space: nowrap;
}
</style>
页面使用
js
<template>
<view>
<Evaluation
ref="evaluation"
:default-values="{ overall: 0, description: 0, logistics: 0, service: 0 }"
:readonly="false"
/>
<button @tap="submit">提交</button>
</view>
</template>
<script>
import Evaluation from '@/components/Evaluation.vue';
export default {
components: { Evaluation },
methods: {
submit() {
const result = this.$refs.evaluation.getResult();
console.log('提交评分结果', result);
}
}
};
</script>
readonly是否允许点击一般详情页可以传true不让点击