Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
T
thermal-control-system
Project
Project
Details
Activity
Releases
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
luwei
thermal-control-system
Commits
b107747a
Commit
b107747a
authored
May 14, 2026
by
luwei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
修改
parent
8160a34d
Changes
8
Hide whitespace changes
Inline
Side-by-side
Showing
8 changed files
with
255 additions
and
63 deletions
+255
-63
data_management.py
backend/app/api/data_management.py
+3
-1
data_management_service.py
backend/app/services/data_management_service.py
+3
-1
global.scss
frontend/src/styles/global.scss
+4
-0
index.vue
frontend/src/views/DataManagement/index.vue
+67
-28
index.vue
frontend/src/views/ModelEvaluation/index.vue
+37
-3
index.vue
frontend/src/views/ModelList/index.vue
+36
-5
index.vue
frontend/src/views/ModelTraining/index.vue
+49
-5
PkgList.vue
frontend/src/views/PackageManagement/components/PkgList.vue
+56
-20
No files found.
backend/app/api/data_management.py
View file @
b107747a
...
@@ -54,6 +54,7 @@ def get_files(
...
@@ -54,6 +54,7 @@ def get_files(
async
def
upload_file
(
async
def
upload_file
(
file
:
UploadFile
=
File
(
...
),
file
:
UploadFile
=
File
(
...
),
category_id
:
str
=
Form
(
default
=
''
),
category_id
:
str
=
Form
(
default
=
''
),
remark
:
str
=
Form
(
default
=
''
),
):
):
try
:
try
:
content
=
await
file
.
read
()
content
=
await
file
.
read
()
...
@@ -64,6 +65,7 @@ async def upload_file(
...
@@ -64,6 +65,7 @@ async def upload_file(
filename
=
file
.
filename
or
'unknown.xlsx'
,
filename
=
file
.
filename
or
'unknown.xlsx'
,
content
=
content
,
content
=
content
,
category_id
=
category_id
,
category_id
=
category_id
,
remark
=
remark
.
strip
()
or
None
,
)
)
return
success_response
(
data
=
file_meta
,
message
=
'文件上传成功'
)
return
success_response
(
data
=
file_meta
,
message
=
'文件上传成功'
)
except
ValueError
as
error
:
except
ValueError
as
error
:
...
@@ -77,7 +79,7 @@ def download_template():
...
@@ -77,7 +79,7 @@ def download_template():
content
=
content
,
content
=
content
,
media_type
=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
,
media_type
=
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
,
headers
=
{
headers
=
{
'Content-Disposition'
:
'attachment; filename=
data_template
.xlsx'
,
'Content-Disposition'
:
'attachment; filename=
上传文件模板
.xlsx'
,
},
},
)
)
...
...
backend/app/services/data_management_service.py
View file @
b107747a
...
@@ -157,7 +157,7 @@ class DataManagementService:
...
@@ -157,7 +157,7 @@ class DataManagementService:
for
item
in
rows
for
item
in
rows
]
]
def
upload_file
(
self
,
filename
:
str
,
content
:
bytes
,
category_id
:
str
=
'all'
)
->
dict
[
str
,
Any
]:
def
upload_file
(
self
,
filename
:
str
,
content
:
bytes
,
category_id
:
str
=
'all'
,
remark
:
str
|
None
=
None
)
->
dict
[
str
,
Any
]:
suffix
=
Path
(
filename
)
.
suffix
.
lower
()
suffix
=
Path
(
filename
)
.
suffix
.
lower
()
if
suffix
not
in
{
'.xlsx'
,
'.xls'
,
'.csv'
}:
if
suffix
not
in
{
'.xlsx'
,
'.xls'
,
'.csv'
}:
raise
ValueError
(
'仅支持数据文件(.xlsx/.xls/.csv)'
)
raise
ValueError
(
'仅支持数据文件(.xlsx/.xls/.csv)'
)
...
@@ -194,6 +194,7 @@ class DataManagementService:
...
@@ -194,6 +194,7 @@ class DataManagementService:
file_path
=
db_file_path
,
file_path
=
db_file_path
,
category_id
=
category_db_id
,
category_id
=
category_db_id
,
data_count
=
total_count
,
data_count
=
total_count
,
remark
=
remark
,
)
)
session
.
add
(
file_meta
)
session
.
add
(
file_meta
)
session
.
commit
()
session
.
commit
()
...
@@ -205,6 +206,7 @@ class DataManagementService:
...
@@ -205,6 +206,7 @@ class DataManagementService:
'stored_name'
:
file_meta
.
stored_name
,
'stored_name'
:
file_meta
.
stored_name
,
'file_path'
:
file_meta
.
file_path
,
'file_path'
:
file_meta
.
file_path
,
'category_id'
:
file_meta
.
category_id
,
'category_id'
:
file_meta
.
category_id
,
'remark'
:
file_meta
.
remark
or
''
,
'uploaded_at'
:
file_meta
.
uploaded_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
file_meta
.
uploaded_at
else
''
,
'uploaded_at'
:
file_meta
.
uploaded_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
file_meta
.
uploaded_at
else
''
,
'data_count'
:
file_meta
.
data_count
,
'data_count'
:
file_meta
.
data_count
,
}
}
...
...
frontend/src/styles/global.scss
View file @
b107747a
...
@@ -151,6 +151,10 @@ body {
...
@@ -151,6 +151,10 @@ body {
border-bottom
:
1px
solid
var
(
--
border-color
);
border-bottom
:
1px
solid
var
(
--
border-color
);
}
}
.el-dialog__body
{
padding-top
:
24px
;
}
/* Tabs (navigation) */
/* Tabs (navigation) */
.el-tabs__item
{
.el-tabs__item
{
font-size
:
14px
!
important
;
font-size
:
14px
!
important
;
...
...
frontend/src/views/DataManagement/index.vue
View file @
b107747a
<
script
setup
>
<
script
setup
>
import
{
DataAnalysis
,
Delete
,
Document
,
Edit
,
Folder
,
FolderOpened
,
Upload
}
from
'@element-plus/icons-vue'
import
{
DataAnalysis
,
Delete
,
Document
,
Edit
,
Folder
,
FolderOpened
,
Upload
,
Search
}
from
'@element-plus/icons-vue'
import
{
computed
,
onBeforeUnmount
,
onMounted
,
reactive
,
ref
}
from
'vue'
import
{
computed
,
onBeforeUnmount
,
onMounted
,
reactive
,
ref
}
from
'vue'
import
{
ElAutoResizer
,
ElMessage
,
ElMessageBox
,
ElTableV2
}
from
'element-plus'
import
{
ElAutoResizer
,
ElMessage
,
ElMessageBox
,
ElTableV2
}
from
'element-plus'
import
{
import
{
...
@@ -57,6 +57,7 @@ const categoryForm = reactive({
...
@@ -57,6 +57,7 @@ const categoryForm = reactive({
const
uploadDialogVisible
=
ref
(
false
)
const
uploadDialogVisible
=
ref
(
false
)
const
uploadForm
=
reactive
({
const
uploadForm
=
reactive
({
categoryId
:
''
,
categoryId
:
''
,
remark
:
''
,
})
})
const
qualityDialogVisible
=
ref
(
false
)
const
qualityDialogVisible
=
ref
(
false
)
...
@@ -304,7 +305,7 @@ const handleDragging = (event) => {
...
@@ -304,7 +305,7 @@ const handleDragging = (event) => {
}
}
const
startDragging
=
(
target
,
event
)
=>
{
const
startDragging
=
(
target
,
event
)
=>
{
if
(
window
.
innerWidth
<=
980
||
!
layoutRef
.
value
)
{
if
(
!
layoutRef
.
value
)
{
return
return
}
}
...
@@ -427,6 +428,7 @@ const handleReset = async () => {
...
@@ -427,6 +428,7 @@ const handleReset = async () => {
const
openUploadDialog
=
()
=>
{
const
openUploadDialog
=
()
=>
{
uploadForm
.
categoryId
=
selectedCategory
.
value
&&
selectedCategory
.
value
!==
'all'
?
String
(
selectedCategory
.
value
)
:
''
uploadForm
.
categoryId
=
selectedCategory
.
value
&&
selectedCategory
.
value
!==
'all'
?
String
(
selectedCategory
.
value
)
:
''
uploadForm
.
remark
=
''
uploadDialogVisible
.
value
=
true
uploadDialogVisible
.
value
=
true
}
}
...
@@ -454,6 +456,9 @@ const handleUpload = async (uploadRequest) => {
...
@@ -454,6 +456,9 @@ const handleUpload = async (uploadRequest) => {
const
formData
=
new
FormData
()
const
formData
=
new
FormData
()
formData
.
append
(
'file'
,
uploadRequest
.
file
)
formData
.
append
(
'file'
,
uploadRequest
.
file
)
formData
.
append
(
'category_id'
,
String
(
uploadForm
.
categoryId
))
formData
.
append
(
'category_id'
,
String
(
uploadForm
.
categoryId
))
if
(
uploadForm
.
remark
.
trim
())
{
formData
.
append
(
'remark'
,
uploadForm
.
remark
.
trim
())
}
try
{
try
{
await
uploadDataFile
(
formData
)
await
uploadDataFile
(
formData
)
...
@@ -472,7 +477,7 @@ const handleDownloadTemplate = async () => {
...
@@ -472,7 +477,7 @@ const handleDownloadTemplate = async () => {
const
url
=
window
.
URL
.
createObjectURL
(
blob
)
const
url
=
window
.
URL
.
createObjectURL
(
blob
)
const
link
=
document
.
createElement
(
'a'
)
const
link
=
document
.
createElement
(
'a'
)
link
.
href
=
url
link
.
href
=
url
link
.
download
=
'
data_template
.xlsx'
link
.
download
=
'
上传文件模板
.xlsx'
document
.
body
.
appendChild
(
link
)
document
.
body
.
appendChild
(
link
)
link
.
click
()
link
.
click
()
link
.
remove
()
link
.
remove
()
...
@@ -508,6 +513,32 @@ const handleDeleteFile = async (row) => {
...
@@ -508,6 +513,32 @@ const handleDeleteFile = async (row) => {
}
}
}
}
const
selectedFiles
=
ref
([])
const
handleFileSelectionChange
=
(
rows
)
=>
{
selectedFiles
.
value
=
rows
}
const
handleBatchDeleteFiles
=
async
()
=>
{
if
(
!
selectedFiles
.
value
.
length
)
return
try
{
await
ElMessageBox
.
confirm
(
`确定删除选中的
${
selectedFiles
.
value
.
length
}
个文件吗?此操作不可恢复。`
,
'批量删除'
,
{
type
:
'warning'
},
)
await
Promise
.
all
(
selectedFiles
.
value
.
map
((
f
)
=>
deleteDataFile
(
f
.
id
)))
ElMessage
.
success
(
`已删除
${
selectedFiles
.
value
.
length
}
个文件`
)
if
(
selectedFiles
.
value
.
some
((
f
)
=>
f
.
id
===
currentFile
.
value
?.
id
))
{
currentFile
.
value
=
null
recordList
.
value
=
[]
}
selectedFiles
.
value
=
[]
await
loadFileList
()
}
catch
{
// user cancelled
}
}
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
loadLayoutCache
()
loadLayoutCache
()
await
loadCategoryTree
()
await
loadCategoryTree
()
...
@@ -572,22 +603,29 @@ onBeforeUnmount(() => {
...
@@ -572,22 +603,29 @@ onBeforeUnmount(() => {
<el-form
:inline=
"true"
class=
"search-form"
>
<el-form
:inline=
"true"
class=
"search-form"
>
<el-form-item>
<el-form-item>
<el-input
v-model=
"searchForm.filename"
placeholder=
"文件名"
clearable
/>
<el-input
v-model=
"searchForm.filename"
placeholder=
"文件名"
clearable
:prefix-icon=
"Search"
/>
</el-form-item>
</el-form-item>
<el-form-item>
<el-form-item
class=
"form-actions"
>
<el-button
type=
"danger"
plain
:disabled=
"!selectedFiles.length"
@
click=
"handleBatchDeleteFiles"
>
批量删除
</el-button>
<el-button
type=
"primary"
@
click=
"handleSearch"
>
搜索
</el-button>
<el-button
type=
"primary"
@
click=
"handleSearch"
>
搜索
</el-button>
<el-button
@
click=
"handleReset"
>
重置
</el-button>
<el-button
@
click=
"handleReset"
>
重置
</el-button>
<el-button
type=
"info"
plain
@
click=
"handleDownloadTemplate"
>
下载模板
</el-button>
<el-button
@
click=
"handleDownloadTemplate"
>
下载模板
</el-button>
<el-button
type=
"primary"
:icon=
"Upload"
@
click=
"openUploadDialog"
>
上传文件
</el-button>
<el-button
type=
"primary"
@
click=
"openUploadDialog"
>
上传文件
</el-button>
</el-form-item>
</el-form-item>
</el-form>
</el-form>
<el-table
:data=
"fileList"
border
stripe
v-loading=
"loadingFiles"
height=
"calc(100vh - 250px)"
row-class-name=
"file-row"
@
row-click=
"handleViewFile"
>
<el-table
:data=
"fileList"
border
stripe
v-loading=
"loadingFiles"
height=
"calc(100vh - 250px)"
row-class-name=
"file-row"
@
row-click=
"handleViewFile"
@
selection-change=
"handleFileSelectionChange"
>
<el-table-column
type=
"selection"
width=
"44"
@
click
.
stop
/>
<el-table-column
prop=
"filename"
label=
"文件名"
min-width=
"160"
/>
<el-table-column
prop=
"filename"
label=
"文件名"
min-width=
"160"
/>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"120"
show-overflow-tooltip
/>
<el-table-column
prop=
"remark"
label=
"备注"
min-width=
"120"
class-name=
"cell-wrap"
/>
<el-table-column
prop=
"uploaded_at"
label=
"上传时间"
min-width=
"170"
/>
<el-table-column
prop=
"uploaded_at"
label=
"上传时间"
min-width=
"170"
/>
<el-table-column
prop=
"data_count"
label=
"数据量"
width=
"90"
/>
<el-table-column
prop=
"data_count"
label=
"数据量"
width=
"90"
/>
<el-table-column
label=
"操作"
width=
"
22
0"
fixed=
"right"
>
<el-table-column
label=
"操作"
width=
"
11
0"
fixed=
"right"
>
<
template
#
default=
"{ row }"
>
<
template
#
default=
"{ row }"
>
<el-button
link
type=
"primary"
:icon=
"Edit"
@
click
.
stop=
"openFileEditDialog(row)"
/>
<el-button
link
type=
"primary"
:icon=
"Edit"
@
click
.
stop=
"openFileEditDialog(row)"
/>
<el-button
link
type=
"success"
:icon=
"DataAnalysis"
@
click
.
stop=
"handleQualityCheck(row)"
title=
"质量判定"
/>
<el-button
link
type=
"success"
:icon=
"DataAnalysis"
@
click
.
stop=
"handleQualityCheck(row)"
title=
"质量判定"
/>
...
@@ -649,6 +687,16 @@ onBeforeUnmount(() => {
...
@@ -649,6 +687,16 @@ onBeforeUnmount(() => {
</el-select>
</el-select>
</el-form-item>
</el-form-item>
<el-form-item
label=
"备注"
>
<el-input
v-model=
"uploadForm.remark"
type=
"textarea"
:rows=
"2"
placeholder=
"可选,填写文件说明或标注"
maxlength=
"500"
/>
</el-form-item>
<el-form-item
label=
"上传文件"
required
>
<el-form-item
label=
"上传文件"
required
>
<el-upload
<el-upload
class=
"upload-panel"
class=
"upload-panel"
...
@@ -972,33 +1020,24 @@ onBeforeUnmount(() => {
...
@@ -972,33 +1020,24 @@ onBeforeUnmount(() => {
}
}
.search-form
{
.search-form
{
display
:
flex
;
margin-bottom
:
4px
;
margin-bottom
:
4px
;
flex-shrink
:
0
;
flex-shrink
:
0
;
}
}
.form-actions
{
margin-left
:
auto
;
}
:deep
(
.el-table
.cell-wrap
.cell
)
{
white-space
:
normal
;
word-break
:
break-word
;
line-height
:
1
.5
;
}
.content-wrap
{
.content-wrap
{
flex
:
1
;
flex
:
1
;
min-height
:
0
;
min-height
:
0
;
}
}
@media
(
max-width
:
980px
)
{
.data-layout
{
display
:
block
;
overflow-y
:
auto
;
}
.pane-divider
{
display
:
none
;
}
.pane-card
{
width
:
100%
!
important
;
min-height
:
420px
;
margin-bottom
:
12px
;
border-right
:
none
;
border-bottom
:
1px
solid
var
(
--
border-color
);
}
}
// quality dialog
// quality dialog
.quality-filename
{
.quality-filename
{
...
...
frontend/src/views/ModelEvaluation/index.vue
View file @
b107747a
...
@@ -139,6 +139,29 @@ const handleDelete = async (row) => {
...
@@ -139,6 +139,29 @@ const handleDelete = async (row) => {
}
}
}
}
const
selectedRecords
=
ref
([])
const
handleRecordSelectionChange
=
(
rows
)
=>
{
selectedRecords
.
value
=
rows
}
const
handleBatchDeleteRecords
=
async
()
=>
{
if
(
!
selectedRecords
.
value
.
length
)
return
try
{
await
ElMessageBox
.
confirm
(
`确定删除选中的
${
selectedRecords
.
value
.
length
}
条评估记录吗?此操作不可恢复。`
,
'批量删除'
,
{
type
:
'warning'
},
)
await
Promise
.
all
(
selectedRecords
.
value
.
map
((
r
)
=>
deleteEvalRecord
(
r
.
id
)))
ElMessage
.
success
(
`已删除
${
selectedRecords
.
value
.
length
}
条评估记录`
)
if
(
selectedRecords
.
value
.
some
((
r
)
=>
r
.
id
===
currentResult
.
value
?.
id
))
currentResult
.
value
=
null
selectedRecords
.
value
=
[]
await
loadRecords
()
}
catch
{
// user cancelled
}
}
const
fmtMetric
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
5
)
:
'-'
)
const
fmtMetric
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
5
)
:
'-'
)
const
fmtMape
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
3
)
+
' %'
:
'-'
)
const
fmtMape
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
3
)
+
' %'
:
'-'
)
const
fmtR2
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
6
)
:
'-'
)
const
fmtR2
=
(
v
)
=>
(
v
!=
null
?
Number
(
v
).
toFixed
(
6
)
:
'-'
)
...
@@ -253,9 +276,18 @@ onMounted(async () => {
...
@@ -253,9 +276,18 @@ onMounted(async () => {
<
template
#
header
>
<
template
#
header
>
<div
class=
"card-header-row"
>
<div
class=
"card-header-row"
>
<span
class=
"card-title"
>
评估记录
</span>
<span
class=
"card-title"
>
评估记录
</span>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loadingRecords"
@
click=
"loadRecords"
>
<div
style=
"display:flex;gap:8px;align-items:center"
>
刷新
<el-button
</el-button>
type=
"danger"
plain
size=
"small"
:disabled=
"!selectedRecords.length"
@
click=
"handleBatchDeleteRecords"
>
批量删除
</el-button>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loadingRecords"
@
click=
"loadRecords"
>
刷新
</el-button>
</div>
</div>
</div>
</
template
>
</
template
>
...
@@ -265,7 +297,9 @@ onMounted(async () => {
...
@@ -265,7 +297,9 @@ onMounted(async () => {
border
border
stripe
stripe
height=
"calc(100vh - 580px)"
height=
"calc(100vh - 580px)"
@
selection-change=
"handleRecordSelectionChange"
>
>
<el-table-column
type=
"selection"
width=
"44"
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"140"
show-overflow-tooltip
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"140"
show-overflow-tooltip
/>
<el-table-column
prop=
"package_name"
label=
"评估数据包"
min-width=
"130"
show-overflow-tooltip
/>
<el-table-column
prop=
"package_name"
label=
"评估数据包"
min-width=
"130"
show-overflow-tooltip
/>
<el-table-column
label=
"样本数"
width=
"88"
align=
"center"
>
<el-table-column
label=
"样本数"
width=
"88"
align=
"center"
>
...
...
frontend/src/views/ModelList/index.vue
View file @
b107747a
...
@@ -69,6 +69,28 @@ const handleDelete = async (row) => {
...
@@ -69,6 +69,28 @@ const handleDelete = async (row) => {
}
}
}
}
const
selectedModels
=
ref
([])
const
handleModelSelectionChange
=
(
rows
)
=>
{
selectedModels
.
value
=
rows
}
const
handleBatchDeleteModels
=
async
()
=>
{
if
(
!
selectedModels
.
value
.
length
)
return
try
{
await
ElMessageBox
.
confirm
(
`确定删除选中的
${
selectedModels
.
value
.
length
}
个模型吗?删除后无法恢复。`
,
'批量删除'
,
{
type
:
'warning'
},
)
await
Promise
.
all
(
selectedModels
.
value
.
map
((
m
)
=>
deleteSavedModel
(
m
.
id
)))
ElMessage
.
success
(
`已删除
${
selectedModels
.
value
.
length
}
个模型`
)
selectedModels
.
value
=
[]
await
loadModels
()
}
catch
{
// user cancelled
}
}
// ── display helpers ───────────────────────────────────────────────────────────
// ── display helpers ───────────────────────────────────────────────────────────
const
formatParams
=
(
params
)
=>
{
const
formatParams
=
(
params
)
=>
{
if
(
!
params
)
return
'-'
if
(
!
params
)
return
'-'
...
@@ -98,14 +120,23 @@ onMounted(loadModels)
...
@@ -98,14 +120,23 @@ onMounted(loadModels)
<template
#
header
>
<template
#
header
>
<div
class=
"card-header-row"
>
<div
class=
"card-header-row"
>
<span
class=
"card-title"
>
已保存模型
</span>
<span
class=
"card-title"
>
已保存模型
</span>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loading"
@
click=
"loadModels"
>
<div
style=
"display:flex;gap:8px;align-items:center"
>
刷新
<el-button
</el-button>
type=
"danger"
plain
size=
"small"
:disabled=
"!selectedModels.length"
@
click=
"handleBatchDeleteModels"
>
批量删除
</el-button>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loading"
@
click=
"loadModels"
>
刷新
</el-button>
</div>
</div>
</div>
</
template
>
</
template
>
<el-table
:data=
"models"
v-loading=
"loading"
border
stripe
height=
"calc(100vh - 160px)"
>
<el-table
:data=
"models"
v-loading=
"loading"
border
stripe
height=
"calc(100vh - 160px)"
@
selection-change=
"handleModelSelectionChange"
>
<el-table-column
type=
"
index"
width=
"55"
label=
"#"
align=
"center
"
/>
<el-table-column
type=
"
selection"
width=
"44
"
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"140"
show-overflow-tooltip
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"140"
show-overflow-tooltip
/>
<el-table-column
prop=
"description"
label=
"说明"
min-width=
"160"
show-overflow-tooltip
>
<el-table-column
prop=
"description"
label=
"说明"
min-width=
"160"
show-overflow-tooltip
>
...
...
frontend/src/views/ModelTraining/index.vue
View file @
b107747a
...
@@ -216,6 +216,36 @@ const handleDelete = async (task) => {
...
@@ -216,6 +216,36 @@ const handleDelete = async (task) => {
}
}
}
}
const
selectedTasks
=
ref
([])
const
handleTaskSelectionChange
=
(
rows
)
=>
{
selectedTasks
.
value
=
rows
}
const
handleBatchDeleteTasks
=
async
()
=>
{
if
(
!
selectedTasks
.
value
.
length
)
return
const
deletable
=
selectedTasks
.
value
.
filter
(
(
t
)
=>
t
.
status
!==
'pending'
&&
t
.
status
!==
'running'
,
)
if
(
!
deletable
.
length
)
{
ElMessage
.
warning
(
'选中的任务均在运行中,请先取消后再删除'
)
return
}
const
skipped
=
selectedTasks
.
value
.
length
-
deletable
.
length
const
tip
=
skipped
>
0
?
`确定删除选中的
${
deletable
.
length
}
个任务?(
${
skipped
}
个运行中的任务将被跳过)`
:
`确定删除选中的
${
deletable
.
length
}
个任务吗?此操作不可恢复。`
try
{
await
ElMessageBox
.
confirm
(
tip
,
'批量删除'
,
{
type
:
'warning'
})
await
Promise
.
all
(
deletable
.
map
((
t
)
=>
deleteTrainTask
(
t
.
id
)))
ElMessage
.
success
(
`已删除
${
deletable
.
length
}
个任务`
)
if
(
deletable
.
some
((
t
)
=>
t
.
id
===
selectedTask
.
value
?.
id
))
selectedTask
.
value
=
null
selectedTasks
.
value
=
[]
await
loadTasks
()
}
catch
{
// user cancelled
}
}
// ── task detail (epoch logs) ──────────────────────────────────────────────────
// ── task detail (epoch logs) ──────────────────────────────────────────────────
const
selectedTask
=
ref
(
null
)
const
selectedTask
=
ref
(
null
)
const
detailMode
=
ref
(
'table'
)
// 'table' | 'chart'
const
detailMode
=
ref
(
'table'
)
// 'table' | 'chart'
...
@@ -408,7 +438,16 @@ onBeforeUnmount(() => {
...
@@ -408,7 +438,16 @@ onBeforeUnmount(() => {
<
template
#
header
>
<
template
#
header
>
<div
class=
"card-header-row"
>
<div
class=
"card-header-row"
>
<span
class=
"card-title"
>
训练记录
</span>
<span
class=
"card-title"
>
训练记录
</span>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loadingTasks"
@
click=
"loadTasks"
>
刷新
</el-button>
<div
style=
"display:flex;gap:8px;align-items:center"
>
<el-button
type=
"danger"
plain
size=
"small"
:disabled=
"!selectedTasks.length"
@
click=
"handleBatchDeleteTasks"
>
批量删除
</el-button>
<el-button
:icon=
"Refresh"
size=
"small"
plain
:loading=
"loadingTasks"
@
click=
"loadTasks"
>
刷新
</el-button>
</div>
</div>
</div>
</
template
>
</
template
>
...
@@ -421,7 +460,9 @@ onBeforeUnmount(() => {
...
@@ -421,7 +460,9 @@ onBeforeUnmount(() => {
height=
"100%"
height=
"100%"
style=
"cursor:pointer"
style=
"cursor:pointer"
@
row-click=
"handleRowClick"
@
row-click=
"handleRowClick"
@
selection-change=
"handleTaskSelectionChange"
>
>
<el-table-column
type=
"selection"
width=
"44"
@
click
.
stop
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"130"
show-overflow-tooltip
/>
<el-table-column
prop=
"model_name"
label=
"模型名称"
min-width=
"130"
show-overflow-tooltip
/>
<el-table-column
label=
"训练集"
min-width=
"120"
show-overflow-tooltip
>
<el-table-column
label=
"训练集"
min-width=
"120"
show-overflow-tooltip
>
<
template
#
default=
"{ row }"
>
{{
row
.
package_name
}}
</
template
>
<
template
#
default=
"{ row }"
>
{{
row
.
package_name
}}
</
template
>
...
@@ -475,10 +516,13 @@ onBeforeUnmount(() => {
...
@@ -475,10 +516,13 @@ onBeforeUnmount(() => {
<
template
#
header
>
<
template
#
header
>
<div
class=
"card-header-row"
>
<div
class=
"card-header-row"
>
<span
class=
"card-title"
>
训练过程 —
{{
selectedTask
.
model_name
}}
</span>
<span
class=
"card-title"
>
训练过程 —
{{
selectedTask
.
model_name
}}
</span>
<el-radio-group
v-model=
"detailMode"
size=
"small"
>
<div
style=
"display:flex;gap:8px;align-items:center"
>
<el-radio-button
value=
"table"
>
表格
</el-radio-button>
<el-radio-group
v-model=
"detailMode"
size=
"small"
>
<el-radio-button
value=
"chart"
>
曲线
</el-radio-button>
<el-radio-button
value=
"table"
>
表格
</el-radio-button>
</el-radio-group>
<el-radio-button
value=
"chart"
>
曲线
</el-radio-button>
</el-radio-group>
<el-button
size=
"small"
plain
@
click=
"selectedTask = null"
>
关闭
</el-button>
</div>
</div>
</div>
</
template
>
</
template
>
...
...
frontend/src/views/PackageManagement/components/PkgList.vue
View file @
b107747a
...
@@ -26,6 +26,11 @@ const loading = ref(false)
...
@@ -26,6 +26,11 @@ const loading = ref(false)
const
packageList
=
ref
([])
const
packageList
=
ref
([])
const
searchName
=
ref
(
''
)
const
searchName
=
ref
(
''
)
const
currentPackage
=
ref
(
null
)
const
currentPackage
=
ref
(
null
)
const
selectedPackages
=
ref
([])
const
handleSelectionChange
=
(
rows
)
=>
{
selectedPackages
.
value
=
rows
}
const
loadPackages
=
async
()
=>
{
const
loadPackages
=
async
()
=>
{
loading
.
value
=
true
loading
.
value
=
true
...
@@ -122,6 +127,26 @@ const handleDelete = async (row) => {
...
@@ -122,6 +127,26 @@ const handleDelete = async (row) => {
}
}
}
}
const
handleBatchDelete
=
async
()
=>
{
if
(
!
selectedPackages
.
value
.
length
)
return
try
{
await
ElMessageBox
.
confirm
(
`确定删除选中的
${
selectedPackages
.
value
.
length
}
个数据包吗?此操作不可恢复。`
,
'批量删除'
,
{
type
:
'warning'
},
)
await
Promise
.
all
(
selectedPackages
.
value
.
map
((
p
)
=>
deletePackage
(
p
.
id
)))
ElMessage
.
success
(
`已删除
${
selectedPackages
.
value
.
length
}
个数据包`
)
if
(
selectedPackages
.
value
.
some
((
p
)
=>
p
.
id
===
currentPackage
.
value
?.
id
))
{
currentPackage
.
value
=
null
}
selectedPackages
.
value
=
[]
await
loadPackages
()
}
catch
{
// user cancelled
}
}
watch
(()
=>
props
.
categoryId
,
loadPackages
)
watch
(()
=>
props
.
categoryId
,
loadPackages
)
watch
(()
=>
props
.
refreshKey
,
()
=>
{
watch
(()
=>
props
.
refreshKey
,
()
=>
{
currentPackage
.
value
=
null
currentPackage
.
value
=
null
...
@@ -136,22 +161,32 @@ onMounted(loadPackages)
...
@@ -136,22 +161,32 @@ onMounted(loadPackages)
<template
#
header
>
<template
#
header
>
<div
class=
"pane-header"
>
<div
class=
"pane-header"
>
<span>
数据包列表
</span>
<span>
数据包列表
</span>
<el-button
type=
"primary"
:icon=
"Plus"
size=
"small"
@
click=
"emit('add')"
>
新增数据包
</el-button>
</div>
</div>
</
template
>
</
template
>
<div
class=
"search-bar"
>
<el-form
:inline=
"true"
class=
"search-form"
>
<el-input
<el-form-item>
v-model=
"searchName"
<el-input
placeholder=
"数据包名称"
v-model=
"searchName"
clearable
placeholder=
"数据包名称"
:prefix-icon=
"Search"
clearable
@
keyup
.
enter=
"handleSearch"
:prefix-icon=
"Search"
@
clear=
"handleReset"
@
keyup
.
enter=
"handleSearch"
/>
@
clear=
"handleReset"
<el-button
type=
"primary"
@
click=
"handleSearch"
>
搜索
</el-button>
/>
<el-button
@
click=
"handleReset"
>
重置
</el-button>
</el-form-item>
</div>
<el-form-item
class=
"form-actions"
>
<el-button
type=
"danger"
plain
:disabled=
"!selectedPackages.length"
@
click=
"handleBatchDelete"
>
批量删除
</el-button>
<el-button
type=
"primary"
@
click=
"handleSearch"
>
搜索
</el-button>
<el-button
@
click=
"handleReset"
>
重置
</el-button>
<el-button
type=
"primary"
@
click=
"emit('add')"
>
新增数据包
</el-button>
</el-form-item>
</el-form>
<el-table
<el-table
:data=
"packageList"
:data=
"packageList"
...
@@ -163,7 +198,9 @@ onMounted(loadPackages)
...
@@ -163,7 +198,9 @@ onMounted(loadPackages)
:row-class-name=
"({ row }) => (currentPackage?.id === row.id ? 'current-row' : '')"
:row-class-name=
"({ row }) => (currentPackage?.id === row.id ? 'current-row' : '')"
style=
"cursor: pointer"
style=
"cursor: pointer"
@
row-click=
"handleView"
@
row-click=
"handleView"
@
selection-change=
"handleSelectionChange"
>
>
<el-table-column
type=
"selection"
width=
"44"
@
click
.
stop
/>
<el-table-column
prop=
"name"
label=
"数据包名称"
min-width=
"100"
show-overflow-tooltip
/>
<el-table-column
prop=
"name"
label=
"数据包名称"
min-width=
"100"
show-overflow-tooltip
/>
<el-table-column
prop=
"created_at"
label=
"创建时间"
min-width=
"100"
/>
<el-table-column
prop=
"created_at"
label=
"创建时间"
min-width=
"100"
/>
<el-table-column
prop=
"data_count"
label=
"数据量"
width=
"80"
align=
"center"
/>
<el-table-column
prop=
"data_count"
label=
"数据量"
width=
"80"
align=
"center"
/>
...
@@ -243,14 +280,13 @@ onMounted(loadPackages)
...
@@ -243,14 +280,13 @@ onMounted(loadPackages)
color
:
var
(
--
text-primary
);
color
:
var
(
--
text-primary
);
}
}
.search-
bar
{
.search-
form
{
display
:
flex
;
display
:
flex
;
gap
:
8px
;
margin-bottom
:
4px
;
margin-bottom
:
12px
;
flex-shrink
:
0
;
}
.el-input
{
.form-actions
{
flex
:
1
;
margin-left
:
auto
;
}
}
}
:deep
(
.el-table
.current-row
>
td
)
{
:deep
(
.el-table
.current-row
>
td
)
{
...
...
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