Skip to content
项目
群组
代码片段
帮助
当前项目
正在载入...
登录 / 注册
切换导航面板
S
saas-lab
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
图表
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
日程
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
图像
聊天
创建新问题
作业
提交
问题看板
Open sidebar
EzijingWeb
saas-lab
Commits
6e912336
提交
6e912336
authored
4月 22, 2024
作者:
王鹏飞
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'ip' into gdrtvu
上级
b70d15e0
72a3bca0
隐藏空白字符变更
内嵌
并排
正在显示
23 个修改的文件
包含
277 行增加
和
232 行删除
+277
-232
.env
.env
+11
-9
.env.dev
.env.dev
+6
-6
.env.test
.env.test
+5
-5
package.json
package.json
+1
-1
base.ts
src/api/base.ts
+23
-0
UploadVideo.vue
src/components/UploadVideo.vue
+13
-118
AppEditor.vue
src/components/base/AppEditor.vue
+8
-16
AppUpload.vue
src/components/base/AppUpload.vue
+10
-17
useUpload.ts
src/composables/useUpload.ts
+118
-0
ViewVideoFormDialog.vue
...es/admin/contest/items/components/ViewVideoFormDialog.vue
+4
-2
ViewVideoPreviewDialog.vue
...admin/contest/items/components/ViewVideoPreviewDialog.vue
+14
-12
types.ts
src/modules/admin/contest/items/types.ts
+1
-0
FormDialog.vue
src/modules/admin/lab/book/components/FormDialog.vue
+1
-1
FormDialog.vue
src/modules/admin/lab/case/components/FormDialog.vue
+1
-1
Index.vue
src/modules/admin/lab/score/views/Index.vue
+1
-1
FormDialog.vue
src/modules/admin/lab/video/components/FormDialog.vue
+4
-2
types.ts
src/modules/admin/lab/video/types.ts
+1
-0
View.vue
src/modules/admin/lab/video/views/View.vue
+14
-12
VideoItem.vue
src/modules/student/contest/components/VideoItem.vue
+15
-13
VideoItem.vue
src/modules/student/lab/components/VideoItem.vue
+15
-13
Index.vue
src/modules/student/lab/views/Index.vue
+1
-1
axios.ts
src/utils/axios.ts
+8
-0
vite.config.ts
vite.config.ts
+2
-2
没有找到文件。
.env
浏览文件 @
6e912336
VITE_LOGIN_URL=https://login.segedu.cn/auth/login/index
VITE_LAB_URL=https://bi.segedu.cn/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.segedu.cn
VITE_DML_PRO_URL=https://saas-dml-pro.segedu.cn
VITE_SAAS_LEARN_URL=https://saas-learn.segedu.cn
VITE_QA_CENTER_URL=https://qa-center.segedu.cn
VITE_X_LEARNING_URL=https://x-learning.segedu.cn
VITE_EXAM_SHOW_URL=https://exam-show.segedu.cn
VITE_SWSJFXS_LOGIN_URL=https://login.segedu.cn/swsjfxs/login/index
VITE_LOGIN_URL=http://172.16.3.201:1001/auth/login/index
VITE_LAB_URL=http://172.16.3.201:1008/bi/viewer?proc=0&action=index
VITE_DML_URL=http://172.16.3.201:1006
VITE_DML_PRO_URL=http://172.16.3.201:1007
VITE_SAAS_LEARN_URL=http://172.16.3.201:1009
VITE_QA_CENTER_URL=http://172.16.3.201:1004
VITE_X_LEARNING_URL=https://x-learning.zijing.chat
VITE_EXAM_SHOW_URL=https://exam-show.zijing.chat
VITE_SWSJFXS_LOGIN_URL=http://172.16.3.201:1001/swsjfxs/login/index
VITE_SYS_FLAG=chat
VITE_STATIC_URL=https://saas-lab-api
.env.dev
浏览文件 @
6e912336
VITE_LOGIN_URL=https://login.ezijing.com/auth/login/index
# VITE_LAB_URL=https://digitalmarketinglab.ezijing.com
VITE_LAB_URL=https://bi.ezijing.com/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.ezijing.com
VITE_DML_PRO_URL=https://saas-dml-pro.ezijing.com
\ No newline at end of file
VITE_LOGIN_URL=https://login.zijing.chat/auth/login/index
# VITE_LAB_URL=https://digitalmarketinglab.zijing.chat
VITE_LAB_URL=https://bi.zijing.chat/bi/viewer?proc=0&action=index
VITE_DML_URL=https://saas-dml.zijing.chat
VITE_DML_PRO_URL=https://saas-dml-pro.zijing.chat
\ No newline at end of file
.env.test
浏览文件 @
6e912336
VITE_LOGIN_URL
=
https
://
login
.
ezijing
.
com
/
auth
/
login
/
index
VITE_LAB_URL
=
https
://
bi
.
ezijing
.
com
/
bi
/
viewer
?
proc
=
0
&
action
=
index
VITE_DML_URL
=
https
://
saas
-
dml
.
ezijing
.
com
VITE_DML_PRO_URL
=
https
://
saas
-
dml
-
pro
.
ezijing
.
com
\ No newline at end of file
VITE_LOGIN_URL
=
https
://
login
.
zijing
.
chat
/
auth
/
login
/
index
VITE_LAB_URL
=
https
://
bi
.
zijing
.
chat
/
bi
/
viewer
?
proc
=
0
&
action
=
index
VITE_DML_URL
=
https
://
saas
-
dml
.
zijing
.
chat
VITE_DML_PRO_URL
=
https
://
saas
-
dml
-
pro
.
zijing
.
chat
\ No newline at end of file
package.json
浏览文件 @
6e912336
...
...
@@ -4,7 +4,7 @@
"type"
:
"module"
,
"scripts"
:
{
"dev"
:
"vite --mode dev"
,
"build"
:
"vue-tsc --noEmit && vite build --mode prod
&& npm run deploy
"
,
"build"
:
"vue-tsc --noEmit && vite build --mode prod"
,
"build:test"
:
"vue-tsc --noEmit && vite build --mode test"
,
"build:pre"
:
"vue-tsc --noEmit && vite build --mode pre"
,
"preview"
:
"vite preview --port 4173"
,
...
...
src/api/base.ts
浏览文件 @
6e912336
...
...
@@ -74,3 +74,26 @@ export function readMessages(params: { message_id: string }) {
params
})
}
// 获取分片大小和唯一文件名
export
function
getLocalFileChunk
(
params
:
{
file_size
:
number
;
file_name
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/common/file/chunk'
,
{
params
})
}
// 上传每个分片前请求接口来获取当前文件是否超时,之前的分片是否被清理,如果被请求则拒绝处理。返回客户端错误码,让客户端户端不再续传剩余分片
// 1文件被清理 0文件未被清理
export
function
checkLocalFile
(
params
:
{
file_name
:
string
})
{
return
httpRequest
.
get
(
'/api/lab/v1/common/file/check'
,
{
params
})
}
// 上传文件
export
function
uploadLocalFile
(
data
:
{
file
:
File
|
Blob
;
file_name
:
string
;
is_continuingly
?:
number
;
now_package_num
:
number
;
total_package_num
:
number
},
options
=
{}
)
{
return
httpRequest
.
post
(
'/api/lab/v1/common/file/upload'
,
data
,
Object
.
assign
({
withCredentials
:
false
,
headers
:
{
'Content-Type'
:
'multipart/form-data'
}
},
options
)
)
}
src/components/UploadVideo.vue
浏览文件 @
6e912336
<
script
setup
lang=
"ts"
>
import
OSS
from
'ali-oss'
import
'@/lib/aliyun-upload-sdk/aliyun-upload-sdk-1.5.3.min.js'
import
{
VideoPlay
}
from
'@element-plus/icons-vue'
import
{
getUploadVideoAuth
,
updateUploadVideoAuth
}
from
'@/api/base'
;(
window
as
any
).
OSS
=
OSS
// uploadInfo 包含要上传的文件信息
interface
UploadInfo
{
bucket
:
string
checkpoint
:
{
file
:
File
;
name
:
string
;
fileSize
:
number
;
partSize
:
number
;
uploadId
:
string
}
endpoint
:
string
file
:
File
fileHash
:
string
isImage
:
boolean
loaded
:
number
object
:
string
region
:
string
retry
:
boolean
ri
:
string
state
:
string
userData
:
string
videoId
:
string
videoInfo
:
any
progress
:
number
}
import
{
useUpload
}
from
'@/composables/useUpload'
interface
Props
{
beforeUpload
?:
(
file
:
any
)
=>
void
}
...
...
@@ -31,12 +10,16 @@ const props = defineProps<Props>()
const
emit
=
defineEmits
([
'success'
])
const
uploader
=
createUploader
()
let
fileList
=
$ref
<
UploadInfo
[]
>
([])
const
{
files
,
addFile
,
stopUpload
}
=
useUpload
({
onUploadSucceed
(
file
)
{
emit
(
'success'
,
file
)
}
})
onUnmounted
(()
=>
{
uploader
&&
uploader
.
stopUpload
()
stopUpload
()
})
const
fileChange
=
async
(
event
:
Event
)
=>
{
const
element
=
event
.
currentTarget
as
HTMLInputElement
const
files
:
FileList
|
null
=
element
.
files
...
...
@@ -44,104 +27,16 @@ const fileChange = async (event: Event) => {
if
(
props
.
beforeUpload
)
{
try
{
await
props
.
beforeUpload
(
files
)
uploader
.
cleanList
()
}
catch
(
error
)
{
console
.
log
(
error
)
return
false
}
}
for
(
const
file
of
files
)
{
// 是否重复上传
const
hasRepeat
=
!!
fileList
.
find
(
item
=>
item
.
file
.
name
===
file
.
name
&&
item
.
file
.
size
===
file
.
size
&&
item
.
file
.
lastModified
===
file
.
lastModified
)
!
hasRepeat
&&
uploader
.
addFile
(
file
,
null
,
null
,
null
,
'{"Vod":{}}'
)
addFile
(
file
)
}
uploader
.
startUpload
()
fileList
=
uploader
.
_uploadList
}
function
updateFileList
(
uploadInfo
:
UploadInfo
)
{
if
(
!
uploadInfo
)
return
fileList
=
fileList
.
map
(
item
=>
{
if
(
item
.
ri
===
uploadInfo
.
ri
)
{
return
{
...
item
,
...
uploadInfo
}
}
return
item
})
}
function
createUploader
()
{
return
new
(
window
as
any
).
AliyunUpload
.
Vod
({
//userID,必填,您可以使用阿里云账号访问账号中心(https://account.console.aliyun.com/),即可查看账号ID
userId
:
'1303984639806000'
,
//上传到视频点播的地域,默认值为'cn-shanghai',
//eu-central-1,ap-southeast-1
region
:
'cn-shanghai'
,
//分片大小默认1 MB,不能小于100 KB(100*1024)
partSize
:
1048576
,
//并行上传分片个数,默认5
parallel
:
5
,
//网络原因失败时,重新上传次数,默认为3
retryCount
:
3
,
//网络原因失败时,重新上传间隔时间,默认为2秒
retryDuration
:
2
,
//开始上传
onUploadstarted
:
onUploadStarted
,
//文件上传成功
onUploadSucceed
:
onUploadSucceed
,
//文件上传失败
onUploadFailed
:
onUploadFailed
,
//文件上传进度,单位:字节
onUploadProgress
:
onUploadProgress
,
//上传凭证或STS token超时
onUploadTokenExpired
:
onUploadTokenExpired
,
//全部文件上传结束
onUploadEnd
:
onUploadEnd
})
}
// 开始上传
function
onUploadStarted
(
uploadInfo
:
UploadInfo
)
{
console
.
log
(
'onUploadStarted'
,
uploadInfo
)
getUploadVideoAuth
({
title
:
uploadInfo
.
file
.
name
,
file_name
:
uploadInfo
.
file
.
name
}).
then
(
res
=>
{
uploader
.
setUploadAuthAndAddress
(
uploadInfo
,
res
.
data
.
upload_auth
,
res
.
data
.
upload_address
,
res
.
data
.
source_id
)
})
updateFileList
(
uploadInfo
)
}
// 文件上传成功
function
onUploadSucceed
(
uploadInfo
:
UploadInfo
)
{
console
.
log
(
'onUploadSucceed'
,
uploadInfo
)
updateFileList
(
uploadInfo
)
emit
(
'success'
,
uploadInfo
)
}
//文件上传失败
function
onUploadFailed
(
uploadInfo
:
UploadInfo
,
code
:
number
,
message
:
string
)
{
console
.
log
(
'onUploadFailed'
,
uploadInfo
,
code
,
message
)
updateFileList
(
uploadInfo
)
}
//文件上传进度,单位:字节
function
onUploadProgress
(
uploadInfo
:
UploadInfo
,
totalSize
:
number
,
loadedPercent
:
number
)
{
console
.
log
(
'onUploadProgress'
,
uploadInfo
.
file
.
name
,
uploadInfo
,
totalSize
,
loadedPercent
)
updateFileList
(
uploadInfo
)
}
//上传凭证或STS token超时
function
onUploadTokenExpired
(
uploadInfo
:
UploadInfo
)
{
console
.
log
(
'onUploadTokenExpired'
,
uploadInfo
)
updateUploadVideoAuth
({
source_id
:
uploadInfo
.
videoId
}).
then
(
res
=>
{
uploader
.
resumeUploadWithAuth
(
res
.
data
.
UploadAuth
)
})
updateFileList
(
uploadInfo
)
}
// 全部文件上传结束
function
onUploadEnd
(
uploadInfo
:
UploadInfo
)
{
console
.
log
(
'onUploadEnd'
,
uploadInfo
)
updateFileList
(
uploadInfo
)
}
// 进度条
function
percentage
(
value
:
number
)
{
return
parseFloat
((
value
?
value
*
100
:
0
).
toFixed
(
2
))
}
defineExpose
({
uploader
,
fileList
:
$$
(
fileList
)
})
defineExpose
({
files
})
</
script
>
<
template
>
<div
class=
"upload-video"
>
...
...
@@ -151,13 +46,13 @@ defineExpose({ uploader, fileList: $$(fileList) })
</div>
<div
class=
"tips"
>
推荐视频格式:帧率为25fps\输出码率为4M\输出格式为mp4,建议采用格式工厂等工具处理后上传。
</div>
<ul
class=
"upload-video-list"
v-for=
"(item, index) in file
List
"
:key=
"index"
>
<ul
class=
"upload-video-list"
v-for=
"(item, index) in file
s
"
:key=
"index"
>
<li>
<div
class=
"upload-video-list__item-info"
>
<el-icon><VideoPlay
/></el-icon>
<p>
{{
item
.
file
?.
name
}}
</p>
</div>
<el-progress
:stroke-width=
"2"
:percentage=
"
percentage(item.loaded)"
v-if=
"item.loaded !== 1
"
/>
<el-progress
:stroke-width=
"2"
:percentage=
"
item.progress"
v-if=
"item.progress !== 100
"
/>
</li>
</ul>
</div>
...
...
src/components/base/AppEditor.vue
浏览文件 @
6e912336
<
script
setup
lang=
"ts"
>
import
Editor
from
'@tinymce/tinymce-vue'
import
md5
from
'blueimp-md5'
import
{
getSignature
,
uploadFile
}
from
'@/api/base'
import
{
getLocalFileChunk
,
uploadLocalFile
}
from
'@/api/base'
const
props
=
defineProps
({
height
:
{
...
...
@@ -13,31 +12,24 @@ const props = defineProps({
const
ImageUploadHandler
=
(
blobInfo
:
any
)
=>
new
Promise
((
resolve
,
reject
)
=>
{
const
file
=
blobInfo
.
blob
()
get
Signature
(
)
get
LocalFileChunk
({
file_name
:
file
.
name
,
file_size
:
file
.
size
}
)
.
then
((
response
:
any
)
=>
{
const
prefix
=
'upload/admin/'
const
key
=
prefix
+
md5
(
file
.
name
+
new
Date
().
getTime
())
+
file
.
name
.
substr
(
file
.
name
.
lastIndexOf
(
'.'
))
const
{
accessid
,
policy
,
signature
,
host
}
=
response
const
params
=
{
key
,
host
,
OSSAccessKeyId
:
accessid
,
policy
,
signature
,
success_action_status
:
'200'
,
file
,
url
:
`
${
host
}
/
${
key
}
`
file_name
:
response
.
data
.
detail
.
file_name
,
now_package_num
:
1
,
total_package_num
:
1
}
uploadFile
(
params
)
upload
Local
File
(
params
)
.
then
((
res
:
any
)
=>
{
resolve
(
res
.
url
)
resolve
(
res
.
data
.
detail
.
uri
)
})
.
catch
(()
=>
{
reject
(
'上传失败'
)
})
})
.
catch
(()
=>
{
reject
(
'
获取Signature
失败'
)
reject
(
'
上传
失败'
)
})
})
...
...
src/components/base/AppUpload.vue
浏览文件 @
6e912336
...
...
@@ -2,8 +2,8 @@
import
type
{
UploadProps
,
UploadUserFile
}
from
'element-plus'
import
{
ElMessage
}
from
'element-plus'
import
{
Plus
}
from
'@element-plus/icons-vue'
import
md5
from
'blueimp-md5'
import
{
get
Signature
}
from
'@/api/base'
//
import md5 from 'blueimp-md5'
import
{
get
LocalFileChunk
}
from
'@/api/base'
interface
Props
{
modelValue
:
string
|
{
name
:
string
;
url
:
string
}[]
...
...
@@ -41,19 +41,12 @@ const handleBeforeUpload = async (file: any) => {
ElMessage
.
error
(
`文件大小不能超过
${
props
.
size
/
1024
/
1024
}
M`
)
return
false
}
const
fileName
=
file
.
name
const
key
=
props
.
prefix
+
md5
(
fileName
+
new
Date
().
getTime
())
+
'/'
+
fileName
const
response
:
Record
<
string
,
any
>
=
await
getSignature
()
const
response
:
Record
<
string
,
any
>
=
await
getLocalFileChunk
({
file_name
:
file
.
name
,
file_size
:
file
.
size
})
uploadData
.
value
=
{
key
,
host
:
response
.
host
,
OSSAccessKeyId
:
response
.
accessid
,
policy
:
response
.
policy
,
signature
:
response
.
signature
,
success_action_status
:
'200'
,
url
:
`
${
response
.
host
}
/
${
key
}
`
file_name
:
response
.
data
.
detail
.
file_name
,
now_package_num
:
1
,
total_package_num
:
1
}
file
.
url
=
`
${
response
.
host
}
/
${
key
}
`
if
(
props
.
beforeUpload
)
{
return
props
.
beforeUpload
(
file
)
}
...
...
@@ -61,6 +54,7 @@ const handleBeforeUpload = async (file: any) => {
// 上传成功
const
handleSuccess
=
(
response
:
any
,
file
:
any
,
files
:
any
)
=>
{
file
.
url
=
response
.
data
.
detail
.
uri
if
(
!
files
.
every
((
item
:
any
)
=>
item
.
status
===
'success'
))
return
if
(
showFileList
.
value
)
{
if
(
props
.
limit
&&
props
.
limit
===
1
)
{
...
...
@@ -89,7 +83,7 @@ const handleSuccess = (response: any, file: any, files: any) => {
)
}
}
else
{
emit
(
'update:modelValue'
,
file
.
raw
.
url
)
emit
(
'update:modelValue'
,
response
.
data
.
detail
.
uri
)
}
emit
(
'success'
,
file
,
files
)
}
...
...
@@ -121,7 +115,7 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
<
template
>
<el-upload
:action=
"uploadData?.host
"
action=
"/api/lab/v1/common/file/upload
"
:data=
"uploadData"
:show-file-list=
"showFileList"
:before-upload=
"handleBeforeUpload"
...
...
@@ -130,8 +124,7 @@ const handlePreview: UploadProps['onPreview'] = uploadFile => {
:on-preview=
"handlePreview"
:on-success=
"handleSuccess"
:file-list=
"fileList"
class=
"uploader"
>
class=
"uploader"
>
<slot>
<template
v-if=
"showFileList"
>
<template
v-if=
"$attrs['list-type'] === 'picture-card'"
>
...
...
src/composables/useUpload.ts
0 → 100644
浏览文件 @
6e912336
import
{
getLocalFileChunk
,
uploadLocalFile
}
from
'@/api/base'
interface
FileItem
{
file
:
File
url
:
string
name
:
string
progress
:
number
abortController
:
AbortController
}
interface
UploadOptions
{
multiple
?:
boolean
autoUpload
?:
boolean
onUploadStarted
?:
(
file
:
FileItem
)
=>
void
onUploadSucceed
?:
(
file
:
FileItem
)
=>
void
onUploadFailed
?:
(
file
:
FileItem
,
error
:
any
)
=>
void
onUploadProgress
?:
(
file
:
FileItem
,
progress
:
number
)
=>
void
onUploadEnd
?:
()
=>
void
}
export
function
useUpload
(
options
:
UploadOptions
=
{})
{
options
=
Object
.
assign
({
autoUpload
:
true
,
multiple
:
false
},
options
)
const
files
=
ref
<
FileItem
[]
>
([])
const
uploading
=
ref
(
false
)
function
addFile
(
file
:
File
)
{
// 检查文件是否已经在队列中
const
existingFileIndex
=
files
.
value
.
findIndex
(
f
=>
f
.
name
===
file
.
name
)
// 如果文件已经在队列中,并且上传已经开始,则直接返回
if
(
existingFileIndex
!==
-
1
&&
files
.
value
[
existingFileIndex
].
progress
>
0
)
return
if
(
!
options
.
multiple
)
stopUpload
()
const
abortController
=
new
AbortController
()
files
.
value
.
push
({
url
:
''
,
name
:
file
.
name
,
file
,
progress
:
0
,
abortController
})
if
(
options
.
autoUpload
&&
!
uploading
.
value
)
startUpload
()
}
async
function
startUpload
()
{
uploading
.
value
=
true
for
(
const
file
of
files
.
value
)
{
if
(
file
.
progress
===
0
)
{
// 只处理尚未开始上传的文件
try
{
options
?.
onUploadStarted
?.(
file
)
await
uploadFile
(
file
)
options
?.
onUploadSucceed
?.(
file
)
}
catch
(
error
)
{
options
?.
onUploadFailed
?.(
file
,
error
)
}
}
}
uploading
.
value
=
false
options
?.
onUploadEnd
?.()
}
async
function
uploadFile
(
file
:
FileItem
)
{
const
{
data
:
{
detail
}
}
=
await
getLocalFileChunk
({
file_name
:
file
.
file
.
name
,
file_size
:
file
.
file
.
size
})
const
fileName
=
detail
.
file_name
const
chunkSize
=
detail
.
chunk_size
const
totalChunks
=
Math
.
ceil
(
file
.
file
.
size
/
chunkSize
)
for
(
let
chunkIndex
=
0
;
chunkIndex
<
totalChunks
;
chunkIndex
++
)
{
const
chunk
=
file
.
file
.
slice
(
chunkIndex
*
chunkSize
,
(
chunkIndex
+
1
)
*
chunkSize
)
await
uploadChunk
({
file
,
chunk
,
chunkIndex
,
fileName
,
totalChunks
})
}
}
async
function
uploadChunk
({
file
,
chunk
,
chunkIndex
,
fileName
,
totalChunks
}:
{
file
:
FileItem
chunk
:
Blob
chunkIndex
:
number
fileName
:
string
totalChunks
:
number
})
{
const
{
data
:
{
detail
}
}
=
await
uploadLocalFile
(
{
file_name
:
fileName
,
file
:
chunk
,
now_package_num
:
chunkIndex
+
1
,
total_package_num
:
totalChunks
},
{
signal
:
file
.
abortController
.
signal
,
onUploadProgress
(
event
:
ProgressEvent
)
{
updateProgress
({
file
,
event
,
chunkIndex
,
totalChunks
})
}
}
)
file
.
url
=
detail
.
uri
}
function
updateProgress
({
event
,
chunkIndex
,
totalChunks
,
file
}:
{
file
:
FileItem
;
event
:
ProgressEvent
;
chunkIndex
:
number
;
totalChunks
:
number
})
{
const
totalSize
=
event
.
total
*
totalChunks
const
loadedSize
=
event
.
loaded
+
chunkIndex
*
event
.
total
const
progressPercent
=
(
loadedSize
/
totalSize
)
*
100
file
.
progress
=
parseFloat
(
progressPercent
.
toFixed
(
2
))
options
?.
onUploadProgress
?.(
file
,
file
.
progress
)
console
.
log
(
file
.
progress
)
}
function
cancelUpload
(
file
:
FileItem
)
{
file
.
abortController
.
abort
()
files
.
value
=
files
.
value
.
filter
(
item
=>
item
.
name
!==
file
.
name
)
}
function
stopUpload
()
{
files
.
value
.
forEach
(
cancelUpload
)
// 停止所有文件的上传
files
.
value
.
length
=
0
uploading
.
value
=
false
// 更新上传状态
}
return
{
files
,
uploading
,
addFile
,
startUpload
,
cancelUpload
,
stopUpload
}
}
src/modules/admin/contest/items/components/ViewVideoFormDialog.vue
浏览文件 @
6e912336
...
...
@@ -29,7 +29,8 @@ const form = reactive<any>({
source_id
:
''
,
duration
:
0
,
type
:
''
,
protocol
:
false
protocol
:
false
,
size
:
''
})
watchEffect
(()
=>
{
if
(
!
props
.
data
)
return
...
...
@@ -69,7 +70,8 @@ function handleBeforeUpload() {
function
handleUploadSuccess
(
uploadInfo
:
any
)
{
form
.
name
=
uploadInfo
.
file
.
name
.
split
(
'.'
).
shift
()
form
.
source_id
=
uploadInfo
.
videoId
form
.
source_id
=
uploadInfo
.
url
form
.
size
=
uploadInfo
.
file
.
size
}
// 提交
function
handleSubmit
()
{
...
...
src/modules/admin/contest/items/components/ViewVideoPreviewDialog.vue
浏览文件 @
6e912336
...
...
@@ -6,21 +6,25 @@ interface Props {
data
:
ContestVideoItem
}
const
props
=
defineProps
<
Props
>
()
let
playList
=
$ref
<
any
>
(
[]
)
let
playList
=
$ref
<
any
>
()
function
fetchInfo
()
{
getContestVideoPalyInfo
({
source_id
:
props
.
data
.
source_id
}).
then
(
res
=>
{
playList
=
res
.
data
.
replay_list
.
play_info_list
})
}
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
if
(
Array
.
isArray
(
playList
))
{
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
}
else
{
return
playList
}
})
onMounted
(()
=>
{
fetchInfo
()
...
...
@@ -29,8 +33,6 @@ onMounted(() => {
<
template
>
<el-dialog
title=
"查阅操作视频"
>
<AppVideoPlayer
:options=
"
{ fluid: true, sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
v-if="playUrl">
</AppVideoPlayer>
<AppVideoPlayer
:options=
"
{ fluid: true, sources: [{ src: playUrl }] }" v-if="playUrl">
</AppVideoPlayer>
</el-dialog>
</
template
>
src/modules/admin/contest/items/types.ts
浏览文件 @
6e912336
...
...
@@ -109,6 +109,7 @@ export interface ContestVideoCreateParams {
status
:
string
duration
:
number
source_id
:
string
size
:
string
}
export
type
ContestVideoUpdateParams
=
ContestVideoCreateParams
&
{
id
:
string
}
src/modules/admin/lab/book/components/FormDialog.vue
浏览文件 @
6e912336
...
...
@@ -77,7 +77,7 @@ function handleBeforeUpload() {
function
handleUploadSuccess
(
file
:
any
)
{
form
.
name
=
file
.
name
.
split
(
'.'
).
shift
()
form
.
size
=
(
file
.
size
/
1024
/
1024
).
toString
()
form
.
url
=
file
.
raw
.
url
form
.
url
=
file
.
url
form
.
type
=
file
.
raw
.
type
||
'unknown'
}
// 提交
...
...
src/modules/admin/lab/case/components/FormDialog.vue
浏览文件 @
6e912336
...
...
@@ -84,7 +84,7 @@ function handleBeforeUpload() {
function
handleUploadSuccess
(
file
:
any
)
{
form
.
name
=
file
.
name
.
split
(
'.'
).
shift
()
form
.
size
=
(
file
.
size
/
1024
/
1024
).
toString
()
form
.
url
=
file
.
raw
.
url
form
.
url
=
file
.
url
form
.
type
=
file
.
raw
.
type
||
'unknown'
}
// 提交
...
...
src/modules/admin/lab/score/views/Index.vue
浏览文件 @
6e912336
...
...
@@ -12,7 +12,7 @@ const ScoreLogDialog = defineAsyncComponent(() => import('../components/ScoreLog
const
{
courses
,
experiments
,
specialties
,
classes
}
=
useFilterList
()
const
cookies
=
useCookies
([
'TGC'
])
const
LAB_URL
=
`
${
import
.
meta
.
env
.
VITE_LAB_URL
}
&token=
${
cookies
.
get
(
'TGC'
)}
`
const
LAB_URL
=
`
${
import
.
meta
.
env
.
VITE_LAB_URL
}
&token=
${
cookies
.
get
(
'TGC'
)}
&sysFlag=
${
import
.
meta
.
env
.
VITE_SYS_FLAG
}
`
const
route
=
useRoute
()
...
...
src/modules/admin/lab/video/components/FormDialog.vue
浏览文件 @
6e912336
...
...
@@ -33,7 +33,8 @@ const form = reactive<VideoCreateItem>({
experiment_id
:
''
,
status
:
'1'
,
source_id
:
''
,
protocol
:
false
protocol
:
false
,
size
:
''
})
watchEffect
(()
=>
{
if
(
!
props
.
data
)
return
...
...
@@ -75,7 +76,8 @@ function handleBeforeUpload() {
function
handleUploadSuccess
(
uploadInfo
:
any
)
{
form
.
name
=
uploadInfo
.
file
.
name
.
split
(
'.'
).
shift
()
form
.
source_id
=
uploadInfo
.
videoId
form
.
source_id
=
uploadInfo
.
url
form
.
size
=
uploadInfo
.
file
.
size
}
// 提交
function
handleSubmit
()
{
...
...
src/modules/admin/lab/video/types.ts
浏览文件 @
6e912336
...
...
@@ -29,4 +29,5 @@ export interface VideoCreateItem {
name
:
string
source_id
:
string
protocol
:
boolean
size
:
string
}
src/modules/admin/lab/video/views/View.vue
浏览文件 @
6e912336
...
...
@@ -9,7 +9,7 @@ interface Props {
const
props
=
defineProps
<
Props
>
()
let
detail
=
$ref
<
VideoItem
|
null
>
(
null
)
let
playList
=
$ref
<
any
>
(
[]
)
let
playList
=
$ref
<
any
>
()
function
fetchInfo
()
{
if
(
!
props
.
id
)
return
...
...
@@ -19,14 +19,18 @@ 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
if
(
Array
.
isArray
(
playList
))
{
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
}
else
{
return
playList
}
})
onMounted
(()
=>
{
fetchInfo
()
...
...
@@ -47,9 +51,7 @@ onMounted(() => {
<el-descriptions-item
label=
"创建时间:"
>
{{
detail
.
created_time
}}
</el-descriptions-item>
</el-descriptions>
<div
style=
"height: 600px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl }] }" style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</
template
>
</AppCard>
...
...
src/modules/student/contest/components/VideoItem.vue
浏览文件 @
6e912336
<
script
setup
lang=
"ts"
>
import
type
{
ExperimentVideoType
,
PlayInfo
}
from
'../types'
import
type
{
ExperimentVideoType
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getExperimentVideoPlayInfo
}
from
'../api'
...
...
@@ -11,7 +11,7 @@ interface Props {
data
:
ExperimentVideoType
}
const
props
=
defineProps
<
Props
>
()
let
playList
=
$ref
<
PlayInfo
[]
>
([]
)
let
playList
=
$ref
<
any
>
(
)
function
fetchInfo
()
{
getExperimentVideoPlayInfo
({
source_id
:
props
.
data
.
source_id
}).
then
(
res
=>
{
playList
=
res
.
data
.
list
.
play_info_list
...
...
@@ -28,14 +28,18 @@ 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
if
(
Array
.
isArray
(
playList
))
{
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
}
else
{
return
playList
}
})
onMounted
(()
=>
{
fetchInfo
()
...
...
@@ -46,9 +50,7 @@ onMounted(() => {
<h2>
{{
data
.
name
}}
</h2>
<!--
<img
:src=
"data.cover"
/>
-->
<div
style=
"height: 200px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }" style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</div>
</
template
>
...
...
src/modules/student/lab/components/VideoItem.vue
浏览文件 @
6e912336
<
script
setup
lang=
"ts"
>
import
type
{
ExperimentVideoType
,
PlayInfo
}
from
'../types'
import
type
{
ExperimentVideoType
,
}
from
'../types'
import
AppVideoPlayer
from
'@/components/base/AppVideoPlayer.vue'
import
{
getExperimentVideoPlayInfo
}
from
'../api'
...
...
@@ -12,7 +12,7 @@ interface Props {
data
:
ExperimentVideoType
}
const
props
=
defineProps
<
Props
>
()
let
playList
=
$ref
<
PlayInfo
[]
>
([]
)
let
playList
=
$ref
<
any
>
(
)
function
fetchInfo
()
{
getExperimentVideoPlayInfo
({
source_id
:
props
.
data
.
source_id
}).
then
(
res
=>
{
playList
=
res
.
data
.
play_info_list
...
...
@@ -29,14 +29,18 @@ 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
if
(
Array
.
isArray
(
playList
))
{
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
}
else
{
return
playList
}
})
onMounted
(()
=>
{
fetchInfo
()
...
...
@@ -47,9 +51,7 @@ onMounted(() => {
<h2>
{{
data
.
name
}}
</h2>
<!--
<img
:src=
"data.cover"
/>
-->
<div
style=
"height: 200px"
v-if=
"playUrl"
>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl, type: 'application/x-mpegURL' }] }"
style="width: 100%; height: 100%">
</AppVideoPlayer>
<AppVideoPlayer
:options=
"
{ sources: [{ src: playUrl }] }" style="width: 100%; height: 100%">
</AppVideoPlayer>
</div>
</div>
</
template
>
...
...
src/modules/student/lab/views/Index.vue
浏览文件 @
6e912336
...
...
@@ -98,7 +98,7 @@ const cookies = useCookies(['TGC'])
const
LAB_URL
=
computed
(()
=>
{
return
experimentInfo
?.
type
===
4
?
`
${
appConfig
.
dmlURL
||
import
.
meta
.
env
.
VITE_DML_URL
}
/trip/my?experiment_id=
${
form
.
experiment_id
}
`
:
`
${
appConfig
.
dmlURL
||
import
.
meta
.
env
.
VITE_LAB_URL
}
&token=
${
cookies
.
get
(
'TGC'
)}
`
:
`
${
appConfig
.
dmlURL
||
import
.
meta
.
env
.
VITE_LAB_URL
}
&token=
${
cookies
.
get
(
'TGC'
)}
&sysFlag=
${
import
.
meta
.
env
.
VITE_SYS_FLAG
}
`
})
let
iframeKey
=
$ref
(
Date
.
now
())
// 返回首页
...
...
src/utils/axios.ts
浏览文件 @
6e912336
...
...
@@ -46,6 +46,14 @@ httpRequest.interceptors.response.use(
ElMessage
.
error
(
data
.
message
||
data
.
msg
)
return
Promise
.
reject
(
data
)
}
if
(
import
.
meta
.
env
.
VITE_STATIC_URL
)
{
try
{
const
regex
=
/
(
http|https
)
:
\/\/(
.*
?)
saas-lab-api/gi
return
JSON
.
parse
(
JSON
.
stringify
(
data
).
replaceAll
(
regex
,
import
.
meta
.
env
.
VITE_STATIC_URL
))
}
catch
(
error
)
{
console
.
log
(
error
)
}
}
return
data
},
function
(
error
)
{
...
...
vite.config.ts
浏览文件 @
6e912336
...
...
@@ -7,8 +7,8 @@ import vue from '@vitejs/plugin-vue'
// import checker from 'vite-plugin-checker'
import
AutoImport
from
'unplugin-auto-import/vite'
export
default
defineConfig
((
{
mode
}
)
=>
({
base
:
mode
===
'prod'
?
'https://webapp-pub.ezijing.com/website/prod/saas-lab/'
:
'/'
,
export
default
defineConfig
(()
=>
({
//
base: mode === 'prod' ? 'https://webapp-pub.ezijing.com/website/prod/saas-lab/' : '/',
plugins
:
[
vue
({
reactivityTransform
:
true
,
script
:
{
defineModel
:
true
}
}),
AutoImport
({
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论