11<template >
2- <n-flex class =" page-root " >
2+ <n-flex class =" !gap-0 !flex-nowrap " >
33 <Side />
4- <n-layout class =" page-main" >
5- <n-layout-header class =" page-header" >
4+
5+ <n-layout class =" flex flex-col" >
6+ <n-layout-header class =" flex items-center w-full h-14" >
67 <Header @back =" handleBack" />
78 </n-layout-header >
8- <n-divider />
9- <n-layout-content
10- class =" page-content"
11- content-style =" overflow: hidden; height: calc(100vh - 56px);"
9+
10+ <n-divider class =" !m-0" />
11+
12+ <n-layout
13+ has-sider
14+ :content-style =" { gap: '24px', height: 'calc(100vh - 4rem)' }"
15+ sider-placement =" right"
16+ class =" w-full"
1217 >
13- <n-grid :cols =" 12" class =" page-grid" >
14- <n-gi :span =" 8" >
15- <div class =" pane-left" >
16- <Upload v-if =" !showPlayer" @parser =" handleParser" class =" pane-fill" />
17-
18- <router-view v-else :key =" route.fullPath" class =" pane-fill" />
19- </div >
20- </n-gi >
21- <n-gi :span =" 4" >
22- <div class =" pane-right" >
23- <SliceList
24- @play =" handlePlay"
25- :json-file =" currentPartJsonFile"
26- @show-upload =" handleShowUpload"
27- class =" pane-fill"
28- />
29- </div >
30- </n-gi >
31- </n-grid >
32- </n-layout-content >
18+ <n-layout-content
19+ :content-style =" {
20+ display: 'flex',
21+ padding: '0 12px 0 20px'
22+ }"
23+ >
24+ <Upload v-if =" !showPlayer" @parser =" handleParser" />
25+
26+ <div v-else ref =" playerWrapRef" class =" w-full h-full" >
27+ <router-view :key =" route.fullPath" />
28+ </div >
29+ </n-layout-content >
30+
31+ <n-layout-sider
32+ bordered
33+ :collapsed-width =" 0"
34+ :width =" 480"
35+ :native-scrollbar =" false"
36+ show-trigger =" bar"
37+ collapse-mode =" width"
38+ v-model:collapsed =" siderCollapsed"
39+ content-style =" padding: 24px;"
40+ >
41+ <SliceList
42+ @play =" handlePlay"
43+ :json-file =" currentPartJsonFile"
44+ @show-upload =" handleShowUpload"
45+ />
46+ </n-layout-sider >
47+ </n-layout >
3348 </n-layout >
3449 </n-flex >
3550</template >
@@ -40,24 +55,28 @@ import Header from '@/layouts/Header/index.vue';
4055import Upload from ' ./Content/Upload/index.vue' ;
4156import SliceList from ' ./Content/SliceList/index.vue' ;
4257
43- import { ref } from ' vue' ;
44- import { useRouter , useRoute } from ' vue-router' ;
4558import { useMessage } from ' naive-ui' ;
46- import { useResolveFile } from ' @/hooks/useResolveFile.ts' ;
4759import type { UploadFileInfo } from ' naive-ui' ;
60+ import { useRouter , useRoute } from ' vue-router' ;
61+ import { useResolveFile } from ' @/hooks/useResolveFile.ts' ;
62+ import { useResizeObserver , useDebounceFn } from ' @vueuse/core' ;
63+ import { ref , watch , onMounted , onBeforeUnmount , nextTick } from ' vue' ;
4864
4965const route = useRoute ();
5066const router = useRouter ();
5167const message = useMessage ();
5268const { fileParser } = useResolveFile ();
5369
70+ const siderCollapsed = ref (false );
71+ const playerWrapRef = ref <HTMLElement | null >(null );
72+
5473const jsonFile = ref <string >(' ' );
5574const videoUrl = ref <string >(' ' );
5675const showPlayer = ref <boolean >(false );
5776const currentPartJsonFile = ref ({});
5877
5978/**
60- * 处理返回时间
79+ * 返回:关闭播放器回到上传
6180 */
6281const handleBack = () => {
6382 showPlayer .value = false ;
@@ -87,61 +106,36 @@ const handleParser = async (options: {
87106
88107/**
89108 * 点击 Item 开始播放
90- *
91- * @param videoUrl
92- * @param type
93- * @param jsonFile
94109 */
95110const handlePlay = (videoUrl : string , type : string , jsonFile : object ) => {
96111 switch (type ) {
97112 case ' mp4' : {
98113 showPlayer .value = true ;
99114 currentPartJsonFile .value = jsonFile ;
100-
101- router .push ({
102- name: ' mp4Player' ,
103- params: { videoUrl }
104- });
105-
115+ router .push ({ name: ' mp4Player' , params: { videoUrl } });
106116 break ;
107117 }
108118 case ' cast' : {
109119 showPlayer .value = true ;
110120 currentPartJsonFile .value = jsonFile ;
111- router .push ({
112- name: ' asciicastPlayer' ,
113- params: { castUrl: videoUrl }
114- });
115-
121+ router .push ({ name: ' asciicastPlayer' , params: { castUrl: videoUrl } });
116122 break ;
117123 }
118124 case ' gua' : {
119125 showPlayer .value = true ;
120- router .push ({
121- name: ' guaPlayer' ,
122- params: { guaUrl: videoUrl }
123- });
124-
126+ router .push ({ name: ' guaPlayer' , params: { guaUrl: videoUrl } });
125127 break ;
126128 }
127129 case ' part' : {
128130 showPlayer .value = true ;
129-
130131 currentPartJsonFile .value = jsonFile ;
131-
132132 setTimeout (() => {
133- router .push ({
134- name: ' guaPlayer' ,
135- params: { guaUrl: videoUrl }
136- });
133+ router .push ({ name: ' guaPlayer' , params: { guaUrl: videoUrl } });
137134 });
138-
139135 break ;
140136 }
141-
142137 default : {
143138 showPlayer .value = false ;
144-
145139 break ;
146140 }
147141 }
@@ -156,65 +150,38 @@ const handleShowUpload = () => {
156150 currentPartJsonFile .value = {};
157151 }, 100 );
158152};
159- </script >
160153
161- <style scoped lang="scss">
162- .page-root {
163- gap : unset !important ;
164- width : 100vw ;
165- height : 100vh ;
166- overflow : hidden ;
167- }
168-
169- .page-main {
170- width : calc (100vw - 65px );
171- height : 100vh ;
172- display : flex ;
173- flex-direction : column ;
174- min-width : 0 ;
175- }
176-
177- .page-header {
178- display : flex ;
179- align-items : center ;
180- width : 100% ;
181- height : 55px ;
182- }
183-
184- :deep(.n-divider ) {
185- margin : 0 !important ;
186- }
187-
188- .page-content {
189- flex : 1 1 auto ;
190- min-height : 0 ;
191- width : 100% ;
192- }
193-
194- .page-grid {
195- width : 100% ;
196- height : 100% ;
197- }
198-
199- .pane-left ,
200- .pane-right {
201- height : 100% ;
202- width : 100% ;
203- display : flex ;
204- }
205-
206- .pane-left {
207- align-items : stretch ;
208- justify-content : stretch ;
209- }
210-
211- .pane-right {
212- align-items : stretch ;
213- justify-content : stretch ;
214- }
215-
216- .pane-fill {
217- width : 100% ;
218- height : 100% ;
219- }
220- </style >
154+ const triggerRecomputeScale = () => {
155+ window .dispatchEvent (new CustomEvent (' recompute-scale' , { detail: Date .now () }));
156+ };
157+
158+ const debouncedTrigger = useDebounceFn (triggerRecomputeScale , 80 );
159+
160+ useResizeObserver (playerWrapRef as any , () => {
161+ debouncedTrigger ();
162+ });
163+
164+ watch (siderCollapsed , async () => {
165+ await nextTick ();
166+
167+ // 立即触发一次
168+ triggerRecomputeScale ();
169+
170+ // 在动画期间多次触发,确保各个阶段都能自适应
171+ const delays = [50 , 150 , 250 , 400 , 600 ];
172+ delays .forEach (delay => {
173+ setTimeout (triggerRecomputeScale , delay );
174+ });
175+ });
176+
177+ onMounted (() => {
178+ window .addEventListener (' resize' , debouncedTrigger );
179+ if (showPlayer .value ) {
180+ setTimeout (triggerRecomputeScale , 200 );
181+ }
182+ });
183+
184+ onBeforeUnmount (() => {
185+ window .removeEventListener (' resize' , debouncedTrigger );
186+ });
187+ </script >
0 commit comments