shop_app/pages/groups/comment.vue

670 lines
17 KiB
Vue
Raw Normal View History

2025-05-08 09:16:37 +08:00
<template>
<!-- 团购评论 -->
<view class="whole">
<view class="tabBox">
<view class="Tit">
<view class="row" @click="changeTab(0)"><view class="tit" :class="tabIndex == 0 ? 'on' : ''">全部</view><text>({{total}})</text></view>
<view class="row" v-for="it in tabList" :key="it.id" @click="changeTab(it.id)">
<view class="tit" :class="tabIndex == it.id ? 'on' : ''">{{it.name}}</view><text>({{it.count}})</text>
</view>
</view>
<view class="filter">
<view class="icon" @click="showFilter = !showFilter">
<text class="iconfont icon-paixu"></text>
</view>
<view class="box" v-if="showFilter">
<view class="row" v-for="it in sortList" :key="it.id" @click="toFilter(it.id)" :class="sort == it.id ? 'on' : ''">{{it.name}}</view>
</view>
</view>
</view>
<scroll-view scroll-y="true" @scrolltolower="scorllBottom" style="height: calc(100vh - 90rpx);" lower-threshold="200">
2025-06-14 10:01:48 +08:00
<view class="contBox">
<view class="oneBox">
<view class="cont">
<view class="top">
<view class="rateBox">
<view class="rate">{{goodCommentRate}}</view>
<text>好评率</text>
</view>
<view class="star">
<up-rate v-model="rateValue" :count="5" allowHalf readonly :size="25" activeColor="#ffd21e" inactiveColor="#eee" />
</view>
2025-05-08 09:16:37 +08:00
</view>
2025-06-14 10:01:48 +08:00
<view class="topBox">
<view class="row" v-for="item in tagsList" :key="item.id" :class="tagIndex === item.id ? 'active' : ''" @click="changeTag(item.id)">{{item.name}}&nbsp;<text>{{item.count}}</text></view>
2025-05-08 09:16:37 +08:00
</view>
</view>
</view>
2025-06-14 10:01:48 +08:00
<view class="listBox">
<view class="item" v-for="(item, index) in infoList" :key="item.id">
<view class="cont">
<view class="userInfo">
<image :src="item.user.avatar"></image>
<view class="box">
<view class="user">
<view class="right">
<view class="name">{{parseName(item.user.nickname)}}</view>
<span class="border" v-if="!from">已购{{item.report && item.report.total_trade_count}}</span>
</view>
<view class="time" v-if="!from">{{item.date.replace(/-/g, '/')}}发布于{{provTxt[item.order.province_id]}}</view>
</view>
<view class="starBox">
<view class="rate">
<view class="star" v-for="i in 5" :key="i">
<up-icon v-if="i <= Math.ceil(item.stars)" name="star-fill" color="#FFD81E" size="18" />
<up-icon v-else name="star-fill" color="#ccc" :size="18" />
</view>
</view>
<view class="tags" v-if="item.tags.length">
<text v-for="(it, i) in item.tags" :key="it.id"><template v-if="i != 0">&nbsp;|&nbsp;</template>{{it.name}}</text>
</view>
</view>
</view>
2025-05-08 09:16:37 +08:00
</view>
2025-06-14 10:01:48 +08:00
<view v-if="item.item" class="sku_name">{{item.item.goods_name}}
<template v-if="item.item.sku_name">{{item.item.sku_name}}</template>
2025-05-08 09:16:37 +08:00
</view>
</view>
2025-06-14 10:01:48 +08:00
<view class="comment"><text @longpress="copyComment(item.comment)">{{item.comment}}</text></view>
<view class="box_zong" v-if="item.material.length">
<view class="box_imgs" v-for="(it, index) in item.material" :key="index">
<image :src="it.url" v-if="it.type === 1" @click="hanleImgs(it, item.material)" mode="aspectFill"></image>
<template v-else>
<image :src="it.img_video" mode="aspectFill"></image>
<view class="play" @click="hanleImgs(it)"><up-icon name="play-right-fill" color="#fff" size="40" /></view>
</template>
</view>
2025-05-08 09:16:37 +08:00
</view>
2025-06-14 10:01:48 +08:00
<view class="replyBox" v-if="item.reply">
<view>店家回复</view>
<view class="text">{{item.reply}}</view>
2025-05-08 09:16:37 +08:00
</view>
2025-06-14 10:01:48 +08:00
<template v-if="item.add_comments && item.add_comments.length">
<view class="addBox" v-for="itm in item.add_comments" :key="itm.id">
<view class="tit">
<text>{{getDayDiff(itm.created_at.substr(0, 10), item.created_at.substr(0, 10))}}追评</text>
<text class="time">{{itm.created_at.substr(0, 16)}}</text>
</view>
<text class="comment" style="display: inline-block;margin-top: 10rpx;" @longpress="copyComment(itm.comment)">
{{itm.comment}}
</text>
<view class="box_zong" v-if="itm.material.length">
<view class="box_imgs" v-for="(it, index) in itm.material" :key="index">
<image :src="it.url" v-if="it.type === 1" @click="hanleImgs(it, itm.material)" mode="aspectFill"></image>
<template v-else>
<image :src="it.img_video" mode="aspectFill"></image>
<view class="play" @click="hanleImgs(it)"><up-icon name="play-right-fill" color="#fff" size="40" /></view>
</template>
</view>
</view>
<view class="replyBox itm" v-if="itm.reply">
<view>店家回复</view>
<view class="text">{{itm.reply}}</view>
2025-05-08 09:16:37 +08:00
</view>
</view>
2025-06-14 10:01:48 +08:00
</template>
<view class="btm">
<view style="display: flex;margin-right: 10px;" @click="toPingbi(index)"><up-icon name="eye-off" color="#666" />屏蔽</view>
<view style="display: flex;" @click="toTousu(item)"><up-icon name="warning-fill" color="#666" />举报</view>
2025-05-08 09:16:37 +08:00
</view>
2025-06-14 10:01:48 +08:00
</view>
<view class="bottom" v-if="page >= lastPage && infoList.length && !loading">- 到底啦 -</view>
<view class="bottom" v-if="loading"><van-loading type="spinner" size="24px" vertical color="#999">加载中...</van-loading></view>
<view v-if="!infoList.length && !loading">
<up-empty icon="https://ct-upimg.yx090.com/g.ii090/images/sprite/empty/comment.png" mode="comment" text="暂无评价喔" />
</view>
2025-05-08 09:16:37 +08:00
</view>
</view>
2025-06-14 10:01:48 +08:00
2025-05-08 09:16:37 +08:00
</scroll-view>
</view>
<video-dialog :url="videoUrl" :show="showVideo" @close="showVideo = false"></video-dialog>
<!-- 隐私协议弹窗 -->
<privacy-popup :show-dialog="showPrivacy" @close="showPrivacy = false" @agree="showPrivacy = false" @refuse="showPrivacy = false" />
</template>
<script>
import { ref, reactive, toRefs } from 'vue'
import { get } from '@/api/request.js'
import { Style } from '@/utils/list.js'
import privacyPopup from '@/components/privacyPopup/index.vue'
2025-06-14 10:01:48 +08:00
import { judgePrivacy, getDayDiff } from '@/components/common.js'
2025-05-08 09:16:37 +08:00
import videoDialog from '@/components/videoDialog/index.vue'
2025-06-14 10:01:48 +08:00
import { provTxt } from '@/components/img.js'
2025-05-08 09:16:37 +08:00
export default {
components: {
privacyPopup, videoDialog
},
setup() {
const data = reactive({
tabIndex: 0,
id: '',
from: '',
infoList: [],
loading: false,
lastPage: 0,
page: 1,
showVideo: false,
videoUrl: '',
total: 0,
tagsList: [],
tagIndex: 0,
Color: uni.getStorageSync('theme_color'),
bgColor: Style[uni.getStorageSync('theme_index') * 1].bgColor,
showPrivacy: false,
is_show_sales: 'true',
goodCommentRate: '',
rateValue: 0,
showFilter: false,
sort: 0,
tabList: [
{id: 1, name: '图片/视频', count: 0},
{id: 2, name: '追评', count: 0},
{id: 3, name: '回头客', count: 0}
],
sortList: [
{id: 0, name: '默认排序'},
{id: 1, name: '最新评价'}
]
})
const hanleImgs = (it, imgs) => {
var img = []
imgs && imgs.forEach((res) => {
if (res.type == 1) {
img.push(res.url)
}
})
if (it.type === 1) {
uni.previewImage({
current: it.url,
urls: img
})
} else {
data.showVideo = true
data.videoUrl = it.url
}
}
// 触底加载下一页
function scorllBottom() {
if (data.page < data.lastPage) {
data.page++
getList()
}
}
const getList = (val = 0) => {
data.loading = true
get(`/api/v1/goods/comment/${data.id}`, {
page: data.page,
pageSize: 20,
type: 1,
stars_level: '',
has_material: '',
comment_tag_type: data.tabIndex || 0,
comment_tag_id: data.tagIndex || 0,
sortType: data.sort + 1
}).then((res) => {
data.infoList = data.infoList.concat(res.data)
data.goodCommentRate = res.goodCommentRate
data.rateValue = (res.goodCommentRate.split('%')[0] * 0.05).toFixed(2)
console.log(data.rateValue)
if(val == 1) {
data.total = res.meta.total
}
data.lastPage = data.is_show_sales == 'true' ? res.meta.last_page : 1
data.loading = false
}).catch(() => {
data.loading = false
})
}
// 复制评论
2025-06-14 10:01:48 +08:00
const copyComment = async (text) => {
if(await judgePrivacy()) {
data.showPrivacy = true
return false
}
2025-05-08 09:16:37 +08:00
uni.setClipboardData({
data: String(text),
success: function () {
uni.showToast({
icon: 'none',
title: '评论已复制'
})
},
fail: function () {
uni.showToast({
icon: 'none',
title: '复制失败'
})
}
})
}
function changeTab(id) {
if(id == data.tabIndex) {
return
}
data.tagIndex = 0
data.tabIndex = id
data.page = 1
data.infoList = []
getList()
}
function changeTag(id) {
if(id == data.tagIndex) {
data.tagIndex = 0
} else {
data.tagIndex = id
}
data.tabIndex = 0
data.page = 1
data.infoList = []
getList()
}
function getTags() {
get(`/api/v1/orderComment/commentTags`, {type: 1, shop_group_goods_id: data.id}).then((res) => {
data.tabList[0].count = res.data[0].count
data.tabList[1].count = res.data[1].count
data.tabList[2].count = res.data[2].count
data.tagsList = res.data.slice(3)
})
}
function toFilter(id) {
data.sort = id
data.showFilter = false
data.page = 1
data.infoList = []
getList()
}
2025-06-14 10:01:48 +08:00
function parseName(name) {
const regex = /^(.{2}).+(.{2})$/
return name.replace(regex, '$1**$2')
}
function toTousu(item) {
let text = '举报用户“' + item.user.nickname + '”' + '在团购ID“' + data.id + '”的评论'
uni.navigateTo({
url: '/pages/mine/msg/complaint?type=2&desc=' + text
})
}
function toPingbi(i) {
data.infoList.splice(i, 1)
}
2025-05-08 09:16:37 +08:00
return {
getDayDiff,
provTxt,
...toRefs(data),
scorllBottom,
getList,
hanleImgs,
copyComment,
changeTab,
changeTag,
getTags,
2025-06-14 10:01:48 +08:00
toFilter,
parseName,
toTousu,
toPingbi
2025-05-08 09:16:37 +08:00
}
},
async onLoad(options) {
this.id = options.id
this.from = options.from || ''
this.is_show_sales = options.is_show_sales ? options.is_show_sales : 'true'
await this.getList(1)
this.getTags()
}
}
</script>
<style lang="scss" scoped>
.whole{
height: 100%;
box-sizing: border-box;
overflow: auto;
.tabBox{
background-color: #fff;
z-index: 2;
position: sticky;
top: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx 0;
box-sizing: border-box;
height: 90rpx;
.Tit{
display: flex;
align-items: center;
.row{
margin-right: 16rpx;
display: flex;
align-items: baseline;
&:last-child{
margin-right: 0;
}
.tit{
font-size: 30rpx;
color: #333;
font-weight: 600;
line-height: 1;
position: relative;
&.on::after{
position: absolute;
left: 0;
width: 56rpx;
height: 4px;
bottom: -5px;
content: '';
background: linear-gradient(to right, #fff -10%, v-bind('Color'));
}
}
text{
font-size: 22rpx;
color: #999;
}
}
}
.filter{
position: relative;
.icon{
display: flex;
2025-06-14 10:01:48 +08:00
flex-direction: column;
justify-content: center;
2025-05-08 09:16:37 +08:00
height: 30px;
2025-06-14 10:01:48 +08:00
padding-top: 3px;
.heng{
width: 28rpx;
height: 2px;
background-color: #444;
margin-bottom: 3px;
&.er{
width: 24rpx;
margin-right: 4rpx;
}
&.san{
width: 20rpx;
margin-right: 10rpx;
}
2025-05-08 09:16:37 +08:00
}
}
.box{
position: absolute;
top: 60rpx;
right: -10rpx;
width: 200rpx;
text-align: center;
font-size: 28rpx;
padding: 14rpx 0;
box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.2);
z-index: 2;
background-color: #fff;
border-radius: 8rpx;
&:before{
position: absolute;
width: 0;
height: 0;
border-color: transparent;
border-style: solid;
border-width: 6px;
content: '';
border-top-width: 0;
border-bottom-color: #fff;
top: -6px;
right: 14rpx;
}
.row{
padding: 14rpx 0;
&.on{
color: v-bind('Color');
}
}
}
}
}
2025-06-14 10:01:48 +08:00
.contBox{
2025-05-08 09:16:37 +08:00
padding: 24rpx;
2025-06-14 10:01:48 +08:00
}
.oneBox{
border-bottom: 1rpx solid #e8e8e8;
2025-05-08 09:16:37 +08:00
.cont{
background-color: #fff;
padding: 24rpx 24rpx 8rpx;
2025-06-14 10:01:48 +08:00
border-radius: 10rpx 10rpx 0 0;
2025-05-08 09:16:37 +08:00
}
.top{
display: flex;
align-items: center;
margin-bottom: 24rpx;
.rateBox{
font-size: 24rpx;
color: v-bind('Color');
.rate{
font-size: 42rpx;
font-weight: 600;
margin-bottom: 4px;
font-family: cursive;
}
}
.star{
border-left: 1px solid #f1f1f1;
padding-left: 24rpx;
margin-left: 24rpx;
}
}
}
.topBox{
display: flex;
align-items: center;
flex-wrap: wrap;
.row{
font-size: 24rpx;
color: #444;
padding: 6rpx 16rpx;
border-radius: 8rpx;
background-color: #F6F6F6;
margin: 0 16rpx 16rpx 0;
&.active{
background: v-bind('bgColor');
}
text{
color: #999;
}
}
}
.listBox{
2025-06-14 10:01:48 +08:00
// padding: 0 24rpx 24rpx;
2025-05-08 09:16:37 +08:00
.item{
padding: 20rpx;
2025-06-14 10:01:48 +08:00
border-bottom: 1rpx solid #F5F5F5;
2025-05-08 09:16:37 +08:00
background-color: #fff;
2025-06-14 10:01:48 +08:00
.cont{
padding: 0 0 20rpx;
border-bottom: 1rpx solid #f7f2f2;
}
2025-05-08 09:16:37 +08:00
.userInfo{
display: flex;
2025-06-14 10:01:48 +08:00
align-items: center;
2025-05-08 09:16:37 +08:00
margin-bottom: 20rpx;
image{
width: 70rpx;
height: 70rpx;
border-radius: 50%;
margin-right: 15rpx;
}
.box{
width: 100%;
2025-06-14 10:01:48 +08:00
.user{
display: flex;
justify-content: space-between;
align-items: center;
}
2025-05-08 09:16:37 +08:00
.right{
font-size: 22rpx;
color: #98989f;
width: calc(100% - 150rpx);
2025-06-14 10:01:48 +08:00
display: flex;
align-items: center;
2025-05-08 09:16:37 +08:00
.name{
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-right: 10rpx;
display: inline-block;
}
.border{
background: rgba(255, 255, 255, 0.39);
border: 1px solid v-bind('Color');
color: v-bind('Color');
2025-06-14 10:01:48 +08:00
padding: 1px 10rpx;
2025-05-08 09:16:37 +08:00
border-radius: 5rpx;
font-size: 20rpx;
font-weight: normal;
}
}
.time{
font-size: 22rpx;
color: #999;
white-space: nowrap;
}
}
}
2025-06-14 10:01:48 +08:00
.sku_name{
color: #999;
margin-top: 10rpx;
font-size: 24rpx;
}
2025-05-08 09:16:37 +08:00
.starBox{
2025-06-14 10:01:48 +08:00
display: flex;
align-items: center;
margin-top: 6rpx;
2025-05-08 09:16:37 +08:00
.rate{
display: flex;
align-items: center;
.star{
margin-right: 3px;
}
}
.tags{
2025-06-14 10:01:48 +08:00
margin-left: 10rpx;
color: #666;
font-size: 20rpx;
2025-05-08 09:16:37 +08:00
}
}
.comment{
display: inline-block;
font-size: 28rpx;
line-height: 48rpx;
margin-top: 20rpx;
}
.box_zong{
display: flex;
flex-wrap: wrap;
.box_imgs{
margin: 20rpx 20rpx 0 0;
position: relative;
width: 180rpx;
height: 180rpx;
&:nth-child(3n+3) {
margin-right: 0;
}
image{
width: 100%;
height: 100%;
vertical-align: bottom;
2025-06-14 10:01:48 +08:00
border-radius: 8rpx;
2025-05-08 09:16:37 +08:00
}
.play{
position: absolute;
left: 0;
top: 0;
background-color: rgba(0, 0, 0, 0.4);
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
2025-06-14 10:01:48 +08:00
border-radius: 8rpx;
2025-05-08 09:16:37 +08:00
z-index: 1;
}
}
}
.replyBox{
padding: 25rpx;
background: rgba(246, 246, 246, 0.6);
border-radius: 10rpx;
font-size: 28rpx;
margin-top: 24rpx;
&.itm{
border-radius: 0;
border-left: 3px solid #ddd;
}
.text{
margin-top: 20rpx;
color: #98989f;
}
}
.addBox{
background-color: #F7F7F7;
border-radius: 10rpx;
padding: 16rpx;
font-size: 28rpx;
margin-top: 24rpx;
.tit{
color: #f00;
display: flex;
align-items: center;
justify-content: space-between;
.time{
color: #98989f;
font-size: 24rpx;
}
}
}
2025-06-14 10:01:48 +08:00
.btm{
display: flex;
align-items: center;
justify-content: flex-end;
font-size: 24rpx;
color: #666;
padding: 16rpx 24rpx;
background: #f6f6f6;
border-radius: 4px;
margin-top: 20rpx;
}
2025-05-08 09:16:37 +08:00
}
.bottom {
text-align: center;
font-size: 24rpx;
color: #999999;
margin: auto;
line-height: 80rpx;
}
}
}
</style>