Files
medical-mall/pages/mall/consumer/messages.uvue

1373 lines
32 KiB
Plaintext
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="messages-page">
<!-- 智能顶部导航栏 - 与主页保持一致 -->
<view class="smart-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="nav-container">
<text class="nav-title">消息中心</text>
<view class="nav-actions">
<view class="action-btn" @click="clearAllUnread">
<text class="action-icon">🧹</text>
<text class="action-text">一键已读</text>
</view>
</view>
</view>
</view>
<!-- 导航栏占位符 - 需要包含statusBarHeight + 导航栏高度44px -->
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 44) + 'px' }"></view>
<!-- 消息分类标签 - 固定在顶部,方便随时切换 -->
<view class="tabs-container">
<view class="message-tabs">
<view
v-for="tab in messageTabs"
:key="tab.id"
:class="['tab-item', { active: activeTab === tab.id }]"
@click="switchTab(tab.id)"
>
<text class="tab-name">{{ tab.name }}</text>
<text v-if="tab.unread > 0" class="tab-badge">{{ tab.unread > 99 ? '99+' : tab.unread }}</text>
</view>
</view>
</view>
<!-- 消息列表内容区 -->
<scroll-view
scroll-y
class="messages-content"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="onRefresh"
:scroll-top="scrollTop"
>
<!-- 客服消息 -->
<view v-if="activeTab === 'service'" class="message-section">
<!-- 在线客服卡片 (hidden) -->
<!-- <view class="customer-service-info">
<view class="service-header">
<text class="service-title">康乐医药在线客服</text>
<text class="service-status online">在线</text>
</view>
<text class="service-desc">专业医药顾问在线解答,服务时间 9:00-22:00</text>
<view class="service-categories">
<view class="category-item" @click="startQuickService('用药咨询')">
<text class="category-icon">💊</text>
<text class="category-name">用药咨询</text>
</view>
<view class="category-item" @click="startQuickService('处方咨询')">
<text class="category-icon">📋</text>
<text class="category-name">处方咨询</text>
</view>
<view class="category-item" @click="startQuickService('副作用咨询')">
<text class="category-icon">⚠️</text>
<text class="category-name">副作用咨询</text>
</view>
<view class="category-item" @click="startQuickService('药品配送')">
<text class="category-icon">🚚</text>
<text class="category-name">药品配送</text>
</view>
</view>
</view> -->
<!-- 客服消息列表 -->
<view
v-for="message in serviceMessages"
:key="message.id"
:class="['message-item', { unread: !message.read, active: message.active }]"
@click="startChatWithService(message)"
>
<view class="message-icon-wrapper">
<image
v-if="message.avatar"
class="message-avatar"
:src="message.avatar"
mode="aspectFill"
/>
<view v-else class="message-icon-default" :style="{ backgroundColor: message.color }">
<text class="message-icon-text">{{ message.icon }}</text>
</view>
<view v-if="message.online" class="online-dot"></view>
</view>
<view class="message-content">
<view class="message-header">
<view class="message-title-wrapper">
<text class="message-title">{{ message.title }}</text>
<text v-if="message.role" class="message-role">{{ message.role }}</text>
</view>
<view class="message-header-right">
<text class="message-time">{{ message.time }}</text>
<text v-if="message.unreadCount > 0" class="message-unread-count">{{ message.unreadCount }}</text>
</view>
</view>
<view class="message-preview-wrapper">
<text class="message-preview">{{ message.content }}</text>
<text v-if="message.lastMessage" class="last-message">{{ message.lastMessage }}</text>
</view>
<view v-if="message.tags" class="message-tags">
<text v-for="tag in message.tags" :key="tag" class="message-tag">{{ tag }}</text>
</view>
</view>
</view>
<!-- 客服系统提示 -->
<view class="service-tips">
<text class="tip-icon">💡</text>
<text class="tip-text">温馨提示:请勿相信任何要求转账、付款的信息,谨防诈骗</text>
</view>
</view>
<!-- 系统通知 -->
<view v-if="activeTab === 'system'" class="message-section">
<view
v-for="message in systemMessages"
:key="message.id"
:class="['message-item', { unread: !message.read }]"
@click="viewSystemMessage(message)"
>
<view class="message-icon-wrapper">
<text class="message-icon">📢</text>
</view>
<view class="message-content">
<view class="message-header">
<text class="message-title">{{ message.title }}</text>
<text class="message-time">{{ message.time }}</text>
</view>
<text class="message-preview">{{ message.content }}</text>
<view v-if="message.important" class="important-tag">重要</view>
</view>
</view>
</view>
<!-- 订单消息 -->
<view v-if="activeTab === 'order'" class="message-section">
<view
v-for="message in orderMessages"
:key="message.id"
:class="['message-item', { unread: !message.read }]"
@click="viewOrderMessage(message)"
>
<view class="message-icon-wrapper">
<text class="message-icon">📦</text>
</view>
<view class="message-content">
<view class="message-header">
<text class="message-title">{{ message.title }}</text>
<text class="message-time">{{ message.time }}</text>
</view>
<text class="message-preview">{{ message.content }}</text>
<text class="order-info" v-if="message.order_no">订单号: {{ message.order_no }}</text>
<view v-if="message.status" class="order-status" :class="message.status">
{{ message.statusText }}
</view>
</view>
</view>
</view>
<!-- 优惠活动 -->
<view v-if="activeTab === 'promo'" class="message-section">
<view
v-for="message in promoMessages"
:key="message.id"
:class="['message-item', { unread: !message.read }]"
@click="viewPromoMessage(message)"
>
<view class="message-icon-wrapper">
<text class="message-icon">🎁</text>
</view>
<view class="message-content">
<view class="message-header">
<text class="message-title">{{ message.title }}</text>
<text class="message-time">{{ message.time }}</text>
</view>
<text class="message-preview">{{ message.content }}</text>
<view v-if="message.coupon" class="coupon-info" @click.stop="claimCoupon(message)">
<text class="coupon-text">{{ message.coupon }}优惠券</text>
<text class="coupon-expiry">有效期至 {{ message.expiry }}</text>
<text class="coupon-action">{{ message.claimed ? '已领取' : '点击领取' }}</text>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="!loading && currentMessages.length === 0 && activeTab !== 'service'" class="empty-messages">
<text class="empty-icon">💬</text>
<text class="empty-title">暂无消息</text>
<text class="empty-desc">暂时没有新消息</text>
</view>
<!-- 底部安全区域 -->
<view class="safe-area"></view>
</scroll-view>
<!-- 底部固定按钮 (Hidden) -->
<!-- <view class="floating-action">
<button class="action-button" @click="startNewChat">
<text class="button-icon">✏️</text>
<text class="button-text">新建聊天</text>
</button>
</view> -->
</view>
</template>
<script setup lang="uts">
import { ref, reactive, computed, onMounted } from 'vue'
import { onShow } from '@dcloudio/uni-app'
import { supabaseService, type Notification, type ChatMessage, type ChatRoom } from '@/utils/supabaseService.uts'
// 定义消息项类型
type MessageItem = {
id: string,
title: string,
content: string,
time: string,
read: boolean,
type: string,
avatar: string | null,
important: boolean,
coupon: string,
expiry: string,
claimed: boolean,
order_no: string,
status: string,
statusText: string,
role: string,
lastMessage: string,
online: boolean,
unreadCount: number,
tags: string[],
icon: string,
color: string,
active: boolean
}
// 定义标签类型
type MessageTab = {
id: string,
name: string,
unread: number
}
// 响应式数据
const activeTab = ref<string>('service')
const refreshing = ref<boolean>(false)
const loading = ref<boolean>(false)
const unreadCount = ref<number>(12)
const statusBarHeight = ref(0)
const scrollTop = ref(0)
const scrollHeight = ref(0)
// 消息分类标签
const messageTabs = reactive<MessageTab[]>([
{ id: 'service', name: '客服消息', unread: 5 },
{ id: 'system', name: '系统通知', unread: 3 },
{ id: 'order', name: '订单消息', unread: 2 },
{ id: 'promo', name: '优惠活动', unread: 2 }
])
// 消息数据
const serviceMessages = reactive<MessageItem[]>([])
const systemMessages = reactive<MessageItem[]>([])
const orderMessages = reactive<MessageItem[]>([])
const promoMessages = reactive<MessageItem[]>([])
// 计算当前显示的消息
const currentMessages = computed<MessageItem[]>(() => {
switch (activeTab.value) {
case 'system': return systemMessages
case 'order': return orderMessages
case 'service': return serviceMessages
case 'promo': return promoMessages
default: return []
}
})
// 简单的日期格式化
const formatTime = (isoString: string): string => {
if (isoString == '') return ''
try {
return isoString.split('T')[0]
} catch(e) {
return isoString
}
}
// 更新未读数量 - 必须在 loadMessages 之前定义
const updateUnreadCount = () => {
let totalUnread = 0
let serviceUnread = 0
serviceMessages.forEach((msg: MessageItem) => {
if (!msg.read) serviceUnread++
})
messageTabs[0].unread = serviceUnread
totalUnread += serviceUnread
let systemUnread = 0
systemMessages.forEach((msg: MessageItem) => {
if (!msg.read) systemUnread++
})
messageTabs[1].unread = systemUnread
totalUnread += systemUnread
let orderUnread = 0
orderMessages.forEach((msg: MessageItem) => {
if (!msg.read) orderUnread++
})
messageTabs[2].unread = orderUnread
totalUnread += orderUnread
let promoUnread = 0
promoMessages.forEach((msg: MessageItem) => {
if (!msg.read) promoUnread++
})
messageTabs[3].unread = promoUnread
totalUnread += promoUnread
unreadCount.value = totalUnread
}
// 初始化页面布局数据
const initPage = () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight
const windowHeight = systemInfo.windowHeight
scrollHeight.value = windowHeight - statusBarHeight.value - 44 - 42
}
// 加载消息函数 - 必须在 updateUnreadCount 之后定义
const loadMessages = async () => {
loading.value = true
try {
// 清空现有数据
serviceMessages.length = 0
systemMessages.length = 0
orderMessages.length = 0
promoMessages.length = 0
// 1. 获取通知 (系统、订单、优惠)
console.log('[loadMessages] 开始获取通知...')
const notes = await supabaseService.getUserNotifications()
console.log('[loadMessages] 获取到通知数量:', notes.length)
// 使用 for 循环替代 forEach
for (let i = 0; i < notes.length; i++) {
const note = notes[i]
console.log('[loadMessages] 通知类型:', note.type, '标题:', note.title)
const item: MessageItem = {
id: note.id,
title: note.title,
content: note.content,
time: formatTime(note.created_at ?? ''),
read: note.is_read,
type: note.type,
avatar: note.icon_url,
important: note.type == 'system',
coupon: '点击查看',
expiry: '',
claimed: false,
order_no: '',
status: '',
statusText: '',
role: '',
lastMessage: '',
online: false,
unreadCount: 0,
tags: [] as string[],
icon: '',
color: '',
active: false
}
if (note.type == 'system') {
systemMessages.push(item)
} else if (note.type == 'order') {
orderMessages.push(item)
} else if (note.type == 'promotion') {
item.type = 'promo'
promoMessages.push(item)
}
}
console.log('[loadMessages] 系统消息:', systemMessages.length, '订单消息:', orderMessages.length, '优惠消息:', promoMessages.length)
// 2. 获取客服消息 (Chat)
const rooms = await supabaseService.getChatRooms()
rooms.forEach((room: ChatRoom) => {
const msgItem: MessageItem = {
id: room.merchant_id,
title: room.shop_name,
role: '商家客服',
content: room.last_message ?? '暂无消息',
lastMessage: room.last_message ?? '暂无消息',
time: formatTime(room.last_message_at ?? ''),
read: room.unread_count === 0,
type: 'service',
avatar: room.shop_logo ?? '/static/icons/shop-default.png',
online: true,
unreadCount: room.unread_count,
tags: [] as string[],
icon: '🏪',
color: '#FF9800',
important: false,
coupon: '',
expiry: '',
claimed: false,
order_no: '',
status: '',
statusText: '',
active: false
}
serviceMessages.push(msgItem)
})
// 如果没有消息,添加默认客服
if (serviceMessages.length === 0) {
const defaultService: MessageItem = {
id: 'default_service',
title: '平台客服',
role: '智能助手',
content: '有问题请随时联系我们',
lastMessage: '欢迎咨询',
time: '刚刚',
read: true,
type: 'service',
avatar: '/static/icons/service-avatar.png',
online: true,
unreadCount: 0,
tags: ['自动回复'],
icon: '🤖',
color: '#2196F3',
important: false,
coupon: '',
expiry: '',
claimed: false,
order_no: '',
status: '',
statusText: '',
active: false
}
serviceMessages.push(defaultService)
}
} catch (e) {
console.error('加载消息失败', e)
} finally {
updateUnreadCount()
loading.value = false
}
}
// 生命周期钩子
onMounted(() => {
console.log('Messages Page Mounted')
initPage()
})
onShow(() => {
console.log('Messages Page Show')
loadMessages()
})
// 切换标签
const switchTab = (tabId: string) => {
activeTab.value = tabId
// 切换标签时回到顶部,使用微小变化触发滚动更新
scrollTop.value = scrollTop.value === 0 ? 0.01 : 0
}
// 开始与客服聊天
const startChatWithService = (message: MessageItem) => {
message.read = true
message.unreadCount = 0
updateUnreadCount()
// 这里的 message.id 已经被我们修改为 conversation partner (merchantId)
// 所以参数传递需要调整
const merchantId = message.id === 'default_service' ? '' : message.id
uni.navigateTo({
url: `/pages/mall/consumer/chat?merchantId=${merchantId}&merchantName=${encodeURIComponent(message.title)}`
})
}
// 快速开始服务
const startQuickService = (category: string) => {
uni.navigateTo({
url: `/pages/mall/consumer/chat?category=${encodeURIComponent(category)}`
})
}
// 新建聊天
const startNewChat = () => {
uni.showActionSheet({
itemList: ['用药咨询', '处方咨询', '副作用咨询', '药品配送', '其他问题'],
success: (res) => {
const categories = ['用药咨询', '处方咨询', '副作用咨询', '药品配送', '其他问题']
const category = categories[res.tapIndex]
startQuickService(category)
}
})
}
// 查看系统消息
const viewSystemMessage = (message: MessageItem) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/message-detail?id=${message.id}&type=system`
})
}
// 查看订单消息
const viewOrderMessage = (message: MessageItem) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/order-detail?id=${message.order_no}`
})
}
// 查看优惠活动
const viewPromoMessage = (message: MessageItem) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/coupons`
})
}
// 领取优惠券
const claimCoupon = (message: MessageItem) => {
if (message.claimed) {
uni.showToast({
title: '您已领取该优惠券',
icon: 'none'
})
return
}
message.claimed = true
// 保存领取状态到本地存储,供个人页读取
const claimedCouponsCount = uni.getStorageSync('claimedCoupons')
const count = (claimedCouponsCount != null) ? (claimedCouponsCount as number) : 0
uni.setStorageSync('claimedCoupons', count + 1)
// 保存详细的优惠券信息到 myCoupons 列表
const myCoupons = uni.getStorageSync('myCoupons')
let couponsList: any[] = []
if (myCoupons != null) {
try {
couponsList = JSON.parse(myCoupons as string) as any[]
} catch (e) {
console.error('Failed to parse myCoupons', e)
}
}
couponsList.push({
title: message.title,
amount: message.coupon,
expiry: message.expiry,
id: message.id
})
uni.setStorageSync('myCoupons', JSON.stringify(couponsList))
uni.showToast({
title: '领取成功',
icon: 'success'
})
}
// 清除所有未读
const clearAllUnread = () => {
uni.showModal({
title: '确认操作',
content: '确定要标记所有消息为已读吗?',
success: (res) => {
if (res.confirm) {
serviceMessages.forEach((msg: MessageItem) => {
msg.read = true
msg.unreadCount = 0
})
systemMessages.forEach((msg: MessageItem) => {
msg.read = true
})
orderMessages.forEach((msg: MessageItem) => {
msg.read = true
})
promoMessages.forEach((msg: MessageItem) => {
msg.read = true
})
messageTabs.forEach((tab: MessageTab) => {
tab.unread = 0
})
unreadCount.value = 0
uni.showToast({
title: '已标记所有消息为已读',
icon: 'success'
})
}
}
})
}
// 下拉刷新
const onRefresh = () => {
refreshing.value = true
setTimeout(() => {
loadMessages()
refreshing.value = false
uni.showToast({
title: '刷新成功',
icon: 'success'
})
}, 1000)
}
</script>
<style>
/* 页面结构优化 - 避免双滚动条 */
.messages-page {
width: 100%;
height: 100%;
background-color: #f8fafc;
display: flex;
flex-direction: column;
overflow: hidden; /* 关键防止body滚动 */
}
/* 智能导航栏 */
.smart-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background-color: #4CAF50;
z-index: 1000;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
display: flex;
flex-direction: column;
justify-content: flex-start;
flex-shrink: 0;
}
.nav-container {
padding: 0 16px;
display: flex;
flex-direction: row; /* 关键修复UVUE默认是column必须显式设置为row才能横向排列 */
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1400px;
margin: 0 auto;
height: 44px; /* 调整为标准高度 44px */
}
.nav-title {
font-size: 18px;
font-weight: bold;
color: white;
flex: 1; /* 自适应宽度 */
}
.nav-actions {
display: flex;
flex-direction: row; /* 显式设置行方向 */
align-items: center;
}
.action-btn {
display: flex;
flex-direction: row; /* 显式设置行方向 */
align-items: center;
background: rgba(255, 255, 255, 0.2);
padding: 4px 12px;
border-radius: 20px;
/* cursor: pointer; removed for uniapp-x support */
transition: all 0.2s ease;
}
.action-btn:hover {
background: rgba(255, 255, 255, 0.3);
}
.action-icon {
font-size: 14px;
margin-right: 4px;
}
.action-text {
font-size: 12px;
color: white;
font-weight: bold;
}
/* 导航栏占位符 */
.navbar-placeholder {
width: 100%;
flex-shrink: 0;
}
/* 消息分类标签容器 */
.tabs-container {
background: white;
padding: 0 10px;
border-bottom: 1px solid #e0e0e0;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
z-index: 900;
height: 42px; /* 减小高度,更紧凑 */
flex-shrink: 0;
}
.message-tabs {
display: flex;
flex-direction: row; /* 横向排列 */
height: 100%;
/* overflow-x: auto; 移除自动滚动,改为自适应宽度 */
max-width: 1400px;
margin: 0 auto;
/* gap: 4px; removed for uniapp-x support */
justify-content: space-between; /* 均匀分布 */
}
.message-tabs::-webkit-scrollbar {
display: none;
}
.tab-item {
padding: 0 4px;
margin: 0 2px; /* replaced gap */
display: flex; /* 改为 flex 布局 */
flex-direction: row; /* 关键:横向排列 文字和数字 */
align-items: center; /* 垂直居中 */
justify-content: center;
position: relative;
height: 100%;
border-bottom: 3px solid transparent;
transition: all 0.3s ease;
white-space: nowrap; /* 防止换行 */
flex: 1; /* 关键:均分宽度,消除滚动条 */
min-width: 0; /* 允许压缩 */
}
.tab-item.active {
color: #4CAF50;
border-bottom-color: #4CAF50;
font-weight: bold;
}
.tab-name {
font-size: 14px; /* 微调字体大小适配小屏 */
overflow: hidden;
text-overflow: ellipsis;
}
/* 徽标样式优化 - 放在文字右边 */
.tab-badge {
background-color: #FF5722;
color: white;
font-size: 10px;
padding: 1px 5px;
border-radius: 10px;
min-width: 16px;
text-align: center;
font-weight: bold;
margin-left: 4px; /* 距离文字的间距 */
display: flex;
align-items: center;
justify-content: center;
height: 16px;
}
/* 消息内容区 */
.messages-content {
flex: 1; /* 关键:自适应剩余高度 */
height: 0; /* 关键强制flex item计算高度 */
width: 100%;
max-width: 1400px;
margin: 0 auto;
padding-bottom: 20px;
}
/* 客服信息区域 */
.customer-service-info {
background: white;
border-radius: 12px;
padding: 20px;
margin: 15px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
}
.service-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.service-title {
font-size: 18px;
font-weight: bold;
color: #333;
}
.service-status {
font-size: 12px;
padding: 4px 10px;
border-radius: 12px;
font-weight: bold;
}
.service-status.online {
background: #E8F5E9;
color: #4CAF50;
}
.service-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 20px;
}
.service-categories {
display: flex;
flex-direction: row;
flex-wrap: wrap; /* allow wrapping to simulate grid */
/* grid-template-columns: repeat(2, 1fr); REMOVED */
/* gap: 12px; removed for uniapp-x support */
padding: 6px; /* compensated padding */
}
.category-item {
background: #f8f9fa;
border-radius: 10px;
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
/* cursor: pointer; removed for uniapp-x support */
transition: all 0.3s ease;
margin: 1%; /* replaced gap */
width: 48%; /* 2 columns */
}
.category-item:hover {
background: #e8f5e9;
transform: translateY(-2px);
box-shadow: 0 4px 8px rgba(76, 175, 80, 0.2);
}
.category-icon {
font-size: 24px;
margin-bottom: 8px;
}
.category-name {
font-size: 13px;
color: #333;
font-weight: bold;
}
/* 消息项 */
.message-section {
padding: 10px;
}
.message-item {
background-color: white;
border-radius: 12px;
padding: 15px;
margin-bottom: 10px;
display: flex;
align-items: flex-start;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
/* cursor: pointer; removed for uniapp-x support */
}
.message-item:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
}
.message-item.unread {
background-color: #f0f9f0;
border-left: 3px solid #4CAF50;
}
.message-item.active {
border: 1px solid #4CAF50;
}
.message-icon-wrapper {
width: 50px;
height: 50px;
border-radius: 25px;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
flex-shrink: 0;
position: relative;
}
.message-icon-default {
width: 100%;
height: 100%;
border-radius: 25px;
display: flex;
align-items: center;
justify-content: center;
}
.message-icon-text {
font-size: 24px;
color: white;
}
.message-avatar {
width: 100%;
height: 100%;
border-radius: 25px;
}
.online-dot {
position: absolute;
bottom: 2px;
right: 2px;
width: 12px;
height: 12px;
background-color: #4CAF50;
border-radius: 6px;
border: 2px solid white;
}
.message-content {
flex: 1;
min-width: 0;
}
.message-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 8px;
}
.message-title-wrapper {
flex: 1;
min-width: 0;
margin-right: 10px;
}
.message-title {
font-size: 16px;
color: #333;
font-weight: bold;
/* display: block; REMOVED for uniapp-x support */
margin-bottom: 4px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.message-role {
font-size: 12px;
color: #4CAF50;
background: #E8F5E9;
padding: 2px 8px;
border-radius: 10px;
/* display: inline-block; REMOVED for uniapp-x support */
}
.message-header-right {
display: flex;
flex-direction: column;
align-items: flex-end;
/* gap: 4px; removed for uniapp-x support */
}
.message-time {
font-size: 12px;
color: #999;
white-space: nowrap;
margin-bottom: 4px; /* replaced gap */
}
.message-unread-count {
font-size: 11px;
color: white;
background: #FF5722;
padding: 2px 6px;
border-radius: 10px;
min-width: 18px;
text-align: center;
font-weight: bold;
}
.message-preview-wrapper {
margin-bottom: 8px;
}
.message-preview {
font-size: 14px;
color: #666;
line-height: 1.4;
margin-bottom: 4px;
/* display: -webkit-box; REMOVED for uniapp-x support */
/* -webkit-line-clamp: 2; REMOVED for uniapp-x support */
/* -webkit-box-orient: vertical; REMOVED for uniapp-x support */
lines: 2; /* UTS text truncation */
overflow: hidden;
text-overflow: ellipsis; /* Ensure standard CSS property is present */
}
.last-message {
font-size: 13px;
color: #999;
/* display: block; REMOVED for uniapp-x support */
}
.message-tags {
display: flex;
/* gap: 6px; removed for uniapp-x support */
flex-wrap: wrap;
}
.message-tag {
font-size: 11px;
color: #666;
background: #f0f0f0;
padding: 3px 8px;
border-radius: 10px;
margin-right: 6px; /* replaced gap */
margin-bottom: 4px; /* for wrapping */
}
.order-info {
font-size: 12px;
color: #4CAF50;
background-color: #E8F5E9;
padding: 4px 10px;
border-radius: 4px;
/* display: inline-block; REMOVED for uniapp-x support */
margin-top: 8px;
align-self: flex-start; /* Ensure it doesn't stretch */
}
.order-status {
/* display: inline-block; REMOVED for uniapp-x support */
font-size: 12px;
padding: 4px 10px;
border-radius: 12px;
margin-top: 8px;
margin-left: 8px;
align-self: flex-start; /* Ensure it doesn't stretch */
}
.order-status.shipping {
background: #E3F2FD;
color: #2196F3;
}
.order-status.processing {
background: #FFF3E0;
color: #FF9800;
}
.order-status.completed {
background: #E8F5E9;
color: #4CAF50;
}
.important-tag {
/* display: inline-block; REMOVED for uniapp-x support */
background-color: #FF5722;
color: white;
font-size: 11px;
padding: 3px 8px;
border-radius: 10px;
margin-top: 8px;
align-self: flex-start; /* Ensure it doesn't stretch */
}
.coupon-info {
background: linear-gradient(135deg, #FF9800, #FF5722);
border-radius: 8px;
padding: 10px;
margin-top: 8px;
color: white;
}
.coupon-text {
font-size: 14px;
font-weight: bold;
/* display: block; REMOVED for uniapp-x support */
margin-bottom: 4px;
}
.coupon-expiry {
font-size: 12px;
opacity: 0.9;
}
.coupon-action {
font-size: 12px;
color: #fff;
background-color: rgba(255, 255, 255, 0.2);
padding: 2px 8px;
border-radius: 10px;
margin-top: 4px;
align-self: flex-start;
}
/* 客服系统提示 */
.service-tips {
background: #FFF3E0;
border-radius: 10px;
padding: 15px;
margin: 15px;
display: flex;
align-items: flex-start;
/* gap: 10px; removed for uniapp-x support */
}
.tip-icon {
font-size: 18px;
color: #FF9800;
flex-shrink: 0;
margin-top: 2px;
margin-right: 10px; /* replaced gap */
}
.tip-text {
font-size: 13px;
color: #666;
line-height: 1.5;
}
/* 空状态 */
.empty-messages {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 80px 20px;
text-align: center;
}
.empty-icon {
font-size: 80px;
color: #ddd;
margin-bottom: 20px;
}
.empty-title {
font-size: 18px;
color: #666;
margin-bottom: 10px;
}
.empty-desc {
font-size: 14px;
color: #999;
}
/* 底部浮动按钮 */
.floating-action {
position: fixed;
bottom: 20px;
right: 20px;
z-index: 100;
}
.action-button {
background: linear-gradient(135deg, #4CAF50, #2E7D32);
color: white;
border: none;
border-radius: 25px;
padding: 12px 20px;
display: flex;
align-items: center;
box-shadow: 0 4px 12px rgba(76, 175, 80, 0.3);
font-weight: bold;
}
.button-icon {
font-size: 18px;
margin-right: 8px;
}
.button-text {
font-size: 14px;
font-weight: bold;
}
.safe-area {
height: 80px;
}
/* 响应式适配 */
@media screen and (max-width: 320px) {
.tab-name {
font-size: 13px;
}
.service-categories {
/* grid-template-columns: 1fr; REMOVED */
}
.category-item {
width: 100%; /* 1 column */
}
}
@media screen and (min-width: 415px) {
.message-item {
padding: 20px;
}
.message-icon-wrapper {
width: 60px;
height: 60px;
border-radius: 30px;
}
.message-icon-text {
font-size: 28px;
}
.customer-service-info {
padding: 25px;
}
.service-categories {
/* grid-template-columns: repeat(4, 1fr); REMOVED */
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.service-categories .category-item {
width: 25%;
}
/* 平板和桌面端优化标签栏显示 */
.message-tabs {
justify-content: flex-start; /* 左对齐 */
}
.tab-item {
padding: 0 24px; /* 增加点击区域 */
}
}
/* 平板设备 */
@media screen and (min-width: 768px) {
.smart-navbar {
padding: 0 30px;
}
.tabs-container {
padding: 0 30px;
}
.message-section {
padding: 20px 30px;
}
.customer-service-info {
margin: 20px 30px;
}
.service-tips {
margin: 20px 30px;
}
}
/* 暗黑模式适配 */
@media (prefers-color-scheme: dark) {
.messages-page {
background-color: #121212;
}
.smart-navbar {
background: linear-gradient(135deg, #2E7D32 0%, #1B5E20 100%);
}
.tabs-container {
background-color: #1e1e1e;
border-bottom-color: #333;
}
.tab-item.active {
color: #4CAF50;
}
.customer-service-info,
.message-item,
.service-tips {
background-color: #1e1e1e;
}
.message-item.unread {
background-color: #2d2d2d;
}
.category-item {
background: #2d2d2d;
}
.category-name {
color: #fff;
}
.service-title,
.message-title {
color: #fff;
}
.service-desc,
.message-preview,
.last-message,
.tip-text {
color: #aaa;
}
.message-icon-wrapper {
background-color: #2d2d2d;
}
.order-info {
background-color: #1b5e20;
color: #a5d6a7;
}
.coupon-info {
background: linear-gradient(135deg, #ff8f00, #ef6c00);
}
.message-tag {
background: #333;
color: #ccc;
}
.action-btn {
background: rgba(255, 255, 255, 0.1);
}
.action-btn:hover {
background: rgba(255, 255, 255, 0.2);
}
}
</style>