YingXingAI/components/AnswerList/messageBoard.vue

476 lines
11 KiB
Vue

<template>
<view class="message-board-component">
<!-- 一个列表一个swipe-action容器更符合官方推荐用法 -->
<view v-for="(item, index) in answerList" :key="item.id">
<uni-swipe-action ref="swipeActionRef">
<!-- :right-options="swipeOptions" -->
<uni-swipe-action-item
:disabled="!canSwipe"
:threshold="0.3"
:auto-close="false"
@change="handleSwipeChange($event, index)"
>
<template v-slot:right>
<view
class="right-btn-box"
@touchend.stop="handleSwipeClick(item, index)"
>
<image
class="right-btn"
src="/static/common/images/icon_delete.png"
></image>
<text>撤回</text>
</view>
</template>
<view class="answer-item-box">
<view
class="answer-item"
:class="{
'no-right-radius': openedItems[index] === 'right',
'is-web-mode': isWebMode,
}"
>
<view class="student-info">
<image
class="student-avatar"
:src="item.studentAvatar || AvatarDefault"
></image>
<view class="student-info-content">
<text class="student-name">{{ item.studentName }}</text>
<text class="answer-time">{{ item.time }}</text>
</view>
</view>
<view class="question">
<image
class="question-tag"
src="/static/common/images/icon_ask.png"
></image>
<view class="question-content">{{ item.question }}</view>
</view>
<view v-if="item.isReply">
<view class="line"></view>
<view class="student-info">
<image
class="student-avatar"
:src="item.teacherAvatar || AvatarDefault"
></image>
<view class="student-info-content">
<text class="student-name">{{ item.teacherName }}</text>
<text class="answer-time">{{ item.replyTime }}</text>
</view>
</view>
<view class="answer">
<image
class="answer-tag"
src="/static/common/images/icon_answer.png"
></image>
<view class="answer-content">
<view class="answer-text">{{ item.fullAnswer }}</view>
</view>
</view>
</view>
<!-- 回复按钮-老师 -->
<view class="reply-btn-box" v-if="isTeacher">
<button
class="reply-btn uni-btn-reset"
:class="{ 'disabled-reply': item.isReply }"
@click.stop="handleReplyClick(item)"
>
<image
class="reply-btn-icon"
src="/static/common/images/icon_reply.png"
></image
>回复
</button>
</view>
</view>
<view
v-if="isWebMode"
class="right-btn-box"
:class="{
'web-mode-cursor': isWebMode,
}"
@click.stop="handleWebModeClick(item)"
>
<image
class="right-btn"
src="/static/common/images/icon_delete.png"
></image>
<text>撤回</text>
</view>
</view>
</uni-swipe-action-item>
</uni-swipe-action>
</view>
<!-- 模态弹窗 -->
<u-popup
v-model="modalShow"
mode="center"
width="70%"
border-radius="16"
:mask-close-able="false"
>
<view class="custom-modal">
<view class="modal-title">提示</view>
<view class="modal-content">{{ content }}</view>
<view class="modal-footer">
<view class="modal-btn cancel" @click="handleCancel">取消</view>
<view class="modal-btn confirm" @click="handleConfirm">确认</view>
</view>
</view>
</u-popup>
</view>
</template>
<script>
import AvatarDefault from "@/static/common/images/avatar_default.jpg";
import UniSwipeAction from "@/uni_modules/uni-swipe-action/components/uni-swipe-action/uni-swipe-action.vue";
export default {
name: "MessageBoard",
components: {
UniSwipeAction,
},
props: {
answerList: {
type: Array,
default: () => [],
},
isTeacher: {
type: Boolean,
default: false,
},
// 是否允许左滑
canSwipe: {
type: Boolean,
default: true,
},
},
data() {
return {
AvatarDefault, // 默认头像
currentItem: {}, // 当前操作的item
currentIndex: 0, // 当前操作的item索引
openedItems: {}, // 记录每个项目的滑动状态
isWebMode: false, // 是否是web模式
// 模态弹窗
modalShow: false,
content: "确定要撤回该留言吗?",
modalOpenTime: 0,
preventCloseOnce: false, // 添加防误触标记
};
},
mounted() {
// #ifdef H5
const userAgent = navigator.userAgent.toLowerCase();
this.isWebMode = !/mobile|android|iphone|ipod|ipad/.test(userAgent);
// #endif
console.log("isWebMode", this.isWebMode);
},
methods: {
// 监听滑动状态变化
handleSwipeChange(e, index) {
// 使用Vue的响应式更新方法来更新对象
this.$set(this.openedItems, index, e);
// console.log("滑动状态", this.openedItems);
},
// 处理左滑按钮点击
handleSwipeClick(item, index) {
// console.log("swipeClick", item, index);
// console.log("swipeActionRef", this.$refs.swipeActionRef);
this.currentItem = item;
this.currentIndex = index;
this.modalShow = true;
this.modalOpenTime = Date.now(); // 记录模态框打开时间
},
// 处理web模式点击
handleWebModeClick(item) {
console.log("web模式点击", item);
},
// 处理回复按钮点击
handleReplyClick(item) {
if (item.isReply) {
return;
}
this.$emit("reply", item);
},
// 处理取消按钮点击
handleCancel() {
this.modalShow = false;
},
// 处理确认按钮点击
handleConfirm() {
// 检查是否是快速点击(防止误触)
if (this.modalOpenTime && Date.now() - this.modalOpenTime < 300) {
console.log("防止误触,请重新点击");
return;
}
// 发送删除事件,并传递回调函数
this.$emit("delete", this.currentItem, (success) => {
// 只有当操作成功时才关闭模态框
console.log("callback success", success);
if (success) {
this.$refs.swipeActionRef[this.currentIndex].closeAll();
this.modalShow = false;
}
});
},
},
};
</script>
<style lang="scss" scoped>
.message-board-component {
.right-btn-box {
width: 120rpx;
// height: inherit;
color: #ffffff;
background-color: #ff5252;
display: flex;
flex-direction: column;
gap: 10rpx;
align-items: center;
justify-content: center;
border-radius: 0 16rpx 16rpx 0;
border: 1px solid #fff; /* 防止初始状态下出现红线 */
margin-bottom: 32rpx;
.right-btn {
width: 32rpx;
height: 32rpx;
}
}
.answer-item-box {
display: flex;
}
.answer-item {
flex: 1;
background-color: #ffffff;
border-radius: 16rpx;
padding: 32rpx;
margin-bottom: 32rpx;
position: relative;
/* 删除 overflow: hidden; 可能妨碍滑动 */
transition: border-radius 0.3s; /* 添加过渡效果使圆角变化更平滑 */
/* 滑动时移除右侧圆角 */
&.no-right-radius {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
}
.is-web-mode {
cursor: default;
}
.web-mode-cursor {
cursor: pointer;
}
.student-info {
display: flex;
align-items: center;
margin-bottom: 32rpx;
.student-avatar {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
margin-right: 16rpx;
}
.student-info-content {
display: flex;
flex-direction: column;
.student-name {
font-size: 28rpx;
color: #333333;
font-weight: 500;
}
.answer-time {
font-size: 24rpx;
color: #666666;
}
}
}
.line {
width: 100%;
height: 1rpx;
background-color: rgba(238, 238, 238, 0.8);
margin: 28rpx 0;
}
.question {
display: flex;
margin-bottom: 24rpx;
.question-tag {
width: 42rpx;
height: 42rpx;
margin-left: 6rpx;
margin-right: 26rpx;
flex-shrink: 0;
}
.question-content {
font-size: 32rpx;
color: #333333;
flex: 1;
}
}
.answer {
display: flex;
align-items: flex-start;
.answer-tag {
width: 42rpx;
height: 42rpx;
margin-left: 6rpx;
margin-right: 26rpx;
flex-shrink: 0;
}
.answer-content {
font-size: 24rpx;
line-height: 40rpx;
color: #666666;
flex: 1;
position: relative;
.answer-text {
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
}
}
.reply-btn-box {
width: 100%;
margin-top: 28rpx;
.uni-btn-reset::after {
border: none !important;
}
.reply-btn {
display: flex;
align-items: center;
justify-content: center;
width: 280rpx;
height: 64rpx;
background: #4f6aff;
border-radius: 16rpx;
font-size: 28rpx;
color: #ffffff;
margin-right: 0;
.reply-btn-icon {
width: 28rpx;
height: 28rpx;
margin-right: 12rpx;
}
}
.disabled-reply {
background: #cccccc;
}
}
}
/* 自定义滑动按钮样式,保持简洁 */
::v-deep .uni-swipe-action__button {
display: flex;
flex-direction: row;
align-items: center;
padding: 0 32rpx;
}
/* 自定义图标样式 - 如果使用默认图标系统 */
::v-deep .uni-swipe-action__button-icon {
width: 32rpx;
height: 32rpx;
}
/* 如果需要使用自定义图标 */
::v-deep .uni-swipe-action__button-image {
width: 32rpx;
height: 32rpx;
margin-right: 8rpx;
}
.custom-modal {
background-color: #fff;
overflow: hidden;
.modal-title {
font-size: 32rpx;
font-weight: 500;
text-align: center;
padding: 30rpx 0;
color: #333;
}
.modal-content {
padding: 30rpx;
font-size: 28rpx;
color: #666;
text-align: center;
min-height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
}
.modal-footer {
display: flex;
border-top: 1px solid #eee;
.modal-btn {
flex: 1;
height: 90rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
&.cancel {
color: #666;
border-right: 1px solid #eee;
}
&.confirm {
color: #4f6aff;
}
}
}
}
</style>