752 lines
20 KiB
Vue
Raw Normal View History

2025-03-29 16:01:43 +08:00
<template>
<view class="page">
<view class="beer-container">
<view class="goods-box">
<image :src="beerInfo.cover" class="cover"></image>
<view class="flex-1">
<view class="flex justify-between align-center" style="margin-bottom: 28rpx;">
<view class="flex flex-1 justify-between align-center">
<text
style="font-size: 40rpx;color: #141415;font-weight: 600;margin-right: 8rpx;">{{ beerInfo.beerName || '--' }}</text>
<text v-if="!isFavor" class="cuIcon-like" style="font-size: 40rpx;color: #0B0E26"
@click="favorBeerFun(1)"></text>
<text v-else class="cuIcon-likefill" style="color: pink;font-size: 40rpx;" @click="favorBeerFun(2)"></text>
<!-- <image src="@/static/verify-mark.png" style="width: 48rpx;height: 48rpx;" @click="favorBeerFun(2)"> -->
</image>
</view>
<!-- <text class="cuIcon-like" style="color: #979797;font-size: 48rpx;"></text> -->
</view>
<view style="margin-bottom: 18rpx;font-size: 30rpx;color: #5F5F63;"> {{ beerInfo.beerStyles || '--' }}
</view>
<!-- <view style="margin-bottom: 18rpx;font-size: 30rpx;color: #5F5F63;">{{ item.launchDate }}</view> -->
<view class="flex align-center" style="gap:20rpx;margin-bottom: 18rpx;">
<uni-rate :readonly="true" :touchable="false" v-model="reviewScoreList.avgOverallRating || 0" />
{{ reviewScoreList.avgOverallRating || 0 }} / 5.0
</view>
<view v-if="beerInfo.breweryId" class="flex align-center justify-between" @click="toBrand">
<view class="flex align-center">
<image :src="beerInfo.brandLogo" style="width: 72rpx;height: 72rpx;margin-right: 12rpx;">
</image>
<text class="">{{ beerInfo.brandName || ''}}</text>
</view>
<image src="/static/arrow-right.png" style="width: 40rpx;height: 40rpx;"></image>
</view>
</view>
</view>
<view>
<view class="info-box flex justify-between margin-bottom">
<view class="info-item">
<text class="label">ABV</text>
<text class="item-text">{{beerInfo.beerAbv || '--'}}</text>
</view>
<view class="info-item">
<text class="label">IBU</text>
<text class="item-text">{{beerInfo.beerIbus || '--'}}</text>
</view>
<view class="info-item">
<text class="label">上市状态</text>
<text class="item-text">{{beerInfo.onSale == 1 ? '在售款' : '停售'}}</text>
</view>
</view>
<!-- <text class="desc word-all" v-html="beerInfo.desc"></text> -->
<rich-text class="desc word-all" :nodes="beerInfo.desc"></rich-text>
</view>
</view>
<!-- 大家评分 -->
<view class="score-box">
<view class="top">
<view class="left flex justify-between align-center">
<image src="/static/ranking.png" class="icon"></image>
<text v-if="currentTab == 1">我的评分</text>
<text v-if="currentTab == 2">大家评分</text>
</view>
<view class="right">
<view class="defTab" :class="{'active':currentTab == 1}" @click="currentTab = 1">
<image src="/static/user.png" class="icon"></image>
</view>
<view class="defTab" :class="{'active':currentTab == 2}" @click="currentTab = 2">
<image src="/static/users.png" class="icon"></image>
</view>
</view>
</view>
<view v-if="currentTab == 1">
<template v-if="myReviewInfo">
<view class="item-label flex justify-between align-center">
<text>颜色</text>
<text>{{myReviewInfo.colorRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="myReviewInfo.colorRating" activeColor="#FEE034"
backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center">
<text>香味</text>
<text>{{myReviewInfo.aromaRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="myReviewInfo.aromaRating" activeColor="#FEE034"
backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center">
<text>口味</text>
<text>{{myReviewInfo.tasteRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="myReviewInfo.tasteRating" activeColor="#FEE034"
backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center margin-top-sm">
<text>综合得分</text>
<text style="font-size: 30rpx;">
<text class="cuIcon-favorfill lg"
style="color:#FEE034;font-size: 40rpx;margin-right:10rpx"></text>
{{myReviewInfo.overallRating || 0}}</text>
</view>
</template>
<view v-else class="flex justify-center align-center" style="height: 80rpx;" @click="toPageBottom">
<text>你还没有评价快来评价吧</text>
</view>
</view>
<view v-if="currentTab == 2">
<view class="item-label flex justify-between align-center">
<text>颜色</text>
<text>{{reviewScoreList.avgColorRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="reviewScoreList.avgColorRating"
activeColor="#FEE034" backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center">
<text>香味</text>
<text>{{reviewScoreList.avgAromaRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="reviewScoreList.avgAromaRating"
activeColor="#FEE034" backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center">
<text>口味</text>
<text>{{reviewScoreList.avgTasteRating}}</text>
</view>
<view class="slider-box">
<slider min="0" max="5" step="1" disabled :value="reviewScoreList.avgTasteRating"
activeColor="#FEE034" backgroundColor="#f2f2f2" block-color="#FEE034" block-size="18" />
</view>
<view class="item-label flex justify-between align-center margin-top-sm">
<text>综合得分</text>
<text style="font-size: 30rpx;">
<text class="cuIcon-favorfill lg"
style="color:#FEE034;font-size: 40rpx;margin-right:10rpx"></text>
{{reviewScoreList.avgOverallRating || 0}}</text>
</view>
</view>
</view>
<!-- 酒款评价 -->
<view class="review-box">
<view class="top">
<view class="left flex justify-between align-center">
<image src="/static/archive-tick.png" class="icon"></image>
<text>酒款评价</text>
</view>
<view v-if="reviewTotal > 0" class="right">
{{ reviewTotal }}
</view>
</view>
<template v-if="reviewList.length > 0">
<view class="review-item" v-for="(item, index) in reviewList" :key="index">
<view class="flex margin-bottom-sm">
<!-- <image v-if="item.reviewImg" :src="item.reviewImg" class="avatar"></image> -->
<view class="flex flex-col">
<text class="beerName">{{ item.userName || '匿名用户' }}</text>
<text class="time">{{ item.createTime.slice(0,10) }}</text>
</view>
</view>
<view class="margin-bottom flex ">
<text class="word-all flex-1">
{{ item.reviewContent }}
</text>
<image v-if="item.reviewImg" :src="item.reviewImg"
style="width: 200rpx;height:200rpx;flex-shrink: 0;margin-left: 20rpx;"></image>
</view>
<view class="flex align-center justify-between">
<view class="flex align-center justify-start">
<image src="@/static/vector.png" class="icon"></image>
<text style="color:#5F5F63;margin-right: 40rpx;">{{ item.compositeScore }}</text>
<!-- <image src="@/static/message.png" class="icon"></image>
<text style="color:#979797">(100)</text> -->
</view>
<text v-if="!item.reviewLike" class="cuIcon-appreciate" style="font-size: 40rpx;margin-right: 40rpx;"
@click="handleLike(item)"></text>
<text v-else class="cuIcon-appreciatefill" style="font-size: 40rpx;margin-right: 40rpx;color: #FEE034;"
@click="handleLike(item)"
></text>
<!-- <view class="cu-btn line-grey round">删除</view> -->
</view>
</view>
<view v-if="reviewList.length < reviewTotal" style="color: #747783;font-size: 24rpx;text-align: center;" @click="reviewPageChange">查看更多评价</view>
<view v-else class="margin-top" style="color: #747783;font-size: 24rpx;text-align: center;">没有更多了</view>
</template>
<template v-else>
<view class="flex align-center justify-center"
style="height: 140rpx;width: 100%;color: #747783;font-size: 24rpx;">暂无评价</view>
</template>
</view>
<view class="flex align-end flex-col bg-white padding margin-bottom">
<view class="lq-tip flex align-center">
<image src="@/static/warning-2.png" style="width: 30rpx;height:30rpx;margin-right: 12rpx;"></image>
首次写酒评还能领取啤酒币哦
</view>
<view class="flex justify-between align-center" style="width: 100%;padding-left: 52rpx;">
<!-- <view class="btn">
生成酒单
</view> -->
<view class="flex flex-col align-center" @click="createPic">
<image src="@/static/share.png" style="width: 48rpx;height: 48rpx;margin-bottom: 10rpx;"></image>
<text style="color: #030416;font-size: 20rpx;">酒款分享</text>
</view>
<view class="flex flex-col align-center" @click="toWinelist">
<image src="@/static/map.png" style="width: 48rpx;height: 48rpx;margin-bottom: 10rpx;"></image>
<text style="color: #030416;font-size: 20rpx;">生成酒单</text>
</view>
<view class="btn" style="background-color: #71F4B4;" @click="toWrite">
写酒评
</view>
</view>
</view>
<loginPopup ref="loginRef"></loginPopup>
<createPoster v-if="showShare" ref="createPosterRef" :url="tempUrl" @close="showShare=false"></createPoster>
<canvas type="2d" id="myCanvas" style="width: 360px;height: 570px;position: fixed;left:8888px"></canvas>
</view>
</template>
<script>
import {
getReviewInfo,
getReviewList,
getReviewScoreList,
getMyReviewInfo,
getBeerInfo,
favorBeer,
likeReview,
getBeerFavorStatus
} from '@/api/bar.js'
import loginPopup from '@/components/loginPopup.vue';
import createPoster from '@/components/createPoster.vue'
export default {
components: {
loginPopup,
createPoster
},
data() {
return {
tempUrl: '',
showShare: false,
beerId: '',
beerInfo: {}, // 酒款信息
reviewInfo: {},
reviewList: [], // 酒评列表
reviewScoreList: [], // 酒评评分
myReviewInfo: null,
currentTab: 2,
reviewTotal: 0, // 酒评总数
queryForm: {
beerId:'',
pageNum: 1,
pageSize: 5,
},
isFavor: false, // 是否收藏
};
},
onLoad({
beerId
}) {
this.beerId = beerId
this.queryForm.beerId = beerId
this.getBeerInfoFun()
this.getBeerFavorStatusFun() // 收藏状态
},
onShow() {
console.log('show')
this.reviewList = []
this.queryForm.pageNum = 1
this.getReviewListFun()
this.getReviewScoreListFun()
if (uni.getStorageSync('token')) {
this.getMyReviewInfoFun()
}
// 滚动到最顶
uni.pageScrollTo({
scrollTop: 0,
duration: 0,
})
},
methods: {
toPageBottom() {
uni.pageScrollTo({
scrollTop: 9999,
})
},
// 查询酒款收藏状态
getBeerFavorStatusFun() {
getBeerFavorStatus(this.beerId).then(res => {
if(res.data) {
this.isFavor = true
}else {
this.isFavor = false
}
})
},
// 获取酒款信息
getBeerInfoFun() {
getBeerInfo(this.beerId).then(res => {
this.beerInfo = res.data
})
},
// 获取酒评信息
getReviewInfoFun() {
getReviewInfo(this.beerId).then(res => {
this.reviewInfo = res.data
})
},
// 获取酒评列表
getReviewListFun() {
getReviewList(this.queryForm).then(res => {
if (res.rows) {
let arr = res.rows.map(it => {
it.compositeScore = this.getScore(it.aromaRating, it.tasteRating, it
.colorRating)
return it
})
if (arr.length > 0) {
arr.forEach(it => {
this.reviewList.push(it)
})
}
}
this.reviewTotal = res.total
})
},
// 酒评列表分页
reviewPageChange() {
this.queryForm.pageNum++
this.getReviewListFun()
},
// 获取酒评评分列表
getReviewScoreListFun() {
getReviewScoreList(this.beerId).then(res => {
this.reviewScoreList = res.data
})
},
// 获取我的酒评信息
getMyReviewInfoFun() {
getMyReviewInfo(this.beerId).then(res => {
this.myReviewInfo = res.data
})
},
// 写酒评
toWrite() {
if (!uni.getStorageSync('token')) {
this.$refs.loginRef.open()
return
}
uni.navigateTo({
url: '/pages/index/writeReview?beerId=' + this.beerId
})
},
// 计算综合评分
getScore(a, b, c) {
let score = 0
score = (a + b + c) / 3
return score.toFixed(0)
},
// 跳转 品牌方
toBrand() {
if (!this.beerInfo.breweryId) return
uni.navigateTo({
url: '/pages/index/brandHome?breweryId=' + this.beerInfo.breweryId
})
},
// 收藏酒款
favorBeerFun(status) {
if (!uni.getStorageSync('token')) {
this.$refs.loginRef.open()
return
}
let data = {
beerId: this.beerId,
status
}
favorBeer(data).then(res => {
if (status == 1) {
uni.showToast({
title: '收藏成功',
icon: 'success'
})
} else {
uni.showToast({
title: '取消收藏',
icon: 'none'
})
}
this.getBeerFavorStatusFun()
})
},
// 点赞
handleLike(item) {
if (!uni.getStorageSync('token')) {
this.$refs.loginRef.open()
return
}
let data = {
reviewId: item.id,
status: item.reviewLike ? 2 : 1
}
likeReview(data).then(res => {
if (data.status == 1) {
uni.showToast({
title: '点赞成功',
icon: 'none'
})
item.reviewLike = true
} else {
uni.showToast({
title: '取消点赞',
icon: 'none'
})
item.reviewLike = false
}
// this.reviewList = []
// this.queryForm.pageNum = 1
// this.getReviewListFun()
})
},
// 生成酒单
toWinelist() {
if (!uni.getStorageSync('token')) {
this.$refs.loginRef.open()
return
}
uni.navigateTo({
url: "/pagesActivity/winelist?beerId=" + this.beerId
})
},
// 分享
createPic() {
uni.showLoading({
title: '加载中'
})
let _this = this
// 获取的画布
uni.createSelectorQuery().select('#myCanvas').fields({
node: true,
size: true
}).exec((res) => {
const ctx = res[0].node.getContext('2d')
const canvas = res[0].node
const dpr = uni.getSystemInfoSync().pixelRatio
// const dpr = uni.getDeviceInfo().devicePixelRatio
// const dpr = uni.getSystemSetting().canvasToTempFilePath()
// 设置画布的宽高
canvas.width = res[0].width * dpr
canvas.height = res[0].height * dpr
ctx.scale(dpr, dpr)
let imageCover = canvas.createImage()
imageCover.src = this.beerInfo.cover
imageCover.onload = () => {
ctx.fillStyle = '#fff';
ctx.fillRect(0, 0, canvas.width, canvas.height);
ctx.drawImage(imageCover, 0, 0, 360, 460)
// 绘制文字
ctx.font = `12px Arial`
ctx.fillStyle = '#0B0E26'
ctx.fillText(this.beerInfo.brandName, 20, 470)
ctx.font = `12px Arial`
ctx.fillStyle = '#0B0E26'
ctx.fillText(this.beerInfo.beerName, 20, 490)
ctx.font = `12px Arial`
ctx.fillStyle = '#5E5F60'
ctx.fillText(this.beerInfo.beerStyles, 20, 510)
// ctx.font = `${this.els.text3.fontSize}px Arial`
// ctx.fillStyle = this.els.text3.color
// ctx.fillText(this.els.text3.value, this.els.text3.x, this.els.text3.y)
// ctx.font = `${this.els.text4.fontSize}px Arial`
// ctx.fillStyle = this.els.text4.color
// ctx.fillText(this.els.text4.value, this.els.text4.x, this.els.text4.y)
// 保存图片
uni.canvasToTempFilePath({
canvas: canvas,
success: (res) => {
_this.tempUrl = res.tempFilePath
this.showShare = true
setTimeout(() => {
uni.hideLoading()
this.$refs.createPosterRef.open()
}, 1000)
},
fail: (err) => {
console.log(err)
uni.hideLoading()
},
})
}
})
},
}
}
</script>
<style lang="scss" scoped>
/deep/ slider {
margin: 0;
}
/deep/ .wx-slider-handle-wrapper {
margin: 0;
height: 36rpx !important;
}
.page {
min-height: 100vh;
background-color: #F6F6F6;
;
display: flex;
flex-direction: column;
justify-content: space-between;
// padding: 0rpx 28rpx 116rpx;
.lq-tip {
border-radius: 20rpx;
background: rgba(233, 233, 233, 0.38);
box-sizing: border-box;
border: 1px solid rgba(233, 233, 233, 0.3804);
padding: 10rpx;
font-size: 20rpx;
color: #5E5F60;
margin-bottom: 14rpx;
// width: 400rpx;
}
.btn {
border-radius: 30rpx;
box-sizing: border-box;
width: 306rpx;
height: 96rpx;
border-radius: 30rpx;
background-color: #FEE034;
font-size: 28rpx;
color: #1E2019;
display: flex;
align-items: center;
justify-content: center;
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 22rpx;
}
}
.beer-container {
border-radius: 0rpx 0rpx 30rpx 30rpx;
background: #FFF;
box-sizing: border-box;
border: 1px solid #F2F2F2;
box-shadow: 0px 5.47px 43.78px 0px rgba(0, 0, 0, 0.05);
padding: 16rpx 40rpx;
.goods-box {
display: flex;
margin-bottom: 34rpx;
.cover {
width: 208rpx;
height: 300rpx;
border-radius: 12rpx;
margin-right: 14rpx;
}
}
.info-box {
.info-item {
border-radius: 36rpx;
background: rgba(255, 188, 17, 0.1);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 160rpx;
height: 164rpx;
.label {
font-family: Source Han Sans;
font-size: 30rpx;
font-weight: 500;
line-height: 44rpx;
color: #5F5F63;
margin-bottom: 9rpx;
}
.item-text {
font-family: Source Han Sans;
font-size: 30rpx;
font-weight: 500;
line-height: 44rpx;
color: #FFBC11;
}
}
}
.desc {
font-family: Source Han Sans;
font-size: 24rpx;
font-weight: 500;
line-height: 48rpx;
color: #A1A1A1;
}
}
.score-box {
border-radius: 30rpx;
background: #FFFFFF;
padding: 24rpx 28rpx 34rpx 34rpx;
margin-bottom: 32rpx;
.top {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 22rpx;
.left {
font-family: Source Han Sans;
font-size: 36rpx;
font-weight: 500;
line-height: 44rpx;
color: #0B0E26;
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 14rpx;
}
}
.right {
border-radius: 20rpx;
background: #F2F2F2;
display: flex;
justify-content: space-between;
.defTab {
width: 50%;
border-radius: 20rpx;
background: #F2F2F2;
display: flex;
justify-content: center;
align-items: center;
padding: 24rpx 36rpx;
.icon {
width: 48rpx;
height: 48rpx;
}
}
.active {
background-color: #71F4B4;
}
}
}
}
.review-box {
width: 100%;
// height: 200rpx;
padding: 0rpx 32rpx 30rpx;
border-radius: 30rpx;
background: #FFFFFF;
box-sizing: border-box;
border: 1px solid #F2F2F2;
.top {
display: flex;
justify-content: space-between;
align-items: center;
padding: 36rpx 10rpx 36rpx 0;
color: #0B0E26;
border-bottom: 1rpx solid rgba(242, 242, 242, 0.9);
.left {
font-family: Source Han Sans;
font-size: 36rpx;
font-weight: 500;
line-height: 44rpx;
.icon {
width: 48rpx;
height: 48rpx;
margin-right: 14rpx;
}
}
}
}
.review-item {
padding: 32rpx 0;
// margin-bottom: 32rpx;
border-bottom: 1rpx solid rgba(242, 242, 242, 0.9);
.avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 36rpx;
}
.beerName {
font-size: 24rpx;
font-weight: bold;
color: #32343E;
margin-bottom: 8rpx;
}
.time {
font-size: 24rpx;
color: #9C9BA6;
}
.icon {
width: 40rpx;
height: 40rpx;
margin-right: 10rpx;
}
}
}
</style>