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
+459
View File
@@ -0,0 +1,459 @@
<template>
<view class="flex items-center pb-36rpx">
<image
src="@img/chef/1330.png"
mode="aspectFill"
class="w-44rpx h-44rpx shrink-0 mr-12rpx"
/>
{{ t('common.comment') }}({{ props.tableTotal }})
</view>
<template v-if="dataList && dataList.length">
<view class="c_comment" v-for="(item1, index1) in dataList" :key="item1.id">
<!-- 一级评论 -->
<CommonComp
:data="item1"
@likeClick="() => likeClick({ item1, index1 })"
@replyClick="() => replyClick({ item1, index1 })"
@deleteClick="() => deleteClick({ item1, index1 })"
/>
<view class="children_item bg-#F5F7FB rounded-16rpx" v-if="item1.childrenShow && item1.childrenShow.length> 0">
<!-- 二级评论 -->
<CommonComp
v-for="(item2, index2) in item1.childrenShow"
:key="item2.id"
:data="item2"
:pData="item1"
@likeClick="() => likeClick({ item1, index1, item2, index2 })"
@replyClick="() => replyClick({ item1, index1, item2, index2 })"
@deleteClick="() => deleteClick({ item1, index1, item2, index2 })"
/>
</view>
<view class="flex items-center pl-100rpx text-#666 pt-10rpx" v-if="item1.children || item1.commentCount> 0">
<!-- 展开二级评论 -->
<view
class="flex items-center"
@click="expandReply(item1)"
>
<text>{{ t('pages-user.recipe.expandReply') }}</text>
<wd-icon name="chevron-down" class="mt-4rpx" size="22px"></wd-icon>
</view>
<!-- 折叠二级评论 -->
<view
class="flex items-center"
@click="shrinkReply(item1)"
>
<text>{{ t('pages-user.recipe.collapseReply') }}</text>
<wd-icon name="chevron-up" class="mt-4rpx" size="22px"></wd-icon>
</view>
</view>
</view>
</template>
<!-- 空盒子 -->
<view class="empty_box" v-else>
<view class="py-100rpx center">
<image class="w-250rpx h-250rpx" src="@img/chef/100.png"></image>
</view>
</view>
<!-- 评论弹窗 -->
<wd-popup ref="cPopupRef" v-model="cPopupShow" custom-style="border-radius:16rpx 16rpx 0 0;" position="bottom" @change="popChange">
<view class="w-full rounded-16rpx px-30rpx py-16rpx">
<view class="flex items-center">
<template v-if="Object.keys(replyTemp).length">
<text class="text_aid">{{ t('pages-user.recipe.replyTo') }}</text>
<image class="w-68rpx h-68rpx rounded-50% mx-10rpx" :src="replyTemp.item2 ? replyTemp.item2.user_avatar : replyTemp.item1.user_avatar" />
<text class="text_main">{{ replyTemp.item2 ? replyTemp.item2.user_name : replyTemp.item1.user_name }}</text>
</template>
</view>
<view class="w-full flex-center-sb gap-30rpx bg-white py-12rpx">
<view class="w-full h-74rpx center bg-#F6F6F6 rounded-16rpx px-28rpx">
<wd-input
no-border
clearable
: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; font-weight: 500;"
@confirm="sendClick"
v-model="commentValue"
:placeholder="commentPlaceholder"
>
</wd-input>
</view>
<wd-button @click="sendClick" class="!h-74rpx !w-120rpx !rounded-16rpx !bg-#333 !text-white !text-30rpx">{{ t('common.send') }}</wd-button>
</view>
</view>
</wd-popup>
<wd-message-box/>
</template>
<script setup>
import CommonComp from "./componets/common";
import {appCommentDeleteCommentIdDelete, appCommentPublishCommentPost, appCommentReplyListPost} from "@/service";
import {useMessage} from "wot-design-uni";
import {formatTimestampWithMonthName} from "@/utils/utils";
const message = useMessage();
const { t } = useI18n();
const props = defineProps({
/** 登陆用户信息
* id: number // 登陆用户id
* user_name: number // 登陆用户名
* user_avatar: string // 登陆用户头像地址
*/
myInfo: {
type: Object,
default: () => {},
},
/** 文章作者信息
* id: number // 文章作者id
* user_name: number // 文章作者名
* user_avatar: string // 文章作者头像地址
*/
userInfo: {
type: Object,
default: () => {},
},
/** 评论列表
* id: number // 评论id
* parent_id: number // 父级评论id
* reply_id: number // 被回复人评论id
* reply_name: string // 被回复人名称
* user_name: string // 用户名
* user_avatar: string // 评论者头像地址
* user_content: string // 评论内容
* is_like: boolean // 是否点赞
* like_count: number // 点赞数统计
* create_time: string // 创建时间
*/
tableData: {
type: Array,
default: () => [],
},
// 评论总数
tableTotal: {
type: Number,
default: 0,
},
// 评论删除模式
// bind - 当被删除的一级评论存在回复评论, 那么该评论内容变更显示为[当前评论内容已被移除]
// only - 仅删除当前评论(后端删除相关联的回复评论, 否则总数显示不对)
// all - 删除所有评论包括回复评论
deleteMode: {
type: String,
default: "all",
},
});
const emit = defineEmits([
"update:tableTotal",
"likeFun", // 点赞事件
"replyFun", // 回复事件
"deleteFun", // 删除事件
]);
// 渲染数据(前端的格式)
let dataList = ref([]);
watch(
() => props.tableData,
(newVal) => {
if (newVal.length !== dataList.value.length) {
let temp = props.tableData;
dataList.value = treeTransForm(temp);
}
},
{ deep: true, immediate: true }
);
// 数据转换
function treeTransForm(data) {
let newData = JSON.parse(JSON.stringify(data));
let result = [];
let map = {};
newData.forEach((item, i) => {
item.owner = item.user_id === props.myInfo.user_id; // 是否为当前登陆用户 可以对自己的评论进行删除 不能回复
// item.author = item.user_id === props.userInfo.user_id; // 是否为作者 显示标记
map[item.id] = item;
});
newData.forEach((item) => {
let parent = map[item.parent_id];
if (parent) {
(parent.children || (parent.children = [])).push(item); // 所有回复
if (parent.children.length === 1) {
(parent.childrenShow = []).push(item); // 显示的回复
}
} else {
result.push(item);
}
});
return result;
}
// 点赞
let setLike = (item) => {
item.is_like = !item.is_like;
item.like_count = item.is_like ? item.like_count + 1 : item.like_count - 1;
};
function likeClick({ item1, index1, item2, index2 }) {
let item = item2 || item1;
setLike(item);
emit("likeFun", { params: item }, (res) => {
// 请求后端失败, 重置点赞
setLike(item);
});
}
// 回复
let cPopupRef = ref(null); // 弹窗实例
const cPopupShow = ref(false); // 弹窗显示状态
let replyTemp = reactive({}); // 临时数据
function replyClick({ item1, index1, item2, index2 }) {
replyTemp = JSON.parse(JSON.stringify({ item1, index1, item2, index2 }));
cPopupShow.value = true;
}
// 发起新评论
let isNewComment = ref(false); // 是否为新评论
defineExpose({ newCommentFun });
function newCommentFun() {
isNewComment.value = true;
cPopupShow.value = true;
}
// 评论弹窗
let focus = ref(false);
function popChange(e) {
// 关闭弹窗
if (!e.show) {
commentValue.value = ""; // 清空输入框值
replyTemp = {}; // 清空被回复人信息
isNewComment.value = false; // 恢复是否为新评论默认值
}
focus.value = e.show;
}
let commentValue = ref(""); // 输入框值
let commentPlaceholder = ref("说点什么..."); // 输入框占位符
// 发送评论
function sendClick({ item1, index1, item2, index2 } = replyTemp) {
console.log('replyTemp', replyTemp.item1)
console.log('replyTemp', replyTemp.item2)
const data = replyTemp.item2 || replyTemp.item1
appCommentPublishCommentPost({
body: {
topId: replyTemp.item2 ? replyTemp.item1.id : data.id,
parentId:data.id,
targetId: data.target_id,
targetType: 1,
content: commentValue.value,
}
}).then(res=> {
console.log(res)
emit("update");
cPopupShow.value = false;
commentValue.value = ""; // 清空输入框值
// shrinkReply(replyTemp.item1)
dataList.value.forEach((item, i) => {
if (item.id === replyTemp.item1.id) {
shrinkReply(item)
}
})
})
}
function expandReply(item1) {
// item1.childrenShow = item1.children
console.log(item1)
appCommentReplyListPost({
params: {
pageNum: 1,
pageSize: 100,
},
body: {
parentId: item1.id,
}
}).then(res=> {
console.log('回复列表', res)
item1.children = 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 {
id: item.id,
topId: item.topId,
parent_id: null, // 评论父级的id
reply_id: null, // 被回复评论的id
reply_name: item.parentUserVo ? `${item.parentUserVo.firstName} ${item.parentUserVo.surname}` : null, // 被回复人名称
target_id: item1.target_id,
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), // 创建时间
owner: userInfo.user_id === props.myInfo.user_id, // 是否为当前登陆用户 可以对自己的评论进行删除 不能回复
}
})
item1.childrenShow = item1.children
})
}
function shrinkReply(item1) {
item1.childrenShow = []
}
// 删除
const delPopupRef = ref(null);
let delTemp = reactive({}); // 临时数据
function deleteClick({ item1, index1, item2, index2 }) {
console.log('删123除', item1, index1, item2, index2)
message
.confirm({
title: t("common.prompt.system-prompt"),
msg: `${t("common.prompt.system-prompt-delete")}`,
confirmButtonText: t("common.yes"),
cancelButtonText: t("common.no"),
cancelButtonProps: {
customClass:
"!h-88rpx !w-258rpx !min-w-auto !text-30rpx !lh-42rpx !font-bold !border-#666666 !rounded-20rpx",
},
confirmButtonProps: {
customClass:
"!h-88rpx !w-258rpx !min-w-auto !text-30rpx !lh-42rpx !font-bold !bg-primary !rounded-20rpx",
},
})
.then(async () => {
appCommentDeleteCommentIdDelete({
params: {
id: item2.id ? item2.id : item1.id,
}
}).then(res=> {
console.log('删除成功', res)
emit("deleteFun");
shrinkReply(item1)
})
})
.catch(() => {
});
}
// 展开评论if
function expandTxtShow({ item1, index1 }) {
return item1.childrenShow?.length && item1.children.length - item1.childrenShow.length;
}
// 展开更多评论
function expandReplyFun({ item1, index1 }) {
let csLen = dataList.value[index1].childrenShow.length;
dataList.value[index1].childrenShow.push(
...dataList.value[index1].children.slice(csLen, csLen + 6) // 截取5条评论
);
}
// 收起评论if
function shrinkTxtShow({ item1, index1 }) {
return item1.childrenShow?.length >= 2 && item1.children.length - item1.childrenShow.length === 0;
}
// 收起更多评论
function shrinkReplyFun({ item1, index1 }) {
let csLen = dataList.value[index1].childrenShow.length;
dataList.value[index1].childrenShow = [];
dataList.value[index1].childrenShow.push(
...dataList.value[index1].children.slice(0, 1) // 截取1条评论
);
}
</script>
<style lang="scss" scoped>
////////////////////////
.center {
display: flex;
align-items: center;
}
////////////////////////
.c_total {
//padding: 20rpx 30rpx 0 30rpx;
font-size: 28rpx;
}
.empty_box {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
padding: 150rpx 10rpx;
font-size: 28rpx;
.txt {
//color: $uni-text-color-disable;
}
.click {
//color: $uni-color-primary;
}
}
.c_comment {
//padding: 20rpx 30rpx;
font-size: 28rpx;
.children_item {
padding: 20rpx 30rpx;
margin-top: 10rpx;
margin-left: 80rpx;
//background-color: $uni-bg-color-grey;
.expand_reply,
.shrink_reply {
margin-top: 10rpx;
margin-left: 80rpx;
.txt {
font-weight: 600;
//color: $uni-color-primary;
}
}
}
}
.c_popup_box {
background-color: #fff;
.reply_text {
@extend .center;
padding: 20rpx 20rpx 0 20rpx;
font-size: 26rpx;
.text_aid {
//color: $uni-text-color-grey;
margin-right: 5rpx;
}
.user_avatar {
width: 48rpx;
height: 48rpx;
border-radius: 50%;
margin-right: 6rpx;
margin-left: 12rpx;
}
.text_main {
}
}
.content {
@extend .center;
.text_area {
flex: 1;
padding: 20rpx;
}
.send_btn {
@extend .center;
justify-content: center;
width: 120rpx;
height: 60rpx;
border-radius: 20rpx;
font-size: 28rpx;
color: #fff;
//background-color: $uni-color-primary;
margin-right: 20rpx;
margin-left: 5rpx;
}
}
}
</style>