fix(navBar): 修复导航栏白屏问题及删除无用客服页代码

- NavBar 组件中移除 useNavigate 钩子,改用 Taro.navigateBack 解决 Taro 4.1.11 不支持 useNavigate 导致白屏的问题
- 删除未注册且重复引用的客服页面源码及配置文件,避免热更新和编译冲突
- 全局搜索确认无其他 useNavigate 和 useReachBottom 引用
- 通过 TypeScript 检查确保修改文件无错误
This commit is contained in:
2026-07-14 20:58:49 +08:00
parent 889a0f983e
commit 1bab629c83
4 changed files with 12 additions and 413 deletions

View File

@@ -1,3 +0,0 @@
export default {
navigationBarTitleText: '在线客服',
}

View File

@@ -1,406 +0,0 @@
import React, { useEffect, useState, useCallback, useRef } from 'react'
import { View, Text, Button, Input, ScrollView } from '@tarojs/components'
import Taro, { useReachBottom } from '@tarojs/taro'
import NavBar from '@/components/NavBar'
import LoadMore from '@/components/common/LoadMore'
import EmptyState from '@/components/common/EmptyState'
import { pageShopChatConversation, addShopChatConversation } from '@/api/shop/shopChatConversation'
import { pageShopChatMessage, addShopChatMessage } from '@/api/shop/shopChatMessage'
import type { ShopChatConversation, ShopChatConversationParam } from '@/api/shop/shopChatConversation/model'
import type { ShopChatMessage } from '@/api/shop/shopChatMessage/model'
import type { PageResult } from '@/api'
definePageConfig({
navigationBarTitleText: '在线客服',
})
const SERVICE_ONLINE_HOURS = '9:00-21:00'
const SERVICE_HOTLINE = '400-888-8888'
const SERVICE_WECHAT = 'shop_service'
const CustomerServicePage: React.FC = () => {
const [conversations, setConversations] = useState<ShopChatConversation[]>([])
const [currentConversation, setCurrentConversation] = useState<ShopChatConversation | null>(null)
const [messages, setMessages] = useState<ShopChatMessage[]>([])
const [inputMessage, setInputMessage] = useState('')
const [loading, setLoading] = useState(true)
const [sending, setSending] = useState(false)
const [loadingMore, setLoadingMore] = useState(false)
const [hasMore, setHasMore] = useState(true)
const [msgPage, setMsgPage] = useState(1)
const [showHistory, setShowHistory] = useState(false)
const scrollToBottom = useRef(false)
const isOnline = () => {
const now = new Date()
const h = now.getHours()
return h >= 9 && h < 21
}
// 加载会话列表
const loadConversations = useCallback(async () => {
setLoading(true)
try {
const params: ShopChatConversationParam = {
page: 1,
limit: 20,
order: 'desc',
sort: 'updateTime',
}
const result: PageResult<ShopChatConversation> = await pageShopChatConversation(params)
if (result?.list) {
setConversations(result.list)
if (result.list.length > 0) {
const latest = result.list[0]
setCurrentConversation(latest)
loadMessages(latest.id!, 1, false)
}
}
} catch (e) {
console.error('获取会话列表失败:', e)
setConversations([])
} finally {
setLoading(false)
}
}, [])
// 加载消息列表
const loadMessages = useCallback(async (conversationId: number, pageNum: number = 1, isLoadMore = false) => {
if (isLoadMore) setLoadingMore(true)
try {
const result = await pageShopChatMessage({
conversationId,
page: pageNum,
limit: 20,
order: 'asc',
sort: 'createTime',
} as any)
if (result?.list) {
if (isLoadMore) {
setMessages(prev => [...result.list!, ...prev])
} else {
setMessages(result.list)
scrollToBottom.current = true
}
setHasMore((result.list.length ?? 0) >= 20)
setMsgPage(pageNum)
}
} catch (e) {
console.error('获取消息列表失败:', e)
if (!isLoadMore) setMessages([])
} finally {
setLoadingMore(false)
}
}, [])
useEffect(() => {
loadConversations()
}, [loadConversations])
useReachBottom(() => {
if (hasMore && currentConversation && !loadingMore) {
loadMessages(currentConversation.id!, msgPage + 1, true)
}
})
// 创建新会话
const ensureConversation = async (): Promise<ShopChatConversation | null> => {
if (currentConversation) return currentConversation
try {
await addShopChatConversation({ type: 0 })
// 重新拉取会话列表获取新会话
const result = await pageShopChatConversation({ page: 1, limit: 20, order: 'desc', sort: 'updateTime' })
if (result?.list && result.list.length > 0) {
const conv = result.list[0]
setConversations(result.list)
setCurrentConversation(conv)
return conv
}
} catch (e) {
console.error('创建会话失败:', e)
}
return null
}
// 发送消息
const handleSendMessage = async () => {
const text = inputMessage.trim()
if (!text) {
Taro.showToast({ title: '请输入消息内容', icon: 'none' })
return
}
setSending(true)
setInputMessage('')
try {
const conv = await ensureConversation()
if (!conv?.id) {
Taro.showToast({ title: '无法创建会话,请稍后重试', icon: 'none' })
setInputMessage(text)
return
}
await addShopChatMessage({
content: text,
type: 'text',
toUserId: 0, // 发送给客服(服务端路由)
} as any)
// 消息发送成功后刷新消息列表
await loadMessages(conv.id, 1, false)
// 同步更新会话列表 lastMessage
setConversations(prev => prev.map(c =>
c.id === conv.id ? { ...c, lastMessage: text, updateTime: new Date().toISOString() } : c
))
} catch (e: any) {
console.error('发送消息失败:', e)
setInputMessage(text)
Taro.showToast({ title: e?.message || '发送失败,请重试', icon: 'none' })
} finally {
setSending(false)
}
}
// 拨打热线
const handleCallHotline = () => {
Taro.makePhoneCall({ phoneNumber: SERVICE_HOTLINE })
}
// 复制微信号
const handleCopyWechat = () => {
Taro.setClipboardData({
data: SERVICE_WECHAT,
success: () => Taro.showToast({ title: '已复制微信号', icon: 'success' }),
})
}
// 跳转帮助中心
const handleGoToHelp = () => {
Taro.navigateTo({ url: '/pages/user/help-center' })
}
// 选择会话
const handleSelectConversation = (conv: ShopChatConversation) => {
setCurrentConversation(conv)
setMessages([])
setMsgPage(1)
setHasMore(true)
loadMessages(conv.id!, 1, false)
}
const formatTime = (timeStr?: string) => {
if (!timeStr) return ''
const date = new Date(timeStr)
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
const h = String(date.getHours()).padStart(2, '0')
const min = String(date.getMinutes()).padStart(2, '0')
return `${m}-${d} ${h}:${min}`
}
const online = isOnline()
return (
<View className="flex flex-col min-h-screen bg-gray-100">
<NavBar title="在线客服" />
<ScrollView scrollY className="flex-1">
{/* 客服状态栏 */}
<View className="bg-white px-4 py-3 flex items-center justify-between">
<View className="flex items-center gap-2">
<View
className="w-2 h-2 rounded-full"
style={{ backgroundColor: online ? '#52c41a' : '#d1d5db' }}
/>
<Text className="text-sm text-gray-700">{online ? '客服在线' : '客服离线'}</Text>
</View>
<Text className="text-xs text-gray-400">: {SERVICE_ONLINE_HOURS}</Text>
</View>
{/* 微信在线客服按钮 */}
<View className="px-4 py-3 bg-white border-t border-gray-50">
<Button
openType="contact"
className="w-full h-12 rounded-lg text-white font-medium text-base border-0"
style={{ backgroundColor: '#07c160', lineHeight: '48px' }}
>
线
</Button>
</View>
{/* 消息记录入口 */}
{!showHistory && (
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
<View
className="flex items-center justify-between"
onClick={() => setShowHistory(true)}
>
<View className="flex items-center gap-2">
<Text className="text-lg">💬</Text>
<Text className="text-sm text-gray-700">
{conversations.length > 0 ? `${conversations.length}条会话)` : ''}
</Text>
</View>
<Text className="text-gray-400 text-sm"></Text>
</View>
</View>
)}
{/* 历史会话与消息列表 */}
{showHistory && (
<View className="mx-4 mt-4">
<View className="flex items-center justify-between mb-2">
<Text className="text-sm font-medium text-gray-700"></Text>
<Text
className="text-sm"
style={{ color: '#3b82f6' }}
onClick={() => setShowHistory(false)}
>
</Text>
</View>
{loading ? (
<LoadMore loading />
) : conversations.length === 0 ? (
<EmptyState message="暂无历史会话" small />
) : (
<View>
{conversations.map(conv => (
<View
key={conv.id}
className="bg-white p-3 rounded-lg mb-2"
style={{ border: currentConversation?.id === conv.id ? '2px solid #10b981' : 'none' }}
onClick={() => handleSelectConversation(conv)}
>
<View className="flex justify-between items-center">
<Text className="text-sm text-gray-700" style={{ flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{conv.content || conv.lastMessage || '暂无消息'}
</Text>
<Text className="text-xs text-gray-400 ml-2">{formatTime(conv.updateTime)}</Text>
</View>
</View>
))}
</View>
)}
{/* 当前会话的消息 */}
{currentConversation && messages.length > 0 && (
<View className="bg-white rounded-lg p-3 mb-4">
<Text className="text-xs text-gray-400 mb-3 block"></Text>
{loadingMore && <LoadMore loading />}
{messages.map(msg => (
<View
key={msg.id}
className={`flex mb-3 ${msg.formUserId === currentConversation.userId ? 'justify-end' : 'justify-start'}`}
>
<View
className="px-3 py-2 rounded-lg max-w-xs"
style={{
backgroundColor: msg.formUserId === currentConversation.userId ? '#10b981' : '#f3f4f6',
maxWidth: '70%',
}}
>
<Text
className="text-sm"
style={{ color: msg.formUserId === currentConversation.userId ? '#fff' : '#374151' }}
>
{msg.content}
</Text>
<Text
className="text-xs block mt-1"
style={{ color: msg.formUserId === currentConversation.userId ? 'rgba(255,255,255,0.7)' : '#9ca3af' }}
>
{formatTime(msg.createTime)}
</Text>
</View>
</View>
))}
</View>
)}
</View>
)}
{/* 其他联系方式 */}
<View className="bg-white mx-4 mt-4 p-4 rounded-lg">
<Text className="text-base font-medium text-gray-800 mb-3 block"></Text>
<View
className="flex items-center justify-between py-3 border-b border-gray-50"
onClick={handleCallHotline}
>
<View className="flex items-center gap-2">
<Text className="text-lg">📞</Text>
<View>
<Text className="text-sm text-gray-700 block">线</Text>
<Text className="text-xs text-gray-400">{SERVICE_HOTLINE}</Text>
</View>
</View>
<Text className="text-xs" style={{ color: '#3b82f6' }}></Text>
</View>
<View
className="flex items-center justify-between py-3"
onClick={handleCopyWechat}
>
<View className="flex items-center gap-2">
<Text className="text-lg">💬</Text>
<View>
<Text className="text-sm text-gray-700 block"></Text>
<Text className="text-xs text-gray-400">{SERVICE_WECHAT}</Text>
</View>
</View>
<Text className="text-xs" style={{ color: '#3b82f6' }}></Text>
</View>
</View>
{/* 帮助中心入口 */}
<View className="bg-white mx-4 mt-4 p-4 rounded-lg mb-4">
<Text className="text-base font-medium text-gray-800 mb-3 block"></Text>
<View
className="flex items-center justify-between py-2"
onClick={handleGoToHelp}
>
<Text className="text-sm text-gray-700"></Text>
<Text className="text-gray-400 text-sm"></Text>
</View>
</View>
{/* 留言(离线时) */}
{!online && (
<View className="bg-white mx-4 mb-4 p-4 rounded-lg">
<Text className="text-base font-medium text-gray-800 mb-3 block">线</Text>
<Text className="text-sm text-gray-400 block">
线线
</Text>
</View>
)}
</ScrollView>
{/* 消息输入区 - 始终显示,支持发送留言 */}
<View className="bg-white border-t border-gray-200 px-4 py-3 flex items-center gap-2">
<Input
className="flex-1 border border-gray-200 rounded-full text-sm"
style={{ paddingLeft: '16px', paddingRight: '16px', height: '40px', lineHeight: '40px' }}
placeholder={online ? '输入消息...' : '留言给客服...'}
value={inputMessage}
onInput={(e: any) => setInputMessage(e.detail.value)}
onConfirm={handleSendMessage}
disabled={sending}
/>
<View
className="rounded-full flex items-center justify-center"
style={{
width: '72px',
height: '40px',
backgroundColor: sending ? '#d1d5db' : '#10b981',
}}
onClick={sending ? undefined : handleSendMessage}
>
<Text className="text-sm text-white">{sending ? '发送中' : '发送'}</Text>
</View>
</View>
</View>
)
}
export default CustomerServicePage