| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255 |
- <template>
- <template v-if="slides.length">
- <Screen v-if="viewMode !== 'student' && screening" key="screen" />
- <KeepAlive>
- <Editor
- v-if="viewMode === 'editor' && _isPC && !screening"
- :courseid="urlParams.courseid"
- key="editor"
- />
- <Editor2
- v-else-if="viewMode === 'editor2' && _isPC && !screening"
- :courseid="urlParams.courseid"
- key="editor2"
- />
- <Editor3
- v-else-if="viewMode === 'editor3' && _isPC && !screening"
- :courseid="urlParams.courseid"
- :userid="urlParams.userid"
- key="editor3"
- />
- <Student
- v-else-if="viewMode === 'student'"
- :courseid="urlParams.courseid"
- :type="urlParams.type"
- :userid="urlParams.userid"
- :oid="urlParams.oid"
- :org="urlParams.org"
- :cid="urlParams.cid"
- key="student"
- />
- <Mobile v-else key="mobile" />
- </KeepAlive>
- </template>
- <FullscreenSpin :tip="lang.ssInitDataWait" v-else loading :mask="false" />
- </template>
- <script lang="ts" setup>
- import { onMounted, ref, provide } from 'vue'
- import { storeToRefs } from 'pinia'
- import {
- useScreenStore,
- useMainStore,
- useSnapshotStore,
- useSlidesStore,
- } from '@/store'
- import { lang } from '@/main'
- import { LOCALSTORAGE_KEY_DISCARDED_DB } from '@/configs/storage'
- import { deleteDiscardedDB } from '@/utils/database'
- import { isPC } from '@/utils/common'
- import api from '@/services'
- import Editor from './views/Editor/index.vue'
- import Editor2 from './views/Editor/index2.vue'
- import Editor3 from './views/Editor/index3.vue'
- import Screen from './views/Screen/index.vue'
- import Mobile from './views/Mobile/index.vue'
- import Student from './views/Student/index.vue'
- import FullscreenSpin from '@/components/FullscreenSpin.vue'
- const _isPC = isPC()
- const mainStore = useMainStore()
- const slidesStore = useSlidesStore()
- const snapshotStore = useSnapshotStore()
- const { databaseId } = storeToRefs(mainStore)
- const { slides } = storeToRefs(slidesStore)
- const { screening } = storeToRefs(useScreenStore())
- // 视图模式:'editor', 'student', 'screen'
- // 支持通过URL参数直接访问学生模式
- const getInitialViewMode = () => {
- // 检查URL参数
- const urlParams = new URLSearchParams(window.location.search)
- const modeFromUrl = urlParams.get('mode')
- console.log(modeFromUrl)
- if (modeFromUrl === 'student') {
- return 'student'
- }
- if (modeFromUrl === 'editor2') {
- return 'editor2'
- }
- if (modeFromUrl === 'editor3') {
- return 'editor3'
- }
- // 检查localStorage
- const modeFromStorage = localStorage.getItem('viewMode')
- if (modeFromStorage) {
- return modeFromStorage
- }
- // 默认返回编辑模式
- return 'editor'
- }
- // 获取URL参数中的courseid和type
- const getUrlParams = () => {
- const urlParams = new URLSearchParams(window.location.search)
- return {
- courseid: urlParams.get('courseid'),
- userid: urlParams.get('userid'),
- oid: urlParams.get('oid'),
- org: urlParams.get('org'),
- cid: urlParams.get('cid'),
- type: urlParams.get('type'),
- }
- }
- const urlParams = getUrlParams()
- const viewMode = ref(getInitialViewMode())
- // 全局切换视图模式的函数
- const switchViewMode = (mode: string) => {
- viewMode.value = mode
- localStorage.setItem('viewMode', mode)
- // 更新URL参数
- const url = new URL(window.location.href)
- if (mode === 'student') {
- url.searchParams.set('mode', 'student')
- }
- else {
- url.searchParams.delete('mode')
- }
- // 使用 history.pushState 更新URL,不刷新页面
- window.history.pushState({}, '', url.toString())
- }
- // 使用provide提供切换函数,供子组件调用
- provide('switchViewMode', switchViewMode)
- if (import.meta.env.MODE !== 'development') {
- window.onbeforeunload = () => false
- }
- onMounted(async () => {
- const slides = await api.getFileData('slides')
- console.log(slides)
- slidesStore.setSlides(slides)
- // 初始化快照数据库
- // await deleteDiscardedDB()
- // snapshotStore.initSnapshotDatabase()
- // 监听视图模式切换事件
- window.addEventListener('viewModeChanged', (event: any) => {
- if (event.detail) {
- switchViewMode(event.detail)
- }
- })
- })
- // 应用注销时向 localStorage 中记录下本次 indexedDB 的数据库ID,用于之后清除数据库
- window.addEventListener('beforeunload', () => {
- const discardedDB = localStorage.getItem(LOCALSTORAGE_KEY_DISCARDED_DB)
- const discardedDBList: string[] = discardedDB ? JSON.parse(discardedDB) : []
- discardedDBList.push(databaseId.value)
- const newDiscardedDB = JSON.stringify(discardedDBList)
- localStorage.setItem(LOCALSTORAGE_KEY_DISCARDED_DB, newDiscardedDB)
- })
- </script>
- <style lang="scss">
- #app {
- height: 100%;
- }
- .image-preview {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.85);
- display: flex;
- flex-direction: column;
- z-index: 6000;
- }
- .image-preview__toolbar {
- display: flex;
- gap: 8px;
- padding: 10px;
- justify-content: center;
- z-index: 9999;
- }
- .image-preview__toolbar button {
- padding: 8px 12px;
- border: none;
- background: linear-gradient(180deg, #3a8bff 0%, #2f80ed 100%);
- color: #fff;
- border-radius: 10px;
- cursor: pointer;
- transition: transform 0.15s ease, box-shadow 0.2s ease, background 0.2s ease;
- box-shadow: 0 2px 8px rgba(47, 128, 237, 0.3);
- }
- .image-preview__toolbar button:hover {
- transform: translateY(-1px);
- box-shadow: 0 6px 16px rgba(47, 128, 237, 0.35);
- }
- .image-preview__toolbar button:active {
- transform: translateY(0);
- box-shadow: 0 2px 8px rgba(47, 128, 237, 0.28);
- background: linear-gradient(180deg, #2f80ed 0%, #1b6dde 100%);
- }
- .image-preview__toolbar button:focus-visible {
- outline: 2px solid rgba(47, 128, 237, 0.6);
- outline-offset: 2px;
- }
- .image-preview__stage {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- cursor: grab;
- }
- .image-preview__stage:active {
- cursor: grabbing;
- }
- .image-preview__img {
- max-width: 92vw;
- max-height: 92vh;
- border-radius: 8px;
- box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
- user-select: none;
- will-change: transform;
- }
- // 清空聊天记录确认弹窗样式
- .clear-confirm {
- padding-top: 4px;
- &__title {
- font-size: 16px;
- font-weight: 600;
- margin-bottom: 12px;
- color: #333;
- }
- &__content {
- font-size: 14px;
- color: #666;
- line-height: 1.6;
- margin-bottom: 20px;
- }
- &__footer {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- }
- }
- </style>
|