first commit

This commit is contained in:
2026-02-26 09:32:03 +08:00
commit 36a8e4c51b
845 changed files with 116474 additions and 0 deletions
+392
View File
@@ -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>