Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
C
corporateCulture-qd
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
王立鹏
corporateCulture-qd
Commits
36f6d06a
Commit
36f6d06a
authored
Jan 28, 2026
by
lijiabin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【需求 20331】 perf: 优化上传视频相关的内容
parent
44b3ef9d
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
106 additions
and
43 deletions
+106
-43
index.vue
src/components/common/UploadVideo/index.vue
+73
-41
index.ts
src/utils/app/index.ts
+31
-0
index.vue
src/views/publishVideo/index.vue
+2
-2
No files found.
src/components/common/UploadVideo/index.vue
View file @
36f6d06a
...
...
@@ -36,24 +36,26 @@
</div>
<!-- 上传完成 -->
<div
v-if=
"videoInfo && !uploading"
class=
"upload-success"
>
<div
class=
"video-preview"
>
<video
:src=
"videoInfo.url"
poster=
"@/assets/img/culture/ask.png"
class=
"video-thumbnail"
muted
></video>
<div
class=
"video-overlay"
>
<el-button
type=
"primary"
size=
"small"
@
click=
"replaceVideo"
>
重新选择
</el-button>
<div
v-if=
"videoInfo && !uploading"
class=
"upload-success-optimized"
>
<div
class=
"video-info-display"
>
<div
class=
"video-icon-wrapper"
>
<el-icon
class=
"video-preview-icon"
><IEpVideoCamera
/></el-icon>
</div>
<div
class=
"video-details-text"
>
<p
class=
"video-name"
>
{{
videoInfo
.
name
}}
</p>
<p
class=
"video-meta"
>
{{
formatFileSize
(
videoInfo
.
size
)
}}
·
{{
videoInfo
.
duration
}}
·
{{
videoInfo
.
resolution
}}
</p>
</div>
</div>
<div
class=
"video-details"
>
<p
class=
"video-name"
>
{{
videoInfo
.
name
}}
</p>
<p
class=
"video-meta"
>
{{
formatFileSize
(
videoInfo
.
size
)
}}
·
{{
videoInfo
.
duration
}}
·
{{
videoInfo
.
resolution
}}
</p>
<div
class=
"video-actions"
>
<el-button
type=
"primary"
@
click=
"replaceVideo"
>
<el-icon
class=
"mr-2"
><IEpRefresh
/></el-icon>
重新选择
</el-button>
<el-button
type=
"success"
@
click=
"previewVideo"
>
<el-icon
class=
"mr-2"
><IEpView
/></el-icon>
预览播放
</el-button>
</div>
</div>
</el-upload>
...
...
@@ -71,6 +73,7 @@
import
{
uploadFile
as
uploadFileApi
}
from
'@/api/common'
import
type
{
UploadFile
}
from
'element-plus'
import
type
{
UploadVideoProps
}
from
'./types'
import
{
getVideoMetadata
}
from
'@/utils'
interface
VideoInfo
{
url
:
string
...
...
@@ -108,30 +111,6 @@ const formatFileSize = (bytes: number): string => {
return
parseFloat
((
bytes
/
Math
.
pow
(
k
,
i
)).
toFixed
(
2
))
+
' '
+
sizes
[
i
]
}
// 获取视频时长和分辨率
const
getVideoMetadata
=
(
file
:
File
):
Promise
<
{
duration
:
string
;
resolution
:
string
}
>
=>
{
return
new
Promise
((
resolve
)
=>
{
const
video
=
document
.
createElement
(
'video'
)
video
.
preload
=
'metadata'
video
.
onloadedmetadata
=
()
=>
{
const
duration
=
formatDuration
(
video
.
duration
)
const
resolution
=
`
${
video
.
videoWidth
}
x
${
video
.
videoHeight
}
`
URL
.
revokeObjectURL
(
video
.
src
)
resolve
({
duration
,
resolution
})
}
video
.
src
=
URL
.
createObjectURL
(
file
)
})
}
// 格式化时长
const
formatDuration
=
(
seconds
:
number
):
string
=>
{
const
mins
=
Math
.
floor
(
seconds
/
60
)
const
secs
=
Math
.
floor
(
seconds
%
60
)
return
`
${
mins
}
:
${
secs
.
toString
().
padStart
(
2
,
'0'
)}
`
}
// 上传前验证
const
beforeUpload
=
(
file
:
File
):
boolean
=>
{
uploadError
.
value
=
''
...
...
@@ -185,7 +164,7 @@ const startUpload = async () => {
console
.
log
(
data
)
// 获取视频元数据
const
metadata
=
await
getVideoMetadata
(
currentFile
.
value
)
const
metadata
=
await
getVideoMetadata
(
data
.
filePath
)
// 根据你的 API 返回结构调整
const
videoData
:
VideoInfo
=
{
...
...
@@ -235,6 +214,11 @@ const replaceVideo = () => {
currentFile
.
value
=
null
}
// 预览播放
const
previewVideo
=
()
=>
{
window
.
open
(
videoInfo
.
value
?.
url
,
'_blank'
)
}
// 重试上传
const
retryUpload
=
()
=>
{
uploadError
.
value
=
''
...
...
@@ -472,4 +456,52 @@ defineExpose({
height
:
90px
;
}
}
.upload-success-optimized
{
display
:
flex
;
flex-direction
:
column
;
/* Stack video info and actions vertically */
align-items
:
center
;
/* Center content horizontally */
justify-content
:
center
;
padding
:
20px
40px
;
/* Add some internal padding */
gap
:
15px
;
/* Space between video info and actions */
}
.video-info-display
{
display
:
flex
;
align-items
:
center
;
/* Vertically align icon and text */
gap
:
15px
;
/* Space between icon and text */
}
.video-icon-wrapper
{
font-size
:
48px
;
/* Larger icon size */
color
:
#6366f1
;
}
.video-details-text
{
text-align
:
left
;
/* Align text within its container */
}
.video-name
{
font-size
:
16px
;
font-weight
:
bold
;
color
:
#303133
;
/* Darker color for prominence */
margin-bottom
:
5px
;
word-break
:
break-all
;
/* Ensure long file names wrap */
}
.video-meta
{
font-size
:
13px
;
color
:
#909399
;
/* Lighter color for secondary info */
}
.video-actions
{
display
:
flex
;
gap
:
10px
;
/* Space between buttons */
margin-top
:
10px
;
/* Space above buttons if stacked below video info */
}
.upload-success-optimized
{
flex-direction
:
row
;
justify-content
:
space-between
;
}
</
style
>
src/utils/app/index.ts
View file @
36f6d06a
...
...
@@ -85,3 +85,34 @@ export function jumpToArticleDetailPage({ type, id }: { type: ArticleTypeEnum; i
window
.
open
(
`/articleDetail/
${
id
}
`
)
}
}
// 根据oss视频链接获取视频元信息
export
function
getVideoMetadata
(
url
:
string
):
Promise
<
{
duration
:
string
resolution
:
string
}
>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
video
=
document
.
createElement
(
'video'
)
video
.
src
=
url
video
.
preload
=
'metadata'
video
.
addEventListener
(
'loadedmetadata'
,
()
=>
{
const
duration
=
formatDuration
(
video
.
duration
)
const
resolution
=
`
${
video
.
videoWidth
}
x
${
video
.
videoHeight
}
`
resolve
({
duration
,
resolution
,
})
})
video
.
addEventListener
(
'error'
,
()
=>
{
reject
(
new
Error
(
'视频加载失败'
))
})
})
}
// 格式化视频时长
export
function
formatDuration
(
seconds
:
number
):
string
{
const
mins
=
Math
.
floor
(
seconds
/
60
)
const
secs
=
Math
.
floor
(
seconds
%
60
)
return
`
${
mins
}
:
${
secs
.
toString
().
padStart
(
2
,
'0'
)}
`
}
src/views/publishVideo/index.vue
View file @
36f6d06a
...
...
@@ -105,7 +105,7 @@
主标签
<el-tooltip
content=
"主标签最多添加一个"
placement=
"top"
>
<el-icon
class=
"text-gray-400 cursor-help"
>
<QuestionFilled
/>
<
IEp
QuestionFilled
/>
</el-icon>
</el-tooltip>
</label>
...
...
@@ -119,7 +119,7 @@
副标签
<el-tooltip
content=
"副标签最多添加3个"
placement=
"top"
>
<el-icon
class=
"text-gray-400 cursor-help"
>
<QuestionFilled
/>
<
IEp
QuestionFilled
/>
</el-icon>
</el-tooltip>
</label>
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment