first commit
This commit is contained in:
@@ -0,0 +1,392 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
appMerchantRecipeRecipeDetailPost,
|
||||
appMerchantRecipeAddViewCountPost,
|
||||
appCollectCollectPost,
|
||||
appCommentCommentListPost, appCommentPublishCommentPost
|
||||
} from "@/service";
|
||||
import { debounce } from 'throttle-debounce'
|
||||
import {CollectionType} from "@/constant/enums";
|
||||
const { t } = useI18n()
|
||||
import RecipeSkeleton from "./components/recipe-skeleton.vue";
|
||||
import CComment from "@/components/cc-comment/cc-comment.vue";
|
||||
import { useScrollThreshold } from "@/hooks/useScrollThreshold";
|
||||
import {formatTimestamp, formatTimestampWithMonthName} from "@/utils/utils";
|
||||
import {useUserStore, useConfigStore} from "@/store";
|
||||
const userStore = useUserStore()
|
||||
const configStore = useConfigStore()
|
||||
// 加载状态
|
||||
const loading = ref(true);
|
||||
// 获取菜谱详情
|
||||
const recipeId = ref('') // 菜谱ID
|
||||
const recipeDetail = ref<any>({})
|
||||
onLoad((options: any)=> {
|
||||
if(options.id) {
|
||||
recipeId.value = options.id
|
||||
getRecipeDetail()
|
||||
|
||||
// 获取评论列表
|
||||
getCommentList()
|
||||
}
|
||||
})
|
||||
function getRecipeDetail() {
|
||||
loading.value = true
|
||||
appMerchantRecipeRecipeDetailPost({
|
||||
params: {
|
||||
recipeId: recipeId.value
|
||||
}
|
||||
}).then(res=> {
|
||||
console.log('菜谱详情', res)
|
||||
recipeDetail.value = res.data
|
||||
|
||||
appMerchantRecipeAddViewCountPost({
|
||||
params: {
|
||||
recipeId: recipeId.value,
|
||||
}
|
||||
})
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 返回上一页
|
||||
const goBack = () => {
|
||||
uni.navigateBack();
|
||||
};
|
||||
|
||||
// 收藏菜谱
|
||||
const collectRecipeDebounce = debounce(1000, () => {
|
||||
appCollectCollectPost({
|
||||
body: {
|
||||
targetId: recipeId.value,
|
||||
targetType: CollectionType.RECIPE
|
||||
}
|
||||
}).then(res=> {
|
||||
recipeDetail.value.isCollect = !recipeDetail.value.isCollect;
|
||||
recipeDetail.value.collectCount = recipeDetail.value.isCollect ? recipeDetail.value.collectCount + 1 : recipeDetail.value.collectCount - 1
|
||||
})
|
||||
}, {
|
||||
atBegin: true, // 立即触发
|
||||
});
|
||||
|
||||
|
||||
const commentList = ref<any>([])
|
||||
function getCommentList() {
|
||||
appCommentCommentListPost({
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 100,
|
||||
},
|
||||
body: {
|
||||
targetId: recipeId.value,
|
||||
targetType: 1,
|
||||
}
|
||||
}).then(res=> {
|
||||
console.log('评论列表', res)
|
||||
commentList.value = res.rows.map(item => {
|
||||
let userInfo = {}
|
||||
// 普通用户
|
||||
if (+item.userPort === 1) {
|
||||
userInfo.user_id = item.userVo.id
|
||||
userInfo.user_name = `${item.userVo.firstName} ${item.userVo.surname}`
|
||||
userInfo.user_avatar = item.userVo.avatar
|
||||
} else {
|
||||
userInfo.user_id = item.merchantVo.userId
|
||||
userInfo.user_name = item.merchantVo.merchantName
|
||||
userInfo.user_avatar = item.merchantVo.logo
|
||||
}
|
||||
return {
|
||||
childList: item.childList,
|
||||
id: item.id,
|
||||
topId: item.topId,
|
||||
parent_id: null, // 评论父级的id
|
||||
reply_id: null, // 被回复评论的id
|
||||
reply_name: null, // 被回复人名称
|
||||
target_id: recipeId.value,
|
||||
commentCount: item.commentCount,
|
||||
user_id: userInfo.user_id, // 用户id
|
||||
user_name: userInfo.user_name, // 用户名
|
||||
user_avatar: userInfo.user_avatar, // 用户头像地址
|
||||
user_content: item.content, // 用户评论内容
|
||||
create_time: formatTimestampWithMonthName(item.createTime), // 创建时间
|
||||
}
|
||||
})
|
||||
tableTotal.value = res.total
|
||||
})
|
||||
}
|
||||
|
||||
// 唤起新评论弹框
|
||||
let ccRef = ref(null);
|
||||
let myInfo = ref({
|
||||
user_id: userStore.userInfo.id, // 用户id
|
||||
});
|
||||
let tableTotal = ref(0); // 评论总数
|
||||
|
||||
const showStatusBar = useScrollThreshold();
|
||||
onPageScroll((e) => {
|
||||
uni.$emit("page-scroll", e);
|
||||
});
|
||||
|
||||
|
||||
const sendValue = ref('');
|
||||
function handleSend() {
|
||||
if (sendValue.value.trim() === '') {
|
||||
return;
|
||||
}
|
||||
console.log(sendValue.value)
|
||||
appCommentPublishCommentPost({
|
||||
body: {
|
||||
userPort: 1, // 1-普通用户 2-商家用户
|
||||
targetId: recipeId.value, // 评论对象ID
|
||||
targetType: 1, // 评论对象类型(1-菜谱 2-菜品 3-配菜)
|
||||
topId: '', // 顶级评论ID(对主体的直接评论)
|
||||
parentId: '', // 上级评论ID
|
||||
content: sendValue.value, // 评论内容
|
||||
}
|
||||
}).then(res=> {
|
||||
console.log(res)
|
||||
uni.showToast({
|
||||
title: t('toast.commentSuccess'),
|
||||
icon: 'none'
|
||||
})
|
||||
sendValue.value = ''
|
||||
getCommentList()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<view class="recipe-page">
|
||||
<!-- 骨架屏 -->
|
||||
<recipe-skeleton v-if="loading" />
|
||||
|
||||
<!-- 实际内容 -->
|
||||
<view v-else class="recipe-content animate-in fade-in animate-duration-300">
|
||||
<status-bar />
|
||||
<!-- 顶部图片区域 -->
|
||||
<view class="relative w-750rpx h-750rpx px-30rpx">
|
||||
<view
|
||||
class="fixed top-0 left-0 z-9 w-full px-30rpx transition-all pt-16rpx"
|
||||
:class="[showStatusBar ? 'bg-#fff' : '']"
|
||||
>
|
||||
<!-- 状态栏 -->
|
||||
<status-bar />
|
||||
<!-- 返回按钮 -->
|
||||
<image
|
||||
@click="goBack"
|
||||
src="@img/chef/1327.png"
|
||||
mode="aspectFill"
|
||||
class="w-48rpx h-48rpx relative z-1"
|
||||
/>
|
||||
</view>
|
||||
<!-- 主图 -->
|
||||
<image
|
||||
:src="recipeDetail?.recipeImage?.split(',')[0]"
|
||||
mode="aspectFill"
|
||||
class="w-750rpx h-750rpx absolute top-0 left-0"
|
||||
/>
|
||||
<!-- 标题 -->
|
||||
<view class="absolute z-1 bottom-102rpx left-0 w-full px-30rpx">
|
||||
<view
|
||||
class="line-clamp-1 text-40rpx lh-40rpx text-#fff font-bold mb-28rpx"
|
||||
>{{ recipeDetail?.recipeName || '' }}</view
|
||||
>
|
||||
<view class="flex-center-sb text-28rpx text-#fff">
|
||||
<text>{{ formatTimestamp(recipeDetail?.createTime) }}</text>
|
||||
<view class="flex items-center">
|
||||
<image
|
||||
src="@img/chef/1326.png"
|
||||
mode="aspectFill"
|
||||
class="w-32rpx h-32rpx mr-8rpx"
|
||||
/>
|
||||
<text>{{ recipeDetail?.viewCount > 999 ? '999+' : recipeDetail?.viewCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 渐变遮罩 -->
|
||||
<view
|
||||
class="absolute bottom-0 left-0 w-full h-240rpx bg-gradient-to-b from-transparent via-[rgba(0,0,0,0.5)] to-black"
|
||||
></view>
|
||||
</view>
|
||||
|
||||
<!-- 内容卡片 -->
|
||||
<view
|
||||
class="content-card bg-white rounded-t-30rpx mt--68rpx relative px-30rpx"
|
||||
>
|
||||
<!-- 标题区域 -->
|
||||
<view class="flex items-center pt-40rpx mb-40rpx">
|
||||
<view class="w-44rpx h-44rpx mr-12rpx">
|
||||
<image
|
||||
src="@img/chef/1325.png"
|
||||
mode="aspectFill"
|
||||
class="w-44rpx h-44rpx"
|
||||
/>
|
||||
</view>
|
||||
<text class="text-36rpx font-medium text-#333">{{ t('pages-user.recipe.title') }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 材料区域 -->
|
||||
<view class="mb-40rpx">
|
||||
<text class="text-30rpx leading-44rpx text-#333">
|
||||
{{ recipeDetail?.ingredients }}
|
||||
</text>
|
||||
</view>
|
||||
|
||||
<!-- 视频预览区域 -->
|
||||
<view
|
||||
class="w-690rpx rounded-20rpx overflow-hidden mb-40rpx"
|
||||
>
|
||||
<template v-for="item in recipeDetail?.recipeImage?.split(',')">
|
||||
<wd-img :enable-preview="true" :src="item" class="mb-20rpx last:mb-0" height="360rpx" mode="aspectFill"
|
||||
radius="20rpx"
|
||||
width="100%"/>
|
||||
</template>
|
||||
</view>
|
||||
|
||||
<!-- 收藏按钮 -->
|
||||
<view class="flex justify-center mb-40rpx relative">
|
||||
<view
|
||||
:class="[recipeDetail.isCollect ? 'bg-#fff border-#FF2806 border-solid border-1px text-#333' : 'bg-#FF2806 text-white']"
|
||||
class="w-310rpx h-88rpx rounded-44rpx center relative z-2"
|
||||
@click="collectRecipeDebounce"
|
||||
>
|
||||
<view class="flex items-center">
|
||||
<image
|
||||
v-if="recipeDetail.isCollect"
|
||||
src="@img/chef/118.png"
|
||||
mode="aspectFill"
|
||||
class="w-44rpx h-44rpx"
|
||||
/>
|
||||
<image
|
||||
v-else
|
||||
src="@img/chef/1332.png"
|
||||
mode="aspectFill"
|
||||
class="w-44rpx h-44rpx"
|
||||
/>
|
||||
<text class="text-30rpx ml-10rpx">{{ recipeDetail.collectCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view
|
||||
v-if="!recipeDetail.isCollect"
|
||||
class="absolute bottom--16rpx left-50% translate-x-[-50%] w-310rpx h-88rpx bg-#FF2806 opacity-[.28] rounded-44rpx blur-18rpx"
|
||||
></view>
|
||||
</view>
|
||||
|
||||
<!-- 收藏提示 -->
|
||||
<view class="text-center mb-40rpx">
|
||||
<text class="text-24rpx lh-24rpx text-#9E9E9E"
|
||||
>{{ t('pages-user.recipe.desc') }}</text
|
||||
>
|
||||
</view>
|
||||
|
||||
<!-- 用户头像列表 -->
|
||||
<view v-if="recipeDetail?.collectUserAvatarList?.length > 0" class="flex justify-center items-center mb-60rpx">
|
||||
<view class="avatar-list flex items-center">
|
||||
<!-- 用户头像 -->
|
||||
<view
|
||||
v-for="i in recipeDetail?.collectUserAvatarList"
|
||||
:key="i"
|
||||
:style="{ zIndex: 1 + i }"
|
||||
class="avatar-item"
|
||||
>
|
||||
<image
|
||||
:src="i"
|
||||
class="w-full h-full rounded-full"
|
||||
mode="aspectFill"
|
||||
/>
|
||||
</view>
|
||||
|
||||
<!-- 更多图标 -->
|
||||
<view class="avatar-item more-icon z-99">
|
||||
<image
|
||||
class="w-full h-full rounded-full"
|
||||
mode="aspectFill"
|
||||
src="@img/chef/1328.png"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
|
||||
<view class="pb-48rpx">
|
||||
<CComment
|
||||
ref="ccRef"
|
||||
v-model:myInfo="myInfo"
|
||||
v-model:tableData="commentList"
|
||||
v-model:tableTotal="tableTotal"
|
||||
:deleteMode="deleteMode"
|
||||
@replyFun="replyFun"
|
||||
@deleteFun="getCommentList"
|
||||
@update="getCommentList"
|
||||
></CComment>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="shadow-lg w-full flex-center-sb gap-30rpx bg-white py-12rpx px-30rpx">
|
||||
<view class="w-full h-74rpx center bg-#F6F6F6 rounded-16rpx px-28rpx">
|
||||
<wd-input
|
||||
no-border
|
||||
clearable
|
||||
:cursorSpacing="10"
|
||||
:focus-when-clear="false"
|
||||
confirm-type="send"
|
||||
use-prefix-slot
|
||||
custom-class="flex items-center !text-30rpx !bg-transparent flex-1"
|
||||
placeholderStyle="font-size: 30rpx;color: #6D6D6D;"
|
||||
:placeholder="t('common.placeholder.pleaseEnter')"
|
||||
v-model="sendValue"
|
||||
@confirm="handleSend"
|
||||
>
|
||||
</wd-input>
|
||||
</view>
|
||||
<wd-button @click="handleSend" class="!h-74rpx !w-120rpx !rounded-16rpx !bg-#333 !text-white !text-30rpx">{{ t('common.send') }}</wd-button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.recipe-page {
|
||||
background-color: #fff;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.recipe-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.image-container {
|
||||
width: 750rpx;
|
||||
height: 750rpx;
|
||||
}
|
||||
|
||||
.content-card {
|
||||
min-height: 1000rpx;
|
||||
}
|
||||
|
||||
// 头像列表样式
|
||||
.avatar-list {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.avatar-item {
|
||||
width: 68rpx;
|
||||
height: 68rpx;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: 2rpx solid #fff;
|
||||
margin-left: -20rpx;
|
||||
position: relative;
|
||||
background-color: #f6f6f6;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
&.more-icon {
|
||||
margin-left: -20rpx;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user