Files
medical-mall/doc_mall/consumer/backup_pages/messages医药.uvue

1280 lines
30 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>
<!-- 瀵艰埅鏍忓崰浣嶇 -->
<view class="navbar-placeholder" :style="{ height: (statusBarHeight + 10) + '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">
<!-- 鍦ㄧ嚎瀹㈡湇鍗$墖 -->
<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">涓撲笟鍖昏嵂椤鹃棶鍦ㄧ嚎瑙g瓟锛屾湇鍔℃椂闂?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>{{ 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>
<!-- 搴曢儴鍥哄畾鎸夐挳 -->
<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 { supabaseService, type Notification, type ChatMessage } from '@/utils/supabaseService.uts'
// 鍝嶅簲寮忔暟鎹?
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 initPage = () => {
const systemInfo = uni.getSystemInfoSync()
statusBarHeight.value = systemInfo.statusBarHeight || 0
// 璁$畻婊氬姩鍖哄煙楂樺害锛氬睆骞曢珮搴?- 鐘舵€佹爮 - 瀵艰埅鏍?44) - 鏍囩鏍?42)
const windowHeight = systemInfo.windowHeight
scrollHeight.value = windowHeight - statusBarHeight.value - 44 - 42
}
// 娑堟伅鍒嗙被鏍囩
const messageTabs = reactive([
{ id: 'service', name: '瀹㈡湇娑堟伅', unread: 5 },
{ id: 'system', name: '绯荤粺閫氱煡', unread: 3 },
{ id: 'order', name: '璁㈠崟娑堟伅', unread: 2 },
{ id: 'promo', name: '浼樻儬娲诲姩', unread: 2 }
])
// Mock 瀹㈡湇娑堟伅鏁版嵁
const serviceMessages = reactive<any[]>([])
const systemMessages = reactive<any[]>([])
const orderMessages = reactive<any[]>([])
// Mock 浼樻儬娲诲姩鏁版嵁
const promoMessages = reactive<any[]>([])
// 璁$畻褰撳墠鏄剧ず鐨勬秷鎭?
const currentMessages = computed(() => {
switch (activeTab.value) {
case 'system': return systemMessages
case 'order': return orderMessages
case 'service': return serviceMessages
case 'promo': return promoMessages
default: return []
}
})
// 鐢熷懡鍛ㄦ湡
onMounted(() => {
console.log('Messages Page Mounted')
initPage()
loadMessages()
})
// 绠€鍗曠殑鏃ユ湡鏍煎紡鍖?
const formatTime = (isoString: string): string => {
if (!isoString) return ''
try {
return isoString.split('T')[0]
} catch(e) {
return isoString
}
}
// 鍔犺浇娑堟伅
const loadMessages = async () => {
loading.value = true
try {
// 娓呯┖鐜版湁Mock鏁版嵁
serviceMessages.length = 0
systemMessages.length = 0
orderMessages.length = 0
promoMessages.length = 0
// 1. 鑾峰彇閫氱煡 (绯荤粺銆佽鍗曘€佷紭鎯?
const notes = await supabaseService.getUserNotifications()
notes.forEach((note: Notification) => {
// 杩欓噷浣跨敤 any 绫诲瀷鏋勫缓瀵硅薄锛屼互鍖归厤 reactive 鏁扮粍鐨勭粨鏋?
const item = {
id: note.id,
title: note.title,
content: note.content,
time: formatTime(note.created_at || ''),
read: note.is_read,
type: note.type, // 'system', 'order', 'promotion' => 'promo'
// 榛樿濉厖瀛楁浠ラ伩鍏嶆覆鏌撴姤閿?
avatar: note.icon_url,
important: note.type === 'system', // 绠€鍗曢€昏緫
coupon: '鐐瑰嚮鏌ョ湅',
expiry: '',
claimed: false,
order_no: '',
status: '',
statusText: '',
role: '',
lastMessage: '',
online: false,
unreadCount: 0,
tags: [],
icon: '',
color: ''
}
if (note.type === 'system') {
systemMessages.push(item)
} else if (note.type === 'order') {
orderMessages.push(item)
} else if (note.type === 'promotion') {
// map type 'promotion' to 'promo' for tab
item.type = 'promo'
promoMessages.push(item)
}
})
// 2. 鑾峰彇瀹㈡湇娑堟伅 (Chat)
const chats = await supabaseService.getUserChatMessages()
if (chats.length > 0) {
// 绠€鍗曞鐞嗭細灏嗘渶鏂颁竴鏉℃樉绀轰负"鍦ㄧ嚎瀹㈡湇"浼氳瘽
const lastMsg = chats[0]
serviceMessages.push({
id: lastMsg.id,
title: '鍦ㄧ嚎瀹㈡湇',
role: '瀹㈡湇涓撳憳',
content: lastMsg.content,
lastMessage: lastMsg.content,
time: formatTime(lastMsg.created_at || ''),
read: lastMsg.is_read,
type: 'service',
avatar: '/static/icons/service-avatar.png',
online: true,
unreadCount: chats.filter((m: ChatMessage) => !m.is_read && !m.is_from_user).length,
tags: ['瀹樻柟瀹㈡湇'],
icon: '馃懇鈥嶐煉?,
color: '#2196F3',
important: false,
coupon: '',
expiry: '',
claimed: false,
order_no: '',
status: '',
statusText: ''
})
} else {
// 濡傛灉娌℃湁鐪熷疄鏁版嵁锛屼繚鐣欎竴涓粯璁ゅ鏈嶅叆鍙?
serviceMessages.push({
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: ''
})
}
} catch (e) {
console.error('鍔犺浇娑堟伅澶辫触', e)
} finally {
updateUnreadCount()
loading.value = false
}
}
// 鏇存柊鏈鏁伴噺
const updateUnreadCount = () => {
let totalUnread = 0
const serviceUnread = serviceMessages.filter(msg => !msg.read).length
messageTabs[0].unread = serviceUnread
totalUnread += serviceUnread
const systemUnread = systemMessages.filter(msg => !msg.read).length
messageTabs[1].unread = systemUnread
totalUnread += systemUnread
const orderUnread = orderMessages.filter(msg => !msg.read).length
messageTabs[2].unread = orderUnread
totalUnread += orderUnread
const promoUnread = promoMessages.filter(msg => !msg.read).length
messageTabs[3].unread = promoUnread
totalUnread += promoUnread
unreadCount.value = totalUnread
}
// 鍒囨崲鏍囩
const switchTab = (tabId: string) => {
activeTab.value = tabId
// 鍒囨崲鏍囩鏃跺洖鍒伴《閮紝浣跨敤寰皬鍙樺寲瑙﹀彂婊氬姩鏇存柊
scrollTop.value = scrollTop.value === 0 ? 0.01 : 0
}
// 寮€濮嬩笌瀹㈡湇鑱婂ぉ
const startChatWithService = (message: any) => {
message.read = true
message.unreadCount = 0
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/chat?id=${message.id}&name=${encodeURIComponent(message.title)}&role=${encodeURIComponent(message.role)}`
})
}
// 蹇€熷紑濮嬫湇鍔?
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: any) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/message-detail?id=${message.id}&type=system`
})
}
// 鏌ョ湅璁㈠崟娑堟伅
const viewOrderMessage = (message: any) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/order-detail?id=${message.order_no}`
})
}
// 鏌ョ湅浼樻儬娲诲姩
const viewPromoMessage = (message: any) => {
message.read = true
updateUnreadCount()
uni.navigateTo({
url: `/pages/mall/consumer/coupons`
})
}
// 棰嗗彇浼樻儬鍒?
const claimCoupon = (message: any) => {
if (message.claimed) {
uni.showToast({
title: '鎮ㄥ凡棰嗗彇璇ヤ紭鎯犲埜',
icon: 'none'
})
return
}
message.claimed = true
// 淇濆瓨棰嗗彇鐘舵€佸埌鏈湴瀛樺偍锛屼緵涓汉椤佃鍙?
const claimedCouponsCount = uni.getStorageSync('claimedCoupons') || 0
uni.setStorageSync('claimedCoupons', (claimedCouponsCount as number) + 1)
// 淇濆瓨璇︾粏鐨勪紭鎯犲埜淇℃伅鍒?myCoupons 鍒楄〃
const myCoupons = uni.getStorageSync('myCoupons')
let couponsList: any[] = []
if (myCoupons) {
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 => {
msg.read = true
msg.unreadCount = 0
})
systemMessages.forEach(msg => msg.read = true)
orderMessages.forEach(msg => msg.read = true)
promoMessages.forEach(msg => msg.read = true)
messageTabs.forEach(tab => 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: 100vh;
background-color: #f8fafc;
display: flex;
flex-direction: column;
overflow: hidden; /* 鍏抽敭锛氶槻姝ody婊氬姩 */
}
/* 鏅鸿兘瀵艰埅鏍?*/
.smart-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
background: linear-gradient(135deg, #4CAF50 0%, #2E7D32 100%);
z-index: 1000;
box-shadow: 0 2px 12px rgba(76, 175, 80, 0.15);
/* height: 50px; 绉婚櫎鍥哄畾楂樺害锛岀敱鍐呭鍐冲畾 */
display: flex;
flex-direction: row; /* 鏄惧紡璁剧疆琛屾柟鍚?*/
align-items: center;
justify-content: center;
flex-shrink: 0; /* 闃叉琚帇缂?*/
}
.nav-container {
padding: 0 16px;
display: flex;
flex-direction: row; /* 鍏抽敭淇锛歎VUE榛樿鏄痗olumn锛屽繀椤绘樉寮忚缃负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;
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: 500;
}
/* 瀵艰埅鏍忓崰浣嶇 */
.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;
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: #ff5000;
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; /* 鍏抽敭锛氬己鍒秄lex 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: 500;
}
.service-status.online {
background: #E8F5E9;
color: #4CAF50;
}
.service-desc {
font-size: 14px;
color: #666;
line-height: 1.5;
margin-bottom: 20px;
}
.service-categories {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 12px;
}
.category-item {
background: #f8f9fa;
border-radius: 10px;
padding: 15px;
display: flex;
flex-direction: column;
align-items: center;
cursor: pointer;
transition: all 0.3s ease;
}
.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: 500;
}
/* 娑堟伅椤?*/
.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;
}
.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-default 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;
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;
}
.message-header-right {
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 4px;
}
.message-time {
font-size: 12px;
color: #999;
white-space: nowrap;
}
.message-unread-count {
font-size: 11px;
color: white;
background: #ff5000;
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;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
}
.last-message {
font-size: 13px;
color: #999;
display: block;
}
.message-tags {
display: flex;
gap: 6px;
flex-wrap: wrap;
}
.message-tag {
font-size: 11px;
color: #666;
background: #f0f0f0;
padding: 3px 8px;
border-radius: 10px;
}
.order-info {
font-size: 12px;
color: #4CAF50;
background-color: #E8F5E9;
padding: 4px 10px;
border-radius: 4px;
display: inline-block;
margin-top: 8px;
}
.order-status {
display: inline-block;
font-size: 12px;
padding: 4px 10px;
border-radius: 12px;
margin-top: 8px;
margin-left: 8px;
}
.order-status.shipping {
background: #E3F2FD;
color: #2196F3;
}
.order-status.processing {
background: #FFF3E0;
color: #ff5000;
}
.order-status.completed {
background: #E8F5E9;
color: #4CAF50;
}
.important-tag {
display: inline-block;
background-color: #ff5000;
color: white;
font-size: 11px;
padding: 3px 8px;
border-radius: 10px;
margin-top: 8px;
}
.coupon-info {
background: linear-gradient(135deg, #ff5000, #ff5000);
border-radius: 8px;
padding: 10px;
margin-top: 8px;
color: white;
}
.coupon-text {
font-size: 14px;
font-weight: bold;
display: block;
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;
}
.tip-icon {
font-size: 18px;
color: #ff5000;
flex-shrink: 0;
margin-top: 2px;
}
.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: 500;
}
.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;
}
}
@media screen and (min-width: 415px) {
.message-item {
padding: 20px;
}
.message-icon-wrapper {
width: 60px;
height: 60px;
border-radius: 30px;
}
.message-icon-default text {
font-size: 28px;
}
.customer-service-info {
padding: 25px;
}
.service-categories {
grid-template-columns: repeat(4, 1fr);
}
/* 骞虫澘鍜屾闈㈢浼樺寲鏍囩鏍忔樉绀?*/
.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>