Files
frontend-app/pages/other_package/productInfo/evaluateDetail.vue
T
2026-08-17 11:32:40 +08:00

573 lines
13 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="content">
<!-- 状态栏占位 -->
<TopSafe></TopSafe>
<!-- 整合后的自定义头部导航栏(包含返回键和用户信息) -->
<view class="custom-header">
<view class="user-info">
<up-image
class="back-icon"
src="/static/common/left.png"
width="18"
height="20"
bgColor="#f1f6ff00"
@tap="goBack"
/>
<image
class="avatar"
:src="commentData.userImage"
mode="aspectFill"
></image>
<text class="nickname">{{ commentData.userName }}</text>
<view class="badge">
<up-image
src="/static/common/rz.png"
width="14"
height="13"
bgColor="#f1f6ff00"
class="badge-icon"
></up-image>
<text class="badge-text">星级用户</text>
</view>
</view>
</view>
<!-- 评价与评论主内容区域 -->
<view class="setting-view">
<view class="evaluation-card">
<!-- 1. 大图晒单区域(支持左右滑动,兼容H5和小程序) -->
<view class="image-wrapper">
<swiper
class="main-swiper"
:current="currentImageIndex"
@change="onSwiperChange"
:circular="true"
>
<swiper-item v-for="(img, index) in imageList" :key="index">
<image
class="main-image"
:src="img"
mode="aspectFill"
></image>
</swiper-item>
</swiper>
<!-- 自定义数字指示器 -->
<view class="image-indicator" v-if="imageList.length > 1">
{{ currentImageIndex + 1 }}/{{ imageList.length }}
</view>
</view>
<!-- 2. 购买规格与星级评分 -->
<view class="rating-row">
<text class="sku-text">已购{{ goodsName }}</text>
<view class="stars-box">
<view class="stars">
<text class="star active" v-for="s in commentData.rating" :key="s">★</text>
</view>
<text class="rating-label">{{ commentData.rating > 4 ? '好评' : '中/差评' }}</text>
</view>
</view>
<!-- 3. 用户评价文本 -->
<view class="comment-content">
{{ commentData.content }}
</view>
<!-- 4. 底部关联商品卡片 -->
<view class="product-mini-card">
<image
class="prod-img"
:src="mainGraph"
mode="aspectFill"
></image>
<view class="prod-info">
<text class="prod-title"
>{{ name }}</text
>
</view>
</view>
<!-- 【新增】5. 评论列表显示区 -->
<view class="comment-section">
<view class="comment-count">{{ commentList.length }}条评论</view>
<!-- 使用递归组件渲染无限层级评论 -->
<RecursiveComment
v-for="(item, index) in commentList"
:key="index"
:item="item"
:userId="userId"
:level="0"
@deleteSuccess="handleDeleteSuccess"
@reply="tapReply"
/>
<!-- 底部到底提示 -->
<view class="no-more">— 到底啦 —</view>
</view>
</view>
</view>
<view class="bottom-bar">
<view class="input-box">
<text class="edit-icon">✏️</text>
<input
type="text"
v-model="inputComment"
:placeholder="placeholder"
placeholder-class="input-placeholder"
confirm-type="send"
@confirm="sendComment"
/>
</view>
</view>
<up-toast ref="uToastRef"></up-toast>
<qiaobao-assistant page-key="pages/other_package/productInfo/evaluateDetail" title="评价详情" />
</view>
</template>
<script>
import { formatDate, copyData } from '@/utils/index.js';
import { getStorageFun, USER_DATA, clear } from "@/utils/auth.js";
import { getGoodsReviewsDetails, addReviewReply } from "@/api/product.js";
import TopSafe from "@/components/common/top-safe.nvue";
import MyBtn from "@/components/common/my-btn.vue";
import RecursiveComment from "./RecursiveComment.vue";
export default {
components: { TopSafe, MyBtn, RecursiveComment },
props: {
evalId: {
type: String || Number,
default: ''
}
},
data() {
return {
commentData: {},
placeholder: '请输入评论',
parentReplyId: '',
mainGraph: '',
name: '',
goodsName: '',
// 图片列表数据
imageList: [],
currentImageIndex: 0, // 当前图片索引
// 【新增】输入框绑定的文本
inputComment: '',
commentList: [ ],
commentId: '',
userId: '',
};
},
onShow() {},
onLoad(options) {
this.mainGraph = options.mainGraph || '';
this.name = options.name || '';
this.goodsName = options.goodsName || '';
this.commentId = options.evalId || '';
this.userId = getStorageFun(USER_DATA).userId,
this.getDetails();
},
methods: {
formatDate,
// 监听滑动切换
onSwiperChange(e) {
this.currentImageIndex = e.detail.current;
},
async getDetails(id) {
const params = {
Id: this.commentId,
};
const resp = await getGoodsReviewsDetails(params);
if (resp && resp.bizcode === 100) {
this.imageList = resp.data.images || [];
this.commentList = resp.data.reviewReplies || [];
// 如果图片是多张返回的是,分割为数组
if (this.imageList.length > 0) {
this.imageList = this.imageList.split(',');
}
this.commentData = resp.data || {};
}
},
// 返回上一页
goBack() {
uni.navigateBack({
delta: 1,
});
},
handleDeleteSuccess(){
this.getDetails();
},
// 【新增】发送评论
async sendComment() {
console.log('评论');
if (!this.inputComment.trim()) return;
const params = {
parentReplyId:this.parentReplyId || '',
reviewId: this.commentData.id,
userId: getStorageFun(USER_DATA).userId,
userName: getStorageFun(USER_DATA).name ,
userImage: getStorageFun(USER_DATA).avatar,
content: this.inputComment,
};
const resp = await addReviewReply(params);
if (resp && resp.bizcode === 100) {
this.getDetails();
}
this.inputComment = '';
uni.showToast({ title: '评论成功', icon: 'none' });
},
// 【新增】点击回复
tapReply(item, parentItem) {
this.parentReplyId = item.id || '';
this.placeholder = `回复:${item.userName}`;
}
},
};
</script>
<style lang="scss" scoped>
.content {
background-color: #f7f7f9;
min-height: 100vh;
position: relative;
}
.custom-header {
width: 100%;
height: 100rpx;
background-color: #ffffff;
display: flex;
align-items: center;
padding: 0 24rpx;
box-sizing: border-box;
border-bottom: 1rpx solid #f2f2f2;
.user-info {
display: flex;
align-items: center;
width: 100%;
.back-icon {
width: 36rpx;
height: 36rpx;
margin-right: 20rpx;
}
.avatar {
width: 68rpx;
height: 68rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.nickname {
font-size: 34rpx;
color: #1a1a1a;
font-weight: 600;
margin-right: 12rpx;
}
.badge {
display: flex;
align-items: center;
background-color: #fdf6ec;
border-radius: 6rpx;
padding: 2rpx 8rpx;
.badge-icon {
margin-right: 6rpx;
}
.badge-text {
font-size: 22rpx;
color: #b88230;
font-weight: 500;
}
}
}
}
.setting-view {
width: 100%;
height: calc(100% - 100rpx - var(--status-bar-height));
display: flex;
flex-direction: column;
align-items: center;
overflow-y: auto;
box-sizing: border-box;
padding-bottom: 120rpx; /* 【新增】留出底部固定栏的距离 */
}
.evaluation-card {
width: 100%;
background-color: #ffffff;
padding-bottom: 30rpx;
display: flex;
flex-direction: column;
// 大图晒单区域优化
.image-wrapper {
width: 100%;
padding: 24rpx 32rpx 0 32rpx;
box-sizing: border-box;
position: relative; // 为指示器绝对定位做准备
.main-swiper {
width: 100%;
height: 500rpx; // swiper必须指定高度,可根据业务UI自行微调
.main-image {
width: 100%;
height: 100%; // 让图片撑满swiper-item
border-radius: 12rpx;
display: block;
}
}
// 右下角微型数字指示器
.image-indicator {
position: absolute;
right: 50rpx;
bottom: 20rpx;
background-color: rgba(0, 0, 0, 0.6);
color: #ffffff;
padding: 4rpx 16rpx;
font-size: 22rpx;
border-radius: 20rpx;
z-index: 10;
}
}
// 规格与星级
.rating-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 24rpx 32rpx 16rpx 32rpx;
.sku-text {
font-size: 26rpx;
color: #a0a0a0;
max-width: 60%;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.stars-box {
display: flex;
align-items: center;
.stars {
display: flex;
margin-right: 12rpx;
.star {
font-size: 28rpx;
color: #e0e0e0;
margin-left: 2rpx;
&.active {
color: #7b3ff7;
}
}
}
.rating-label {
font-size: 26rpx;
color: #7b3ff7;
font-weight: 500;
}
}
}
// 评价文本
.comment-content {
font-size: 28rpx;
color: #262626;
line-height: 1.5;
padding: 0 32rpx 24rpx 32rpx;
text-align: justify;
word-break: break-all;
}
// 底部链接商品卡片
.product-mini-card {
margin: 0 32rpx;
background-color: #f5f6f8;
border-radius: 16rpx;
padding: 16rpx;
display: flex;
align-items: center;
.prod-img {
width: 110rpx;
height: 110rpx;
border-radius: 12rpx;
background-color: #e5e5e5;
flex-shrink: 0;
}
.prod-info {
margin-left: 20rpx;
flex: 1;
.prod-title {
font-size: 26rpx;
color: #1a1a1a;
line-height: 1.4;
font-weight: 500;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
}
}
}
/* ================= 【新增样式】评论显示区 ================= */
.comment-section {
margin-top: 40rpx;
padding: 0 32rpx;
border-top: 1rpx solid #f2f2f2;
.comment-count {
font-size: 26rpx;
color: #999999;
padding: 24rpx 0;
}
.comment-item {
display: flex;
padding: 24rpx 0;
position: relative;
.c-avatar {
width: 64rpx;
height: 64rpx;
border-radius: 50%;
margin-right: 16rpx;
background-color: #f0f0f0;
}
.c-body {
flex: 1;
.c-user-line {
display: flex;
align-items: center;
margin-bottom: 8rpx;
.c-nickname {
font-size: 26rpx;
color: #888888;
margin-right: 10rpx;
}
.c-author {
font-size: 20rpx;
color: #ffffff;
background-color: #ff6b6b;
padding: 2rpx 8rpx;
border-radius: 4rpx;
margin-right: 8rpx;
}
.c-vip {
font-size: 20rpx;
color: #ffffff;
background: linear-gradient(135deg, #ff6b6b, #ffa940);
padding: 2rpx 8rpx;
border-radius: 4rpx;
margin-right: 8rpx;
}
}
.c-reply-at {
color: #7b3ff7;
}
.c-text {
font-size: 28rpx;
color: #262626;
line-height: 1.4;
margin-bottom: 12rpx;
}
.c-footer {
display: flex;
align-items: center;
font-size: 24rpx;
color: #aaaaaa;
.c-time {
margin-right: 20rpx;
}
.c-reply-btn {
color: #555555;
cursor: pointer;
}
}
}
.c-like-box {
padding: 10rpx;
}
}
.no-more {
text-align: center;
font-size: 24rpx;
color: #cccccc;
padding: 40rpx 0 10rpx 0;
}
}
/* ================= 【新增样式】底部固定评论/操作栏 ================= */
.bottom-bar {
position: fixed;
bottom: 0;
left: 0;
right: 0;
height: 110rpx;
background-color: #ffffff;
border-top: 1rpx solid #eeeeee;
display: flex;
align-items: center;
padding: 0 24rpx;
box-sizing: border-box;
z-index: 100;
padding-bottom: env(safe-area-inset-bottom); /* 适配全面屏底部安全区 */
.input-box {
flex: 1;
height: 70rpx;
background-color: #f5f6f8;
border-radius: 35rpx;
display: flex;
align-items: center;
padding: 0 24rpx;
margin-right: 24rpx;
.edit-icon {
font-size: 26rpx;
color: #999999;
margin-right: 12rpx;
}
input {
flex: 1;
font-size: 26rpx;
color: #262626;
}
.input-placeholder {
color: #bbbbbb;
}
}
}
</style>