455 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script setup lang="ts">
import { globalStore, subjectStore, userStore } from '@/stores'
import { LogType, useLog, useSocket, useTeacherExplain } from '@/hooks'
import { $XXL, getSN, isStudyMachine, isWebEvn } from '@/utils'
import hud from '@/utils/hud'
import { useRouter } from 'vue-router'
import { storeToRefs } from 'pinia'
import sass from '@/utils/sass'
const OSS_URL = import.meta.env.VITE_OSS_URL
const lock_bg = `url(${OSS_URL}/urm/lock_bg.png) top left/cover no-repeat`
const logo = sass.getLogo()
const router = useRouter()
const { system, init, getDict, initDevTool, destroyDevTool } = globalStore()
const { getUserInfo } = userStore()
const { getSubjectAllList } = subjectStore()
const LOCK_HOME_PAGE_REFRESH_INTERVAL = 10_000
let lockHomePageTimer: ReturnType<typeof setInterval> | null = null
let lockHomePageRefreshing = false
if (!isWebEvn) {
// 注入安卓方法
window.webViewGoBack = () => {
const { system } = storeToRefs(globalStore())
if (system.value.fullPlay) {
system.value.fullPlay = false
return 1
}
if (router.currentRoute.value.path === '/') {
return 0
}
if (window.history.state?.position === 1 || !window.history.state.back) {
router.push('/')
return 0
} else {
const afterRoute = router.currentRoute.value.fullPath
router.back()
setTimeout(() => {
const nowRoute = router.currentRoute.value.fullPath
if (afterRoute === nowRoute) {
console.log('back后路由没变动手动跳转')
router.replace(window.history.state.back)
}
}, 300)
return 1
}
}
window.initDevTool = initDevTool
window.destroyDevTool = destroyDevTool
window.goTo = (path = '/') => {
router.replace(path)
}
// 检查更新
// await checkUpdate(false).then(res => {
// if (!res) return;
// duration.value = 5;
// useTips({
// title: '更新提示',
// html: `<div style="text-align:center;"><p>新版本已经准备好,马上更新</p><p>当前版本:${getVersion() || ''}#${getVersion(1) || ''}</p><p style="color:#67C23A">最新版本:${system.latestVersion.apk?.versionNum || getVersion()}#${system.latestVersion.web?.versionNum || ''}</p></div>`,
// showCancel: false,
// showClose: false,
// zIndex: 10000,
// 'close-on-click-modal': false,
// 'close-on-press-escape': false,
// onOk: async () => {
// await apkUpdate();
// },
// });
// $XXL.closeLoading();
// });
}
init()
getDict()
getSubjectAllList()
const { init: socketInit } = useSocket()
socketInit()
// 初始化老师讲解 WebSocket 连接
const {
init: teacherExplainInit,
reconnect: teacherExplainReconnect,
onMessage: onTeacherMessage,
} = useTeacherExplain()
teacherExplainInit()
function getLockFlag(data: anyObj, fallback: 0 | 1): 0 | 1 {
const locked = data.msg?.locked
if (locked === true || locked === 'true') return 1
if (locked === false || locked === 'false') return 0
const lockFlag = Number(locked)
return lockFlag === 0 || lockFlag === 1 ? lockFlag : fallback
}
function updateLockScreen(lockFlag: 0 | 1) {
if (!isStudyMachine) return
system.isLock = lockFlag === 1
useLog(LogType.LockScreenTime, {
simSerialNumber: getSN(),
lockFlag,
})
}
async function refreshLockHomePage() {
if (lockHomePageRefreshing) return
lockHomePageRefreshing = true
try {
await getUserInfo()
} catch (err) {
console.warn('锁屏状态刷新首页信息失败', err)
} finally {
lockHomePageRefreshing = false
}
}
function startLockHomePageRefresh() {
if (lockHomePageTimer) return
refreshLockHomePage()
lockHomePageTimer = setInterval(refreshLockHomePage, LOCK_HOME_PAGE_REFRESH_INTERVAL)
}
function stopLockHomePageRefresh() {
if (!lockHomePageTimer) return
clearInterval(lockHomePageTimer)
lockHomePageTimer = null
}
// 重新登录后重连 WebSocket
watch(
() => router.currentRoute.value.path,
(to, from) => {
if (from === '/login' && to !== '/login') {
teacherExplainReconnect()
}
},
)
watch(
() => system.isLock,
isLock => {
if (isLock) {
startLockHomePageRefresh()
return
}
stopLockHomePageRefresh()
},
{ immediate: true },
)
// 监听老师讲解 WebSocket 消息,收到 questionExplainStart 且 explaining 为 true 时跳转讲解页面
// 如果当前已在讲解页面则不跳转,由讲解页面内部根据 questionId 切换题目
onTeacherMessage(data => {
if (data.type === 'lockScreen') {
updateLockScreen(getLockFlag(data, 1))
return
}
if (data.type === 'unLockScreen') {
updateLockScreen(getLockFlag(data, 0))
return
}
if (data.type === 'questionExplainStart') {
let msg = data.msg
if (typeof msg === 'string') {
try {
msg = JSON.parse(msg)
} catch {
return
}
}
if (msg?.explaining === true) {
const currentPath = router.currentRoute.value.path
if (currentPath === '/subject/explain') return
hud.info('老师开始讲解啦,正在跳转...')
router.push({
path: '/subject/explain',
query: msg,
})
}
}
})
// 更新督学信息
// const { getInspectorInfo } = urgeStore()
// getInspectorInfo()
// if (system.debug || import.meta.env.VITE_INIT_VCONSOLE === '1') {
// initDevTool();
// }
// 不缓存的组件存放的是组件名defineOptions中的名字
const excludeRouter = [
'SentenceSortStatistics', // 语感训练的统计界面
'SentenceSort', // 语感训练ing
'WordDetails',
'StudyAnalysis', // 学情分析
'RankingOtherDetail',
'NormalMode',
'HardMode',
'LearnWord',
'WordTest',
'WordTableTest',
'WordReview',
'WordTestRecords',
'TestList',
'WordTestRecords',
'Login',
'WordDictionaries',
'ShortRead',
'Homework', // 作业
'HomeWorkReports',
'HomeWorkDoWork',
'TextbookDetail', // 自由学书本详情
'DoWork', // 做题
'QuestionExplain', // 讲解
'Chat', // AI聊天
'StudentManage', // 学员管理
'CoinDetail', // 乐贝明细
'SelfCoinDetail', // 乐贝明细
'SetBook', // 教材设置
'SetTask', // 任务设置
'AddTask', // 添加任务
'TaskDetail', // 任务详情
'WordTable', // 单词书
'UserTaskRecords', // 导学任务列表
'UserWordBook', // 学员单词书
'Ranking',
]
const showLoad = ref(true)
function onLoaded() {
showLoad.value = false
}
// 播放点击音效
// function onPageClick() {
// if (!system.volume.effectsStatus) return;
// if (touchEffectsBlacklist.includes(route.path)) return;
// useAudio({
// url: touchEffects,
// autoplay: true,
// volume: system.volume.effectsVolume / 100,
// });
// }
// window.$xxl &&
// (window.$xxl.setAppsInfo = (res: any) => {
// info.value = res;
// console.log(res);
// });
// watch(
// () => system.isLock,
// newVal => {
// if (newVal) $XXL.closeLoading();
// },
// );
onMounted(() => {
$XXL.closeLoading()
})
onUnmounted(() => {
stopLockHomePageRefresh()
})
</script>
<template>
<!-- @click="onPageClick" -->
<main>
<div v-if="system.isLock" class="lock">
<div class="title">
<div class="logo">
<img :src="logo" />
</div>
<span>解锁学习的快乐与自由</span>
</div>
<div class="lock-tips">
<Icon name="lock_screen" size="86" />
<div class="lock-tips-text">学习机已锁定,请联系家长解锁吧~</div>
</div>
</div>
<template v-else>
<UrmLoad v-if="showLoad" @loaded="onLoaded" />
<router-view v-slot="{ Component }">
<keep-alive :max="15" :exclude="excludeRouter">
<component :is="Component" />
</keep-alive>
</router-view>
</template>
<!-- <transition name="animate" mode="out-in" appear></transition> -->
<div id="x-audio"></div>
</main>
</template>
<style lang="scss">
@font-face {
font-family: 'AlimamaShuHeiTi';
src: url(https://lxx-web-1313840333.cos.ap-chengdu.myqcloud.com/font/AlimamaShuHeiTi.otf)
format('opentype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'AlibabaHealth';
src: url(https://lxx-web-1313840333.cos.ap-chengdu.myqcloud.com/font/AlibabaHealth.ttf)
format('truetype');
font-weight: normal;
font-style: normal;
}
main {
width: 1920px;
height: 100vh;
max-width: 100%;
box-sizing: border-box;
overflow: hidden;
#x-audio,
#learn-audio {
display: none;
visibility: hidden;
}
.component {
width: 100%;
height: 100%;
box-sizing: border-box;
overflow: hidden;
}
.lock {
width: 100%;
height: 100%;
background: v-bind(lock_bg);
.title {
position: fixed;
bottom: 3.67vh;
left: 50%;
transform: translateX(-50%);
text-align: center;
.logo {
margin-bottom: $space-2_h;
img {
background: #fff;
border-radius: 0.83vh;
width: 56px;
height: 4.67vh;
}
}
span {
color: #282b53;
text-align: center;
font-family: $font-cn;
font-size: $font-size-6;
line-height: 1.5em;
position: relative;
&::after,
&::before {
position: absolute;
top: 50%;
transform: translateY(-50%);
content: '';
display: block;
width: 48px;
height: 0.33vh;
background: #282b53;
}
&::after {
left: -64px;
}
&::before {
right: -64px;
}
}
}
.lock-tips {
z-index: 2;
text-align: center;
position: relative;
top: 10vh;
&-text {
margin-top: $space-6_h;
color: #fff;
text-align: center;
font-size: $space-4;
}
}
&::after {
content: '';
display: block;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
position: fixed;
top: 0;
left: 0;
z-index: 1;
}
}
}
.mask.videoReward {
z-index: 100;
}
.animate-enter-active {
animation: zoomIn 0.3s;
// transform-origin: left bottom;
}
.animate-leave-active {
animation: zoomIn 0.3s reverse;
// transform-origin: left bottom;
}
@keyframes pulse {
from {
transform: scale3d(1, 1, 1);
}
50% {
transform: scale3d(1.01, 1.01, 1.01);
}
to {
transform: scale3d(1, 1, 1);
}
}
@keyframes rotateInUpLeft {
from {
transform: rotate3d(0, 0, 1, 45deg);
opacity: 0;
}
to {
transform: translate3d(0, 0, 0);
opacity: 1;
}
}
@keyframes zoomIn {
from {
opacity: 0;
transform: scale3d(0.9, 0.9, 0.9);
}
50% {
opacity: 1;
}
}
</style>