476 lines
11 KiB
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>
|