Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
AI_BOOK_TOOL
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
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
template admin
AI_BOOK_TOOL
Commits
0a1309af
Commit
0a1309af
authored
Jun 10, 2025
by
15011343103
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat: 异步调用显示灯
parent
4bc039f6
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
1000 additions
and
497 deletions
+1000
-497
index.css
form/index.css
+195
-1
index.html
form/index.html
+332
-235
index.js
form/index.js
+473
-261
No files found.
form/index.css
View file @
0a1309af
...
...
@@ -730,3 +730,197 @@ html, body {
pointer-events
:
none
;
}
.custom-audio-marker-blue
{
color
:
#00ff00
!important
;
}
.upload-container
{
position
:
fixed
;
top
:
0
;
left
:
0
;
width
:
100%
;
height
:
100%
;
background-color
:
#f5f7fa
;
display
:
flex
;
justify-content
:
center
;
align-items
:
center
;
z-index
:
1000
;
}
.upload-area
{
width
:
80%
;
max-width
:
800px
;
background
:
white
;
padding
:
40px
;
border-radius
:
8px
;
box-shadow
:
0
2px
12px
0
rgba
(
0
,
0
,
0
,
0.1
);
}
.upload-section
{
margin-bottom
:
30px
;
}
.upload-section
h3
{
margin-bottom
:
15px
;
color
:
#303133
;
font-size
:
18px
;
}
.upload-box
{
border
:
2px
dashed
#dcdfe6
;
border-radius
:
6px
;
padding
:
40px
;
text-align
:
center
;
cursor
:
pointer
;
transition
:
all
0.3s
;
position
:
relative
;
}
.upload-box
:hover
{
border-color
:
#409eff
;
}
.upload-box.is-dragover
{
border-color
:
#409eff
;
background-color
:
rgba
(
64
,
158
,
255
,
0.1
);
}
.upload-box
i
{
font-size
:
48px
;
color
:
#909399
;
margin-bottom
:
15px
;
}
.upload-text
{
color
:
#606266
;
}
.upload-text
p
{
margin
:
5px
0
;
}
.upload-hint
{
font-size
:
12px
;
color
:
#909399
;
}
.file-info
{
margin-top
:
15px
;
padding
:
10px
;
background-color
:
#f5f7fa
;
border-radius
:
4px
;
display
:
flex
;
justify-content
:
space-between
;
align-items
:
center
;
}
.file-info
p
{
margin
:
0
;
color
:
#606266
;
}
.upload-actions
{
text-align
:
center
;
margin-top
:
30px
;
}
/* 时间轴样式 */
.timeline-content
{
padding
:
10px
0
;
}
.timeline-content
h4
{
margin
:
0
0
10px
0
;
color
:
#303133
;
font-size
:
16px
;
}
.timeline-content
.el-progress
{
margin
:
10px
0
;
}
.error-message
{
color
:
#f56c6c
;
font-size
:
12px
;
margin
:
5px
0
0
0
;
}
.el-timeline-item__node
{
background-color
:
#409eff
;
}
.el-timeline-item__node--success
{
background-color
:
#67c23a
;
}
.el-timeline-item__node--warning
{
background-color
:
#e6a23c
;
}
.el-timeline-item__node--danger
{
background-color
:
#f56c6c
;
}
.el-timeline-item__node--info
{
background-color
:
#909399
;
}
.el-timeline-item__timestamp
{
color
:
#909399
;
font-size
:
12px
;
}
.el-timeline-item__content
{
color
:
#303133
;
}
.el-timeline-item__wrapper
{
padding-left
:
28px
;
position
:
relative
;
padding-bottom
:
20px
;
}
.el-timeline-item__tail
{
position
:
absolute
;
left
:
4px
;
height
:
100%
;
border-left
:
2px
solid
#e4e7ed
;
}
.el-timeline-item__icon
{
color
:
#fff
;
font-size
:
13px
;
}
.el-timeline-item__node--normal
{
left
:
-1px
;
width
:
12px
;
height
:
12px
;
border-radius
:
50%
;
background-color
:
#909399
;
}
.el-timeline-item__node--large
{
left
:
-2px
;
width
:
14px
;
height
:
14px
;
border-radius
:
50%
;
background-color
:
#909399
;
}
.el-timeline-item__node--primary
{
background-color
:
#409eff
;
}
.el-timeline-item__node--success
{
background-color
:
#67c23a
;
}
.el-timeline-item__node--warning
{
background-color
:
#e6a23c
;
}
.el-timeline-item__node--danger
{
background-color
:
#f56c6c
;
}
.el-timeline-item__node--info
{
background-color
:
#909399
;
}
\ No newline at end of file
form/index.html
View file @
0a1309af
...
...
@@ -29,6 +29,102 @@
</head>
<body>
<div
id=
"app"
v-loading
.
fullscreen
.
lock=
"loading"
>
<!-- 文件上传界面 -->
<div
class=
"upload-container"
v-if=
"!hasData"
>
<div
class=
"upload-area"
>
<div
class=
"upload-section"
>
<h3>
音频文件上传
</h3>
<el-upload
class=
"upload-box"
drag
action=
"#"
:auto-upload=
"true"
:http-request=
"handleAudioUpload"
:show-file-list=
"true"
:limit=
"1"
:on-exceed=
"handleExceed"
:before-upload=
"beforeAudioUpload"
:on-remove=
"removeAudioFile"
accept=
".mp3"
>
<i
class=
"el-icon-upload"
></i>
<div
class=
"upload-text"
>
<p>
点击或拖拽文件到此处上传
</p>
<p
class=
"upload-hint"
>
支持 .mp3 格式
</p>
</div>
</el-upload>
<el-progress
v-if=
"audioUploadProgress > 0 && audioUploadProgress < 100"
:percentage=
"audioUploadProgress"
:format=
"progressFormat"
></el-progress>
</div>
<div
class=
"upload-section"
>
<h3>
PDF文件上传
</h3>
<el-upload
class=
"upload-box"
drag
action=
"#"
:auto-upload=
"true"
:http-request=
"handlePdfUpload"
:show-file-list=
"true"
:limit=
"1"
:on-exceed=
"handleExceed"
:before-upload=
"beforePdfUpload"
:on-remove=
"removePdfFile"
accept=
".pdf"
>
<i
class=
"el-icon-upload"
></i>
<div
class=
"upload-text"
>
<p>
点击或拖拽文件到此处上传
</p>
<p
class=
"upload-hint"
>
支持 .pdf 格式
</p>
</div>
</el-upload>
<el-progress
v-if=
"pdfUploadProgress > 0 && pdfUploadProgress < 100"
:percentage=
"pdfUploadProgress"
:format=
"progressFormat"
></el-progress>
</div>
<!-- 添加任务处理进度时间轴 -->
<div
class=
"upload-section"
v-if=
"queueTasks.length > 0"
>
<h3>
处理进度
</h3>
<el-timeline>
<el-timeline-item
v-for=
"(task, index) in queueTasks"
:key=
"index"
:type=
"getTimelineItemType(task.step_status)"
:color=
"getTimelineItemColor(task.step_status)"
:timestamp=
"task.create_at"
>
<div
class=
"timeline-content"
>
<h4>
{{ task.step_code_desc }}
</h4>
<el-progress
:percentage=
"task.step_process"
:status=
"getProgressStatus(task.step_status)"
></el-progress>
<p
v-if=
"task.err_massage"
class=
"error-message"
>
{{ task.err_massage }}
</p>
</div>
</el-timeline-item>
</el-timeline>
</div>
<div
class=
"upload-actions"
>
<el-button
type=
"primary"
size=
"large"
:disabled=
"!isUploadComplete"
@
click=
"handleProcess"
>
开始处理
</el-button>
</div>
</div>
</div>
<el-container
v-else
>
<el-container>
<!-- 主要内容区域 -->
<el-main>
...
...
@@ -348,6 +444,7 @@
</div>
</div>
</div>
</el-container>
</div>
</body>
<script
src=
"./index.js"
></script>
...
...
form/index.js
View file @
0a1309af
...
...
@@ -67,6 +67,17 @@ new Vue({
lastCheckedTime
:
0
,
// 用于优化检查间隔
isDataModified
:
false
,
hasData
:
false
,
audioFile
:
null
,
pdfFile
:
null
,
audioUploadProgress
:
0
,
pdfUploadProgress
:
0
,
audioUploadComplete
:
false
,
pdfUploadComplete
:
false
,
isAudioDragover
:
false
,
isPdfDragover
:
false
,
queueTasks
:
[],
},
mounted
()
{
...
...
@@ -86,6 +97,28 @@ new Vue({
},
methods
:
{
// 心跳实时查询异步处理进度及信息
async
checkProcess
(
_data
)
{
let
data
=
_data
;
if
(
!
data
)
{
data
=
await
new
Promise
((
resolve
)
=>
{
window
.
courseware
.
getData
((
data
)
=>
{
resolve
(
data
);
},
t_name
);
});
}
if
(
data
&&
data
.
data_type
==
'
AI_QUEUE
'
)
{
// 更新队列任务列表
this
.
queueTasks
=
data
.
rows
;
// 继续检查进度
await
new
Promise
(
resolve
=>
setTimeout
(
resolve
,
1000
));
this
.
checkProcess
();
}
else
{
this
.
hasData
=
true
;
this
.
initPage
(
data
);
this
.
$message
.
success
(
'
处理完成
'
);
}
},
// 加载默认数据
async
loadDefaultData
()
{
this
.
loading
=
true
;
...
...
@@ -99,10 +132,28 @@ new Vue({
});
if
(
!
data
)
{
this
.
loading
=
false
;
// TODO 上传文件以及上传后的AI处理状态展示,直到AI处理完成才会有 data
return
;
}
if
(
data
&&
data
.
data_type
==
'
AI_QUEUE
'
)
{
// 说明是AI处理队列,需要实时查询进度
this
.
loading
=
false
;
this
.
checkProcess
();
return
;
}
this
.
hasData
=
true
;
this
.
initPage
(
data
);
this
.
loading
=
false
;
this
.
$message
.
success
(
'
数据加载成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
Error loading data:
'
,
error
);
this
.
$message
.
error
(
'
数据加载失败
'
);
this
.
loading
=
false
;
throw
error
;
}
},
initPage
(
data
)
{
this
.
audioPoints
=
data
.
audio_points
;
this
.
pages
=
data
.
pages
;
...
...
@@ -113,15 +164,6 @@ new Vue({
// 开始预加载图片
this
.
preloadImages
();
this
.
loading
=
false
;
this
.
$message
.
success
(
'
数据加载成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
Error loading data:
'
,
error
);
this
.
$message
.
error
(
'
数据加载失败
'
);
this
.
loading
=
false
;
throw
error
;
}
},
// 预加载所有图片
...
...
@@ -1072,7 +1114,7 @@ new Vue({
console
.
error
(
'
Error deleting rectangle from group:
'
,
err
);
this
.
$message
.
error
(
'
删除失败
'
);
}
}).
catch
(()
=>
{});
}).
catch
(()
=>
{
});
},
// 删除整个分组
...
...
@@ -1108,7 +1150,7 @@ new Vue({
console
.
error
(
'
Error deleting group:
'
,
err
);
this
.
$message
.
error
(
'
删除失败
'
);
}
}).
catch
(()
=>
{});
}).
catch
(()
=>
{
});
},
// 处理矩形列表行点击
...
...
@@ -2039,12 +2081,12 @@ new Vue({
window
.
courseware
.
setData
(
data
,
()
=>
{
console
.
log
(
'
----save----
'
);
this
.
isDataModified
=
false
;
},
t_name
);
},
t_name
);
},
// 处理秒的显示
formatTime
(
_seconds
)
{
let
seconds
=
Math
.
round
(
_seconds
*
100
)
/
100
;
let
seconds
=
Math
.
round
(
_seconds
*
100
)
/
100
;
// 小数点后保留两位,如果只有一位则补充 0
if
(
seconds
.
toString
().
indexOf
(
'
.
'
)
===
-
1
)
{
seconds
=
seconds
+
'
.00
'
;
...
...
@@ -2052,7 +2094,172 @@ new Vue({
seconds
=
seconds
+
'
0
'
;
}
return
seconds
.
toString
();
},
progressFormat
(
percentage
)
{
return
percentage
===
100
?
'
上传完成
'
:
`
${
percentage
}
%`
;
},
handleExceed
()
{
this
.
$message
.
warning
(
'
只能上传一个文件
'
);
},
beforeAudioUpload
(
file
)
{
const
isMP3
=
file
.
type
===
'
audio/mp3
'
||
file
.
type
===
'
audio/mpeg
'
||
file
.
name
.
toLowerCase
().
endsWith
(
'
.mp3
'
);
if
(
!
isMP3
)
{
this
.
$message
.
error
(
'
请上传 MP3 格式的音频文件
'
);
}
return
isMP3
;
},
beforePdfUpload
(
file
)
{
const
isPDF
=
file
.
type
===
'
application/pdf
'
||
file
.
name
.
toLowerCase
().
endsWith
(
'
.pdf
'
);
if
(
!
isPDF
)
{
this
.
$message
.
error
(
'
请上传 PDF 格式的文件
'
);
}
return
isPDF
;
},
async
handleAudioUpload
(
options
)
{
const
{
file
}
=
options
;
this
.
audioUploadProgress
=
0
;
this
.
audioUploadComplete
=
false
;
const
uploadUrl
=
window
.
courseware
.
uploadUrl
();
const
uploadData
=
window
.
courseware
.
uploadData
();
try
{
const
formData
=
new
FormData
();
for
(
const
key
in
uploadData
)
{
formData
.
append
(
key
,
uploadData
[
key
]);
}
formData
.
append
(
'
file
'
,
file
);
const
result
=
await
this
.
uploadFile
(
formData
,
uploadUrl
,
(
progress
)
=>
{
this
.
audioUploadProgress
=
progress
;
});
this
.
audioFile
=
result
.
url
;
this
.
audioUploadComplete
=
true
;
this
.
$message
.
success
(
'
音频文件上传成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
Error uploading audio:
'
,
error
);
this
.
$message
.
error
(
'
音频文件上传失败
'
);
this
.
audioUploadProgress
=
0
;
this
.
audioUploadComplete
=
false
;
}
},
async
handlePdfUpload
(
options
)
{
const
{
file
}
=
options
;
this
.
pdfUploadProgress
=
0
;
this
.
pdfUploadComplete
=
false
;
const
uploadUrl
=
window
.
courseware
.
uploadUrl
();
const
uploadData
=
window
.
courseware
.
uploadData
();
try
{
const
formData
=
new
FormData
();
for
(
const
key
in
uploadData
)
{
formData
.
append
(
key
,
uploadData
[
key
]);
}
formData
.
append
(
'
file
'
,
file
);
const
result
=
await
this
.
uploadFile
(
formData
,
uploadUrl
,
(
progress
)
=>
{
this
.
pdfUploadProgress
=
progress
;
});
this
.
pdfFile
=
result
.
url
;
this
.
pdfUploadComplete
=
true
;
this
.
$message
.
success
(
'
PDF文件上传成功
'
);
}
catch
(
error
)
{
console
.
error
(
'
Error uploading PDF:
'
,
error
);
this
.
$message
.
error
(
'
PDF文件上传失败
'
);
this
.
pdfUploadProgress
=
0
;
this
.
pdfUploadComplete
=
false
;
}
},
uploadFile
(
formData
,
url
,
progressCallback
)
{
return
new
Promise
((
resolve
,
reject
)
=>
{
const
xhr
=
new
XMLHttpRequest
();
xhr
.
open
(
'
POST
'
,
url
);
xhr
.
upload
.
onprogress
=
(
event
)
=>
{
if
(
event
.
lengthComputable
)
{
const
percent
=
Math
.
round
((
event
.
loaded
/
event
.
total
)
*
100
);
progressCallback
(
percent
);
}
};
xhr
.
onreadystatechange
=
()
=>
{
if
(
xhr
.
readyState
===
4
)
{
if
(
xhr
.
status
===
200
)
{
resolve
(
JSON
.
parse
(
xhr
.
responseText
));
}
else
{
reject
(
new
Error
(
'
Upload failed
'
));
}
}
};
xhr
.
send
(
formData
);
});
},
removeAudioFile
()
{
this
.
audioFile
=
null
;
this
.
audioUploadProgress
=
0
;
this
.
audioUploadComplete
=
false
;
},
removePdfFile
()
{
this
.
pdfFile
=
null
;
this
.
pdfUploadProgress
=
0
;
this
.
pdfUploadComplete
=
false
;
},
async
handleProcess
()
{
if
(
!
this
.
isUploadComplete
)
{
this
.
$message
.
warning
(
'
请等待文件上传完成
'
);
return
;
}
this
.
loading
=
true
;
window
.
courseware
.
setData
({
audioUrl
:
this
.
audioFile
,
pdfUrl
:
this
.
pdfFile
},
()
=>
{
this
.
loading
=
false
;
this
.
checkProcess
();
},
t_name
);
},
getTimelineItemType
(
status
)
{
switch
(
status
)
{
case
'
1
'
:
return
'
info
'
;
// 未开始
case
'
2
'
:
return
'
primary
'
;
// 进行中
case
'
3
'
:
return
'
success
'
;
// 已完成
case
'
4
'
:
return
'
danger
'
;
// 失败
default
:
return
'
info
'
;
}
},
getTimelineItemColor
(
status
)
{
switch
(
status
)
{
case
'
1
'
:
return
'
#909399
'
;
// 未开始
case
'
2
'
:
return
'
#409eff
'
;
// 进行中
case
'
3
'
:
return
'
#67c23a
'
;
// 已完成
case
'
4
'
:
return
'
#f56c6c
'
;
// 失败
default
:
return
'
#909399
'
;
}
},
getProgressStatus
(
status
)
{
switch
(
status
)
{
case
'
1
'
:
return
''
;
// 未开始
case
'
2
'
:
return
''
;
// 进行中
case
'
3
'
:
return
'
success
'
;
// 已完成
case
'
4
'
:
return
'
exception
'
;
// 失败
default
:
return
''
;
}
},
},
beforeDestroy
()
{
...
...
@@ -2075,5 +2282,10 @@ new Vue({
console
.
error
(
'
Error destroying wavesurfer:
'
,
err
);
}
}
},
computed
:
{
isUploadComplete
()
{
return
this
.
audioUploadComplete
&&
this
.
pdfUploadComplete
&&
this
.
audioFile
&&
this
.
pdfFile
;
}
}
});
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