hookehuyr

feat(消息): 新增消息详情组件并重构消息页面

将消息详情弹框抽离为独立组件 MessageDetail,支持消息发送功能
重构消息页面代码,移除冗余样式,优化消息列表布局
......@@ -10,6 +10,7 @@ declare module 'vue' {
BrandModelPicker: typeof import('./src/components/BrandModelPicker.vue')['default']
FeaturedRecommendations: typeof import('./src/components/FeaturedRecommendations.vue')['default']
LatestScooters: typeof import('./src/components/LatestScooters.vue')['default']
MessageDetail: typeof import('./src/components/MessageDetail.vue')['default']
NavBar: typeof import('./src/components/navBar.vue')['default']
NutActionSheet: typeof import('@nutui/nutui-taro')['ActionSheet']
NutButton: typeof import('@nutui/nutui-taro')['Button']
......
This diff is collapsed. Click to expand it.
......@@ -40,8 +40,9 @@
</nut-sticky>
<!-- 消息列表内容 -->
<scroll-view ref="scrollViewRef" class="conversation-list" :style="scrollStyle" :scroll-y="true" @scrolltolower="loadMore"
@scroll="scroll" :lower-threshold="100" :enable-flex="false" :scroll-top="scrollTop">
<scroll-view ref="scrollViewRef" class="conversation-list" :style="scrollStyle" :scroll-y="true"
@scrolltolower="loadMore" @scroll="scroll" :lower-threshold="100" :enable-flex="false"
:scroll-top="scrollTop">
<view v-for="conversation in filteredConversations" :key="conversation.id"
class="conversation-item mt-2 mb-4 border-b border-gray-100 pb-2"
@click="onConversationClick(conversation)">
......@@ -70,7 +71,8 @@
</view>
<!-- 空状态提示 -->
<view v-if="filteredConversations.length === 0 && !loading && !hasMore" class="empty-state py-8 text-center">
<view v-if="filteredConversations.length === 0 && !loading && !hasMore"
class="empty-state py-8 text-center">
<Message size="48" color="#9ca3af" class="mb-4" />
<text class="text-gray-500 text-base block mb-2">暂无消息</text>
<text class="text-gray-400 text-sm">当前筛选条件下没有找到相关消息</text>
......@@ -91,39 +93,8 @@
<TabBar />
<!-- 消息详情弹框 -->
<nut-popup v-model:visible="showMessageDetail" position="right" :style="{ width: '100%', height: '100%' }"
closeable close-icon-position="top-right" @close="closeMessageDetail">
<view class="message-detail-container">
<!-- 详情页头部 -->
<view class="detail-header">
<view class="flex items-center">
<image v-if="selectedConversation?.avatar" :src="selectedConversation.avatar"
class="w-12 h-12 rounded-full object-cover mr-3" mode="aspectFill" />
<view v-else class="w-12 h-12 rounded-full bg-gray-100 flex items-center justify-center mr-3">
<component :is="selectedConversation?.icon" />
</view>
<view class="flex-1">
<text class="text-lg font-medium">{{ selectedConversation?.name }}</text>
<text class="text-sm text-gray-500 block">{{ selectedConversation?.time }}</text>
</view>
</view>
</view>
<!-- 消息内容 -->
<view class="detail-content">
<view class="message-content">
<text class="text-base">{{ selectedConversation?.lastMessage }}</text>
</view>
</view>
<!-- 底部关闭按钮 -->
<view class="detail-footer">
<nut-button type="primary" size="large" block @click="closeMessageDetail" color="orange">
关闭
</nut-button>
</view>
</view>
</nut-popup>
<MessageDetail v-model="showMessageDetail" :conversation="selectedConversation" @close="closeMessageDetail"
@sendMessage="handleSendMessage" />
</view>
</template>
......@@ -131,10 +102,12 @@
import { ref, computed, onMounted, markRaw } from 'vue'
import { Search2, Notice, Message } from '@nutui/icons-vue-taro'
import TabBar from '@/components/TabBar.vue'
import MessageDetail from '@/components/MessageDetail.vue'
import { $ } from '@tarojs/extend'
import Taro from '@tarojs/taro'
// 默认头像
const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
// const defaultAvatar = 'https://cdn.ipadbiz.cn/mlaj/images/icon_1.jpeg'
const scrollStyle = ref({
height: 'calc(100vh - 500rpx)'
......@@ -329,6 +302,25 @@ const markAsRead = (conversationId) => {
}
}
// 处理发送消息
const handleSendMessage = (data) => {
const { conversation, message } = data
if (!conversation || !message.trim()) return
// 更新对话的最后一条消息
const conv = conversations.value.find(conv => conv.id === conversation.id)
if (conv) {
conv.lastMessage = message
conv.time = '刚刚'
conv.unread = false // 标记为已读
}
Taro.showToast({
title: '消息发送成功',
icon: 'success'
})
}
// 页面加载时初始化数据
onMounted(() => {
// 设置滚动列表可视高度
......@@ -560,69 +552,7 @@ onMounted(() => {
position: relative;
}
/* 消息详情弹框样式 */
.message-detail-container {
height: 100%;
display: flex;
flex-direction: column;
background: #ffffff;
}
.detail-header {
padding: 32rpx 24rpx;
border-bottom: 1rpx solid #f0f0f0;
background: #ffffff;
flex-shrink: 0;
}
.detail-content {
flex: 1;
padding: 24rpx;
overflow-y: auto;
background: #f8f9fa;
}
.message-content {
background: #ffffff;
padding: 24rpx;
border-radius: 16rpx;
margin-bottom: 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
}
.message-history {
.message-item {
background: #ffffff;
padding: 20rpx;
border-radius: 12rpx;
margin-bottom: 16rpx;
box-shadow: 0 1rpx 4rpx rgba(0, 0, 0, 0.05);
}
}
.detail-footer {
padding: 24rpx;
background: #ffffff;
border-top: 1rpx solid #f0f0f0;
flex-shrink: 0;
}
.text-lg {
font-size: 36rpx;
}
.text-base {
font-size: 32rpx;
line-height: 1.5;
}
.mr-3 {
margin-right: 24rpx;
}
.mt-1 {
margin-top: 8rpx;
}
}
......@@ -659,7 +589,8 @@ onMounted(() => {
/* 状态筛选标签 */
.status-tabs {
background: white;
padding: 20rpx 35rpx; /* 增加内边距 */
padding: 20rpx 35rpx;
/* 增加内边距 */
border-bottom: 1rpx solid #e5e7eb;
display: flex;
position: relative;
......@@ -675,7 +606,8 @@ onMounted(() => {
.tab-item {
margin-right: 48rpx;
padding-bottom: 16rpx;
font-size: 30rpx; /* 增大字体 */
font-size: 30rpx;
/* 增大字体 */
color: #6b7280;
position: relative;
cursor: pointer;
......@@ -720,6 +652,7 @@ onMounted(() => {
transform: scaleX(0);
opacity: 0;
}
100% {
transform: scaleX(1);
opacity: 1;
......@@ -737,6 +670,7 @@ onMounted(() => {
opacity: 0;
transform: translateY(20rpx);
}
to {
opacity: 1;
transform: translateY(0);
......@@ -749,17 +683,32 @@ onMounted(() => {
animation-fill-mode: both;
}
.conversation-item:nth-child(1) { animation-delay: 0.1s; }
.conversation-item:nth-child(2) { animation-delay: 0.15s; }
.conversation-item:nth-child(3) { animation-delay: 0.2s; }
.conversation-item:nth-child(4) { animation-delay: 0.25s; }
.conversation-item:nth-child(5) { animation-delay: 0.3s; }
.conversation-item:nth-child(1) {
animation-delay: 0.1s;
}
.conversation-item:nth-child(2) {
animation-delay: 0.15s;
}
.conversation-item:nth-child(3) {
animation-delay: 0.2s;
}
.conversation-item:nth-child(4) {
animation-delay: 0.25s;
}
.conversation-item:nth-child(5) {
animation-delay: 0.3s;
}
@keyframes fadeInItem {
from {
opacity: 0;
transform: translateX(-20rpx);
}
to {
opacity: 1;
transform: translateX(0);
......