1280 lines
30 KiB
Plaintext
1280 lines
30 KiB
Plaintext
<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>
|
||
|