上传小程序修改合入

This commit is contained in:
HycJack
2026-05-05 12:24:48 +08:00
parent 7e29958ffa
commit bb87bff0d3
19 changed files with 684 additions and 283 deletions
@@ -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;
+71 -3
View File
@@ -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>
+1 -1
View File
@@ -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>