1229 lines
36 KiB
Vue
1229 lines
36 KiB
Vue
<template>
|
||
<div>
|
||
<van-popup
|
||
v-if="props.renderType === 'popup'"
|
||
v-model:show="isShow"
|
||
position="bottom"
|
||
z-index="2001"
|
||
teleport="body"
|
||
:overlay="!props.overlayTransparent"
|
||
:overlay-style="props.overlayTransparent ? { background: 'transparent' } : undefined"
|
||
@close="emits('close')"
|
||
>
|
||
<div ref="CommentBox" class="comment-wrap"></div>
|
||
</van-popup>
|
||
<div v-else ref="CommentBox" class="comment-wrap"></div>
|
||
<Teleport v-if="CommentBox" :to="CommentBox">
|
||
<div class="wrapper" :class="[props.theme, props.renderType || '', { 'is-peek-video': props.overlayTransparent }]">
|
||
<div class="headerBox">
|
||
<div class="horizontalLineBlock"></div>
|
||
<div class="title">{{ state.lfCount }}条评论</div>
|
||
</div>
|
||
|
||
<div class="main overflow-y-auto">
|
||
<template v-if="!state.loading">
|
||
<PullRefresh :params="moreParams" @onLoad="onLoad" @onRefresh="onRefresh">
|
||
<div class="commentBox" v-for="(item, index) in state.list" :key="item.id">
|
||
<div class="goldComment" v-if="item.isGodComment"></div>
|
||
<LazyImg
|
||
:src="item.userPortrait"
|
||
class="avatar"
|
||
:key="item.userPortrait"
|
||
@click.stop="router.push({ path: '/userInfo', query: { uid: item.userID } })"
|
||
/>
|
||
<div class="commentInfoBox">
|
||
<div class="name flex-vcenter">
|
||
<span class="ellipsis">
|
||
{{ item.userName || '未知用户' }}
|
||
</span>
|
||
<!-- <div class="vip" v-if="item.vipName">{{ item.vipName }}</div> -->
|
||
<SvgIcon name="vipLabel" class="vipLabel" v-if="item.vipLevel" />
|
||
</div>
|
||
<div class="content">
|
||
<!-- {{ item.content }} -->
|
||
<HighlightText
|
||
:text="item.content"
|
||
:keyword="item.searchKeyword"
|
||
:linkStr="item.linkStr"
|
||
:linkType="item.linkType"
|
||
></HighlightText>
|
||
<lazyImg v-if="item.image" class="image" :src="item.image" @lz-image-click="toPicPage"></lazyImg>
|
||
</div>
|
||
<!-- 置顶评论下方:评论区 Banner 轮播 -->
|
||
<div v-if="index === 0 && commentBanners.length" class="commentTopBanner">
|
||
<van-swipe :autoplay="4000" :show-indicators="commentBanners.length > 1" lazy-render>
|
||
<van-swipe-item
|
||
v-for="banner in commentBanners"
|
||
:key="banner.id"
|
||
class="commentTopBannerItem"
|
||
style="width: 291px; height: 61px"
|
||
@click="handleCommentBannerClick(banner)"
|
||
>
|
||
<LazyImg :src="banner.imageUrl" :key="banner.imageUrl" class="bannerImg" />
|
||
</van-swipe-item>
|
||
</van-swipe>
|
||
</div>
|
||
<div class="commentInfo flex-center-between">
|
||
<div class="time">{{ moment(item.createdAt).format('YYYY-MM-DD HH:mm') }}</div>
|
||
<div v-if="![1, 2].includes(item.linkType)" class="optBox flex-vcenter">
|
||
<div class="zanBox">
|
||
<SvgIcon @click="clickLike(item)" v-if="item.isLike" name="movieZan" />
|
||
<SvgIcon @click="clickLike(item)" v-else name="movieUnZan" />
|
||
{{ watchCount(item.likeCount) }}
|
||
</div>
|
||
<SvgIcon :name="props.theme ? 'msg2' : 'msg'" @click="reply(item, undefined, undefined)" />
|
||
</div>
|
||
</div>
|
||
<!-- 子评论区域 -->
|
||
<div class="subCommentArea">
|
||
<div class="subShow" v-if="item.Info.length">
|
||
<div class="subCommentBox" v-for="subItem in item.Info" :key="subItem.id">
|
||
<LazyImg :src="subItem.userPortrait" class="avatar" :key="subItem.userPortrait" />
|
||
<div class="commentInfoBox">
|
||
<div class="nameBox flex-vcenter">
|
||
<div class="name">{{ subItem.userName || '未知用户' }}</div>
|
||
<!-- <span
|
||
class="privateLetter"
|
||
@click.stop="router.push({ path: '/privateLetter', query: { uid: subItem.userID } })"
|
||
v-if="userInfo.uid != subItem.userID"
|
||
>
|
||
私信TA
|
||
</span> -->
|
||
<!-- <span v-if="subItem.toUserName">回复了</span> -->
|
||
<SvgIcon v-if="subItem.toUserName" name="rightArrow3" />
|
||
<div class="name light-color-3">{{ subItem.toUserName }}</div>
|
||
</div>
|
||
<div class="content">
|
||
<!-- {{ subItem.content }} -->
|
||
<HighlightText
|
||
:text="subItem.content"
|
||
:keyword="subItem.searchKeyword"
|
||
:linkStr="subItem.linkStr"
|
||
:linkType="subItem.linkType"
|
||
></HighlightText>
|
||
<lazyImg
|
||
v-if="subItem.image"
|
||
class="image"
|
||
:src="subItem.image"
|
||
@lz-image-click="toPicPage"
|
||
></lazyImg>
|
||
</div>
|
||
<div class="commentInfo flex-center-between">
|
||
<div class="time">{{ moment(subItem.createdAt).format('YYYY-MM-DD HH:mm') }}</div>
|
||
<div v-if="![1, 2].includes(subItem.linkType)" class="optBox flex-vcenter">
|
||
<div class="zanBox">
|
||
<SvgIcon @click="clickLike(subItem)" v-if="subItem.isLike" name="movieZan" />
|
||
<SvgIcon @click="clickLike(subItem)" v-else name="movieUnZan" />
|
||
{{ watchCount(subItem.likeCount) }}
|
||
</div>
|
||
<SvgIcon
|
||
:name="props.theme ? 'msg2' : 'msg'"
|
||
@click="reply(subItem, subItem.userID, item.id)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- <div class="horizontalLine" v-if="item.subList && item.subList.length"></div> -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="subShow pdunset" v-if="item.subShow">
|
||
<div class="subCommentBox" v-for="subItem in item.subList" :key="subItem.id">
|
||
<LazyImg
|
||
:src="subItem.userPortrait"
|
||
class="avatar"
|
||
:key="subItem.userPortrait"
|
||
@click.stop="router.push({ path: '/userInfo', query: { uid: subItem.userID } })"
|
||
/>
|
||
<div class="commentInfoBox">
|
||
<div class="nameBox flex-vcenter">
|
||
<div class="name">{{ subItem.userName || '未知用户' }}</div>
|
||
<!-- <span
|
||
class="privateLetter"
|
||
@click.stop="router.push({ path: '/privateLetter', query: { uid: subItem.userID } })"
|
||
v-if="userInfo.uid != subItem.userID && !subItem.toUserName"
|
||
>
|
||
私信TA
|
||
</span> -->
|
||
<!-- <span v-if="subItem.toUserName">回复了</span> -->
|
||
<SvgIcon v-if="subItem.toUserName" name="rightArrow3" />
|
||
<div class="name light-color-3">{{ subItem.toUserName }}</div>
|
||
</div>
|
||
<div class="content">
|
||
<!-- {{ subItem.content }} -->
|
||
<HighlightText
|
||
:text="subItem.content"
|
||
:keyword="subItem.searchKeyword"
|
||
:linkStr="subItem.linkStr"
|
||
:linkType="subItem.linkType"
|
||
></HighlightText>
|
||
<lazyImg
|
||
v-if="subItem.image"
|
||
class="image"
|
||
:src="subItem.image"
|
||
@lz-image-click="toPicPage"
|
||
></lazyImg>
|
||
</div>
|
||
<div class="commentInfo flex-center-between">
|
||
<div class="time">{{ moment(subItem.createdAt).format('YYYY-MM-DD HH:mm') }}</div>
|
||
<div v-if="![1, 2].includes(subItem.linkType)" class="optBox flex-vcenter">
|
||
<div class="zanBox">
|
||
<SvgIcon @click="clickLike(subItem)" v-if="subItem.isLike" name="movieZan" />
|
||
<SvgIcon @click="clickLike(subItem)" v-else name="movieUnZan" />
|
||
{{ watchCount(subItem.likeCount) }}
|
||
</div>
|
||
<SvgIcon
|
||
:name="props.theme ? 'msg2' : 'msg'"
|
||
@click="reply(subItem, subItem.userID, item.id)"
|
||
/>
|
||
</div>
|
||
</div>
|
||
<!-- <div class="horizontalLine" v-if="index != item.subList.length - 1"></div> -->
|
||
</div>
|
||
</div>
|
||
<Loading v-if="item.subLoading" />
|
||
</div>
|
||
<div class="expandBox flex-vcenter" v-if="item.commCount - 1 > 0 && item.Info.length > 0">
|
||
<div
|
||
class="expandMore flex-vcenter"
|
||
@click.stop="expandComment(item)"
|
||
v-if="!(item.subList && item.subList.length) && !item.subHasNext"
|
||
>
|
||
<div class="flex-vcenter">
|
||
—— 展开{{ item.commCount - 1 }}条回复
|
||
<van-icon name="arrow-down" class="comment-expand" />
|
||
</div>
|
||
</div>
|
||
<template v-else>
|
||
<div class="expandMore flex-vcenter" @click.stop="loadMore(item)" v-if="!item.subHasNext">
|
||
<div class="flex-vcenter expand">
|
||
—— 查看更多
|
||
<van-icon name="arrow-down" class="comment-expand" />
|
||
</div>
|
||
</div>
|
||
<div
|
||
class="closeBox"
|
||
@click.stop="expandComment(item)"
|
||
v-if="item.subList && item.subList.length"
|
||
>
|
||
<div class="close flex-center-center">
|
||
收起
|
||
<van-icon name="arrow-up" class="comment-expand" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</PullRefresh>
|
||
</template>
|
||
<Loading v-else />
|
||
</div>
|
||
<div class="mask" v-if="state.childrenCommentObj" @click.stop="cancelReply"></div>
|
||
<!-- 评论 -->
|
||
<div id="comment-bottom-box" ref="sendBox" class="bottomBox" :class="[props.theme]">
|
||
<!-- <div class="send-img" v-if="state.img">
|
||
<img class="close" @click="delImg" src="@/assets/images/png/close1.png" alt="" />
|
||
<DecryptImg class="img" :src="state.img" />
|
||
</div> -->
|
||
<div class="commentBtnBox flex-center-between" :class="[props.theme]">
|
||
<div class="commentInput flex-vcenter">
|
||
<van-field
|
||
ref="contentRef"
|
||
v-model="state.content"
|
||
:placeholder="state.placeholder"
|
||
input-align="left"
|
||
:clearable="false"
|
||
:border="false"
|
||
@blur="blur"
|
||
/>
|
||
</div>
|
||
<UploadImgMess
|
||
:maxCount="1"
|
||
:isCover="false"
|
||
@uploadSuccess="uploadSuccess"
|
||
style="margin-left: 0"
|
||
:key="imgKey"
|
||
ref="uploadFileRef"
|
||
:commentType="props.commentType"
|
||
/>
|
||
<template v-if="props.commentType == 'short'">
|
||
<div class="post_item flex-vcenter" @click.stop="sendBtn">
|
||
<SvgIcon class="icon" name="post_send" />
|
||
</div>
|
||
</template>
|
||
<template v-if="props.commentType == 'normal'">
|
||
<div class="flex-vcenter comment_post">
|
||
<div class="post_item flex-vcenter" @click.stop="clickCollect" v-if="showCollected">
|
||
<SvgIcon class="icon" v-if="state.videoInfo.vidStatus?.hasCollected" name="post_followin" />
|
||
<SvgIcon class="icon" v-else name="post_follow" />
|
||
</div>
|
||
<div class="post_item flex-vcenter" @click.stop="sendBtn">
|
||
<SvgIcon class="icon" name="post_send" />
|
||
</div>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</Teleport>
|
||
</div>
|
||
</template>
|
||
<script lang="ts" setup>
|
||
import { computed, reactive, ref, PropType, unref, watch } from 'vue';
|
||
import { showToast } from 'vant';
|
||
import useLoading from '@/hooks/loading';
|
||
import moment from 'moment';
|
||
import { LikeType } from '@/enums/app';
|
||
import CommunityApi from '@/service/community';
|
||
import AppApi from '@/service/app';
|
||
import VideoApi from '@/service/video';
|
||
import { watchCount, compatibleAppStoreJump, innerJump } from '@/utils/index';
|
||
import { useUserStore } from '@/stores/user';
|
||
import { useRouter } from 'vue-router';
|
||
|
||
const router = useRouter();
|
||
const userStore = useUserStore();
|
||
const { userInfo } = userStore;
|
||
const imgKey = ref(1);
|
||
const CommentBox = ref();
|
||
const uploadFileRef = ref(null);
|
||
/** 评论区置顶 Banner */
|
||
const commentBanners = ref<Types.App.CommentBannerItem[]>([]);
|
||
const props = defineProps({
|
||
isOpen: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
objType: {
|
||
type: String,
|
||
default: 'video',
|
||
},
|
||
renderType: {
|
||
type: String as PropType<'popup' | 'normal'>, //popup弹窗形式,normal正常布局流
|
||
default: 'popup',
|
||
},
|
||
theme: {
|
||
type: String as PropType<'black' | ''>,
|
||
default: 'black',
|
||
},
|
||
SendBtnTeleportEl: {
|
||
type: String,
|
||
default: '',
|
||
},
|
||
commentType: {
|
||
type: String as PropType<'normal' | 'short'>, //normal默认底部样式,short短视频底部样式
|
||
default: 'normal',
|
||
},
|
||
collectType: {
|
||
type: String,
|
||
default: LikeType.SP,
|
||
},
|
||
showCollected: {
|
||
type: Boolean,
|
||
default: () => true,
|
||
},
|
||
/** 短剧等场景:遮罩透明,顶部露出全宽播放(对齐抖音评论) */
|
||
overlayTransparent: {
|
||
type: Boolean,
|
||
default: false,
|
||
},
|
||
});
|
||
const sendBox = ref();
|
||
watch(sendBox, (el) => {
|
||
if (!el) return;
|
||
if (!props.SendBtnTeleportEl) return;
|
||
document.querySelector(props.SendBtnTeleportEl)?.appendChild(el);
|
||
});
|
||
const { openLoading, closeLoading } = useLoading();
|
||
const isShow = computed(() => {
|
||
return props.isOpen;
|
||
});
|
||
const emits = defineEmits(['addComment', 'close', 'getTotal']);
|
||
const contentRef = ref<HTMLElement>();
|
||
const state = reactive({
|
||
content: '',
|
||
list: <Array<any>>[], // 评论列表
|
||
loading: true,
|
||
pageNumber: 1, // 评论页码
|
||
pageSize: 15, // 评论一页的数量
|
||
subPageNumber: 1, // 子评论页码
|
||
subPageSize: 15, // 子评论一页的数量
|
||
videoInfo: <any>{},
|
||
lfCount: 0,
|
||
placeholder: '请输入评论',
|
||
childrenCommentObj: <any>null,
|
||
img: '',
|
||
});
|
||
// 上拉加载及下拉刷新参数
|
||
const moreParams = reactive({
|
||
loading: false,
|
||
refreshing: false,
|
||
finished: false,
|
||
isNoData: false,
|
||
error: false,
|
||
isHigehtMax: false,
|
||
});
|
||
// 获取评论数据
|
||
const getList = async (type: string) => {
|
||
const requestPage = state.pageNumber;
|
||
try {
|
||
let req = {
|
||
objID: String(state.videoInfo.id),
|
||
objType: props.objType,
|
||
curTime: new Date().toISOString(),
|
||
pageNumber: String(requestPage),
|
||
pageSize: String(state.pageSize),
|
||
};
|
||
let res: any = await CommunityApi.getCommentList(req);
|
||
state.loading = false;
|
||
state.lfCount = res.total || 0;
|
||
emits('getTotal', unref(state.lfCount));
|
||
moreParams.loading = false;
|
||
moreParams.refreshing = false;
|
||
const { list } = res;
|
||
|
||
if (type === 'refresh' || requestPage === 1) {
|
||
state.list = list || [];
|
||
} else if (list && list.length) {
|
||
state.list = state.list.concat(list);
|
||
}
|
||
if (requestPage == 1 && state.list.length == 0) {
|
||
moreParams.isNoData = true;
|
||
}
|
||
if (!res.hasNext) {
|
||
moreParams.finished = true;
|
||
}
|
||
// 用请求页码判断,避免 onLoad 抢先 pageNumber++ 后漏拉置顶 Banner
|
||
if (requestPage === 1) {
|
||
void loadCommentTopBanners();
|
||
}
|
||
} catch (e) {
|
||
moreParams.loading = false;
|
||
moreParams.error = true;
|
||
}
|
||
};
|
||
|
||
/** 拉取评论区置顶 Banner(COMMENT_TOP) */
|
||
const loadCommentTopBanners = async () => {
|
||
try {
|
||
const res = await AppApi.getBannerList({ scene: 'COMMENT_TOP' });
|
||
const list = (res?.list || []).slice().sort((a, b) => (b.sort || 0) - (a.sort || 0));
|
||
commentBanners.value = list;
|
||
} catch (e) {
|
||
console.log(e);
|
||
commentBanners.value = [];
|
||
}
|
||
};
|
||
|
||
const handleCommentBannerClick = (banner: Types.App.CommentBannerItem) => {
|
||
if (!banner || banner.linkType === 'NONE' || !banner.linkValue) return;
|
||
const linkValue = banner.linkValue.trim();
|
||
if (banner.linkType === 'EXTERNAL') {
|
||
compatibleAppStoreJump(linkValue);
|
||
return;
|
||
}
|
||
// INTERNAL
|
||
if (linkValue.startsWith('yinseinner://')) {
|
||
innerJump(linkValue.substring('yinseinner://'.length));
|
||
return;
|
||
}
|
||
if (linkValue.startsWith('video://')) {
|
||
const path = linkValue.slice('video://'.length);
|
||
const queryIndex = path.indexOf('?');
|
||
const routeName = queryIndex === -1 ? path : path.slice(0, queryIndex);
|
||
const query = queryIndex === -1 ? '' : path.slice(queryIndex + 1);
|
||
const idMatch = query.match(/(?:^|&)id=([^&]*)/);
|
||
const id = idMatch ? decodeURIComponent(idMatch[1]) : '';
|
||
if ((routeName === 'detail' || routeName === 'video_page') && id) {
|
||
router.push({
|
||
path: '/movieDetails',
|
||
query: { vid: id },
|
||
});
|
||
}
|
||
return;
|
||
}
|
||
if (/^https?:\/\//i.test(linkValue)) {
|
||
compatibleAppStoreJump(linkValue);
|
||
return;
|
||
}
|
||
innerJump(linkValue);
|
||
};
|
||
const onLoad = () => {
|
||
moreParams.loading = true;
|
||
state.pageNumber++;
|
||
getList('');
|
||
};
|
||
const onRefresh = () => {
|
||
state.pageNumber = 1;
|
||
moreParams.refreshing = true;
|
||
moreParams.finished = false;
|
||
moreParams.isNoData = false;
|
||
getList('refresh');
|
||
};
|
||
// 评论成功后增加评论数据
|
||
const addComment = (res: any, childrenCommentObj: any) => {
|
||
moreParams.isNoData = false;
|
||
if (childrenCommentObj) {
|
||
state.list.map((comment: any) => {
|
||
if (comment.id == childrenCommentObj.id) {
|
||
if (!comment.subList) {
|
||
comment.subLoading = false;
|
||
comment.subList = [];
|
||
comment.subHasNext = true;
|
||
comment.subShow = true;
|
||
}
|
||
comment.Info.unshift(res);
|
||
}
|
||
return comment;
|
||
});
|
||
} else {
|
||
// 插入都到置顶评论后
|
||
state.list.splice(1, 0, res);
|
||
}
|
||
};
|
||
// 点赞
|
||
const clickLike = async (comment: any) => {
|
||
try {
|
||
if (!comment.isLike) {
|
||
let req = {
|
||
type: LikeType.COMMENT,
|
||
objID: comment.id,
|
||
};
|
||
await CommunityApi.zan(req);
|
||
comment.isLike = true;
|
||
comment.likeCount++;
|
||
showToast('点赞成功');
|
||
} else {
|
||
let req = {
|
||
type: LikeType.COMMENT,
|
||
objIDs: [comment.id],
|
||
};
|
||
await CommunityApi.cancleZan(req);
|
||
comment.isLike = false;
|
||
comment.likeCount--;
|
||
showToast('取消成功');
|
||
}
|
||
} catch (e) {
|
||
console.log(e);
|
||
}
|
||
};
|
||
// 收藏
|
||
const clickCollect = async () => {
|
||
let hasCollected = state.videoInfo.vidStatus.hasCollected;
|
||
let req = {
|
||
objID: state.videoInfo.id,
|
||
isCollect: !hasCollected,
|
||
type: props.collectType,
|
||
};
|
||
CommunityApi.collect(req).then(() => {
|
||
state.videoInfo.vidStatus.hasCollected = !hasCollected;
|
||
if (hasCollected) {
|
||
showToast('取消成功');
|
||
state.videoInfo.collectCount--;
|
||
} else {
|
||
showToast('收藏成功');
|
||
state.videoInfo.collectCount++;
|
||
}
|
||
});
|
||
};
|
||
// 回复二级评论
|
||
const reply = (comment: any, toUserID: any, parentId: any) => {
|
||
comment.toUserID = toUserID ? toUserID : undefined;
|
||
comment.rid = toUserID ? comment.id : undefined;
|
||
comment.id = toUserID ? parentId : comment.id;
|
||
state.childrenCommentObj = comment;
|
||
state.placeholder = `回复@${comment.userName}`;
|
||
contentRef.value?.focus();
|
||
};
|
||
// 展开评论
|
||
const expandComment = async (comment: any) => {
|
||
if (!comment.subShow) {
|
||
comment.subShow = true;
|
||
comment.subHasNext = true; // 子评论是否还有下一页数据
|
||
comment.subList = []; // 子评论列表
|
||
comment.subLoading = true; // 子评论加载loading
|
||
state.subPageNumber = 1;
|
||
const resObj: any = await getSubList(comment);
|
||
comment.subLoading = false;
|
||
comment.subHasNext = resObj.subHasNext;
|
||
if (resObj.list.length) {
|
||
if (!comment.subList) {
|
||
comment.subList = [];
|
||
}
|
||
comment.subList = comment.subList.concat(resObj.list);
|
||
} else {
|
||
comment.commCount = comment.commCount - 1;
|
||
}
|
||
} else {
|
||
comment.subLoading = false;
|
||
comment.subList = []; // 清空子评论列表
|
||
comment.subHasNext = false; // 子评论是否还有下一页数据
|
||
comment.subShow = false;
|
||
}
|
||
};
|
||
// 获取更多评论
|
||
const loadMore = async (comment: any) => {
|
||
comment.subLoading = true;
|
||
state.subPageNumber++;
|
||
const resObj: any = await getSubList(comment);
|
||
comment.subLoading = false;
|
||
comment.subList = comment.subList.concat(resObj.list);
|
||
comment.subHasNext = resObj.subHasNext;
|
||
};
|
||
// 获取二级评论
|
||
const getSubList = async (comment: any) => {
|
||
let resObj = {
|
||
subHasNext: true,
|
||
list: <any>[],
|
||
};
|
||
let req = {
|
||
objID: comment.objID,
|
||
cmtId: comment.id,
|
||
fstID: comment.Info[0].id, // 第一条评论的ID
|
||
curTime: new Date().toISOString(),
|
||
pageNumber: state.subPageNumber,
|
||
pageSize: state.subPageSize,
|
||
};
|
||
let res: any = await CommunityApi.getSecCommentList(req);
|
||
if (res.list && res.list.length > 0) {
|
||
resObj.list = res.list;
|
||
}
|
||
if (res.hasNext) {
|
||
resObj.subHasNext = false;
|
||
}
|
||
return resObj;
|
||
};
|
||
const sendBtn = async () => {
|
||
if (!state.content) return showToast('请输入评论内容');
|
||
try {
|
||
let req = {};
|
||
if (!state.childrenCommentObj) {
|
||
req = {
|
||
objID: state.videoInfo.id,
|
||
objType: props.objType,
|
||
level: 1,
|
||
content: state.content,
|
||
image: state.img,
|
||
};
|
||
} else {
|
||
// 二级评论参数
|
||
req = {
|
||
objID: state.videoInfo.id,
|
||
objType: props.objType,
|
||
level: 2,
|
||
content: state.content,
|
||
cid: state.childrenCommentObj.id,
|
||
userID: state.childrenCommentObj.userID,
|
||
rid: state.childrenCommentObj.toUserID ? state.childrenCommentObj.rid : undefined,
|
||
toUserID: state.childrenCommentObj.toUserID,
|
||
image: state.img,
|
||
};
|
||
}
|
||
openLoading();
|
||
let res: any = await VideoApi.sendComment(req);
|
||
state.content = ''; // 评论输入框内容清空
|
||
state.lfCount++;
|
||
if (typeof res === 'object') {
|
||
addComment(res, state.childrenCommentObj);
|
||
}
|
||
emits('addComment');
|
||
cancelReply();
|
||
showToast({
|
||
message: '已提交,审核通过后将展示',
|
||
className: 'comment-review-toast',
|
||
});
|
||
(uploadFileRef.value as any).reset();
|
||
state.img = '';
|
||
} catch (e) {
|
||
console.log(e);
|
||
} finally {
|
||
closeLoading();
|
||
}
|
||
};
|
||
// 回复一级评论
|
||
const cancelReply = () => {
|
||
state.childrenCommentObj = <any>null;
|
||
state.placeholder = '请输入评论';
|
||
state.content = '';
|
||
};
|
||
const getCommentList = () => {
|
||
state.pageNumber = 1;
|
||
moreParams.loading = false;
|
||
moreParams.finished = false;
|
||
moreParams.isNoData = false;
|
||
state.list = <Array<Types.Video.CommentItem>>[];
|
||
// 置顶 Banner 单独接口,打开评论区时先拉,不依赖评论分页状态
|
||
void loadCommentTopBanners();
|
||
getList('refresh');
|
||
};
|
||
const blur = () => {
|
||
window.scrollTo(0, 0);
|
||
};
|
||
defineExpose({
|
||
getCommentList,
|
||
moreParams,
|
||
state,
|
||
getList,
|
||
});
|
||
|
||
const uploadSuccess = (url: string) => {
|
||
state.img = url;
|
||
};
|
||
const delImg = () => {
|
||
state.img = '';
|
||
imgKey.value++;
|
||
};
|
||
const toPicPage = (img: string) => {
|
||
router.push({
|
||
path: '/picturePreview',
|
||
query: {
|
||
imgs: [img],
|
||
index: 0,
|
||
},
|
||
});
|
||
};
|
||
</script>
|
||
<style lang="scss" scoped>
|
||
:deep(.van-popup) {
|
||
background: transparent !important;
|
||
}
|
||
.wrapper {
|
||
height: 70vh;
|
||
background: #0f0f0f;
|
||
|
||
.headerBox {
|
||
background: #0f0f0f;
|
||
border-radius: 20px 20px 0 0;
|
||
padding: 18px 16px 0;
|
||
box-sizing: border-box;
|
||
z-index: 2;
|
||
top: 1px;
|
||
|
||
.horizontalLineBlock {
|
||
height: 5px;
|
||
width: 36px;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border-radius: 100px;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.title {
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 20px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
margin-top: 10px;
|
||
padding: 0;
|
||
}
|
||
}
|
||
|
||
.main {
|
||
height: 55vh;
|
||
padding: 18px 16px;
|
||
background: #0f0f0f;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
box-sizing: border-box;
|
||
|
||
.commentBox {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin-bottom: 18px;
|
||
padding-bottom: 0;
|
||
position: relative;
|
||
|
||
.goldComment {
|
||
position: absolute;
|
||
top: 0px;
|
||
right: -5px;
|
||
height: 67px;
|
||
width: 83px;
|
||
background: url('@/assets/images/png/home/goldComment.png') no-repeat;
|
||
background-size: 100% 100%;
|
||
}
|
||
|
||
.avatar {
|
||
height: 40px;
|
||
width: 40px;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
position: relative;
|
||
flex-shrink: 0;
|
||
transform: translateZ(0px);
|
||
|
||
:deep(.loading) {
|
||
background-size: 18px 18px;
|
||
}
|
||
}
|
||
|
||
.commentInfoBox {
|
||
flex: 1;
|
||
min-width: 0;
|
||
margin-left: 0;
|
||
font-size: 14px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
|
||
.name {
|
||
color: rgba(255, 255, 255, 0.55);
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
line-height: 20px;
|
||
height: auto;
|
||
flex: 1;
|
||
gap: 4px;
|
||
|
||
span {
|
||
max-width: 240px;
|
||
}
|
||
|
||
.vipLabel {
|
||
width: 22px;
|
||
height: 14px;
|
||
margin-left: 0;
|
||
}
|
||
|
||
.vip {
|
||
height: 14px;
|
||
width: 30px;
|
||
margin-left: 8px;
|
||
color: black;
|
||
font-size: 10px;
|
||
font-weight: 500;
|
||
background-color: rgba(255, 211, 0, 1);
|
||
border-radius: 14px;
|
||
text-align: center;
|
||
line-height: 15px;
|
||
}
|
||
}
|
||
|
||
.content {
|
||
margin-top: 0;
|
||
line-height: 20px;
|
||
font-size: 14px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
|
||
.image {
|
||
width: 120px;
|
||
max-height: 160px;
|
||
border-radius: 10px;
|
||
margin: 4px 0 0;
|
||
overflow: hidden;
|
||
}
|
||
|
||
:deep(.highlighted) {
|
||
font-size: 14px;
|
||
line-height: 20px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
|
||
span {
|
||
color: rgba(246, 136, 4, 1);
|
||
}
|
||
}
|
||
}
|
||
|
||
.commentTopBanner {
|
||
margin-top: 0;
|
||
width: 291px;
|
||
max-width: 100%;
|
||
height: 61px;
|
||
border-radius: 12px;
|
||
overflow: hidden;
|
||
|
||
:deep(.van-swipe) {
|
||
width: 291px;
|
||
max-width: 100%;
|
||
height: 61px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
:deep(.van-swipe__track) {
|
||
height: 100%;
|
||
}
|
||
|
||
:deep(.van-swipe-item) {
|
||
width: 291px !important;
|
||
height: 61px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.commentTopBannerItem {
|
||
width: 291px;
|
||
height: 61px;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.bannerImg {
|
||
display: block;
|
||
width: 291px !important;
|
||
max-width: 100%;
|
||
height: 61px !important;
|
||
overflow: hidden;
|
||
|
||
:deep(.laz_com),
|
||
:deep(.lazy_placeholder),
|
||
:deep(img) {
|
||
width: 100% !important;
|
||
height: 100% !important;
|
||
object-fit: cover;
|
||
}
|
||
}
|
||
|
||
:deep(.van-swipe__indicators) {
|
||
bottom: 4px;
|
||
display: flex;
|
||
justify-content: center;
|
||
gap: 4px;
|
||
width: 100%;
|
||
padding: 0;
|
||
}
|
||
|
||
:deep(.van-swipe__indicator) {
|
||
width: 4px;
|
||
height: 4px;
|
||
border-radius: 3px;
|
||
background: rgba(255, 255, 255, 0.5);
|
||
opacity: 1;
|
||
margin: 0;
|
||
}
|
||
|
||
:deep(.van-swipe__indicator--active) {
|
||
width: 8px;
|
||
height: 4px;
|
||
border-radius: 3px;
|
||
background: #fff;
|
||
}
|
||
}
|
||
|
||
.commentInfo {
|
||
margin-top: 0;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
|
||
.time {
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
}
|
||
|
||
.optBox {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
gap: 12px;
|
||
|
||
svg {
|
||
width: 20px;
|
||
height: 20px;
|
||
}
|
||
|
||
.zanBox {
|
||
margin-right: 0;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 4px;
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.subCommentArea {
|
||
border-radius: 12px;
|
||
margin-top: 0;
|
||
|
||
.subShow {
|
||
padding: 0;
|
||
|
||
.subCommentBox {
|
||
display: flex;
|
||
gap: 12px;
|
||
margin: 12px 0 0;
|
||
|
||
.avatar {
|
||
height: 36px;
|
||
width: 36px;
|
||
border-radius: 50%;
|
||
overflow: hidden;
|
||
position: relative;
|
||
transform: translateZ(0px);
|
||
|
||
:deep(.loading) {
|
||
background-size: 13px 13px;
|
||
}
|
||
}
|
||
|
||
.commentInfoBox {
|
||
flex: 1;
|
||
margin-left: 0;
|
||
font-size: 14px;
|
||
gap: 10px;
|
||
|
||
.nameBox {
|
||
.name {
|
||
height: auto;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
font-weight: 500;
|
||
font-size: 14px;
|
||
line-height: 20px;
|
||
flex: unset;
|
||
}
|
||
|
||
> svg {
|
||
width: 18px;
|
||
height: 18px;
|
||
}
|
||
|
||
span {
|
||
color: rgba(255, 255, 255, 0.55);
|
||
margin: 0 5px;
|
||
}
|
||
}
|
||
|
||
.content {
|
||
margin-top: 0;
|
||
line-height: 20px;
|
||
font-size: 14px;
|
||
}
|
||
|
||
.commentInfo {
|
||
margin-top: 0;
|
||
|
||
.time {
|
||
font-size: 12px;
|
||
color: rgba(255, 255, 255, 0.55);
|
||
}
|
||
}
|
||
|
||
.horizontalLine {
|
||
height: 1px;
|
||
width: 100%;
|
||
background-color: rgba(255, 255, 255, 0.1);
|
||
margin: 12px 0 0;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.pdunset {
|
||
padding-top: unset;
|
||
}
|
||
|
||
.expandBox {
|
||
color: #656565;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
line-height: 18px;
|
||
flex: 1;
|
||
padding: 0;
|
||
justify-content: space-between;
|
||
|
||
.expandMore {
|
||
gap: 6px;
|
||
}
|
||
|
||
.comment-expand {
|
||
margin-left: 0;
|
||
font-size: 12px;
|
||
}
|
||
|
||
.closeBox {
|
||
width: 100%;
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
align-items: center;
|
||
|
||
.close {
|
||
height: 22px;
|
||
width: 55px;
|
||
border-radius: 22px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.mask {
|
||
height: 100vh;
|
||
width: 100vw;
|
||
position: fixed;
|
||
bottom: 0;
|
||
right: 0;
|
||
z-index: 88;
|
||
background-color: rgba(0, 0, 0, 0.4);
|
||
}
|
||
|
||
&.black {
|
||
.headerBox {
|
||
background: #0f0f0f;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
|
||
.horizontalLineBlock {
|
||
background: rgba(255, 255, 255, 0.2);
|
||
}
|
||
}
|
||
|
||
.main {
|
||
background: #0f0f0f;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 短剧等顶部露视频:弹层高度锁死为 70dvh,内部 flex,避免 header+main(55vh)+输入栏撑破后上溢盖住视频。
|
||
* 与 DramaPlayPage --drama-peek-h: 30dvh 对齐(30+70=100dvh)。
|
||
*/
|
||
&.is-peek-video {
|
||
height: 70dvh;
|
||
max-height: 70dvh;
|
||
overflow: hidden;
|
||
display: flex;
|
||
flex-direction: column;
|
||
box-sizing: border-box;
|
||
background: #0f0f0f;
|
||
|
||
.headerBox {
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.main {
|
||
flex: 1 1 auto;
|
||
min-height: 0;
|
||
height: auto;
|
||
}
|
||
|
||
.bottomBox,
|
||
#comment-bottom-box {
|
||
position: relative !important;
|
||
flex-shrink: 0;
|
||
bottom: auto !important;
|
||
right: auto !important;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
background: #0f0f0f;
|
||
box-sizing: border-box;
|
||
}
|
||
}
|
||
}
|
||
|
||
.wrapper.popup {
|
||
border-radius: 20px 20px 0 0;
|
||
overflow: hidden;
|
||
}
|
||
</style>
|
||
<style lang="scss">
|
||
#comment-bottom-box {
|
||
width: 100%;
|
||
position: absolute;
|
||
bottom: 0;
|
||
right: 0;
|
||
z-index: 99;
|
||
background: #0f0f0f;
|
||
|
||
.send-img {
|
||
width: 60px;
|
||
height: 60px;
|
||
margin-left: 12px;
|
||
position: relative;
|
||
|
||
.close {
|
||
width: 20px;
|
||
height: 20px;
|
||
position: absolute;
|
||
top: -10px;
|
||
right: -10px;
|
||
z-index: 1;
|
||
}
|
||
|
||
.img {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 7px;
|
||
overflow: hidden;
|
||
margin: 10px 0;
|
||
}
|
||
}
|
||
|
||
.commentBtnBox {
|
||
height: 58px;
|
||
width: 100%;
|
||
padding: 14px 16px;
|
||
box-sizing: border-box;
|
||
background: #0f0f0f;
|
||
gap: 12px;
|
||
|
||
.commentInput {
|
||
border: unset;
|
||
border-radius: 16px;
|
||
background: rgba(255, 255, 255, 0.1);
|
||
height: 32px;
|
||
position: relative;
|
||
padding: 7px 16px;
|
||
flex: 1;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
|
||
.van-cell {
|
||
padding: 0;
|
||
height: 18px;
|
||
min-height: 18px;
|
||
background: transparent;
|
||
align-items: center;
|
||
|
||
::placeholder {
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: #bdbdbd;
|
||
}
|
||
|
||
.van-field__control {
|
||
font-size: 12px;
|
||
line-height: 18px;
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
}
|
||
|
||
.post_item {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
margin-left: 0;
|
||
flex-shrink: 0;
|
||
|
||
.icon {
|
||
width: 20px;
|
||
height: 20px;
|
||
}
|
||
}
|
||
|
||
.comment_post {
|
||
font-size: 14px;
|
||
gap: 12px;
|
||
}
|
||
|
||
&.black {
|
||
.commentInput {
|
||
background: rgba(255, 255, 255, 0.1);
|
||
|
||
.van-cell {
|
||
background: transparent;
|
||
|
||
::placeholder {
|
||
color: #bdbdbd;
|
||
}
|
||
|
||
.van-field__control {
|
||
color: rgba(255, 255, 255, 0.9);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
&.black {
|
||
.commentBtnBox {
|
||
background: #0f0f0f;
|
||
}
|
||
}
|
||
}
|
||
</style>
|