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
36e2cc19
Commit
36e2cc19
authored
Apr 27, 2026
by
luwei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
提交修改
parent
3fd809ab
Changes
17
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
17 changed files
with
304 additions
and
62 deletions
+304
-62
package_management.py
backend/app/api/package_management.py
+16
-1
train_management.py
backend/app/api/train_management.py
+0
-1
lstm_trainer.py
backend/app/ml/lstm_trainer.py
+31
-9
data_management.py
backend/app/models/data_management.py
+2
-1
train_management.py
backend/app/models/train_management.py
+2
-0
package_management_service.py
backend/app/services/package_management_service.py
+74
-9
train_service.py
backend/app/services/train_service.py
+4
-0
task_8.pt
backend/saved_models/task_8.pt
+0
-0
index-B6mx_Oi_.js
frontend/dist/assets/index-B6mx_Oi_.js
+0
-2
index-Bdu9N6pH.css
frontend/dist/assets/index-Bdu9N6pH.css
+0
-1
index.html
frontend/dist/index.html
+2
-2
App.vue
frontend/src/App.vue
+4
-4
DataCurve.vue
frontend/src/views/DataManagement/components/DataCurve.vue
+27
-13
index.vue
frontend/src/views/ModelTraining/index.vue
+4
-16
AddPackage.vue
...end/src/views/PackageManagement/components/AddPackage.vue
+129
-2
vite.config.js
frontend/vite.config.js
+0
-1
init_tables.sql
sql/init_tables.sql
+9
-0
No files found.
backend/app/api/package_management.py
View file @
36e2cc19
...
@@ -18,6 +18,16 @@ class CategoryUpdateRequest(BaseModel):
...
@@ -18,6 +18,16 @@ class CategoryUpdateRequest(BaseModel):
name
:
str
=
Field
(
min_length
=
1
,
max_length
=
100
)
name
:
str
=
Field
(
min_length
=
1
,
max_length
=
100
)
class
CleanRules
(
BaseModel
):
enabled
:
bool
=
False
current_min
:
float
|
None
=
None
current_max
:
float
|
None
=
None
voltage_min
:
float
|
None
=
None
voltage_max
:
float
|
None
=
None
temperature_min
:
float
|
None
=
None
temperature_max
:
float
|
None
=
None
@
router
.
get
(
'/categories'
)
@
router
.
get
(
'/categories'
)
def
get_categories
():
def
get_categories
():
return
success_response
(
data
=
service
.
get_category_tree
())
return
success_response
(
data
=
service
.
get_category_tree
())
...
@@ -62,16 +72,19 @@ class PackageCreateRequest(BaseModel):
...
@@ -62,16 +72,19 @@ class PackageCreateRequest(BaseModel):
category_id
:
str
|
int
|
None
=
Field
(
default
=
None
)
category_id
:
str
|
int
|
None
=
Field
(
default
=
None
)
remark
:
str
|
None
=
Field
(
default
=
None
)
remark
:
str
|
None
=
Field
(
default
=
None
)
file_ids
:
list
[
int
]
=
Field
(
default_factory
=
list
)
file_ids
:
list
[
int
]
=
Field
(
default_factory
=
list
)
clean_rules
:
CleanRules
|
None
=
Field
(
default
=
None
)
class
PreviewRequest
(
BaseModel
):
class
PreviewRequest
(
BaseModel
):
file_ids
:
list
[
int
]
=
Field
(
default_factory
=
list
)
file_ids
:
list
[
int
]
=
Field
(
default_factory
=
list
)
clean_rules
:
CleanRules
|
None
=
Field
(
default
=
None
)
@
router
.
post
(
'/preview'
)
@
router
.
post
(
'/preview'
)
def
preview_package
(
request
:
PreviewRequest
,
limit
:
int
=
Query
(
default
=
300
,
ge
=
1
,
le
=
2000
)):
def
preview_package
(
request
:
PreviewRequest
,
limit
:
int
=
Query
(
default
=
300
,
ge
=
1
,
le
=
2000
)):
try
:
try
:
result
=
service
.
preview_records
(
file_ids
=
request
.
file_ids
,
limit
=
limit
)
clean_rules
=
request
.
clean_rules
.
model_dump
()
if
request
.
clean_rules
else
None
result
=
service
.
preview_records
(
file_ids
=
request
.
file_ids
,
limit
=
limit
,
clean_rules
=
clean_rules
)
return
success_response
(
data
=
result
)
return
success_response
(
data
=
result
)
except
ValueError
as
error
:
except
ValueError
as
error
:
raise
HTTPException
(
status_code
=
400
,
detail
=
str
(
error
))
from
error
raise
HTTPException
(
status_code
=
400
,
detail
=
str
(
error
))
from
error
...
@@ -89,11 +102,13 @@ def list_packages(
...
@@ -89,11 +102,13 @@ def list_packages(
def
create_package
(
request
:
PackageCreateRequest
):
def
create_package
(
request
:
PackageCreateRequest
):
try
:
try
:
category_id
=
None
if
request
.
category_id
in
(
None
,
''
,
'all'
)
else
str
(
request
.
category_id
)
category_id
=
None
if
request
.
category_id
in
(
None
,
''
,
'all'
)
else
str
(
request
.
category_id
)
clean_rules
=
request
.
clean_rules
.
model_dump
()
if
request
.
clean_rules
else
None
pkg
=
service
.
create_package
(
pkg
=
service
.
create_package
(
name
=
request
.
name
.
strip
(),
name
=
request
.
name
.
strip
(),
category_id
=
category_id
,
category_id
=
category_id
,
remark
=
request
.
remark
,
remark
=
request
.
remark
,
file_ids
=
request
.
file_ids
,
file_ids
=
request
.
file_ids
,
clean_rules
=
clean_rules
,
)
)
return
success_response
(
data
=
pkg
,
message
=
'数据包创建成功'
)
return
success_response
(
data
=
pkg
,
message
=
'数据包创建成功'
)
except
ValueError
as
error
:
except
ValueError
as
error
:
...
...
backend/app/api/train_management.py
View file @
36e2cc19
...
@@ -17,7 +17,6 @@ class LSTMParams(BaseModel):
...
@@ -17,7 +17,6 @@ class LSTMParams(BaseModel):
epochs
:
int
=
Field
(
default
=
50
,
ge
=
1
,
le
=
2000
)
epochs
:
int
=
Field
(
default
=
50
,
ge
=
1
,
le
=
2000
)
batch_size
:
int
=
Field
(
default
=
32
,
ge
=
1
,
le
=
512
)
batch_size
:
int
=
Field
(
default
=
32
,
ge
=
1
,
le
=
512
)
learning_rate
:
float
=
Field
(
default
=
0.001
,
gt
=
0
,
le
=
1
)
learning_rate
:
float
=
Field
(
default
=
0.001
,
gt
=
0
,
le
=
1
)
train_ratio
:
float
=
Field
(
default
=
0.8
,
ge
=
0.5
,
le
=
0.99
)
class
CreateTaskRequest
(
BaseModel
):
class
CreateTaskRequest
(
BaseModel
):
...
...
backend/app/ml/lstm_trainer.py
View file @
36e2cc19
"""LSTM temperature forecasting trainer.
"""LSTM temperature forecasting trainer.
Uses PyTorch if available. If not installed, raises a descriptive RuntimeError
so the train service can mark the task as failed with a helpful message.
"""
"""
from
__future__
import
annotations
from
__future__
import
annotations
...
@@ -46,6 +44,11 @@ FEATURE_COLS = ['current', 'voltage', 'set_temperature', 'actual_temperature']
...
@@ -46,6 +44,11 @@ FEATURE_COLS = ['current', 'voltage', 'set_temperature', 'actual_temperature']
TARGET_COL
=
'actual_temperature'
TARGET_COL
=
'actual_temperature'
TARGET_IDX
=
FEATURE_COLS
.
index
(
TARGET_COL
)
TARGET_IDX
=
FEATURE_COLS
.
index
(
TARGET_COL
)
# Fixed dataset split ratios (train / val / test)
_TRAIN_RATIO
=
0.70
_VAL_RATIO
=
0.15
# test = 1 - _TRAIN_RATIO - _VAL_RATIO (≈ 0.15)
def
_check_torch
()
->
None
:
def
_check_torch
()
->
None
:
if
not
_TORCH_AVAILABLE
:
if
not
_TORCH_AVAILABLE
:
...
@@ -97,13 +100,14 @@ def train_lstm(
...
@@ -97,13 +100,14 @@ def train_lstm(
Args:
Args:
records: list of dicts with keys in FEATURE_COLS.
records: list of dicts with keys in FEATURE_COLS.
params: hyper-parameter dict (seq_len, hidden_size, num_layers,
params: hyper-parameter dict (seq_len, hidden_size, num_layers,
epochs, batch_size, learning_rate, train_ratio).
epochs, batch_size, learning_rate).
train_ratio is ignored – fixed 70/15/15 split is used.
save_path: destination .pt file.
save_path: destination .pt file.
on_progress: callback(pct, train_loss, val_loss) called after each epoch.
on_progress: callback(pct, train_loss, val_loss) called after each epoch.
cancel_event: when set, training stops with InterruptedError.
cancel_event: when set, training stops with InterruptedError.
Returns:
Returns:
{'train_loss': float, 'val_loss': float|None}
{'train_loss': float, 'val_loss': float|None
, 'test_loss': float|None
}
"""
"""
_check_torch
()
_check_torch
()
...
@@ -113,7 +117,6 @@ def train_lstm(
...
@@ -113,7 +117,6 @@ def train_lstm(
epochs
=
max
(
1
,
int
(
params
.
get
(
'epochs'
,
50
)))
epochs
=
max
(
1
,
int
(
params
.
get
(
'epochs'
,
50
)))
batch_size
=
max
(
1
,
int
(
params
.
get
(
'batch_size'
,
32
)))
batch_size
=
max
(
1
,
int
(
params
.
get
(
'batch_size'
,
32
)))
lr
=
float
(
params
.
get
(
'learning_rate'
,
0.001
))
lr
=
float
(
params
.
get
(
'learning_rate'
,
0.001
))
train_ratio
=
min
(
0.99
,
max
(
0.5
,
float
(
params
.
get
(
'train_ratio'
,
0.8
))))
# ── data preparation ────────────────────────────────────────────────────
# ── data preparation ────────────────────────────────────────────────────
data
=
_extract_features
(
records
)
data
=
_extract_features
(
records
)
...
@@ -132,17 +135,27 @@ def train_lstm(
...
@@ -132,17 +135,27 @@ def train_lstm(
data_norm
=
(
data
-
data_min
)
/
data_range
data_norm
=
(
data
-
data_min
)
/
data_range
X
,
y
=
_make_sequences
(
data_norm
,
seq_len
)
X
,
y
=
_make_sequences
(
data_norm
,
seq_len
)
n_train
=
max
(
1
,
int
(
len
(
X
)
*
train_ratio
))
# Fixed 70 / 15 / 15 split
n_total
=
len
(
X
)
n_train
=
max
(
1
,
int
(
n_total
*
_TRAIN_RATIO
))
n_val
=
max
(
1
,
int
(
n_total
*
_VAL_RATIO
))
# test gets the remainder so the three parts always sum to n_total
X_train
,
y_train
=
X
[:
n_train
],
y
[:
n_train
]
X_train
,
y_train
=
X
[:
n_train
],
y
[:
n_train
]
X_val
,
y_val
=
X
[
n_train
:],
y
[
n_train
:]
X_val
,
y_val
=
X
[
n_train
:
n_train
+
n_val
],
y
[
n_train
:
n_train
+
n_val
]
X_test
,
y_test
=
X
[
n_train
+
n_val
:],
y
[
n_train
+
n_val
:]
device
=
torch
.
device
(
'cpu'
)
device
=
torch
.
device
(
'cpu'
)
X_train_t
=
torch
.
tensor
(
X_train
)
.
to
(
device
)
X_train_t
=
torch
.
tensor
(
X_train
)
.
to
(
device
)
y_train_t
=
torch
.
tensor
(
y_train
)
.
to
(
device
)
y_train_t
=
torch
.
tensor
(
y_train
)
.
to
(
device
)
has_val
=
len
(
X_val
)
>
0
has_val
=
len
(
X_val
)
>
0
has_test
=
len
(
X_test
)
>
0
if
has_val
:
if
has_val
:
X_val_t
=
torch
.
tensor
(
X_val
)
.
to
(
device
)
X_val_t
=
torch
.
tensor
(
X_val
)
.
to
(
device
)
y_val_t
=
torch
.
tensor
(
y_val
)
.
to
(
device
)
y_val_t
=
torch
.
tensor
(
y_val
)
.
to
(
device
)
if
has_test
:
X_test_t
=
torch
.
tensor
(
X_test
)
.
to
(
device
)
y_test_t
=
torch
.
tensor
(
y_test
)
.
to
(
device
)
train_loader
=
DataLoader
(
train_loader
=
DataLoader
(
TensorDataset
(
X_train_t
,
y_train_t
),
TensorDataset
(
X_train_t
,
y_train_t
),
...
@@ -186,6 +199,14 @@ def train_lstm(
...
@@ -186,6 +199,14 @@ def train_lstm(
pct
=
int
((
epoch
+
1
)
/
epochs
*
100
)
pct
=
int
((
epoch
+
1
)
/
epochs
*
100
)
on_progress
(
pct
,
train_loss
,
val_loss
)
on_progress
(
pct
,
train_loss
,
val_loss
)
# ── test-set evaluation (completely hidden during training) ──────────────
test_loss
:
float
|
None
=
None
if
has_test
:
model
.
eval
()
with
torch
.
no_grad
():
test_pred
=
model
(
X_test_t
)
test_loss
=
criterion
(
test_pred
,
y_test_t
)
.
item
()
# ── persist ──────────────────────────────────────────────────────────────
# ── persist ──────────────────────────────────────────────────────────────
save_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
save_path
.
parent
.
mkdir
(
parents
=
True
,
exist_ok
=
True
)
torch
.
save
(
torch
.
save
(
...
@@ -206,5 +227,6 @@ def train_lstm(
...
@@ -206,5 +227,6 @@ def train_lstm(
return
{
return
{
'train_loss'
:
round
(
float
(
train_loss
),
6
),
'train_loss'
:
round
(
float
(
train_loss
),
6
),
'val_loss'
:
round
(
float
(
val_loss
),
6
)
if
val_loss
is
not
None
else
None
,
'val_loss'
:
round
(
float
(
val_loss
),
6
)
if
val_loss
is
not
None
else
None
,
'test_loss'
:
round
(
float
(
test_loss
),
6
)
if
test_loss
is
not
None
else
None
,
}
}
backend/app/models/data_management.py
View file @
36e2cc19
from
sqlalchemy
import
BIGINT
,
TIMESTAMP
,
Enum
,
Index
,
Integer
,
String
,
Text
,
text
from
sqlalchemy
import
BIGINT
,
TIMESTAMP
,
Enum
,
Index
,
Integer
,
JSON
,
String
,
Text
,
text
from
sqlalchemy.orm
import
Mapped
,
mapped_column
from
sqlalchemy.orm
import
Mapped
,
mapped_column
from
app.database
import
Base
from
app.database
import
Base
...
@@ -58,6 +58,7 @@ class DataPackage(Base):
...
@@ -58,6 +58,7 @@ class DataPackage(Base):
category_id
:
Mapped
[
int
|
None
]
=
mapped_column
(
BIGINT
,
nullable
=
True
,
comment
=
'分类ID(data_package类型)'
)
category_id
:
Mapped
[
int
|
None
]
=
mapped_column
(
BIGINT
,
nullable
=
True
,
comment
=
'分类ID(data_package类型)'
)
remark
:
Mapped
[
str
|
None
]
=
mapped_column
(
Text
,
nullable
=
True
,
comment
=
'备注'
)
remark
:
Mapped
[
str
|
None
]
=
mapped_column
(
Text
,
nullable
=
True
,
comment
=
'备注'
)
data_count
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'数据条数'
)
data_count
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'数据条数'
)
clean_rules
:
Mapped
[
dict
|
None
]
=
mapped_column
(
JSON
,
nullable
=
True
,
comment
=
'野值清洗规则'
)
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
updated_at
:
Mapped
[
str
]
=
mapped_column
(
updated_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
TIMESTAMP
,
...
...
backend/app/models/train_management.py
View file @
36e2cc19
...
@@ -25,6 +25,7 @@ class TrainTask(Base):
...
@@ -25,6 +25,7 @@ class TrainTask(Base):
progress
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'进度 0-100'
)
progress
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'进度 0-100'
)
train_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
,
comment
=
'训练损失'
)
train_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
,
comment
=
'训练损失'
)
val_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
,
comment
=
'验证损失'
)
val_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
,
comment
=
'验证损失'
)
test_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
,
comment
=
'测试损失'
)
error_msg
:
Mapped
[
str
|
None
]
=
mapped_column
(
Text
,
nullable
=
True
,
comment
=
'错误信息'
)
error_msg
:
Mapped
[
str
|
None
]
=
mapped_column
(
Text
,
nullable
=
True
,
comment
=
'错误信息'
)
is_saved
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'是否已保存为模型'
)
is_saved
:
Mapped
[
int
]
=
mapped_column
(
Integer
,
nullable
=
False
,
server_default
=
text
(
'0'
),
comment
=
'是否已保存为模型'
)
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
...
@@ -51,4 +52,5 @@ class SavedModel(Base):
...
@@ -51,4 +52,5 @@ class SavedModel(Base):
file_path
:
Mapped
[
str
]
=
mapped_column
(
String
(
500
),
nullable
=
False
,
comment
=
'模型文件路径'
)
file_path
:
Mapped
[
str
]
=
mapped_column
(
String
(
500
),
nullable
=
False
,
comment
=
'模型文件路径'
)
train_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
)
train_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
)
val_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
)
val_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
)
test_loss
:
Mapped
[
float
|
None
]
=
mapped_column
(
FLOAT
,
nullable
=
True
)
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
created_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
backend/app/services/package_management_service.py
View file @
36e2cc19
...
@@ -153,6 +153,7 @@ class PackageManagementService:
...
@@ -153,6 +153,7 @@ class PackageManagementService:
category_id
:
str
|
None
,
category_id
:
str
|
None
,
remark
:
str
|
None
,
remark
:
str
|
None
,
file_ids
:
list
[
int
],
file_ids
:
list
[
int
],
clean_rules
:
dict
|
None
=
None
,
)
->
dict
[
str
,
Any
]:
)
->
dict
[
str
,
Any
]:
if
not
name
:
if
not
name
:
raise
ValueError
(
'数据包名称不能为空'
)
raise
ValueError
(
'数据包名称不能为空'
)
...
@@ -177,13 +178,22 @@ class PackageManagementService:
...
@@ -177,13 +178,22 @@ class PackageManagementService:
if
len
(
files
)
!=
len
(
file_ids
):
if
len
(
files
)
!=
len
(
file_ids
):
raise
ValueError
(
'部分数据文件不存在'
)
raise
ValueError
(
'部分数据文件不存在'
)
total_count
=
sum
(
f
.
data_count
for
f
in
files
)
file_map
=
{
f
.
id
:
f
for
f
in
files
}
if
clean_rules
and
clean_rules
.
get
(
'enabled'
):
result
=
self
.
_merge_records
(
file_ids
,
file_map
,
limit
=
None
,
clean_rules
=
clean_rules
)
total_count
=
result
[
'count'
]
stored_rules
:
dict
|
None
=
clean_rules
else
:
total_count
=
sum
(
f
.
data_count
for
f
in
files
)
stored_rules
=
None
pkg
=
DataPackage
(
pkg
=
DataPackage
(
name
=
name
,
name
=
name
,
category_id
=
cat_db_id
,
category_id
=
cat_db_id
,
remark
=
remark
,
remark
=
remark
,
data_count
=
total_count
,
data_count
=
total_count
,
clean_rules
=
stored_rules
,
)
)
session
.
add
(
pkg
)
session
.
add
(
pkg
)
session
.
flush
()
session
.
flush
()
...
@@ -214,6 +224,8 @@ class PackageManagementService:
...
@@ -214,6 +224,8 @@ class PackageManagementService:
if
not
pkg
:
if
not
pkg
:
raise
ValueError
(
'数据包不存在'
)
raise
ValueError
(
'数据包不存在'
)
stored_clean_rules
=
pkg
.
clean_rules
pf_rows
=
(
pf_rows
=
(
session
.
query
(
DataPackageFile
)
session
.
query
(
DataPackageFile
)
.
filter
(
DataPackageFile
.
package_id
==
db_id
)
.
filter
(
DataPackageFile
.
package_id
==
db_id
)
...
@@ -224,9 +236,14 @@ class PackageManagementService:
...
@@ -224,9 +236,14 @@ class PackageManagementService:
files
=
session
.
query
(
DataFile
)
.
filter
(
DataFile
.
id
.
in_
(
file_ids
))
.
all
()
files
=
session
.
query
(
DataFile
)
.
filter
(
DataFile
.
id
.
in_
(
file_ids
))
.
all
()
file_map
=
{
f
.
id
:
f
for
f
in
files
}
file_map
=
{
f
.
id
:
f
for
f
in
files
}
return
self
.
_merge_records
(
file_ids
,
file_map
,
limit
)
return
self
.
_merge_records
(
file_ids
,
file_map
,
limit
,
clean_rules
=
stored_clean_rules
)
def
preview_records
(
self
,
file_ids
:
list
[
int
],
limit
:
int
=
300
)
->
dict
[
str
,
Any
]:
def
preview_records
(
self
,
file_ids
:
list
[
int
],
limit
:
int
=
300
,
clean_rules
:
dict
|
None
=
None
,
)
->
dict
[
str
,
Any
]:
if
not
file_ids
:
if
not
file_ids
:
return
{
'records'
:
[],
'count'
:
0
}
return
{
'records'
:
[],
'count'
:
0
}
...
@@ -234,7 +251,7 @@ class PackageManagementService:
...
@@ -234,7 +251,7 @@ class PackageManagementService:
files
=
session
.
query
(
DataFile
)
.
filter
(
DataFile
.
id
.
in_
(
file_ids
))
.
all
()
files
=
session
.
query
(
DataFile
)
.
filter
(
DataFile
.
id
.
in_
(
file_ids
))
.
all
()
file_map
=
{
f
.
id
:
f
for
f
in
files
}
file_map
=
{
f
.
id
:
f
for
f
in
files
}
return
self
.
_merge_records
(
file_ids
,
file_map
,
limit
)
return
self
.
_merge_records
(
file_ids
,
file_map
,
limit
,
clean_rules
=
clean_rules
)
# ── helpers ──────────────────────────────────────────────────────────────
# ── helpers ──────────────────────────────────────────────────────────────
...
@@ -242,8 +259,10 @@ class PackageManagementService:
...
@@ -242,8 +259,10 @@ class PackageManagementService:
self
,
self
,
file_ids
:
list
[
int
],
file_ids
:
list
[
int
],
file_map
:
dict
[
int
,
Any
],
file_map
:
dict
[
int
,
Any
],
limit
:
int
,
limit
:
int
|
None
,
clean_rules
:
dict
|
None
=
None
,
)
->
dict
[
str
,
Any
]:
)
->
dict
[
str
,
Any
]:
use_filter
=
bool
(
clean_rules
and
clean_rules
.
get
(
'enabled'
))
all_records
:
list
[
dict
[
str
,
Any
]]
=
[]
all_records
:
list
[
dict
[
str
,
Any
]]
=
[]
total_count
=
0
total_count
=
0
remaining
=
limit
remaining
=
limit
...
@@ -253,15 +272,61 @@ class PackageManagementService:
...
@@ -253,15 +272,61 @@ class PackageManagementService:
continue
continue
file_meta
=
file_map
[
fid
]
file_meta
=
file_map
[
fid
]
path
=
self
.
_dm
.
_resolve_local_file_path
(
file_meta
.
file_path
,
file_meta
.
stored_name
)
path
=
self
.
_dm
.
_resolve_local_file_path
(
file_meta
.
file_path
,
file_meta
.
stored_name
)
records
,
count
=
self
.
_dm
.
_read_records
(
path
,
limit
=
remaining
if
remaining
>
0
else
0
)
if
use_filter
:
# Read all rows so filter can be applied correctly
records
,
count
=
self
.
_dm
.
_read_records
(
path
,
limit
=
None
)
else
:
read_limit
=
(
remaining
if
remaining
is
not
None
and
remaining
>
0
else
0
)
if
remaining
is
not
None
else
None
records
,
count
=
self
.
_dm
.
_read_records
(
path
,
limit
=
read_limit
)
total_count
+=
count
total_count
+=
count
all_records
.
extend
(
records
)
all_records
.
extend
(
records
)
remaining
-=
len
(
records
)
if
not
use_filter
and
remaining
is
not
None
:
if
remaining
<=
0
:
remaining
-=
len
(
records
)
break
if
remaining
<=
0
:
break
if
use_filter
:
all_records
=
self
.
_apply_clean_rules
(
all_records
,
clean_rules
)
total_count
=
len
(
all_records
)
if
limit
is
not
None
and
limit
>
0
:
return
{
'records'
:
all_records
[:
limit
],
'count'
:
total_count
}
return
{
'records'
:
all_records
,
'count'
:
total_count
}
return
{
'records'
:
all_records
,
'count'
:
total_count
}
@
staticmethod
def
_apply_clean_rules
(
records
:
list
[
dict
[
str
,
Any
]],
clean_rules
:
dict
)
->
list
[
dict
[
str
,
Any
]]:
c_min
=
clean_rules
.
get
(
'current_min'
)
c_max
=
clean_rules
.
get
(
'current_max'
)
v_min
=
clean_rules
.
get
(
'voltage_min'
)
v_max
=
clean_rules
.
get
(
'voltage_max'
)
t_min
=
clean_rules
.
get
(
'temperature_min'
)
t_max
=
clean_rules
.
get
(
'temperature_max'
)
result
=
[]
for
r
in
records
:
current
=
r
.
get
(
'current'
)
voltage
=
r
.
get
(
'voltage'
)
temp
=
r
.
get
(
'actual_temperature'
)
if
current
is
not
None
:
if
c_min
is
not
None
and
current
<
c_min
:
continue
if
c_max
is
not
None
and
current
>
c_max
:
continue
if
voltage
is
not
None
:
if
v_min
is
not
None
and
voltage
<
v_min
:
continue
if
v_max
is
not
None
and
voltage
>
v_max
:
continue
if
temp
is
not
None
:
if
t_min
is
not
None
and
temp
<
t_min
:
continue
if
t_max
is
not
None
and
temp
>
t_max
:
continue
result
.
append
(
r
)
return
result
def
_pkg_to_dict
(
self
,
pkg
:
DataPackage
)
->
dict
[
str
,
Any
]:
def
_pkg_to_dict
(
self
,
pkg
:
DataPackage
)
->
dict
[
str
,
Any
]:
return
{
return
{
'id'
:
pkg
.
id
,
'id'
:
pkg
.
id
,
...
...
backend/app/services/train_service.py
View file @
36e2cc19
...
@@ -150,6 +150,7 @@ class TrainService:
...
@@ -150,6 +150,7 @@ class TrainService:
file_path
=
str
(
model_path
),
file_path
=
str
(
model_path
),
train_loss
=
task
.
train_loss
,
train_loss
=
task
.
train_loss
,
val_loss
=
task
.
val_loss
,
val_loss
=
task
.
val_loss
,
test_loss
=
task
.
test_loss
,
)
)
session
.
add
(
saved
)
session
.
add
(
saved
)
task
.
is_saved
=
1
task
.
is_saved
=
1
...
@@ -242,6 +243,7 @@ class TrainService:
...
@@ -242,6 +243,7 @@ class TrainService:
progress
=
100
,
progress
=
100
,
train_loss
=
result
[
'train_loss'
],
train_loss
=
result
[
'train_loss'
],
val_loss
=
result
.
get
(
'val_loss'
),
val_loss
=
result
.
get
(
'val_loss'
),
test_loss
=
result
.
get
(
'test_loss'
),
)
)
except
InterruptedError
:
except
InterruptedError
:
...
@@ -300,6 +302,7 @@ class TrainService:
...
@@ -300,6 +302,7 @@ class TrainService:
'progress'
:
task
.
progress
,
'progress'
:
task
.
progress
,
'train_loss'
:
task
.
train_loss
,
'train_loss'
:
task
.
train_loss
,
'val_loss'
:
task
.
val_loss
,
'val_loss'
:
task
.
val_loss
,
'test_loss'
:
task
.
test_loss
,
'error_msg'
:
task
.
error_msg
,
'error_msg'
:
task
.
error_msg
,
'is_saved'
:
bool
(
task
.
is_saved
),
'is_saved'
:
bool
(
task
.
is_saved
),
'created_at'
:
task
.
created_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
task
.
created_at
else
''
,
'created_at'
:
task
.
created_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
task
.
created_at
else
''
,
...
@@ -316,5 +319,6 @@ class TrainService:
...
@@ -316,5 +319,6 @@ class TrainService:
'params'
:
model
.
params
,
'params'
:
model
.
params
,
'train_loss'
:
model
.
train_loss
,
'train_loss'
:
model
.
train_loss
,
'val_loss'
:
model
.
val_loss
,
'val_loss'
:
model
.
val_loss
,
'test_loss'
:
model
.
test_loss
,
'created_at'
:
model
.
created_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
model
.
created_at
else
''
,
'created_at'
:
model
.
created_at
.
strftime
(
'
%
Y-
%
m-
%
d
%
H:
%
M:
%
S'
)
if
model
.
created_at
else
''
,
}
}
backend/saved_models/task_8.pt
0 → 100644
View file @
36e2cc19
File added
frontend/dist/assets/index-B6mx_Oi_.js
deleted
100644 → 0
View file @
3fd809ab
This diff is collapsed.
Click to expand it.
frontend/dist/assets/index-Bdu9N6pH.css
deleted
100644 → 0
View file @
3fd809ab
This source diff could not be displayed because it is too large. You can
view the blob
instead.
frontend/dist/index.html
View file @
36e2cc19
...
@@ -5,10 +5,10 @@
...
@@ -5,10 +5,10 @@
<link
rel=
"icon"
href=
"/favicon.ico"
>
<link
rel=
"icon"
href=
"/favicon.ico"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
热实验温度控制系统
</title>
<title>
热实验温度控制系统
</title>
<script
type=
"module"
crossorigin
src=
"/assets/index-
B6mx_Oi_
.js"
></script>
<script
type=
"module"
crossorigin
src=
"/assets/index-
D5S_ELZm
.js"
></script>
<link
rel=
"modulepreload"
crossorigin
href=
"/assets/_plugin-vue_export-helper-D1RKUtCV.js"
>
<link
rel=
"modulepreload"
crossorigin
href=
"/assets/_plugin-vue_export-helper-D1RKUtCV.js"
>
<link
rel=
"modulepreload"
crossorigin
href=
"/assets/es-DCOtnflc.js"
>
<link
rel=
"modulepreload"
crossorigin
href=
"/assets/es-DCOtnflc.js"
>
<link
rel=
"stylesheet"
crossorigin
href=
"/assets/index-B
du9N6pH
.css"
>
<link
rel=
"stylesheet"
crossorigin
href=
"/assets/index-B
rwBxauG
.css"
>
</head>
</head>
<body>
<body>
<div
id=
"app"
></div>
<div
id=
"app"
></div>
...
...
frontend/src/App.vue
View file @
36e2cc19
...
@@ -6,10 +6,10 @@ const route = useRoute()
...
@@ -6,10 +6,10 @@ const route = useRoute()
const
router
=
useRouter
()
const
router
=
useRouter
()
const
tabs
=
[
const
tabs
=
[
{
label
:
'数据
管理
'
,
path
:
'/data-management'
},
{
label
:
'数据
文件
'
,
path
:
'/data-management'
},
{
label
:
'数据包
管理
'
,
path
:
'/package-management'
},
{
label
:
'数据包'
,
path
:
'/package-management'
},
{
label
:
'模型训练'
,
path
:
'/model-training'
},
{
label
:
'模型训练'
,
path
:
'/model-training'
},
{
label
:
'模型
列表
'
,
path
:
'/model-list'
},
{
label
:
'模型
库
'
,
path
:
'/model-list'
},
{
label
:
'模型评估'
,
path
:
'/model-evaluation'
},
{
label
:
'模型评估'
,
path
:
'/model-evaluation'
},
{
label
:
'实时监控'
,
path
:
'/realtime-monitor'
},
{
label
:
'实时监控'
,
path
:
'/realtime-monitor'
},
{
label
:
'历史数据'
,
path
:
'/history-data'
},
{
label
:
'历史数据'
,
path
:
'/history-data'
},
...
@@ -30,7 +30,7 @@ const handleTabChange = (path) => {
...
@@ -30,7 +30,7 @@ const handleTabChange = (path) => {
<
template
>
<
template
>
<div
class=
"app-shell"
>
<div
class=
"app-shell"
>
<header
class=
"top-header"
>
<header
class=
"top-header"
>
<div
class=
"project-title"
>
热
实
验温度控制系统
</div>
<div
class=
"project-title"
>
热
试
验温度控制系统
</div>
<div
class=
"header-divider"
></div>
<div
class=
"header-divider"
></div>
<el-tabs
class=
"top-tabs"
:model-value=
"activeTab"
@
tab-change=
"handleTabChange"
>
<el-tabs
class=
"top-tabs"
:model-value=
"activeTab"
@
tab-change=
"handleTabChange"
>
<el-tab-pane
<el-tab-pane
...
...
frontend/src/views/DataManagement/components/DataCurve.vue
View file @
36e2cc19
...
@@ -81,12 +81,15 @@ const renderChart = () => {
...
@@ -81,12 +81,15 @@ const renderChart = () => {
return
''
return
''
}
}
const
unitMap
=
{
'电流(A)'
:
' A'
,
'电压(V)'
:
' V'
,
'温度(℃)'
:
' ℃'
}
const
lines
=
[
`<div style="margin-bottom:6px;font-weight:600;">
${
params
[
0
].
axisValue
}
</div>`
]
const
lines
=
[
`<div style="margin-bottom:6px;font-weight:600;">
${
params
[
0
].
axisValue
}
</div>`
]
params
.
forEach
((
item
)
=>
{
params
.
forEach
((
item
)
=>
{
const
val
=
Number
.
isFinite
(
item
.
data
)
?
Number
(
item
.
data
).
toFixed
(
2
)
:
'--'
const
unit
=
unitMap
[
item
.
seriesName
]
??
''
lines
.
push
(
lines
.
push
(
`<div style="display:flex;align-items:center;gap:6px;min-width:160px;justify-content:space-between;">
`<div style="display:flex;align-items:center;gap:6px;min-width:160px;justify-content:space-between;">
<span>
${
item
.
marker
}${
item
.
seriesName
}
</span>
<span>
${
item
.
marker
}${
item
.
seriesName
}
</span>
<strong>
${
formatValue
(
item
.
data
)
}
</strong>
<strong>
${
val
}${
unit
}
</strong>
</div>`
,
</div>`
,
)
)
})
})
...
@@ -103,9 +106,9 @@ const renderChart = () => {
...
@@ -103,9 +106,9 @@ const renderChart = () => {
data
:
[
'电流(A)'
,
'电压(V)'
,
'温度(℃)'
],
data
:
[
'电流(A)'
,
'电压(V)'
,
'温度(℃)'
],
},
},
grid
:
{
grid
:
{
top
:
24
,
top
:
48
,
left
:
24
,
left
:
24
,
right
:
24
,
right
:
60
,
bottom
:
56
,
bottom
:
56
,
containLabel
:
true
,
containLabel
:
true
,
},
},
...
@@ -123,18 +126,26 @@ const renderChart = () => {
...
@@ -123,18 +126,26 @@ const renderChart = () => {
},
},
},
},
},
},
yAxis
:
{
yAxis
:
[
type
:
'value'
,
{
axisLabel
:
{
type
:
'value'
,
color
:
'#64748b
'
,
name
:
'温度 (℃)
'
,
}
,
position
:
'left'
,
splitLine
:
{
nameTextStyle
:
{
color
:
'#64748b'
,
fontSize
:
11
},
lineStyle
:
{
axisLabel
:
{
color
:
'#64748b'
,
formatter
:
'{value} ℃'
},
type
:
'dashed'
,
splitLine
:
{
color
:
'rgba(148, 163, 184, 0.45)'
,
lineStyle
:
{
type
:
'dashed'
,
color
:
'rgba(148, 163, 184, 0.45)'
}
,
},
},
},
},
},
{
type
:
'value'
,
name
:
'电流 / 电压'
,
position
:
'right'
,
nameTextStyle
:
{
color
:
'#64748b'
,
fontSize
:
11
},
axisLabel
:
{
color
:
'#64748b'
},
splitLine
:
{
show
:
false
},
},
],
series
:
[
series
:
[
{
{
name
:
'电流(A)'
,
name
:
'电流(A)'
,
...
@@ -142,6 +153,7 @@ const renderChart = () => {
...
@@ -142,6 +153,7 @@ const renderChart = () => {
smooth
:
true
,
smooth
:
true
,
symbol
:
'circle'
,
symbol
:
'circle'
,
symbolSize
:
7
,
symbolSize
:
7
,
yAxisIndex
:
1
,
data
:
getSeriesData
(
'current'
),
data
:
getSeriesData
(
'current'
),
},
},
{
{
...
@@ -150,6 +162,7 @@ const renderChart = () => {
...
@@ -150,6 +162,7 @@ const renderChart = () => {
smooth
:
true
,
smooth
:
true
,
symbol
:
'circle'
,
symbol
:
'circle'
,
symbolSize
:
7
,
symbolSize
:
7
,
yAxisIndex
:
1
,
data
:
getSeriesData
(
'voltage'
),
data
:
getSeriesData
(
'voltage'
),
},
},
{
{
...
@@ -158,6 +171,7 @@ const renderChart = () => {
...
@@ -158,6 +171,7 @@ const renderChart = () => {
smooth
:
true
,
smooth
:
true
,
symbol
:
'circle'
,
symbol
:
'circle'
,
symbolSize
:
7
,
symbolSize
:
7
,
yAxisIndex
:
0
,
data
:
getSeriesData
(
'actual_temperature'
),
data
:
getSeriesData
(
'actual_temperature'
),
},
},
],
],
...
...
frontend/src/views/ModelTraining/index.vue
View file @
36e2cc19
...
@@ -23,7 +23,6 @@ const form = reactive({
...
@@ -23,7 +23,6 @@ const form = reactive({
epochs
:
50
,
epochs
:
50
,
batch_size
:
32
,
batch_size
:
32
,
learning_rate
:
0.001
,
learning_rate
:
0.001
,
train_ratio
:
0.8
,
},
},
})
})
...
@@ -152,15 +151,15 @@ const formatParams = (params) => {
...
@@ -152,15 +151,15 @@ const formatParams = (params) => {
`轮数
${
params
.
epochs
}
`
,
`轮数
${
params
.
epochs
}
`
,
`批次
${
params
.
batch_size
}
`
,
`批次
${
params
.
batch_size
}
`
,
`学习率
${
params
.
learning_rate
}
`
,
`学习率
${
params
.
learning_rate
}
`
,
`训练比
${
params
.
train_ratio
}
`
,
].
join
(
' / '
)
].
join
(
' / '
)
}
}
const
formatLoss
=
(
task
)
=>
{
const
formatLoss
=
(
task
)
=>
{
if
(
task
.
train_loss
==
null
)
return
'-'
if
(
task
.
train_loss
==
null
)
return
'-'
const
train
=
`训练:
${
Number
(
task
.
train_loss
).
toFixed
(
5
)}
`
const
parts
=
[
`训练:
${
Number
(
task
.
train_loss
).
toFixed
(
5
)}
`
]
const
val
=
task
.
val_loss
!=
null
?
` / 验证:
${
Number
(
task
.
val_loss
).
toFixed
(
5
)}
`
:
''
if
(
task
.
val_loss
!=
null
)
parts
.
push
(
`验证:
${
Number
(
task
.
val_loss
).
toFixed
(
5
)}
`
)
return
train
+
val
if
(
task
.
test_loss
!=
null
)
parts
.
push
(
`测试:
${
Number
(
task
.
test_loss
).
toFixed
(
5
)}
`
)
return
parts
.
join
(
' / '
)
}
}
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
...
@@ -279,17 +278,6 @@ onBeforeUnmount(stopPolling)
...
@@ -279,17 +278,6 @@ onBeforeUnmount(stopPolling)
style=
"width: 100%"
style=
"width: 100%"
/>
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"训练集比例"
>
<el-input-number
v-model=
"form.params.train_ratio"
:min=
"0.5"
:max=
"0.99"
:step=
"0.05"
:precision=
"2"
controls-position=
"right"
style=
"width: 100%"
/>
</el-form-item>
</div>
</div>
</div>
</div>
...
...
frontend/src/views/PackageManagement/components/AddPackage.vue
View file @
36e2cc19
...
@@ -45,6 +45,30 @@ const form = reactive({
...
@@ -45,6 +45,30 @@ const form = reactive({
remark
:
''
,
remark
:
''
,
})
})
// ── clean rules ───────────────────────────────────────────────────────────────
const
cleanRules
=
reactive
({
enabled
:
false
,
current_min
:
null
,
current_max
:
null
,
voltage_min
:
null
,
voltage_max
:
null
,
temperature_min
:
null
,
temperature_max
:
null
,
})
const
cleanRulesPayload
=
computed
(()
=>
{
if
(
!
cleanRules
.
enabled
)
return
null
return
{
enabled
:
true
,
current_min
:
cleanRules
.
current_min
??
null
,
current_max
:
cleanRules
.
current_max
??
null
,
voltage_min
:
cleanRules
.
voltage_min
??
null
,
voltage_max
:
cleanRules
.
voltage_max
??
null
,
temperature_min
:
cleanRules
.
temperature_min
??
null
,
temperature_max
:
cleanRules
.
temperature_max
??
null
,
}
})
// ── preview ───────────────────────────────────────────────────────────────────
// ── preview ───────────────────────────────────────────────────────────────────
const
previewLoading
=
ref
(
false
)
const
previewLoading
=
ref
(
false
)
const
previewRecords
=
ref
([])
const
previewRecords
=
ref
([])
...
@@ -62,7 +86,10 @@ const triggerPreview = () => {
...
@@ -62,7 +86,10 @@ const triggerPreview = () => {
previewDebounceTimer
=
setTimeout
(
async
()
=>
{
previewDebounceTimer
=
setTimeout
(
async
()
=>
{
previewLoading
.
value
=
true
previewLoading
.
value
=
true
try
{
try
{
const
result
=
await
previewPackage
({
file_ids
:
selectedFileIds
.
value
},
{
limit
:
300
})
const
result
=
await
previewPackage
(
{
file_ids
:
selectedFileIds
.
value
,
clean_rules
:
cleanRulesPayload
.
value
},
{
limit
:
300
},
)
previewRecords
.
value
=
result
.
records
previewRecords
.
value
=
result
.
records
previewTotal
.
value
=
result
.
count
previewTotal
.
value
=
result
.
count
}
finally
{
}
finally
{
...
@@ -72,6 +99,7 @@ const triggerPreview = () => {
...
@@ -72,6 +99,7 @@ const triggerPreview = () => {
}
}
watch
(
selectedFileIds
,
triggerPreview
,
{
deep
:
true
})
watch
(
selectedFileIds
,
triggerPreview
,
{
deep
:
true
})
watch
(
cleanRules
,
triggerPreview
,
{
deep
:
true
})
// ── save ──────────────────────────────────────────────────────────────────────
// ── save ──────────────────────────────────────────────────────────────────────
const
saving
=
ref
(
false
)
const
saving
=
ref
(
false
)
...
@@ -93,6 +121,7 @@ const handleGenerate = async () => {
...
@@ -93,6 +121,7 @@ const handleGenerate = async () => {
category_id
:
form
.
categoryId
||
null
,
category_id
:
form
.
categoryId
||
null
,
remark
:
form
.
remark
.
trim
()
||
null
,
remark
:
form
.
remark
.
trim
()
||
null
,
file_ids
:
selectedFileIds
.
value
,
file_ids
:
selectedFileIds
.
value
,
clean_rules
:
cleanRulesPayload
.
value
,
})
})
ElMessage
.
success
(
'数据包创建成功'
)
ElMessage
.
success
(
'数据包创建成功'
)
emit
(
'saved'
)
emit
(
'saved'
)
...
@@ -165,6 +194,67 @@ onMounted(async () => {
...
@@ -165,6 +194,67 @@ onMounted(async () => {
<el-form-item
label=
"数据包名称"
required
>
<el-form-item
label=
"数据包名称"
required
>
<el-input
v-model=
"form.name"
maxlength=
"100"
show-word-limit
placeholder=
"请输入数据包名称"
/>
<el-input
v-model=
"form.name"
maxlength=
"100"
show-word-limit
placeholder=
"请输入数据包名称"
/>
</el-form-item>
</el-form-item>
<el-form-item
label=
"清洗规则"
>
<div
class=
"clean-rules-wrap"
>
<el-checkbox
v-model=
"cleanRules.enabled"
>
野值清理
</el-checkbox>
<div
v-if=
"cleanRules.enabled"
class=
"clean-range-grid"
>
<div
class=
"clean-range-row"
>
<span
class=
"clean-range-label"
>
电流范围 (A)
</span>
<el-input-number
v-model=
"cleanRules.current_min"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最小值"
class=
"range-input"
/>
<span
class=
"range-sep"
>
~
</span>
<el-input-number
v-model=
"cleanRules.current_max"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最大值"
class=
"range-input"
/>
</div>
<div
class=
"clean-range-row"
>
<span
class=
"clean-range-label"
>
电压范围 (V)
</span>
<el-input-number
v-model=
"cleanRules.voltage_min"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最小值"
class=
"range-input"
/>
<span
class=
"range-sep"
>
~
</span>
<el-input-number
v-model=
"cleanRules.voltage_max"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最大值"
class=
"range-input"
/>
</div>
<div
class=
"clean-range-row"
>
<span
class=
"clean-range-label"
>
温度范围 (℃)
</span>
<el-input-number
v-model=
"cleanRules.temperature_min"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最小值"
class=
"range-input"
/>
<span
class=
"range-sep"
>
~
</span>
<el-input-number
v-model=
"cleanRules.temperature_max"
:controls=
"false"
:value-on-clear=
"null"
placeholder=
"最大值"
class=
"range-input"
/>
</div>
</div>
</div>
</el-form-item>
<el-form-item
label=
"备注"
>
<el-form-item
label=
"备注"
>
<el-input
<el-input
v-model=
"form.remark"
v-model=
"form.remark"
...
@@ -323,4 +413,41 @@ onMounted(async () => {
...
@@ -323,4 +413,41 @@ onMounted(async () => {
margin-bottom
:
16px
;
margin-bottom
:
16px
;
}
}
}
}
</
style
>
.clean-rules-wrap
{
width
:
100%
;
}
.clean-range-grid
{
margin-top
:
8px
;
display
:
flex
;
flex-direction
:
column
;
gap
:
8px
;
}
.clean-range-row
{
display
:
flex
;
align-items
:
center
;
gap
:
6px
;
}
.clean-range-label
{
font-size
:
12px
;
color
:
#475569
;
width
:
86px
;
flex-shrink
:
0
;
}
.range-input
{
width
:
88px
;
:deep
(
.el-input__inner
)
{
text-align
:
center
;
}
}
.range-sep
{
font-size
:
13px
;
color
:
#94a3b8
;
}
</
style
>
\ No newline at end of file
frontend/vite.config.js
View file @
36e2cc19
...
@@ -8,7 +8,6 @@ import vueDevTools from 'vite-plugin-vue-devtools'
...
@@ -8,7 +8,6 @@ import vueDevTools from 'vite-plugin-vue-devtools'
export
default
defineConfig
({
export
default
defineConfig
({
plugins
:
[
plugins
:
[
vue
(),
vue
(),
vueDevTools
(),
],
],
server
:
{
server
:
{
proxy
:
{
proxy
:
{
...
...
sql/init_tables.sql
View file @
36e2cc19
...
@@ -42,6 +42,9 @@ CREATE TABLE IF NOT EXISTS data_packages (
...
@@ -42,6 +42,9 @@ CREATE TABLE IF NOT EXISTS data_packages (
INDEX
idx_pkg_category
(
category_id
)
INDEX
idx_pkg_category
(
category_id
)
)
COMMENT
=
'数据包表'
;
)
COMMENT
=
'数据包表'
;
ALTER
TABLE
data_packages
ADD
COLUMN
IF
NOT
EXISTS
clean_rules
JSON
NULL
COMMENT
'野值清洗规则'
;
CREATE
TABLE
IF
NOT
EXISTS
data_package_files
(
CREATE
TABLE
IF
NOT
EXISTS
data_package_files
(
id
BIGINT
PRIMARY
KEY
AUTO_INCREMENT
,
id
BIGINT
PRIMARY
KEY
AUTO_INCREMENT
,
package_id
BIGINT
NOT
NULL
COMMENT
'数据包ID'
,
package_id
BIGINT
NOT
NULL
COMMENT
'数据包ID'
,
...
@@ -50,3 +53,9 @@ CREATE TABLE IF NOT EXISTS data_package_files (
...
@@ -50,3 +53,9 @@ CREATE TABLE IF NOT EXISTS data_package_files (
INDEX
idx_dpf_package
(
package_id
),
INDEX
idx_dpf_package
(
package_id
),
INDEX
idx_dpf_file
(
file_id
)
INDEX
idx_dpf_file
(
file_id
)
)
COMMENT
=
'数据包文件关联表'
;
)
COMMENT
=
'数据包文件关联表'
;
ALTER
TABLE
train_tasks
ADD
COLUMN
IF
NOT
EXISTS
test_loss
FLOAT
NULL
COMMENT
'测试集损失'
;
ALTER
TABLE
saved_models
ADD
COLUMN
IF
NOT
EXISTS
test_loss
FLOAT
NULL
COMMENT
'测试集损失'
;
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