Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-lab
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-lab
Commits
1e6c31b8
提交
1e6c31b8
authored
8月 28, 2026
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
feat: 增加视频播放URL和类型的获取功能,优化视频组件
上级
466a9d89
隐藏空白字符变更
内嵌
并排
正在显示
8 个修改的文件
包含
49 行增加
和
42 行删除
+49
-42
Preview.vue
src/components/Preview.vue
+1
-1
UploadVideo.vue
src/components/UploadVideo.vue
+4
-4
ViewVideoPreviewDialog.vue
...admin/contest/items/components/ViewVideoPreviewDialog.vue
+4
-9
View.vue
src/modules/admin/lab/video/views/View.vue
+4
-9
VideoItem.vue
src/modules/student/contest/components/VideoItem.vue
+4
-9
VideoItem.vue
src/modules/student/lab/components/VideoItem.vue
+4
-9
onlinePreview.ts
src/utils/onlinePreview.ts
+10
-1
video.ts
src/utils/video.ts
+18
-0
没有找到文件。
src/components/Preview.vue
浏览文件 @
1e6c31b8
...
...
@@ -17,7 +17,7 @@ const fileExtensionName = $computed(() => {
})
const
isOfficeFile
=
$computed
(()
=>
{
return
[
'pptx'
,
'doc'
,
'docx'
,
'xls'
,
'xlsx'
].
includes
(
fileExtensionName
)
return
[
'ppt
'
,
'ppt
x'
,
'doc'
,
'docx'
,
'xls'
,
'xlsx'
].
includes
(
fileExtensionName
)
})
const
isVideoFile
=
$computed
(()
=>
{
...
...
src/components/UploadVideo.vue
浏览文件 @
1e6c31b8
<
script
setup
lang=
"ts"
>
import
{
VideoPlay
}
from
'@element-plus/icons-vue'
import
{
createVideoUploader
,
type
VideoUploadItem
}
from
'@/utils/videoUpload'
import
{
createVideoUploader
,
getUploadedVideoSourceId
,
type
VideoUploadItem
}
from
'@/utils/videoUpload'
interface
Props
{
beforeUpload
?:
(
file
:
any
)
=>
void
...
...
@@ -14,7 +14,7 @@ const emit = defineEmits<{
const
{
files
,
uploading
,
addFile
,
stopUpload
}
=
createVideoUploader
({
onUploadSucceed
(
file
:
VideoUploadItem
)
{
// 保持与现有调用方兼容的接口
emit
(
'success'
,
{
file
:
{
name
:
file
.
name
},
videoId
:
file
.
videoId
})
emit
(
'success'
,
{
file
:
{
name
:
file
.
name
},
videoId
:
getUploadedVideoSourceId
(
file
)
})
},
})
...
...
@@ -56,7 +56,7 @@ function addFileToUploader(file: File) {
}
function
percentage
(
value
:
number
)
{
return
parseFloat
((
value
?
value
*
100
:
0
).
toFixed
(
2
))
return
parseFloat
((
value
||
0
).
toFixed
(
2
))
}
</
script
>
<
template
>
...
...
@@ -65,7 +65,7 @@ function percentage(value: number) {
{{
uploading
?
'上传中...'
:
'本地文件'
}}
<input
accept=
".mp4"
type=
"file"
@
change=
"fileChange"
:disabled=
"uploading"
/>
</div>
<div
class=
"tips"
>
推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传
。
</div>
<div
class=
"tips"
>
支持视频格式:mp4,推荐帧率为25fps、输出码率为4M
。
</div>
<ul
class=
"upload-video-list"
v-for=
"(item, index) in files"
:key=
"index"
>
<li>
...
...
src/modules/admin/contest/items/components/ViewVideoPreviewDialog.vue
浏览文件 @
1e6c31b8
...
...
@@ -2,6 +2,7 @@
import
type
{
ContestVideoItem
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getContestVideoPalyInfo
}
from
'../api'
import
{
getVideoPlayUrl
,
getVideoSourceType
}
from
'@/utils/video'
interface
Props
{
data
:
ContestVideoItem
}
...
...
@@ -13,15 +14,9 @@ function fetchInfo() {
})
}
const
playUrl
=
$computed
(()
=>
{
const
definitionUrl
=
playList
.
reduce
(
(
result
:
any
,
item
:
any
)
=>
{
result
[
item
.
Definition
]
=
item
.
PlayURL
return
result
},
{
SD
:
''
,
LD
:
''
,
FD
:
''
}
)
return
definitionUrl
.
SD
||
definitionUrl
.
LD
||
definitionUrl
.
FD
return
getVideoPlayUrl
(
playList
)
})
const
playType
=
$computed
(()
=>
getVideoSourceType
(
playUrl
))
onMounted
(()
=>
{
fetchInfo
()
})
...
...
@@ -30,7 +25,7 @@ onMounted(() => {
<
template
>
<el-dialog
title=
"查阅操作视频"
>
<AppVideoPlayer
:options=
"
{ fluid: true, sources: [{ src: playUrl, type:
'application/x-mpegURL'
}] }"
:options=
"
{ fluid: true, sources: [{ src: playUrl, type:
playType
}] }"
v-if="playUrl">
</AppVideoPlayer>
</el-dialog>
</
template
>
src/modules/admin/lab/video/views/View.vue
浏览文件 @
1e6c31b8
...
...
@@ -2,6 +2,7 @@
import
type
{
VideoItem
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getVideo
}
from
'../api'
import
{
getVideoPlayUrl
,
getVideoSourceType
}
from
'@/utils/video'
interface
Props
{
id
:
string
...
...
@@ -19,15 +20,9 @@ function fetchInfo() {
})
}
const
playUrl
=
$computed
(()
=>
{
const
definitionUrl
=
playList
.
reduce
(
(
result
:
any
,
item
:
any
)
=>
{
result
[
item
.
Definition
]
=
item
.
PlayURL
return
result
},
{
SD
:
''
,
LD
:
''
,
FD
:
''
}
)
return
definitionUrl
.
SD
||
definitionUrl
.
LD
||
definitionUrl
.
FD
return
getVideoPlayUrl
(
playList
)
})
const
playType
=
$computed
(()
=>
getVideoSourceType
(
playUrl
))
onMounted
(()
=>
{
fetchInfo
()
})
...
...
@@ -48,7 +43,7 @@ onMounted(() => {
</el-descriptions>
<div
style=
"height: 600px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type:
'application/x-mpegURL'
}] }"
:options=
"
{ sources: [{ src: playUrl, type:
playType
}] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</
template
>
...
...
src/modules/student/contest/components/VideoItem.vue
浏览文件 @
1e6c31b8
...
...
@@ -2,6 +2,7 @@
import
type
{
ExperimentVideoType
,
PlayInfo
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getExperimentVideoPlayInfo
}
from
'../api'
import
{
getVideoPlayUrl
,
getVideoSourceType
}
from
'@/utils/video'
// import { useLog } from '@/composables/useLog'
// const log = useLog()
...
...
@@ -28,15 +29,9 @@ function fetchInfo() {
})
}
const
playUrl
=
$computed
(()
=>
{
const
definitionUrl
=
playList
.
reduce
(
(
result
:
any
,
item
:
any
)
=>
{
result
[
item
.
Definition
]
=
item
.
PlayURL
return
result
},
{
SD
:
''
,
LD
:
''
,
FD
:
''
}
)
return
definitionUrl
.
SD
||
definitionUrl
.
LD
||
definitionUrl
.
FD
return
getVideoPlayUrl
(
playList
)
})
const
playType
=
$computed
(()
=>
getVideoSourceType
(
playUrl
))
onMounted
(()
=>
{
fetchInfo
()
})
...
...
@@ -47,7 +42,7 @@ onMounted(() => {
<!--
<img
:src=
"data.cover"
/>
-->
<div
style=
"height: 200px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type:
'application/x-mpegURL'
}] }"
:options=
"
{ sources: [{ src: playUrl, type:
playType
}] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</div>
...
...
src/modules/student/lab/components/VideoItem.vue
浏览文件 @
1e6c31b8
...
...
@@ -2,6 +2,7 @@
import
type
{
ExperimentVideoType
,
PlayInfo
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getExperimentVideoPlayInfo
}
from
'../api'
import
{
getVideoPlayUrl
,
getVideoSourceType
}
from
'@/utils/video'
import
{
useLog
}
from
'@/composables/useLog'
const
log
=
useLog
()
...
...
@@ -29,15 +30,9 @@ function fetchInfo() {
})
}
const
playUrl
=
$computed
(()
=>
{
const
definitionUrl
=
playList
.
reduce
(
(
result
:
any
,
item
:
any
)
=>
{
result
[
item
.
Definition
]
=
item
.
PlayURL
return
result
},
{
SD
:
''
,
LD
:
''
,
FD
:
''
}
)
return
definitionUrl
.
SD
||
definitionUrl
.
LD
||
definitionUrl
.
FD
return
getVideoPlayUrl
(
playList
)
})
const
playType
=
$computed
(()
=>
getVideoSourceType
(
playUrl
))
onMounted
(()
=>
{
fetchInfo
()
})
...
...
@@ -48,7 +43,7 @@ onMounted(() => {
<!--
<img
:src=
"data.cover"
/>
-->
<div
style=
"height: 200px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type:
'application/x-mpegURL'
}] }"
:options=
"
{ sources: [{ src: playUrl, type:
playType
}] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</div>
...
...
src/utils/onlinePreview.ts
浏览文件 @
1e6c31b8
...
...
@@ -5,9 +5,18 @@
*/
import
{
urls
}
from
'./env'
function
toBase64
(
value
:
string
)
{
const
bytes
=
new
TextEncoder
().
encode
(
value
)
let
binary
=
''
bytes
.
forEach
(
byte
=>
{
binary
+=
String
.
fromCharCode
(
byte
)
})
return
btoa
(
binary
)
}
export
function
getOnlinePreviewUrl
(
url
:
string
):
string
{
if
(
import
.
meta
.
env
.
MODE
===
'school'
)
{
return
`
${
urls
.
filePreview
}
/onlinePreview?url=
${
encodeURIComponent
(
btoa
(
url
))}
`
return
`
${
urls
.
filePreview
}
/onlinePreview?url=
${
encodeURIComponent
(
toBase64
(
encodeURI
(
url
)
))}
`
}
return
`https://view.officeapps.live.com/op/view.aspx?src=
${
url
}
`
}
src/utils/video.ts
0 → 100644
浏览文件 @
1e6c31b8
export
function
getVideoPlayUrl
(
playInfo
?:
any
)
{
if
(
typeof
playInfo
===
'string'
)
return
playInfo
if
(
!
Array
.
isArray
(
playInfo
))
return
''
const
definitionUrl
=
playInfo
.
reduce
(
(
result
:
Record
<
string
,
string
>
,
item
:
any
)
=>
{
if
(
item
?.
Definition
&&
item
?.
PlayURL
)
result
[
item
.
Definition
]
=
item
.
PlayURL
return
result
},
{
SD
:
''
,
LD
:
''
,
FD
:
''
},
)
return
definitionUrl
.
SD
||
definitionUrl
.
LD
||
definitionUrl
.
FD
||
playInfo
[
0
]?.
PlayURL
||
''
}
export
function
getVideoSourceType
(
url
:
string
)
{
return
/
\.
m3u8
(?:[
?#
]
|$
)
/i
.
test
(
url
)
?
'application/x-mpegURL'
:
'video/mp4'
}
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论