| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630 |
- <template>
- <z-paging
- ref="paging"
- use-page-scroll
- @closeF2="closeF2"
- v-model="oneCommentList"
- @query="queryList"
- class="comment-paging"
- >
- <view
- class="comment-box"
- v-for="oneItem in oneCommentList"
- :key="oneItem.id"
- >
- <!-- 一级评论 -->
- <view
- :class="[
- 'item-comment',
- oneItem.replyList && oneItem.replyList.length > 0 ? 'child' : '',
- ]"
- >
- <view class="avatar">
- <up-avatar
- @click="toUserPage(oneItem.userId)"
- shape="circle"
- :src="oneItem.usePicture"
- ></up-avatar>
- </view>
- <view class="content">
- <view class="user-box" @click="toUserPage(oneItem.userId)">
- <text class="username">{{ oneItem.userName }}</text>
- <text class="author-tag" v-if="oneItem.authorMark">作者</text>
- </view>
- <view
- @longpress="longpress"
- :data-one-level-id="oneItem.id"
- class="reply-box"
- @click="handleClickComment(oneItem)"
- >
- <text class="text">{{ oneItem.content }}</text>
- <text class="time">{{
- timeFormat(oneItem.createTime, "yyyy-mm-dd")
- }}</text>
- <text class="reply-tip">回复</text>
- </view>
- </view>
- <view class="right-box">
- <uni-icons
- v-show="!oneItem.likeMark"
- customPrefix="iconfont"
- size="18"
- color="#2E2E2E"
- class="icon-state icon-dianzan"
-
- :class="{
- animated: likeAnimationIds.includes(oneItem.id),
- heartBeat: likeAnimationIds.includes(oneItem.id),
- }"
- />
- <uni-icons
- v-show="oneItem.likeMark"
- customPrefix="iconfont"
- size="18"
- color="#FF2442"
- class="icon-state icon-dianzanxuanzhong"
-
- :class="{
- animated: likeAnimationIds.includes(oneItem.id),
- heartBeat: likeAnimationIds.includes(oneItem.id),
- }"
- />
- <text class="count">{{ oneItem.likeCount }}</text>
- </view>
- </view>
- <!-- 二级评论 -->
- <div
- class="child-comment-box"
- v-if="oneItem.replyList && oneItem.replyList.length > 0"
- >
- <view
- class="item-comment sub-comment"
- v-for="(twoItem, idx) in oneItem.replyList"
- :key="idx"
- >
- <view class="avatar" >
- <up-avatar
- class="sub-avatar"
- shape="circle"
- size="25"
- :src="twoItem.usePicture"
- ></up-avatar>
- </view>
- <view class="content">
- <view class="user-box" >
- <text class="username">{{ twoItem.userName }}</text>
- <text class="author-tag" v-if="twoItem.authorMark">作者</text>
- </view>
- <view
- @longpress="longpress"
- :data-one-level-id="oneItem.id"
- :data-two-level-id="twoItem.id"
- class="reply-box"
-
- >
- <text class="text" v-if="twoItem.replyMark"
- >回复<text>{{ twoItem.replyName }}: </text
- >{{ twoItem.content }}</text
- >
- <text class="text" v-else>{{ twoItem.content }}</text>
- <text class="time">{{
- timeFormat(twoItem.createTime, "yyyy-mm-dd")
- }}</text>
- <text class="reply-tip">回复</text>
- </view>
- </view>
- <view class="right-box">
- <uni-icons
- v-show="!twoItem.likeMark"
- customPrefix="iconfont"
- size="18"
- color="#2E2E2E"
- class="icon-state icon-dianzan"
-
- :class="{
- animated: likeAnimationIds.includes(twoItem.id),
- heartBeat: likeAnimationIds.includes(twoItem.id),
- }"
- />
- <uni-icons
- v-show="twoItem.likeMark"
- customPrefix="iconfont"
- size="18"
- color="#FF2442"
- class="icon-state icon-dianzanxuanzhong"
-
- :class="{
- animated: likeAnimationIds.includes(twoItem.id),
- heartBeat: likeAnimationIds.includes(twoItem.id),
- }"
- />
- <text class="count">{{ twoItem.likeCount }}</text>
- </view>
- </view>
- <view
- v-if="
- oneItem.replyList &&
- oneItem.replyList.length > 0 &&
- oneItem.replyPage.hasMore &&
- oneItem.replyCount > 1
- "
- class="load-more"
-
- >
- <!-- <text>展示{{ twoItem.allReply }}条回复</text> -->
- <text>展开更多回复</text>
- </view>
- </div>
- </view>
- </z-paging>
- <up-action-sheet
- :actions="actionList"
- :closeOnClickOverlay="true"
- cancelText="取消"
- :show="showActionSheet"
- @close="closeActionSheet"
- @select="clickActionSheet"
- ></up-action-sheet>
- </template>
- <script setup>
- import { ref, computed } from "vue";
- import useZPaging from "@/uni_modules/z-paging/components/z-paging/js/hooks/useZPaging.js";
- import { onLoad } from "@dcloudio/uni-app";
- // import {
- // getCommentOne,
- // getCommentTwo,
- // setUserState,
- // addComment,
- // deleteComment,
- // } from "@/api/book";
- import { timeFormat } from "@/uni_modules/uview-plus";
- import { useToast } from "@/hooks/useToast";
- const { Toast } = useToast();
- const emit = defineEmits(["clickComment", "moveMessageTop", "inputDone"]);
- // defineExpose({ handleAddComment });
- const props = defineProps({
- /**
- * @param add 新增评论
- * @param reply 回复评论
- */
- commentType: {
- type: String,
- default: "add",
- },
- });
- const showActionSheet = ref(false);
- function closeActionSheet() {
- showActionSheet.value = false;
- }
- function clickActionSheet(item) {
- if (item.value === 0) {
- // 删除评论
- handleDeleteComment();
- } else if (item.value === 1) {
- // 回复评论
- let comment = null;
- if (pressTwoLevelId.value !== 0) {
- // 二级评论
- const result = findCommentById(pressTwoLevelId.value, "two");
- if (result && result.twoItem) {
- comment = result.twoItem;
- }
- } else if (pressOneLevelId.value !== 0) {
- // 一级评论
- comment = findCommentById(pressOneLevelId.value, "one");
- }
- if (comment) {
- handleClickComment(comment);
- }
- }
- showActionSheet.value = false;
- }
- // 删除评论
- async function handleDeleteComment() {
- try {
- await deleteComment(deleteId.value);
- Toast({ title: "删除成功" });
- removeItemComment();
- } catch (error) {
- console.error("deleteComment", error);
- } finally {
- pressOneLevelId.value = pressTwoLevelId.value = deleteId.value = 0;
- }
- }
- // 接口删除成功后 需要将数据列表中的评论删除
- function removeItemComment() {
- // 删除一级评论
- if (pressOneLevelId.value !== 0 && pressTwoLevelId.value === 0) {
- const oneItem = findCommentById(pressOneLevelId.value, "one");
- if (oneItem) {
- const itemIndex = oneCommentList.value.findIndex(
- (v) => v.id === oneItem.id
- );
- if (itemIndex !== -1) {
- oneCommentList.value.splice(itemIndex, 1);
- }
- }
- } else if (pressTwoLevelId.value !== 0) {
- // 删除二级评论
- const result = findCommentById(pressTwoLevelId.value, "two");
- if (result && result.oneItem && result.twoItem) {
- const twoIndex = result.oneItem.replyList.findIndex(
- (two) => two.id === result.twoItem.id
- );
- if (twoIndex !== -1) {
- result.oneItem.replyList.splice(twoIndex, 1);
- }
- }
- }
- }
- // 根据id查找评论(一级或二级)
- function findCommentById(id, type = "one") {
- if (type === "one") {
- // 查找一级评论
- return oneCommentList.value.find((item) => item.id === id) || null;
- } else if (type === "two") {
- // 查找二级评论,返回 {oneItem, twoItem}
- for (const oneItem of oneCommentList.value) {
- if (Array.isArray(oneItem.replyList)) {
- const twoItem = oneItem.replyList.find((two) => two.id === id);
- if (twoItem) {
- return { oneItem, twoItem };
- }
- }
- }
- return null;
- }
- return null;
- }
- // 长按某条评论时触发
- const deleteId = ref(0);
- const pressOneLevelId = ref(0);
- const pressTwoLevelId = ref(0);
- function longpress(event) {
- // 如果是二级评论 直接删除二级
- if (event.currentTarget.dataset?.twoLevelId) {
- deleteId.value = pressTwoLevelId.value =
- event.currentTarget.dataset.twoLevelId;
- // 没有二级评论 删除一级
- } else if (event.currentTarget.dataset?.oneLevelId) {
- deleteId.value = pressOneLevelId.value =
- event.currentTarget.dataset?.oneLevelId;
- }
- showActionSheet.value = true;
- }
- const actionList = computed(() => {
- return [
- { name: "删除", value: 0 },
- { name: "回复", value: 1 },
- ];
- });
- const articleId = ref("");
- onLoad((options) => {
- if (!options.id) return;
- articleId.value = options.id;
- });
- // // 获取一级评论
- const oneCommentList = ref([]);
- // async function fetchCommentOne(page, pageSize) {
- // try {
- // const params = {
- // page,
- // limit: pageSize,
- // bookId: articleId.value,
- // };
- // const { data } = await getCommentOne(params);
- // const list = data.list.map((item) => {
- // return {
- // ...item,
- // replyList: item.replyList || [],
- // replyPage: {
- // page: 1,
- // limit: 5,
- // hasMore: true,
- // loading: false,
- // },
- // };
- // });
- // paging.value.completeByTotal(list, data.total);
- // } catch (error) {
- // paging.value.complete(false)
- // console.error("oneCommentList error", error);
- // }
- // }
- // // 获取二级评论
- // const twoCommentTotalPage = ref(1);
- // async function loadMoreComment(oneItem) {
- // try {
- // const params = {
- // page: oneItem.replyPage.page,
- // limit: oneItem.replyPage.limit,
- // bookId: articleId.value,
- // oneLevelId: oneItem.id,
- // };
- // const { data } = await getCommentTwo(params);
- // twoCommentTotalPage.value = data.totalPage;
- // if (oneItem.replyPage.page >= data.totalPage) {
- // oneItem.replyPage.hasMore = false;
- // }
- // // 当第一页时直接赋值
- // if (oneItem.replyPage.page === 1) {
- // oneItem.replyList = data.list;
- // } else {
- // // 更多页时才合并数据
- // oneItem.replyList = [...oneItem.replyList, ...data.list];
- // }
- // oneItem.replyPage.page += 1;
- // } catch (error) {
- // console.error("loadMoreComment error", error);
- // }
- // }
- // 点击评论触发弹窗事件
- const parentId = ref(0);
- const currentReplyId = ref(0);
- const replyId = ref(0);
- const oneLevelId = ref(0);
- function handleClickComment(comment) {
- // 点击了一级评论
- if (comment.parentId === 0) {
- currentReplyId.value = comment.id;
- parentId.value = comment.id;
- oneLevelId.value = comment.id;
- } else {
- // 点击了二级评论
- currentReplyId.value = comment.id;
- parentId.value = comment.parentId;
- oneLevelId.value = comment.oneLevelId;
- }
- replyId.value = comment.userId;
- emit("clickComment", comment);
- }
- // // 评论事件
- // async function handleAddComment(value) {
- // try {
- // // 回复评论参数
- // const replyParams = {
- // bookId: articleId.value, // 文章ID
- // content: value, // 评论内容
- // parentId: currentReplyId.value, // 父评论ID
- // replyId: replyId.value, // 回复的用户ID
- // oneLevelId: oneLevelId.value,
- // };
- // // 新增评论参数
- // const addParams = {
- // bookId: articleId.value,
- // content: value,
- // };
- // let params = props.commentType === "add" ? addParams : replyParams;
- // let { data } = await addComment(params);
- // Toast({ title: "您的评论已发布" });
- // // 新增一级评论
- // if (props.commentType === "add") {
- // data.replyPage = {
- // page: 1,
- // limit: 5,
- // hasMore: true,
- // loading: false,
- // };
- // // oneCommentList.value.unshift(data);
- // paging.value.addDataFromTop(data);
- // // 新增评论后定位评论区到最顶部
- // emit("moveMessageTop");
- // } else {
- // // 回复别人的评论
- // console.log("oneLevelId", oneLevelId.value);
- // // 根据oneLevelId找到这一条一级评论
- // const oneCommentItem = oneCommentList.value.find(
- // (v) => v.id === oneLevelId.value
- // );
- // console.log("oneCommentItem", oneCommentItem);
- // // 一级评论可能没有子评论接口返回为null,这里需要初始化一下
- // if (
- // !oneCommentItem?.replyList ||
- // !Array.isArray(oneCommentItem?.replyList)
- // ) {
- // oneCommentItem.replyList = [];
- // }
- // // 向子评论列表的第一项插入最新评论
- // // paging.value.addDataFromTop(data);
- // oneCommentItem.replyList.unshift(data);
- // // 如果二级评论列表的page大于 二级评论的总页数,隐藏加载更多
- // if (oneCommentItem.replyPage.page >= twoCommentTotalPage.value) {
- // oneCommentItem.replyPage.hasMore = false;
- // }
- // console.log("oneCommentItem", oneCommentItem);
- // }
- // emit("inputDone");
- // } catch (error) {
- // console.error("addComment error", error);
- // emit("inputDone");
- // Toast({ title: "发送失败" });
- // }
- // }
- function closeF2() {}
- // paging相关
- const paging = ref(null);
- useZPaging(paging);
- // z-paging绑定的数据刷新时调用
- function queryList(page, pageSize) {
- // fetchCommentOne(page, pageSize);
- }
- // 点赞相关
- const likeLoading = ref(false);
- const likeAnimationIds = ref([]);
- // async function handleLike(item) {
- // try {
- // if (likeLoading.value) return;
- // likeLoading.value = true;
- // await setUserState({ type: 3, commentId: item.id });
- // item.likeMark = !item.likeMark;
- // if (item.likeMark) {
- // item.likeCount += 1;
- // const idx = likeAnimationIds.value.indexOf(item.id);
- // if (idx > -1) likeAnimationIds.value.splice(idx, 1);
- // likeAnimationIds.value.push(item.id);
- // } else {
- // item.likeCount -= 1;
- // const idx2 = likeAnimationIds.value.indexOf(item.id);
- // if (idx2 > -1) likeAnimationIds.value.splice(idx2, 1);
- // }
- // likeLoading.value = false;
- // } catch (error) {
- // likeLoading.value = false;
- // Toast({ title: "点赞失败" });
- // console.error("handleLike", error);
- // }
- // }
- // 跳转用户详情页
- function toUserPage(id) {
- uni.navigateTo({ url: `/pages/user/personal?id=${id}` });
- }
- </script>
- <style lang="scss" scoped>
- .comment-paging {
- // min-height: 80vh;
- // height: 500px;
- }
- .comment-box {
- // display: flex;
- }
- .item-comment {
- width: 100%;
- display: flex;
- margin-bottom: 50rpx;
- &.child {
- margin-bottom: 0;
- }
- .avatar {
- margin-right: 20rpx;
- }
- .content {
- flex: 1;
- font-size: 26rpx;
- .user-box {
- color: #9d9d9d;
- .author-tag {
- color: rgb(246, 40, 77);
- background-color: rgba(246, 40, 77, 0.2);
- padding: 2rpx 10rpx;
- border-radius: 50rpx;
- font-size: 20rpx;
- margin-left: 10rpx;
- font-weight: 700;
- }
- }
- .reply-box {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- line-height: 38rpx;
- margin: 8rpx 0;
- .text {
- overflow-wrap: break-word;
- margin-right: 16rpx;
- }
- .time {
- // margin-left: 16rpx;
- white-space: nowrap;
- color: #999;
- font-size: 22rpx;
- margin-right: 10rpx;
- }
- .reply-tip {
- font-size: 22rpx;
- color: #999;
- }
- }
- }
- .right-box {
- display: flex;
- align-items: center;
- .icon-state {
- margin-right: 5rpx;
- // vertical-align: mid;
- }
- .count {
- // vertical-align: middle;
- }
- }
- }
- .child-comment-box {
- padding-left: 80rpx;
- margin-bottom: 50rpx;
- .load-more {
- padding-left: 65rpx;
- color: #223b72;
- font-size: 26rpx;
- margin-top: 10rpx;
- }
- .sub-comment {
- margin-top: 20rpx;
- margin-bottom: 0;
- .avatar {
- margin-right: 15rpx;
- }
- .reply-box {
- display: block; // 关键:不要用flex
- .text,
- .time,
- .reply-tip {
- display: inline;
- vertical-align: middle;
- }
- .text {
- margin-right: 10rpx;
- overflow-wrap: break-word;
- line-height: 35rpx;
- }
- .time {
- color: #999;
- font-size: 22rpx;
- margin-right: 10rpx;
- white-space: nowrap;
- }
- .reply-tip {
- font-size: 22rpx;
- color: #999;
- }
- }
- }
- }
- </style>
|