商品详情:评论列表

pull/26/head^2
YunaiV 2023-12-10 17:25:15 +08:00
parent e3264329e7
commit 0e189711a8
7 changed files with 259 additions and 242 deletions

View File

@ -1,170 +1,167 @@
<!-- --> <!-- 商品评论的分-->
<template> <template>
<s-layout title="全部评价"> <s-layout title="全部评价">
<su-tabs :list="state.type" :scrollable="false" @change="onTabsChange" :current="state.currentTab"></su-tabs> <su-tabs
<view class="ss-m-t-20"> :list="state.type"
<view class="list-item" v-for="item in state.pagination.data" :key="item"> :scrollable="false"
<comment-item :item="item"></comment-item> @change="onTabsChange"
</view> :current="state.currentTab"
</view> />
<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" /> <!-- 评论列表 -->
<uni-load-more v-if="state.pagination.total > 0" :status="state.loadStatus" :content-text="{ <view class="ss-m-t-20">
<view class="list-item" v-for="item in state.pagination.list" :key="item">
<comment-item :item="item" />
</view>
</view>
<s-empty v-if="state.pagination.total === 0" text="暂无数据" icon="/static/data-empty.png" />
<!-- 下拉 -->
<uni-load-more
v-if="state.pagination.total > 0"
:status="state.loadStatus"
:content-text="{
contentdown: '上拉加载更多', contentdown: '上拉加载更多',
}" @tap="loadmore" /> }"
</s-layout> @tap="loadMore"
/>
</s-layout>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; import CommentApi from '@/sheep/api/product/comment';
import { import { onLoad, onReachBottom } from '@dcloudio/uni-app';
onLoad, import { reactive } from 'vue';
onReachBottom import _ from 'lodash';
} from '@dcloudio/uni-app'; import commentItem from '../components/detail/comment-item.vue';
import {
computed,
reactive
} from 'vue';
import _ from 'lodash';
import commentItem from '../components/detail/comment-item.vue';
const pagination = { const state = reactive({
data: [], id: 0, // SPU
current_page: 1, type: [
total: 1, { type: 0, name: '全部' },
last_page: 1, { type: 1, name: '好评' },
}; { type: 2, name: '中评' },
const state = reactive({ { type: 3, name: '差评' },
list: [], ],
type: [], currentTab: 0, // TAB
currentTab: 0, loadStatus: '',
pagination: { pagination: {
data: [], list: [],
current_page: 1, total: 0,
total: 1, pageNo: 1,
last_page: 1, pageSize: 1,
}, },
commentId: 0, });
code: 0,
}); //
// function onTabsChange(e) {
function onTabsChange(e) { state.currentTab = e.index;
state.pagination = pagination //
state.currentTab = e.index; state.pagination.pageNo = 1;
state.code = e.code; state.pagination.list = [];
getList(state.commentId, e.code); state.pagination.total = 0;
} getList();
async function getType(id) { }
const {
error, async function getList() {
data //
} = await sheep.$api.goods.getType(id); state.loadStatus = 'loading';
if (error === 0) { let res = await CommentApi.getCommentPage(
console.log(data) state.id,
// state.type = data; state.pagination.pageNo,
state.type = [{code:0,name:'全部'},{code:1,name:'好评'},{code:2,name:'中评'},{code:3,name:'差评'}]; state.pagination.pageSize,
} state.type[state.currentTab].type,
} );
async function getList(id, code=0, page = 1, list_rows = 6) { if (res.code !== 0) {
state.loadStatus = 'loading'; return;
let res = await sheep.$api.goods.comment2(id, { }
type: code, //
pageSize: list_rows, state.pagination.list = _.concat(state.pagination.list, res.data.list);
pageNo: page, state.pagination.total = res.data.total;
}); state.loadStatus = state.pagination.list.length < state.pagination.total ? 'more' : 'noMore';
console.log(res); }
if (res.code === 0) {
let orderList = _.concat(state.pagination.data, res.data.list); //
state.pagination = { function loadMore() {
...res.data, if (state.loadStatus === 'noMore') {
data: orderList, return;
}; }
if (state.pagination.current_page < state.pagination.last_page) { state.pagination.pageNo++;
state.loadStatus = 'more'; getList();
} else { }
state.loadStatus = 'noMore';
} onLoad((options) => {
} state.id = options.id;
} getList();
// });
function loadmore() {
if (state.loadStatus !== 'noMore') { //
getList(state.commentId, state.code, state.pagination.current_page + 1); onReachBottom(() => {
} loadMore();
} });
onLoad((options) => {
state.commentId = options.id;
getType(state.commentId);
getList(state.commentId);
});
//
onReachBottom(() => {
loadmore();
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.list-item { .list-item {
padding: 32rpx 30rpx 20rpx 20rpx; padding: 32rpx 30rpx 20rpx 20rpx;
background: #fff; background: #fff;
.avatar { .avatar {
width: 52rpx; width: 52rpx;
height: 52rpx; height: 52rpx;
border-radius: 50%; border-radius: 50%;
} }
.nickname { .nickname {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
color: #999999; color: #999999;
} }
.create-time { .create-time {
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: #c4c4c4; color: #c4c4c4;
} }
.content-title { .content-title {
font-size: 26rpx; font-size: 26rpx;
font-weight: 400; font-weight: 400;
color: #666666; color: #666666;
line-height: 42rpx; line-height: 42rpx;
} }
.content-img { .content-img {
width: 174rpx; width: 174rpx;
height: 174rpx; height: 174rpx;
} }
.cicon-info-o { .cicon-info-o {
font-size: 26rpx; font-size: 26rpx;
color: #c4c4c4; color: #c4c4c4;
} }
.foot-title { .foot-title {
font-size: 24rpx; font-size: 24rpx;
font-weight: 500; font-weight: 500;
color: #999999; color: #999999;
} }
} }
.btn-box { .btn-box {
width: 100%; width: 100%;
height: 120rpx; height: 120rpx;
background: #fff; background: #fff;
border-top: 2rpx solid #eee; border-top: 2rpx solid #eee;
} }
.tab-btn { .tab-btn {
width: 130rpx; width: 130rpx;
height: 62rpx; height: 62rpx;
background: #eeeeee; background: #eeeeee;
border-radius: 31rpx; border-radius: 31rpx;
font-size: 28rpx; font-size: 28rpx;
font-weight: 400; font-weight: 400;
color: #999999; color: #999999;
border: 1px solid #e5e5e5; border: 1px solid #e5e5e5;
margin-right: 10rpx; margin-right: 10rpx;
} }
</style> </style>

View File

@ -1,95 +1,94 @@
<!-- 商品评论项 -->
<template> <template>
<view> <view>
<!-- 用户评论 -->
<view class="user ss-flex ss-m-b-14"> <view class="user ss-flex ss-m-b-14">
<view class="ss-m-r-20 ss-flex"> <view class="ss-m-r-20 ss-flex">
<image class="avatar" :src="sheep.$url.cdn(item.user_avatar)"></image> <image class="avatar" :src="item.userAvatar"></image>
</view>
<view class="nickname ss-m-r-20">
{{ item.userNickname }}
</view> </view>
<view class="nickname ss-m-r-20">{{ item.userNickname }}</view>
<view class=""> <view class="">
<uni-rate :readonly="true" v-model="item.scores" size="18" /> <uni-rate :readonly="true" v-model="item.scores" size="18" />
</view> </view>
</view> </view>
<view class="content"> <view class="content"> {{ item.content }} </view>
{{ item.content }}
</view>
<view class="ss-m-t-24" v-if="item.picUrls?.length"> <view class="ss-m-t-24" v-if="item.picUrls?.length">
<scroll-view class="scroll-box" scroll-x scroll-anchoring> <scroll-view class="scroll-box" scroll-x scroll-anchoring>
<view class="ss-flex"> <view class="ss-flex">
<view v-for="(item, index) in item.picUrls" :key="item" class="ss-m-r-10"> <view v-for="(picUrl, index) in item.picUrls" :key="picUrl" class="ss-m-r-10">
<su-image class="content-img" isPreview :previewList="state.commentImages" :current="index" :src="item" <su-image
:height="120" :width="120" mode="aspectFill"></su-image> class="content-img"
isPreview
:previewList="item.picUrls"
:current="index"
:src="picUrl"
:height="120"
:width="120"
mode="aspectFill"
/>
</view> </view>
</view> </view>
</scroll-view> </scroll-view>
</view> </view>
<view class="ss-m-t-20 reply-box" v-if="item.reply_time"> <!-- 商家回复 -->
<view class="ss-m-t-20 reply-box" v-if="item.replyTime">
<view class="reply-title">商家回复</view> <view class="reply-title">商家回复</view>
<view class="reply-content">{{ item.reply_content }}</view> <view class="reply-content">{{ item.replyContent }}</view>
</view> </view>
</view> </view>
</template> </template>
<script setup> <script setup>
import sheep from '@/sheep'; const props = defineProps({
import { reactive } from 'vue'; item: {
const props = defineProps({ type: Object,
item: { default() {},
type: Object, },
default() { }, });
},
});
const state = reactive({
commentImages: [],
});
props.item.images?.forEach((i) => {
state.commentImages.push(sheep.$url.cdn(i));
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.avatar { .avatar {
width: 52rpx; width: 52rpx;
height: 52rpx; height: 52rpx;
border-radius: 50%; border-radius: 50%;
} }
.nickname { .nickname {
font-size: 26rpx; font-size: 26rpx;
font-weight: 500; font-weight: 500;
color: #999999; color: #999999;
} }
.content { .content {
width: 636rpx; width: 636rpx;
font-size: 26rpx; font-size: 26rpx;
font-weight: 400; font-weight: 400;
color: #333333; color: #333333;
} }
.reply-box { .reply-box {
position: relative; position: relative;
background: #f8f8f8; background: #f8f8f8;
border-radius: 8rpx; border-radius: 8rpx;
padding: 16rpx; padding: 16rpx;
} }
.reply-title { .reply-title {
position: absolute; position: absolute;
left: 16rpx; left: 16rpx;
top: 16rpx; top: 16rpx;
font-weight: 400; font-weight: 400;
font-size: 26rpx; font-size: 26rpx;
line-height: 40rpx; line-height: 40rpx;
color: #333333; color: #333333;
} }
.reply-content { .reply-content {
text-indent: 128rpx; text-indent: 128rpx;
font-weight: 400; font-weight: 400;
font-size: 26rpx; font-size: 26rpx;
line-height: 40rpx; line-height: 40rpx;
color: #333333; color: #333333;
} }
</style> </style>

View File

@ -1,3 +1,4 @@
<!-- 商品评论的卡片 -->
<template> <template>
<view class="detail-comment-card bg-white"> <view class="detail-comment-card bg-white">
<view class="card-header ss-flex ss-col-center ss-row-between ss-p-b-30"> <view class="card-header ss-flex ss-col-center ss-row-between ss-p-b-30">
@ -12,19 +13,20 @@
v-if="state.commentList.length > 0" v-if="state.commentList.length > 0"
> >
<button class="ss-reset-button more-btn">查看全部</button> <button class="ss-reset-button more-btn">查看全部</button>
<text class="cicon-forward"></text> <text class="cicon-forward" />
</view> </view>
</view> </view>
<!-- 评论列表 -->
<view class="card-content"> <view class="card-content">
<view class="comment-box ss-p-y-30" v-for="item in state.commentList" :key="item.id"> <view class="comment-box ss-p-y-30" v-for="item in state.commentList" :key="item.id">
<comment-item :item="item"></comment-item> <comment-item :item="item" />
</view> </view>
<s-empty <s-empty
v-if="state.commentList.length === 0" v-if="state.commentList.length === 0"
paddingTop="0" paddingTop="0"
icon="/static/comment-empty.png" icon="/static/comment-empty.png"
text="期待您的第一个评价" text="期待您的第一个评价"
></s-empty> />
</view> </view>
</view> </view>
</template> </template>
@ -32,25 +34,27 @@
<script setup> <script setup>
import { reactive, onBeforeMount } from 'vue'; import { reactive, onBeforeMount } from 'vue';
import sheep from '@/sheep'; import sheep from '@/sheep';
import CommentApi from '@/sheep/api/product/comment';
import commentItem from './comment-item.vue'; import commentItem from './comment-item.vue';
const props = defineProps({ const props = defineProps({
goodsId: { goodsId: {
type: [Number, String], type: [Number, String],
default: 0, default: 0,
}, },
}); });
const state = reactive({ const state = reactive({
commentList: [], commentList: [], // 3
total: 0, total: 0, //
}); });
async function getComment(id) { async function getComment(id) {
const { data } = await sheep.$api.goods.comment(id, { const { data } = await CommentApi.getCommentPage(id, 1, 3, 0);
list_rows: 3, state.commentList = data.list;
}); state.total = data.total;
const {data:datas} = await sheep.$api.goods.comment2(id);
state.commentList = data;
state.total = datas.total;
} }
onBeforeMount(() => { onBeforeMount(() => {
getComment(props.goodsId); getComment(props.goodsId);
}); });

View File

@ -34,8 +34,7 @@
<view class="discounts-box ss-flex ss-row-between ss-m-b-28"> <view class="discounts-box ss-flex ss-row-between ss-m-b-28">
<div class="tag-content"> <div class="tag-content">
<view class="tag-box ss-flex"> <view class="tag-box ss-flex">
<view class="tag ss-m-r-10" v-for="promos in state.goodsInfo.promos" <view class="tag ss-m-r-10" v-for="promos in state.goodsInfo.promos" :key="promos.id" @tap="onActivity">
:key="promos.id" @tap="onActivity">
{{ promos.title }} {{ promos.title }}
</view> </view>
</view> </view>
@ -71,10 +70,10 @@
<!-- 详情 --> <!-- 详情 -->
<detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" /> <detail-content-card class="detail-content-selector" :content="state.goodsInfo.description" />
<!-- 活动跳转 --> <!-- TODO 芋艿活动跳转 -->
<detail-activity-tip v-if="state.goodsInfo.activities" :data="state.goodsInfo"></detail-activity-tip> <detail-activity-tip v-if="state.goodsInfo.activities" :data="state.goodsInfo" />
<!-- 详情tabbar --> <!-- 详情 tabbar -->
<detail-tabbar v-model="state.goodsInfo"> <detail-tabbar v-model="state.goodsInfo">
<view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0"> <view class="buy-box ss-flex ss-col-center ss-p-r-20" v-if="state.goodsInfo.stock > 0">
<button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true"> <button class="ss-reset-button add-btn ui-Shadow-Main" @tap="state.showSelectSku = true">

View File

@ -49,16 +49,6 @@ export default {
showError: false, showError: false,
}, },
}), }),
comment2: (id, params = {pageNo:1,pageSize:10,type:0}) =>
request2({
url: 'product/comment/page?spuId='+id,
method: 'GET',
params,
custom: {
showLoading: false,
showError: false,
},
}),
// 商品评价类型 // 商品评价类型
getType: (id) => getType: (id) =>
request({ request({

View File

@ -0,0 +1,18 @@
import request from '@/sheep/request';
const CommentApi = {
// 获得商品评价分页
getCommentPage: (spuId, pageNo, pageSize, type) => {
return request({
url: '/app-api/product/comment/page',
method: 'GET',
params: {
spuId,
pageNo,
pageSize,
type
},
});
},
};
export default CommentApi;

View File

@ -157,6 +157,16 @@ function getDayjsTime(time) {
} }
} }
/**
* 将分转成元
*
* @param price 例如说 100
* @returns {string} 例如说 1.00
*/
export function fen2yuan(price) {
return (price / 100.0).toFixed(2)
}
/** /**
* 从商品 SKU 数组中转换出商品属性的数组 * 从商品 SKU 数组中转换出商品属性的数组
* *