Files
tra-app/src/pages/courseDetail/components/comment.vue
T
2025-08-28 23:15:08 +08:00

507 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="comment_div">
<view class="message_div" v-for="(item, index) in message_list">
<view class="head_div">
<image-preview v-if="item.userIconImgId" class="img-box img" :src="item.userIconImgId"></image-preview>
<image v-else src="/static/images/me/default_user_avatar.png" class="img"></image>
</view>
<view class="msg_content">
<view class="title_div">
<view class="name">{{ item.userName }}</view>
<view class="title_tip">
<image
class="img"
v-if="item.bbsTag === 'ASK'"
src="@/static/images/courseDetail/ASK.png"
mode="widthFix"
></image>
<image
class="img"
v-if="item.bbsTag === 'SUG'"
src="@/static/images/courseDetail/SUG.png"
mode="widthFix"
></image>
</view>
<!-- <view class="title_tip">{{item.bbsTagDesc}}</view> -->
</view>
<view class="title">
{{ item.bbsContent }}
</view>
<view class="img_div pic-cell">
<image-preview
v-for="img in item.imgIdList"
@click="showPicture(item.imgIdList, img)"
:src="img"
class="pic-item"
mode="scaleToFill"
></image-preview>
</view>
<view class="time_and_reply">
<view class="time">{{ item.bbsTm.substr(0, 16) }}</view>
<view class="reply" @click="click_reply(item, item.bbsId, index)">回复</view>
</view>
<view class="show_close_div" v-if="item.children?.length" @click="item.show = !item.show">
<template v-if="item.show">
<view>收起</view>
<view class="back_div">
<view class="back-icon"></view>
</view>
</template>
<template v-else>
<view>展开{{ item.children?.length }}条回复</view>
<view class="back_div">
<view class="back-icon"></view>
</view>
</template>
</view>
<view class="children" v-if="item.show">
<view class="message_div" v-for="childrenItem in item.children">
<view class="head_div">
<image-preview
v-if="childrenItem.userIconImgId"
class="img-box img"
:src="childrenItem.userIconImgId"
></image-preview>
<image v-else src="/static/images/me/default_user_avatar.png" class="img"></image>
</view>
<view class="msg_content">
<view class="title_div">
<view class="name">{{ childrenItem.userName }}</view>
<view class="arrow"></view>
<view class="name">{{ childrenItem.rtnUName }}</view>
</view>
<view class="title">
{{ childrenItem.bbsContent }}
</view>
<view class="img_div"></view>
<view class="time_and_reply">
<view class="time">{{ childrenItem.bbsTm.substr(0, 16) }}</view>
<view class="reply" @click="click_reply(childrenItem, childrenItem.parentId, index)">
回复
</view>
</view>
</view>
</view>
</view>
</view>
</view>
<view class="empty_message_div" v-if="status == 'nomore' && total == 0">
<image class="img" src="@/static/images/courseDetail/empty_message.png" mode="widthFix"></image>
<view class="text">这里什么都没有</view>
</view>
<uv-load-more v-if="total != 0" :status="status" loadmore-text="轻轻上拉加载"></uv-load-more>
<uni-popup ref="popup" class="popup">
<view class="bottom_reply_input_div" :style="{ marginBottom: popup_input_bottom }">
<uv-input
v-model="reply_data.bbsContent"
:adjustPosition="false"
placeholderStyle="color:#999999;font-size:26rpx"
:placeholder="reply_data.placeholder"
:focus="true"
:maxlength="100"
></uv-input>
<view class="" style="display: flex; flex-direction: column; margin: 10rpx 20rpx">
<view class="fl1"></view>
<uv-button class="bottom_reply_button" type="primary" :throttleTime="500" @click="reply_send">
回复
</uv-button>
<view class="fl1"></view>
</view>
</view>
</uni-popup>
</view>
</template>
<script setup>
import { ref, reactive, onMounted, nextTick } from 'vue';
import { queryTraCrsBbsInfoByCrsId, replyTraCrsBbsInfo, qryChildTraCrsBbsInfo } from '@/api/courseDetail.js';
import common from '@/common/common.js';
import { get_base_url } from '@/api/request.js';
const popup = ref(null);
const popup_input_bottom = ref('0px');
const inputValue = ref('');
const placeholder = ref('');
const message_list = reactive([]);
const status = ref(''); // loadmore - 加载前,loading - 加载中,nomore - 没有数据
const limit = 20;
const total = ref(-1);
const page = ref(0);
const reply_data_init = {
index: null,
parentBbsId: null,
bbsId: null,
bbsContent: '',
placeholder: ''
};
const reply_data = reactive({
...reply_data_init
});
const props = defineProps({
crsId: String,
imgId: String
});
const click_reply = (item, parentBbsId, index) => {
popup.value.open('bottom');
reply_data.placeholder = '回复 ' + item.userName + '';
reply_data.parentBbsId = parentBbsId;
reply_data.bbsId = item.bbsId;
reply_data.index = index;
const systemInfo = uni.getSystemInfoSync();
// 安全区域底部到屏幕底部的高度(即底部安全区域高度)
const safeAreaBottomHeight = systemInfo.screenHeight - systemInfo.safeArea.bottom;
nextTick(() => {
uni.onKeyboardHeightChange((res) => {
let keyboardHeight = res.height;
// 仅在iOS端补偿安全区域高度
if (systemInfo.platform === 'ios') {
keyboardHeight = Math.max(0, keyboardHeight - safeAreaBottomHeight); // 避免负数
}
popup_input_bottom.value = `${keyboardHeight}px`;
});
});
};
const reply_send = async () => {
if (reply_data.bbsContent === '') {
return common.msg('回复内容不能为空');
}
try {
popup.value.close();
common.loading('发送中...');
await replyTraCrsBbsInfo({
parentBbsId: reply_data.parentBbsId,
bbsId: reply_data.bbsId,
bbsContent: reply_data.bbsContent,
imgIdList: []
});
common.msg('发送成功');
const res = await qryChildTraCrsBbsInfo({
bbsId: reply_data.parentBbsId
});
message_list[reply_data.index]['children'] = res.body;
Object.assign(reply_data, reply_data_init);
} catch (e) {
common.msg('发送失败');
} finally {
common.hideLoading();
}
};
const getData = (from = '') => {
if (from == 'submit') {
status.value = '';
total.value = -1;
page.value = 1;
message_list.length = 0;
} else {
if (status.value !== 'loadmore' && status.value !== '') return;
status.value = 'loading';
page.value++;
}
queryTraCrsBbsInfoByCrsId({
crsId: props.crsId,
page: page.value,
limit: limit
})
.then((res) => {
total.value = res.total;
message_list.push(...res.body);
})
.finally(() => {
if (message_list.length >= total.value) {
status.value = 'nomore';
} else {
status.value = 'loadmore';
}
});
};
// 大图展示
const showPicture = (urls, item) => {
const base_url = get_base_url();
const current = urls.findIndex((res) => item === res);
if (current === -1) {
return false;
}
const urlsList = urls.map((res) => base_url + res);
console.log(urlsList, current);
uni.previewImage({
current: current || 0,
urls: urlsList,
longPressActions: {
// itemList: ['发送给朋友', '保存图片', '收藏'],
itemList: ['保存图片'],
success: function (data) {
uni.saveImageToPhotosAlbum({
filePath: urlsList[data.index],
success: function () {}
});
},
fail: function (err) {}
}
});
};
defineExpose({
getData
});
onMounted(() => {
getData();
});
</script>
<style lang="scss" scoped>
.popup {
z-index: 18000;
}
.empty_message_div {
display: flex;
justify-content: center;
align-items: center;
padding: 80rpx 0;
flex-direction: column;
.img {
width: 20vw;
// height: 276rpx;
}
.text {
margin-top: 24rpx;
font-weight: 400;
font-size: 29rpx;
color: #999999;
text-align: center;
font-style: normal;
}
}
.pic-cell {
width: 100%;
display: flex;
flex-wrap: wrap;
justify-content: flex-start;
}
.pic-item {
margin-right: 16rpx;
width: 170rpx;
height: 170rpx;
border-radius: 8rpx;
// background-color: red;
margin-bottom: 24rpx;
overflow: hidden;
}
.send_evaluate_div_height {
height: 170rpx;
}
.send_evaluate_div {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 10rpx 20rpx 30rpx 20rpx;
// padding-top: 10rpx;
// padding-bottom: 30rpx;
background-color: #ffffff;
// background-color: red;
:deep(.uv-button-wrapper) {
.uv-button {
border-radius: 14rpx;
height: 92rpx;
}
}
}
// 顶部随着输入法弹出的框
.bottom_reply_input_div {
width: 100%;
height: 100%;
background-color: #ffffff;
display: flex;
bottom: 40vh;
align-items: center;
padding: 10rpx 10rpx;
.bottom_reply_button {
border: none;
border-radius: 4rpx;
background-color: #0066ff;
color: #ffffff;
overflow: hidden;
:deep(.uv-button) {
height: 60rpx;
}
}
}
.back_div {
margin-left: 6rpx;
.back-icon {
position: relative;
width: 26rpx;
height: 26rpx;
cursor: pointer;
}
.back-icon::before,
.back-icon::after {
content: '';
position: absolute;
transition: all 0.3s ease;
}
.back-icon::after {
top: 2%;
left: 25%;
width: 50%;
height: 50%;
border-top: 3rpx solid #0066ff;
border-left: 3rpx solid #0066ff;
transform: rotate(-135deg);
}
}
.message_div:not(:first-child) {
margin-top: 30rpx;
}
.message_div {
// margin-top: 30rpx;
display: flex;
margin-left: 20rpx;
margin-right: 20rpx;
.head_div {
.img {
border-radius: 100%;
width: 78rpx;
height: 78rpx;
overflow: hidden;
}
}
.msg_content {
margin-left: 22rpx;
flex: 1;
overflow: hidden;
.title_div {
display: flex;
align-items: center;
.name {
height: 35rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 25rpx;
color: #666666;
line-height: 35rpx;
text-align: left;
font-style: normal;
}
.title_tip {
margin-left: 16rpx;
display: flex;
align-items: center;
.img {
width: 92rpx;
}
}
.arrow {
margin: 0 12rpx 0 14rpx;
position: relative;
width: 12rpx;
height: 20rpx;
overflow: hidden;
}
.arrow::after {
content: '';
position: absolute;
background-color: #666666;
right: 4rpx;
width: 18rpx;
height: 12rpx;
transform: rotate(45deg);
}
}
.title {
margin: 4rpx 0;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 30rpx;
color: #333333;
text-align: left;
line-height: 48rpx;
text-overflow: ellipsis;
white-space: wrap;
overflow-wrap: anywhere;
}
.time_and_reply {
display: flex;
align-items: center;
height: 28rpx;
.time {
font-family: ArialMT;
font-size: 24rpx;
color: #666666;
line-height: 27rpx;
text-align: left;
font-style: normal;
}
.reply {
margin-left: 16rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #0066ff;
line-height: 33rpx;
text-align: left;
font-style: normal;
}
}
.show_close_div {
margin-top: 12rpx;
margin-bottom: 20rpx;
font-family: PingFangSC, PingFang SC;
font-weight: 400;
font-size: 24rpx;
color: #0066ff;
line-height: 30rpx;
text-align: left;
font-style: normal;
display: flex;
align-items: center;
}
}
}
.comment_scroll {
height: calc(100vh - 92rpx - 22rpx - 187rpx);
}
.comment_div {
padding: 20rpx 0 20rpx 0;
background-color: #ffffff;
border-radius: 24rpx;
position: relative;
// height: calc(100vh - 88rpx - 162rpx - 256rpx - 70rpx);
// overflow: hidden;
}
</style>