上传小程序修改合入
This commit is contained in:
@@ -174,6 +174,33 @@
|
||||
}
|
||||
}
|
||||
|
||||
.audio-bubble {
|
||||
min-width: 220px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
|
||||
&.playing {
|
||||
opacity: 0.82;
|
||||
}
|
||||
}
|
||||
|
||||
.audio-bubble-icon {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.audio-bubble-text {
|
||||
flex: 1;
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.audio-bubble-duration {
|
||||
font-size: 24px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
.empty-chat {
|
||||
min-height: 320px;
|
||||
display: flex;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { View, Text, ScrollView, Image, Input } from '@tarojs/components'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import Taro, { useRouter } from '@tarojs/taro'
|
||||
import {
|
||||
getMessages,
|
||||
@@ -47,6 +47,11 @@ function formatParticipantLabel(message: ChatMessage): string {
|
||||
return `${senderType} ${senderId}`
|
||||
}
|
||||
|
||||
function formatAudioDuration(durationMs?: number | null): string {
|
||||
const totalSeconds = Math.max(1, Math.round((durationMs || 0) / 1000))
|
||||
return `${totalSeconds}s`
|
||||
}
|
||||
|
||||
export default function ChatDetail() {
|
||||
const router = useRouter()
|
||||
const params = router.params
|
||||
@@ -85,6 +90,29 @@ export default function ChatDetail() {
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [inputText, setInputText] = useState('')
|
||||
const [sending, setSending] = useState(false)
|
||||
const [playingMessageId, setPlayingMessageId] = useState<number | null>(null)
|
||||
const audioContextRef = useRef<Taro.InnerAudioContext | null>(null)
|
||||
|
||||
useEffect(() => {
|
||||
const audioContext = Taro.createInnerAudioContext()
|
||||
audioContextRef.current = audioContext
|
||||
|
||||
audioContext.onEnded(() => {
|
||||
setPlayingMessageId(null)
|
||||
})
|
||||
audioContext.onStop(() => {
|
||||
setPlayingMessageId(null)
|
||||
})
|
||||
audioContext.onError(() => {
|
||||
setPlayingMessageId(null)
|
||||
Taro.showToast({ title: '语音播放失败', icon: 'none' })
|
||||
})
|
||||
|
||||
return () => {
|
||||
audioContext.destroy()
|
||||
audioContextRef.current = null
|
||||
}
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
void loadChatData(activeConversationId)
|
||||
@@ -157,6 +185,46 @@ export default function ChatDetail() {
|
||||
}
|
||||
}
|
||||
|
||||
const handlePlayAudio = (message: ChatMessage) => {
|
||||
if (message.contentType !== 2 || !message.mediaUrl) {
|
||||
Taro.showToast({ title: '语音地址不存在', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
const audioContext = audioContextRef.current
|
||||
if (!audioContext) {
|
||||
Taro.showToast({ title: '播放器未就绪', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
if (playingMessageId === message.id) {
|
||||
audioContext.stop()
|
||||
setPlayingMessageId(null)
|
||||
return
|
||||
}
|
||||
|
||||
audioContext.stop()
|
||||
audioContext.src = message.mediaUrl
|
||||
audioContext.autoplay = true
|
||||
audioContext.play()
|
||||
setPlayingMessageId(message.id)
|
||||
}
|
||||
|
||||
const renderMessageBody = (msg: ChatMessage) => {
|
||||
if (msg.contentType === 2 && msg.mediaUrl) {
|
||||
const isPlaying = playingMessageId === msg.id
|
||||
return (
|
||||
<View className={`audio-bubble ${isPlaying ? 'playing' : ''}`} onClick={() => handlePlayAudio(msg)}>
|
||||
<Text className='audio-bubble-icon'>{isPlaying ? '[]' : '>'}</Text>
|
||||
<Text className='audio-bubble-text'>{msg.mediaTranscriptText || '点击播放语音'}</Text>
|
||||
<Text className='audio-bubble-duration'>{formatAudioDuration(msg.mediaDurationMs)}</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return <Text>{msg.content}</Text>
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='chat-detail-page'>
|
||||
<View className='chat-header'>
|
||||
@@ -199,7 +267,7 @@ export default function ChatDetail() {
|
||||
<View className='message-main'>
|
||||
<Text className='message-meta'>{formatParticipantLabel(msg)}</Text>
|
||||
<View className='message-bubble'>
|
||||
<Text>{msg.content}</Text>
|
||||
{renderMessageBody(msg)}
|
||||
</View>
|
||||
</View>
|
||||
<View className='user-avatar'>
|
||||
@@ -214,7 +282,7 @@ export default function ChatDetail() {
|
||||
<View className='message-main'>
|
||||
<Text className='message-meta'>{formatParticipantLabel(msg)}</Text>
|
||||
<View className='message-bubble'>
|
||||
<Text>{msg.content}</Text>
|
||||
{renderMessageBody(msg)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
@@ -65,7 +65,7 @@ export default function Chat() {
|
||||
</View>
|
||||
) : conversations.length === 0 ? (
|
||||
<View className='empty-card'>
|
||||
<Text className='empty-title'>暂无玩伴会话</Text>
|
||||
<Text className='empty-title'>当前孩子暂无会话</Text>
|
||||
</View>
|
||||
) : (
|
||||
<ScrollView className='conversation-list' scrollY>
|
||||
|
||||
Reference in New Issue
Block a user