| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345 |
- import { defineStore } from 'pinia'
- import { ref, computed } from 'vue'
- import * as api from '@/api/screen'
- export const useScreenStore = defineStore('screen', () => {
- // 机器人状态
- const robotStatus = ref({
- batteryLevel: 85,
- networkStatus: 'online',
- workStatus: 'idle',
- chargeStatus: 'not_charging',
- faultFlag: false
- })
- // 播放方案
- const playPlan = ref(null)
- const currentMediaIndex = ref(0)
- const isPlaying = ref(true)
- // 播放方案版本号(用于检测方案是否变化)
- const playPlanVersion = ref('')
- // 待切换的新播放方案(version 变化时暂存)
- const pendingPlayPlan = ref(null)
- // 当前播放方案是否已完成至少一轮(所有素材都播放过一次)
- const currentPlanCycleComplete = ref(false)
- // 待机页模式: 'welcome' | 'playlist'
- const idleMode = ref('welcome')
- // 是否有播放方案(计算属性 + 手动覆盖)
- const hasPlayPlan = ref(false)
- // 强制模式覆盖(开发调试用)
- const forceMode = ref(null)
- // 播报状态
- const broadcastState = ref({
- broadcasting: false,
- title: '',
- content: '',
- startTime: null,
- endTime: null
- })
- // 语音指令
- const latestCommand = ref(null)
- // 全局提示
- const globalAlert = ref({
- show: false,
- type: 'info',
- title: '',
- message: '',
- duration: 3000
- })
- // 音量
- const volume = ref(80)
- const muted = ref(false)
- // 屏幕配置
- const screenConfig = ref({
- robotName: '智能迎宾机器人',
- logoUrl: '',
- idleTimeout: 60,
- theme: 'default'
- })
- // 待机页主题配置
- const screenTheme = ref(null)
- // 计算属性
- const isIdle = computed(() => robotStatus.value.workStatus === 'idle')
- const isCharging = computed(() => robotStatus.value.chargeStatus === 'charging')
- const hasFault = computed(() => robotStatus.value.faultFlag)
- const networkOk = computed(() => robotStatus.value.networkStatus === 'online')
- const currentMedia = computed(() => {
- if (!playPlan.value || !playPlan.value.items || playPlan.value.items.length === 0) {
- return null
- }
- return playPlan.value.items[currentMediaIndex.value] || null
- })
- // 实际的待机页展示模式
- const actualIdleMode = computed(() => {
- if (forceMode.value !== null) {
- return forceMode.value
- }
- return hasPlayPlan.value ? 'playlist' : 'welcome'
- })
- // 方法
- async function fetchScreenConfig() {
- try {
- const res = await api.getScreenConfig()
- const data = res && res.data !== undefined ? res.data : res
- screenConfig.value = data
- } catch (e) {
- console.error('Failed to fetch screen config:', e)
- }
- }
- async function fetchScreenTheme() {
- try {
- const res = await api.getScreenTheme()
- const data = res && res.data !== undefined ? res.data : res
- screenTheme.value = data
- } catch (e) {
- console.error('Failed to fetch screen theme:', e)
- }
- }
- async function fetchRobotStatus() {
- try {
- const res = await api.getRobotStatus()
- const data = res && res.data !== undefined ? res.data : res
- robotStatus.value = data
- } catch (e) {
- console.error('Failed to fetch robot status:', e)
- }
- }
- async function fetchPlayPlan() {
- try {
- const res = await api.getPlayPlan()
- // 处理 RuoYi 风格的响应结构 { msg, code, data }
- const data = res && res.data !== undefined ? res.data : res
- // 检查版本是否变化(支持 version 字段或不带 version 的兼容处理)
- const newVersion = data?.version || ''
- const hasVersionField = data && 'version' in data
- // 如果是首次加载(playPlanVersion 为空),直接应用方案
- if (!playPlanVersion.value) {
- playPlan.value = data
- playPlanVersion.value = newVersion
- pendingPlayPlan.value = null
- currentMediaIndex.value = 0
- hasPlayPlan.value = !!(
- data &&
- data.enabled !== false &&
- Array.isArray(data.items) &&
- data.items.length > 0
- )
- console.log('[Store] 首次加载播放方案', hasPlayPlan.value ? '有播放方案' : '无播放方案')
- return
- }
- // 后续轮询:检查是否有 version 字段
- if (!hasVersionField) {
- // 接口没有 version 字段,保持当前播放进度不变
- console.log('[Store] 接口无 version 字段,保持当前播放状态')
- return
- }
- // 有 version 字段时,进行版本对比
- const isVersionChanged = newVersion && playPlanVersion.value !== newVersion
- if (isVersionChanged) {
- // 版本变化,暂存新方案
- pendingPlayPlan.value = data
- console.log('[Store] 播放方案版本变化,暂存待切换方案:', newVersion)
- } else {
- // 版本未变化,保持当前播放进度不变
- console.log('[Store] 播放方案版本未变化,保持当前播放进度')
- }
- } catch (e) {
- console.error('[Store] 播放方案加载失败:', e)
- playPlan.value = null
- pendingPlayPlan.value = null
- hasPlayPlan.value = false
- }
- }
- /**
- * 切换到待生效的新播放方案
- * 在当前方案完成一轮后调用
- */
- function applyPendingPlayPlan() {
- if (pendingPlayPlan.value) {
- console.log('[Store] 应用待切换的播放方案')
- playPlan.value = pendingPlayPlan.value
- playPlanVersion.value = pendingPlayPlan.value?.version || ''
- pendingPlayPlan.value = null
- currentMediaIndex.value = 0
- currentPlanCycleComplete.value = false
- hasPlayPlan.value = !!(
- playPlan.value &&
- playPlan.value.enabled !== false &&
- Array.isArray(playPlan.value.items) &&
- playPlan.value.items.length > 0
- )
- }
- }
- /**
- * 标记当前方案已完成一轮播放
- */
- function markCurrentPlanCycleComplete() {
- if (!currentPlanCycleComplete.value && pendingPlayPlan.value) {
- currentPlanCycleComplete.value = true
- console.log('[Store] 当前方案已完成一轮,可切换新方案')
- }
- }
- /**
- * 丢弃待切换的播放方案(当原方案被取消时)
- */
- function discardPendingPlayPlan() {
- if (pendingPlayPlan.value) {
- console.log('[Store] 丢弃待切换的播放方案')
- pendingPlayPlan.value = null
- }
- }
- async function fetchBroadcastState() {
- try {
- const res = await api.getBroadcastState()
- const data = res && res.data !== undefined ? res.data : res
- broadcastState.value = data
- } catch (e) {
- console.error('Failed to fetch broadcast state:', e)
- }
- }
- async function fetchLatestCommand() {
- try {
- const res = await api.getLatestCommand()
- if (res && res.commandId !== latestCommand.value?.commandId) {
- latestCommand.value = res
- return res
- }
- } catch (e) {
- console.error('Failed to fetch latest command:', e)
- }
- return null
- }
- async function ackCommand(commandId) {
- try {
- await api.ackCommand(commandId)
- } catch (e) {
- console.error('Failed to ack command:', e)
- }
- }
- function nextMedia() {
- if (playPlan.value && playPlan.value.items && playPlan.value.items.length > 0) {
- currentMediaIndex.value = (currentMediaIndex.value + 1) % playPlan.value.items.length
- }
- }
- function showAlert(options) {
- const { type = 'info', title = '', message = '', duration = 3000 } = options
- globalAlert.value = { show: true, type, title, message, duration }
- if (duration > 0) {
- setTimeout(() => {
- globalAlert.value.show = false
- }, duration)
- }
- }
- function hideAlert() {
- globalAlert.value.show = false
- }
- function setVolume(val) {
- volume.value = Math.max(0, Math.min(100, val))
- }
- function toggleMute() {
- muted.value = !muted.value
- }
- function pausePlay() {
- isPlaying.value = false
- }
- function resumePlay() {
- isPlaying.value = true
- }
- function resetToIdle() {
- robotStatus.value.workStatus = 'idle'
- }
- // 切换待机页模式(开发调试用)
- function toggleIdleMode() {
- forceMode.value = actualIdleMode.value === 'welcome' ? 'playlist' : 'welcome'
- }
- // 重置强制模式
- function resetForceMode() {
- forceMode.value = null
- }
- return {
- // 状态
- robotStatus,
- playPlan,
- currentMediaIndex,
- isPlaying,
- playPlanVersion,
- pendingPlayPlan,
- currentPlanCycleComplete,
- broadcastState,
- latestCommand,
- globalAlert,
- volume,
- muted,
- screenConfig,
- screenTheme,
- idleMode,
- hasPlayPlan,
- forceMode,
- // 计算属性
- isIdle,
- isCharging,
- hasFault,
- networkOk,
- currentMedia,
- actualIdleMode,
- // 方法
- fetchScreenConfig,
- fetchScreenTheme,
- fetchRobotStatus,
- fetchPlayPlan,
- applyPendingPlayPlan,
- discardPendingPlayPlan,
- markCurrentPlanCycleComplete,
- fetchBroadcastState,
- fetchLatestCommand,
- ackCommand,
- nextMedia,
- showAlert,
- hideAlert,
- setVolume,
- toggleMute,
- pausePlay,
- resumePlay,
- resetToIdle,
- toggleIdleMode,
- resetForceMode
- }
- })
|