feat: 添加音频播放组件
This commit is contained in:
@@ -59,6 +59,7 @@ import { ref, computed, watch } from 'vue'
|
|||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import MarkdownIt from 'markdown-it'
|
import MarkdownIt from 'markdown-it'
|
||||||
import { Service } from '@/api/services/Service'
|
import { Service } from '@/api/services/Service'
|
||||||
|
import { useAudioPlayer } from '@/composables/useAudioPlayer'
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
visible: boolean
|
visible: boolean
|
||||||
@@ -81,6 +82,9 @@ const visible = computed({
|
|||||||
const confirming = ref(false)
|
const confirming = ref(false)
|
||||||
const activeNoticeKey = ref('')
|
const activeNoticeKey = ref('')
|
||||||
|
|
||||||
|
// 音频播放器
|
||||||
|
const { playSound } = useAudioPlayer()
|
||||||
|
|
||||||
// 初始化 markdown 解析器
|
// 初始化 markdown 解析器
|
||||||
const md = new MarkdownIt({
|
const md = new MarkdownIt({
|
||||||
html: true,
|
html: true,
|
||||||
@@ -159,10 +163,12 @@ watch(
|
|||||||
{ immediate: true }
|
{ immediate: true }
|
||||||
)
|
)
|
||||||
|
|
||||||
// 监听弹窗显示状态,重置到第一个公告
|
// 监听弹窗显示状态,重置到第一个公告并播放音频
|
||||||
watch(visible, newVisible => {
|
watch(visible, async (newVisible) => {
|
||||||
if (newVisible && notices.value.length > 0) {
|
if (newVisible && notices.value.length > 0) {
|
||||||
activeNoticeKey.value = notices.value[0]
|
activeNoticeKey.value = notices.value[0]
|
||||||
|
// 当公告模态框显示时播放音频
|
||||||
|
await playSound('simple/announcement_display')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
99
frontend/src/composables/useAudioPlayer.ts
Normal file
99
frontend/src/composables/useAudioPlayer.ts
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
|
import { message } from 'ant-design-vue'
|
||||||
|
import { useSettingsApi } from '@/composables/useSettingsApi'
|
||||||
|
import { API_ENDPOINTS } from '@/config/mirrors'
|
||||||
|
|
||||||
|
export function useAudioPlayer() {
|
||||||
|
const { getSettings } = useSettingsApi()
|
||||||
|
const currentAudio = ref<HTMLAudioElement | null>(null)
|
||||||
|
const isPlaying = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止当前播放的音频
|
||||||
|
*/
|
||||||
|
const stopCurrentAudio = () => {
|
||||||
|
if (currentAudio.value) {
|
||||||
|
currentAudio.value.pause()
|
||||||
|
currentAudio.value.currentTime = 0
|
||||||
|
currentAudio.value = null
|
||||||
|
isPlaying.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 播放音频
|
||||||
|
* @param soundPath 音频路径,例如: "noisy/welcome" 或 "welcome"
|
||||||
|
*/
|
||||||
|
const playSound = async (soundPath: string): Promise<boolean> => {
|
||||||
|
if (!soundPath) {
|
||||||
|
console.warn('音频路径不能为空')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 首先检查语音设置
|
||||||
|
const settings = await getSettings()
|
||||||
|
if (!settings?.Voice?.Enabled) {
|
||||||
|
console.log('语音功能已禁用,跳过音频播放')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 停止当前播放的音频
|
||||||
|
stopCurrentAudio()
|
||||||
|
|
||||||
|
// 构建音频URL
|
||||||
|
const audioUrl = `${API_ENDPOINTS.local}/api/res/sounds/${soundPath}.wav`
|
||||||
|
|
||||||
|
// 创建新的音频对象
|
||||||
|
const audio = new Audio(audioUrl)
|
||||||
|
currentAudio.value = audio
|
||||||
|
|
||||||
|
// 设置音频事件监听器
|
||||||
|
audio.addEventListener('loadstart', () => {
|
||||||
|
isPlaying.value = true
|
||||||
|
})
|
||||||
|
|
||||||
|
audio.addEventListener('ended', () => {
|
||||||
|
isPlaying.value = false
|
||||||
|
currentAudio.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
audio.addEventListener('error', (e) => {
|
||||||
|
console.error('音频播放失败:', e)
|
||||||
|
message.error(`音频播放失败: ${soundPath}`)
|
||||||
|
isPlaying.value = false
|
||||||
|
currentAudio.value = null
|
||||||
|
})
|
||||||
|
|
||||||
|
// 播放音频
|
||||||
|
await audio.play()
|
||||||
|
return true
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('播放音频时发生错误:', error)
|
||||||
|
message.error('音频播放失败,请检查网络连接')
|
||||||
|
isPlaying.value = false
|
||||||
|
currentAudio.value = null
|
||||||
|
return false
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止播放
|
||||||
|
*/
|
||||||
|
const stopSound = () => {
|
||||||
|
stopCurrentAudio()
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
isPlaying,
|
||||||
|
loading,
|
||||||
|
playSound,
|
||||||
|
stopSound
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -240,6 +240,7 @@ import { message } from 'ant-design-vue'
|
|||||||
import { ClockCircleOutlined, UserOutlined, BellOutlined } from '@ant-design/icons-vue'
|
import { ClockCircleOutlined, UserOutlined, BellOutlined } from '@ant-design/icons-vue'
|
||||||
import { Service } from '@/api/services/Service'
|
import { Service } from '@/api/services/Service'
|
||||||
import NoticeModal from '@/components/NoticeModal.vue'
|
import NoticeModal from '@/components/NoticeModal.vue'
|
||||||
|
import { useAudioPlayer } from '@/composables/useAudioPlayer'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { API_ENDPOINTS } from '@/config/mirrors.ts'
|
import { API_ENDPOINTS } from '@/config/mirrors.ts'
|
||||||
|
|
||||||
@@ -296,6 +297,9 @@ const noticeVisible = ref(false)
|
|||||||
const noticeData = ref<Record<string, string>>({})
|
const noticeData = ref<Record<string, string>>({})
|
||||||
const noticeLoading = ref(false)
|
const noticeLoading = ref(false)
|
||||||
|
|
||||||
|
// 音频播放器
|
||||||
|
const { playSound } = useAudioPlayer()
|
||||||
|
|
||||||
// 获取当前活动信息
|
// 获取当前活动信息
|
||||||
const currentActivity = computed(() => {
|
const currentActivity = computed(() => {
|
||||||
if (!activityData.value.length) return null
|
if (!activityData.value.length) return null
|
||||||
@@ -435,6 +439,8 @@ const fetchNoticeData = async () => {
|
|||||||
if (response.if_need_show && response.data && Object.keys(response.data).length > 0) {
|
if (response.if_need_show && response.data && Object.keys(response.data).length > 0) {
|
||||||
noticeData.value = response.data
|
noticeData.value = response.data
|
||||||
noticeVisible.value = true
|
noticeVisible.value = true
|
||||||
|
// 播放公告展示音频
|
||||||
|
await playSound('simple/announcement_display')
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
console.warn('获取公告失败:', response.message)
|
console.warn('获取公告失败:', response.message)
|
||||||
@@ -461,6 +467,8 @@ const showNotice = async () => {
|
|||||||
if (response.data && Object.keys(response.data).length > 0) {
|
if (response.data && Object.keys(response.data).length > 0) {
|
||||||
noticeData.value = response.data
|
noticeData.value = response.data
|
||||||
noticeVisible.value = true
|
noticeVisible.value = true
|
||||||
|
// 手动查看公告时也播放音频
|
||||||
|
await playSound('simple/announcement_display')
|
||||||
} else {
|
} else {
|
||||||
message.info('暂无公告信息')
|
message.info('暂无公告信息')
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user