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
b599ac8f
Commit
b599ac8f
authored
Apr 16, 2026
by
luwei
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
数据管理功能
parent
6ca52b5b
Pipeline
#362
failed with stages
Changes
10
Pipelines
1
Expand all
Show whitespace changes
Inline
Side-by-side
Showing
10 changed files
with
580 additions
and
229 deletions
+580
-229
database.py
backend/app/database.py
+1
-38
main.py
backend/app/main.py
+0
-5
data_management.py
backend/app/models/data_management.py
+1
-0
data_management_service.py
backend/app/services/data_management_service.py
+96
-39
温度2_20260416165018140191_19日15时38分33秒转换结果_回路76_-对应控点1.csv
...温度2_20260416165018140191_19日15时38分33秒转换结果_回路76_-对应控点1.csv
+0
-0
package-lock.json
frontend/package-lock.json
+33
-1
package.json
frontend/package.json
+2
-1
DataCurve.vue
frontend/src/views/DataManagement/components/DataCurve.vue
+196
-106
index.vue
frontend/src/views/DataManagement/index.vue
+219
-39
init_tables.sql
sql/init_tables.sql
+32
-0
No files found.
backend/app/database.py
View file @
b599ac8f
from
contextlib
import
contextmanager
from
contextlib
import
contextmanager
from
typing
import
Generator
from
typing
import
Generator
from
sqlalchemy
import
create_engine
,
text
from
sqlalchemy
import
create_engine
from
sqlalchemy.engine
import
URL
from
sqlalchemy.engine
import
URL
from
sqlalchemy.orm
import
Session
,
declarative_base
,
sessionmaker
from
sqlalchemy.orm
import
Session
,
declarative_base
,
sessionmaker
...
@@ -38,40 +38,3 @@ def db_session() -> Generator[Session, None, None]:
...
@@ -38,40 +38,3 @@ def db_session() -> Generator[Session, None, None]:
yield
session
yield
session
finally
:
finally
:
session
.
close
()
session
.
close
()
def
init_database
()
->
None
:
admin_engine
=
create_engine
(
_build_mysql_url
(),
pool_pre_ping
=
True
)
try
:
with
admin_engine
.
begin
()
as
connection
:
connection
.
execute
(
text
(
f
"CREATE DATABASE IF NOT EXISTS `{settings.mysql_database}` "
'CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci'
)
)
finally
:
admin_engine
.
dispose
()
with
engine
.
begin
()
as
connection
:
connection
.
execute
(
text
(
"""
CREATE TABLE IF NOT EXISTS categories (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
name VARCHAR(100) NOT NULL COMMENT '分类名称',
type ENUM('data_file', 'data_package') NOT NULL COMMENT '分类类型',
parent_id BIGINT DEFAULT NULL COMMENT '父分类ID',
sort_order INT DEFAULT 0 COMMENT '排序',
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_type (type),
INDEX idx_parent (parent_id)
) COMMENT='数据分类表'
"""
)
)
from
app.models
import
data_management
# noqa: F401
Base
.
metadata
.
create_all
(
bind
=
engine
)
\ No newline at end of file
backend/app/main.py
View file @
b599ac8f
...
@@ -4,7 +4,6 @@ from fastapi.middleware.cors import CORSMiddleware
...
@@ -4,7 +4,6 @@ from fastapi.middleware.cors import CORSMiddleware
from
fastapi.responses
import
JSONResponse
from
fastapi.responses
import
JSONResponse
from
app.api.data_management
import
router
as
data_management_router
from
app.api.data_management
import
router
as
data_management_router
from
app.database
import
init_database
from
app.utils.response
import
error_response
,
success_response
from
app.utils.response
import
error_response
,
success_response
...
@@ -47,10 +46,6 @@ def create_app() -> FastAPI:
...
@@ -47,10 +46,6 @@ def create_app() -> FastAPI:
async
def
health_check
():
async
def
health_check
():
return
success_response
(
data
=
{
'status'
:
'ok'
},
message
=
'服务正常'
)
return
success_response
(
data
=
{
'status'
:
'ok'
},
message
=
'服务正常'
)
@
app
.
on_event
(
'startup'
)
def
startup_event
():
init_database
()
app
.
include_router
(
data_management_router
,
prefix
=
'/api/data'
,
tags
=
[
'数据管理'
])
app
.
include_router
(
data_management_router
,
prefix
=
'/api/data'
,
tags
=
[
'数据管理'
])
return
app
return
app
...
...
backend/app/models/data_management.py
View file @
b599ac8f
...
@@ -39,6 +39,7 @@ class DataFile(Base):
...
@@ -39,6 +39,7 @@ class DataFile(Base):
id
:
Mapped
[
int
]
=
mapped_column
(
BIGINT
,
primary_key
=
True
,
autoincrement
=
True
)
id
:
Mapped
[
int
]
=
mapped_column
(
BIGINT
,
primary_key
=
True
,
autoincrement
=
True
)
filename
:
Mapped
[
str
]
=
mapped_column
(
String
(
255
),
nullable
=
False
,
comment
=
'原始文件名'
)
filename
:
Mapped
[
str
]
=
mapped_column
(
String
(
255
),
nullable
=
False
,
comment
=
'原始文件名'
)
stored_name
:
Mapped
[
str
]
=
mapped_column
(
String
(
255
),
nullable
=
False
,
unique
=
True
,
comment
=
'存储文件名'
)
stored_name
:
Mapped
[
str
]
=
mapped_column
(
String
(
255
),
nullable
=
False
,
unique
=
True
,
comment
=
'存储文件名'
)
file_path
:
Mapped
[
str
]
=
mapped_column
(
String
(
255
),
nullable
=
False
,
comment
=
'文件路径'
)
category_id
:
Mapped
[
int
|
None
]
=
mapped_column
(
BIGINT
,
nullable
=
True
,
comment
=
'分类ID'
)
category_id
:
Mapped
[
int
|
None
]
=
mapped_column
(
BIGINT
,
nullable
=
True
,
comment
=
'分类ID'
)
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
=
'数据条数'
)
uploaded_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
uploaded_at
:
Mapped
[
str
]
=
mapped_column
(
TIMESTAMP
,
nullable
=
False
,
server_default
=
text
(
'CURRENT_TIMESTAMP'
))
...
...
backend/app/services/data_management_service.py
View file @
b599ac8f
This diff is collapsed.
Click to expand it.
backend/uploads/data/温度2_20260416165018140191_19日15时38分33秒转换结果_回路76_-对应控点1.csv
0 → 100644
View file @
b599ac8f
This diff is collapsed.
Click to expand it.
frontend/package-lock.json
View file @
b599ac8f
...
@@ -8,6 +8,8 @@
...
@@ -8,6 +8,8 @@
"name"
:
"frontend"
,
"name"
:
"frontend"
,
"version"
:
"0.0.0"
,
"version"
:
"0.0.0"
,
"dependencies"
:
{
"dependencies"
:
{
"@element-plus/icons-vue"
:
"^2.3.1"
,
"echarts"
:
"^6.0.0"
,
"pinia"
:
"^3.0.4"
,
"pinia"
:
"^3.0.4"
,
"vue"
:
"^3.5.31"
,
"vue"
:
"^3.5.31"
,
"vue-router"
:
"^5.0.4"
"vue-router"
:
"^5.0.4"
...
@@ -500,7 +502,6 @@
...
@@ -500,7 +502,6 @@
"version"
:
"2.3.2"
,
"version"
:
"2.3.2"
,
"resolved"
:
"https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz"
,
"resolved"
:
"https://registry.npmjs.org/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz"
,
"integrity"
:
"sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A=="
,
"integrity"
:
"sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A=="
,
"dev"
:
true
,
"license"
:
"MIT"
,
"license"
:
"MIT"
,
"peerDependencies"
:
{
"peerDependencies"
:
{
"vue"
:
"^3.2.0"
"vue"
:
"^3.2.0"
...
@@ -1939,6 +1940,22 @@
...
@@ -1939,6 +1940,22 @@
"node"
:
">= 0.4"
"node"
:
">= 0.4"
}
}
},
},
"node_modules/echarts"
:
{
"version"
:
"6.0.0"
,
"resolved"
:
"https://registry.npmjs.org/echarts/-/echarts-6.0.0.tgz"
,
"integrity"
:
"sha512-Tte/grDQRiETQP4xz3iZWSvoHrkCQtwqd6hs+mifXcjrCuo2iKWbajFObuLJVBlDIJlOzgQPd1hsaKt/3+OMkQ=="
,
"license"
:
"Apache-2.0"
,
"dependencies"
:
{
"tslib"
:
"2.3.0"
,
"zrender"
:
"6.0.0"
}
},
"node_modules/echarts/node_modules/tslib"
:
{
"version"
:
"2.3.0"
,
"resolved"
:
"https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz"
,
"integrity"
:
"sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
,
"license"
:
"0BSD"
},
"node_modules/electron-to-chromium"
:
{
"node_modules/electron-to-chromium"
:
{
"version"
:
"1.5.338"
,
"version"
:
"1.5.338"
,
"resolved"
:
"https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.338.tgz"
,
"resolved"
:
"https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.338.tgz"
,
...
@@ -4067,6 +4084,21 @@
...
@@ -4067,6 +4084,21 @@
"funding"
:
{
"funding"
:
{
"url"
:
"https://github.com/sponsors/eemeli"
"url"
:
"https://github.com/sponsors/eemeli"
}
}
},
"node_modules/zrender"
:
{
"version"
:
"6.0.0"
,
"resolved"
:
"https://registry.npmjs.org/zrender/-/zrender-6.0.0.tgz"
,
"integrity"
:
"sha512-41dFXEEXuJpNecuUQq6JlbybmnHaqqpGlbH1yxnA5V9MMP4SbohSVZsJIwz+zdjQXSSlR1Vc34EgH1zxyTDvhg=="
,
"license"
:
"BSD-3-Clause"
,
"dependencies"
:
{
"tslib"
:
"2.3.0"
}
},
"node_modules/zrender/node_modules/tslib"
:
{
"version"
:
"2.3.0"
,
"resolved"
:
"https://registry.npmjs.org/tslib/-/tslib-2.3.0.tgz"
,
"integrity"
:
"sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
,
"license"
:
"0BSD"
}
}
}
}
}
}
frontend/package.json
View file @
b599ac8f
...
@@ -10,14 +10,15 @@
...
@@ -10,14 +10,15 @@
},
},
"dependencies"
:
{
"dependencies"
:
{
"@element-plus/icons-vue"
:
"^2.3.1"
,
"@element-plus/icons-vue"
:
"^2.3.1"
,
"echarts"
:
"^6.0.0"
,
"pinia"
:
"^3.0.4"
,
"pinia"
:
"^3.0.4"
,
"vue"
:
"^3.5.31"
,
"vue"
:
"^3.5.31"
,
"vue-router"
:
"^5.0.4"
"vue-router"
:
"^5.0.4"
},
},
"devDependencies"
:
{
"devDependencies"
:
{
"@vitejs/plugin-vue"
:
"^6.0.5"
,
"axios"
:
"^1.12.2"
,
"axios"
:
"^1.12.2"
,
"element-plus"
:
"^2.11.3"
,
"element-plus"
:
"^2.11.3"
,
"@vitejs/plugin-vue"
:
"^6.0.5"
,
"sass-embedded"
:
"^1.99.0"
,
"sass-embedded"
:
"^1.99.0"
,
"vite"
:
"^8.0.3"
,
"vite"
:
"^8.0.3"
,
"vite-plugin-vue-devtools"
:
"^8.1.1"
"vite-plugin-vue-devtools"
:
"^8.1.1"
...
...
frontend/src/views/DataManagement/components/DataCurve.vue
View file @
b599ac8f
<
script
setup
>
<
script
setup
>
import
{
computed
,
nextTick
,
onMounted
,
ref
,
watch
}
from
'vue'
import
*
as
echarts
from
'echarts'
import
{
computed
,
nextTick
,
onBeforeUnmount
,
onMounted
,
ref
,
watch
}
from
'vue'
const
props
=
defineProps
({
const
props
=
defineProps
({
records
:
{
records
:
{
...
@@ -8,124 +9,221 @@ const props = defineProps({
...
@@ -8,124 +9,221 @@ const props = defineProps({
},
},
})
})
const
canvasRef
=
ref
(
null
)
const
chartRef
=
ref
(
null
)
let
chartInstance
=
null
const
numericPoints
=
computed
(()
=>
{
const
chartData
=
computed
(()
=>
{
return
props
.
records
const
source
=
Array
.
isArray
(
props
.
records
)
?
props
.
records
:
[]
return
source
.
map
((
item
,
idx
)
=>
({
.
map
((
item
,
idx
)
=>
({
x
:
idx
,
idx
,
time
:
item
.
time
||
`第
${
idx
+
1
}
条`
,
current
:
Number
(
item
.
current
),
current
:
Number
(
item
.
current
),
voltage
:
Number
(
item
.
voltage
),
voltage
:
Number
(
item
.
voltage
),
temperature
:
Number
(
item
.
temperature
),
actual_temperature
:
Number
(
item
.
actual_
temperature
),
}))
}))
.
filter
((
item
)
=>
{
.
filter
((
item
)
=>
{
return
(
return
(
Number
.
isFinite
(
item
.
current
)
||
Number
.
isFinite
(
item
.
current
)
||
Number
.
isFinite
(
item
.
voltage
)
||
Number
.
isFinite
(
item
.
voltage
)
||
Number
.
isFinite
(
item
.
temperature
)
Number
.
isFinite
(
item
.
actual_
temperature
)
)
)
})
})
})
})
const
draw
=
()
=>
{
const
stats
=
computed
(()
=>
{
const
canvas
=
canvasRef
.
value
const
temps
=
chartData
.
value
if
(
!
canvas
)
{
.
map
((
item
)
=>
item
.
actual_temperature
)
return
.
filter
((
value
)
=>
Number
.
isFinite
(
value
))
return
{
count
:
chartData
.
value
.
length
,
max
:
temps
.
length
?
Math
.
max
(...
temps
)
:
0
,
min
:
temps
.
length
?
Math
.
min
(...
temps
)
:
0
,
}
}
})
const
dpr
=
window
.
devicePixelRatio
||
1
const
formatValue
=
(
value
)
=>
{
const
width
=
canvas
.
clientWidth
return
Number
.
isFinite
(
value
)
?
Number
(
value
).
toFixed
(
2
)
:
'--'
const
height
=
canvas
.
clientHeight
}
canvas
.
width
=
width
*
dpr
canvas
.
height
=
height
*
dpr
const
ctx
=
canvas
.
getContext
(
'2d'
)
const
getSeriesData
=
(
key
)
=>
{
ctx
.
scale
(
dpr
,
dpr
)
return
chartData
.
value
.
map
((
item
)
=>
(
Number
.
isFinite
(
item
[
key
])
?
item
[
key
]
:
null
)
)
ctx
.
clearRect
(
0
,
0
,
width
,
height
)
}
if
(
numericPoints
.
value
.
length
<
2
)
{
const
renderChart
=
()
=>
{
ctx
.
fillStyle
=
'#64748b'
if
(
!
chartRef
.
value
)
{
ctx
.
font
=
'14px Poppins, Noto Sans SC, sans-serif'
ctx
.
fillText
(
'暂无足够数据生成曲线'
,
20
,
height
/
2
)
return
return
}
}
const
padding
=
{
top
:
18
,
right
:
20
,
bottom
:
32
,
left
:
44
}
if
(
!
chartInstance
)
{
const
innerWidth
=
width
-
padding
.
left
-
padding
.
right
chartInstance
=
echarts
.
init
(
chartRef
.
value
)
const
innerHeight
=
height
-
padding
.
top
-
padding
.
bottom
const
allValues
=
numericPoints
.
value
.
flatMap
((
item
)
=>
[
item
.
current
,
item
.
voltage
,
item
.
temperature
])
.
filter
((
value
)
=>
Number
.
isFinite
(
value
))
const
minValue
=
Math
.
min
(...
allValues
)
const
maxValue
=
Math
.
max
(...
allValues
)
const
diff
=
maxValue
-
minValue
||
1
ctx
.
strokeStyle
=
'rgba(15, 23, 42, 0.14)'
ctx
.
lineWidth
=
1
ctx
.
beginPath
()
ctx
.
moveTo
(
padding
.
left
,
padding
.
top
)
ctx
.
lineTo
(
padding
.
left
,
height
-
padding
.
bottom
)
ctx
.
lineTo
(
width
-
padding
.
right
,
height
-
padding
.
bottom
)
ctx
.
stroke
()
const
drawLine
=
(
key
,
color
)
=>
{
ctx
.
beginPath
()
let
started
=
false
numericPoints
.
value
.
forEach
((
point
,
index
)
=>
{
const
value
=
point
[
key
]
if
(
!
Number
.
isFinite
(
value
))
{
return
}
}
const
x
=
padding
.
left
+
(
index
/
(
numericPoints
.
value
.
length
-
1
))
*
innerWidth
const
hasData
=
chartData
.
value
.
length
>
0
const
y
=
padding
.
top
+
((
maxValue
-
value
)
/
diff
)
*
innerHeight
if
(
!
started
)
{
chartInstance
.
setOption
(
ctx
.
moveTo
(
x
,
y
)
{
started
=
true
animation
:
true
,
}
else
{
color
:
[
'#409EFF'
,
'#67C23A'
,
'#FF6B6B'
],
ctx
.
lineTo
(
x
,
y
)
tooltip
:
{
trigger
:
'axis'
,
backgroundColor
:
'rgba(255,255,255,0.96)'
,
borderColor
:
'#e2e8f0'
,
borderWidth
:
1
,
textStyle
:
{
color
:
'#334155'
,
},
extraCssText
:
'box-shadow: 0 10px 30px rgba(15,23,42,0.16); border-radius: 12px;'
,
formatter
(
params
)
{
if
(
!
params
?.
length
)
{
return
''
}
}
})
if
(
started
)
{
const
lines
=
[
`<div style="margin-bottom:6px;font-weight:600;">
${
params
[
0
].
axisValue
}
</div>`
]
ctx
.
strokeStyle
=
color
params
.
forEach
((
item
)
=>
{
ctx
.
lineWidth
=
2
lines
.
push
(
ctx
.
stroke
()
`<div style="display:flex;align-items:center;gap:6px;min-width:160px;justify-content:space-between;">
}
<span>
${
item
.
marker
}${
item
.
seriesName
}
</span>
}
<strong>
${
formatValue
(
item
.
data
)}
</strong>
</div>`
,
)
})
return
lines
.
join
(
''
)
},
},
legend
:
{
bottom
:
0
,
itemWidth
:
18
,
itemHeight
:
10
,
textStyle
:
{
color
:
'#475569'
,
},
data
:
[
'电流(A)'
,
'电压(V)'
,
'温度(℃)'
],
},
grid
:
{
top
:
24
,
left
:
24
,
right
:
24
,
bottom
:
56
,
containLabel
:
true
,
},
xAxis
:
{
type
:
'category'
,
boundaryGap
:
false
,
data
:
chartData
.
value
.
map
((
item
)
=>
item
.
time
),
axisLabel
:
{
color
:
'#64748b'
,
rotate
:
35
,
},
axisLine
:
{
lineStyle
:
{
color
:
'#94a3b8'
,
},
},
},
yAxis
:
{
type
:
'value'
,
axisLabel
:
{
color
:
'#64748b'
,
},
splitLine
:
{
lineStyle
:
{
type
:
'dashed'
,
color
:
'rgba(148, 163, 184, 0.45)'
,
},
},
},
series
:
[
{
name
:
'电流(A)'
,
type
:
'line'
,
smooth
:
true
,
symbol
:
'circle'
,
symbolSize
:
7
,
data
:
getSeriesData
(
'current'
),
},
{
name
:
'电压(V)'
,
type
:
'line'
,
smooth
:
true
,
symbol
:
'circle'
,
symbolSize
:
7
,
data
:
getSeriesData
(
'voltage'
),
},
{
name
:
'温度(℃)'
,
type
:
'line'
,
smooth
:
true
,
symbol
:
'circle'
,
symbolSize
:
7
,
data
:
getSeriesData
(
'actual_temperature'
),
},
],
graphic
:
hasData
?
[]
:
[
{
type
:
'text'
,
left
:
'center'
,
top
:
'middle'
,
style
:
{
text
:
'暂无足够数据生成曲线'
,
fill
:
'#64748b'
,
fontSize
:
14
,
},
},
],
},
true
,
)
}
drawLine
(
'current'
,
'#0ea5e9'
)
const
resizeChart
=
()
=>
{
drawLine
(
'voltage'
,
'#f97316'
)
chartInstance
?.
resize
()
drawLine
(
'temperature'
,
'#ef4444'
)
}
}
onMounted
(
async
()
=>
{
onMounted
(
async
()
=>
{
await
nextTick
()
await
nextTick
()
draw
()
renderChart
()
window
.
addEventListener
(
'resize'
,
resizeChart
)
})
})
watch
(
watch
(
()
=>
props
.
records
,
()
=>
props
.
records
,
async
()
=>
{
async
()
=>
{
await
nextTick
()
await
nextTick
()
draw
()
renderChart
()
},
},
{
deep
:
true
},
{
deep
:
true
},
)
)
onBeforeUnmount
(()
=>
{
window
.
removeEventListener
(
'resize'
,
resizeChart
)
chartInstance
?.
dispose
()
chartInstance
=
null
})
</
script
>
</
script
>
<
template
>
<
template
>
<div
class=
"curve-box"
>
<div
class=
"curve-box"
>
<div
class=
"legend-row"
>
<div
class=
"stats-row"
>
<span
class=
"legend-item"
><i
class=
"dot dot-current"
/>
电流
</span>
<div
class=
"stat-card"
>
<span
class=
"legend-item"
><i
class=
"dot dot-voltage"
/>
电压
</span>
<div
class=
"stat-label"
>
总条数
</div>
<span
class=
"legend-item"
><i
class=
"dot dot-temperature"
/>
温度
</span>
<div
class=
"stat-value"
>
{{
stats
.
count
}}
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-label"
>
最高温度
</div>
<div
class=
"stat-value"
>
{{
formatValue
(
stats
.
max
)
}}
°C
</div>
</div>
<div
class=
"stat-card"
>
<div
class=
"stat-label"
>
最低温度
</div>
<div
class=
"stat-value"
>
{{
formatValue
(
stats
.
min
)
}}
°C
</div>
</div>
</div>
<canvas
ref=
"canvasRef"
class=
"curve-canvas"
/>
</div>
<div
ref=
"chartRef"
class=
"echarts-box"
/>
</div>
</div>
</
template
>
</
template
>
...
@@ -134,46 +232,38 @@ watch(
...
@@ -134,46 +232,38 @@ watch(
height
:
100%
;
height
:
100%
;
display
:
flex
;
display
:
flex
;
flex-direction
:
column
;
flex-direction
:
column
;
gap
:
10px
;
gap
:
14px
;
}
.legend-row
{
display
:
flex
;
gap
:
16px
;
color
:
#334155
;
font-size
:
13px
;
}
}
.
legend-item
{
.
stats-row
{
display
:
inline-flex
;
display
:
grid
;
align-items
:
center
;
grid-template-columns
:
repeat
(
3
,
minmax
(
0
,
1fr
))
;
gap
:
6
px
;
gap
:
12
px
;
}
}
.
dot
{
.
stat-card
{
width
:
9
px
;
padding
:
14px
16
px
;
height
:
9
px
;
border-radius
:
14
px
;
b
order-radius
:
50%
;
b
ackground
:
#f3f6fb
;
}
}
.dot-current
{
.stat-label
{
background
:
#0ea5e9
;
font-size
:
13px
;
}
color
:
#64748b
;
margin-bottom
:
8px
;
.dot-voltage
{
background
:
#f97316
;
}
}
.dot-temperature
{
.stat-value
{
background
:
#ef4444
;
font-size
:
18px
;
font-weight
:
700
;
color
:
#0f172a
;
}
}
.curve-canvas
{
.echarts-box
{
width
:
100%
;
flex
:
1
;
flex
:
1
;
min-height
:
28
0px
;
min-height
:
36
0px
;
border-radius
:
1
2
px
;
border-radius
:
1
4
px
;
border
:
1px
solid
rgba
(
15
,
23
,
42
,
0
.1
2
);
border
:
1px
solid
rgba
(
15
,
23
,
42
,
0
.1
);
background
:
linear-gradient
(
180deg
,
#ffffff
,
#f8fafc
);
background
:
linear-gradient
(
180deg
,
#ffffff
,
#f8fafc
);
}
}
</
style
>
</
style
>
frontend/src/views/DataManagement/index.vue
View file @
b599ac8f
This diff is collapsed.
Click to expand it.
sql/init_tables.sql
0 → 100644
View file @
b599ac8f
CREATE
DATABASE
IF
NOT
EXISTS
thermal_control_system
CHARACTER
SET
utf8mb4
COLLATE
utf8mb4_unicode_ci
;
USE
thermal_control_system
;
CREATE
TABLE
IF
NOT
EXISTS
categories
(
id
BIGINT
PRIMARY
KEY
AUTO_INCREMENT
,
name
VARCHAR
(
100
)
NOT
NULL
COMMENT
'分类名称'
,
type
ENUM
(
'data_file'
,
'data_package'
)
NOT
NULL
COMMENT
'分类类型'
,
parent_id
BIGINT
DEFAULT
NULL
COMMENT
'父分类ID'
,
sort_order
INT
NOT
NULL
DEFAULT
0
COMMENT
'排序'
,
created_at
TIMESTAMP
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
,
updated_at
TIMESTAMP
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
ON
UPDATE
CURRENT_TIMESTAMP
,
INDEX
idx_type
(
type
),
INDEX
idx_parent
(
parent_id
)
)
COMMENT
=
'数据分类表'
;
CREATE
TABLE
IF
NOT
EXISTS
data_files
(
id
BIGINT
PRIMARY
KEY
AUTO_INCREMENT
,
filename
VARCHAR
(
255
)
NOT
NULL
COMMENT
'原始文件名'
,
stored_name
VARCHAR
(
255
)
NOT
NULL
UNIQUE
COMMENT
'存储文件名'
,
file_path
VARCHAR
(
500
)
NOT
NULL
COMMENT
'文件路径'
,
category_id
BIGINT
DEFAULT
NULL
COMMENT
'分类ID'
,
data_count
INT
NOT
NULL
DEFAULT
0
COMMENT
'数据条数'
,
uploaded_at
TIMESTAMP
NOT
NULL
DEFAULT
CURRENT_TIMESTAMP
,
remark
TEXT
NULL
COMMENT
'备注'
,
INDEX
idx_file_category
(
category_id
)
)
COMMENT
=
'数据文件表'
;
ALTER
TABLE
data_files
ADD
COLUMN
IF
NOT
EXISTS
file_path
VARCHAR
(
500
)
NOT
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