优化了检测管理页
This commit is contained in:
parent
1e2e7c9567
commit
31a5a78224
@ -15,7 +15,7 @@ import {
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const isCollapse = ref(false);
|
const isCollapse = ref(false);
|
||||||
const activeMenu = ref(route.path); // 初始化为当前路由路径
|
const activeMenu = ref(route.path); // 初始化为当前路由路径
|
||||||
|
|
||||||
// 监听路由变化
|
// 监听路由变化
|
||||||
watch(
|
watch(
|
||||||
|
|||||||
@ -170,8 +170,8 @@ onMounted(() => {
|
|||||||
<!-- 统计卡片 -->
|
<!-- 统计卡片 -->
|
||||||
<el-row :gutter="20" class="mt-20">
|
<el-row :gutter="20" class="mt-20">
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-card
|
<el-card
|
||||||
class="statistics-card"
|
class="statistics-card"
|
||||||
shadow="hover"
|
shadow="hover"
|
||||||
@click="handleCardClick('/monitor/species')"
|
@click="handleCardClick('/monitor/species')"
|
||||||
>
|
>
|
||||||
@ -192,10 +192,7 @@ onMounted(() => {
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-card
|
<el-card class="statistics-card" shadow="hover">
|
||||||
class="statistics-card"
|
|
||||||
shadow="hover"
|
|
||||||
>
|
|
||||||
<template #header>
|
<template #header>
|
||||||
<div class="statistics-header">
|
<div class="statistics-header">
|
||||||
<span>环境监测</span>
|
<span>环境监测</span>
|
||||||
@ -219,8 +216,8 @@ onMounted(() => {
|
|||||||
</el-col>
|
</el-col>
|
||||||
|
|
||||||
<el-col :span="8">
|
<el-col :span="8">
|
||||||
<el-card
|
<el-card
|
||||||
class="statistics-card"
|
class="statistics-card"
|
||||||
shadow="hover"
|
shadow="hover"
|
||||||
@click="handleCardClick('/patrol/tasks')"
|
@click="handleCardClick('/patrol/tasks')"
|
||||||
>
|
>
|
||||||
|
|||||||
@ -31,6 +31,38 @@ const tableData = ref<EnvData[]>([
|
|||||||
},
|
},
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// 环境指标数据
|
||||||
|
const envStats = ref([
|
||||||
|
{
|
||||||
|
label: "水质指数",
|
||||||
|
value: 92.5,
|
||||||
|
status: "优",
|
||||||
|
type: "success",
|
||||||
|
trend: [85, 88, 90, 92.5], // 用于迷你图表
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "空气质量",
|
||||||
|
value: 76.8,
|
||||||
|
status: "良好",
|
||||||
|
type: "success",
|
||||||
|
trend: [75, 72, 78, 76.8],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "温度(°C)",
|
||||||
|
value: 25.6,
|
||||||
|
status: "正常",
|
||||||
|
type: "success",
|
||||||
|
trend: [24, 25, 26, 25.6],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "湿度(%)",
|
||||||
|
value: 65,
|
||||||
|
status: "正常",
|
||||||
|
type: "success",
|
||||||
|
trend: [62, 64, 66, 65],
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
const initChart = () => {
|
const initChart = () => {
|
||||||
const chartDom = document.getElementById("envChart");
|
const chartDom = document.getElementById("envChart");
|
||||||
if (!chartDom) return;
|
if (!chartDom) return;
|
||||||
@ -39,17 +71,46 @@ const initChart = () => {
|
|||||||
const option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: "24小时温度趋势",
|
text: "24小时温度趋势",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: "#2c3e50",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.9)",
|
||||||
|
borderColor: "#eee",
|
||||||
|
textStyle: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
data: ["00:00", "06:00", "12:00", "18:00", "24:00"],
|
data: ["00:00", "06:00", "12:00", "18:00", "24:00"],
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#ddd",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
name: "温度(°C)",
|
name: "温度(°C)",
|
||||||
|
nameTextStyle: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#eee",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@ -57,10 +118,39 @@ const initChart = () => {
|
|||||||
data: [22, 20, 25, 28, 23],
|
data: [22, 20, 25, 28, 23],
|
||||||
smooth: true,
|
smooth: true,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#409EFF",
|
color: "#1677ff",
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: "linear",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: "rgba(22,119,255,0.2)" },
|
||||||
|
{ offset: 1, color: "rgba(22,119,255,0.01)" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
color: "#1677ff",
|
||||||
|
borderColor: "#fff",
|
||||||
|
borderWidth: 2,
|
||||||
|
shadowColor: "rgba(0,0,0,0.2)",
|
||||||
|
shadowBlur: 10,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
grid: {
|
||||||
|
left: "3%",
|
||||||
|
right: "4%",
|
||||||
|
bottom: "3%",
|
||||||
|
top: "60px",
|
||||||
|
containLabel: true,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
myChart.setOption(option);
|
myChart.setOption(option);
|
||||||
@ -83,13 +173,142 @@ const handleExportConfirm = () => {
|
|||||||
exportDialogVisible.value = false;
|
exportDialogVisible.value = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 初始化迷你图表
|
||||||
|
const initMiniChart = (el: HTMLElement, data: number[]) => {
|
||||||
|
const chart = echarts.init(el);
|
||||||
|
chart.setOption({
|
||||||
|
grid: {
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
top: 0,
|
||||||
|
bottom: 0,
|
||||||
|
},
|
||||||
|
xAxis: {
|
||||||
|
type: "category",
|
||||||
|
show: false,
|
||||||
|
boundaryGap: false,
|
||||||
|
},
|
||||||
|
yAxis: {
|
||||||
|
type: "value",
|
||||||
|
show: false,
|
||||||
|
scale: true,
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
data: data,
|
||||||
|
type: "line",
|
||||||
|
smooth: true,
|
||||||
|
symbol: "circle",
|
||||||
|
symbolSize: 0,
|
||||||
|
sampling: "average",
|
||||||
|
showSymbol: false,
|
||||||
|
emphasis: {
|
||||||
|
focus: "series",
|
||||||
|
showSymbol: true,
|
||||||
|
symbolSize: 4,
|
||||||
|
},
|
||||||
|
lineStyle: {
|
||||||
|
color: {
|
||||||
|
type: "linear",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: "#409EFF",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: "#36CFFB",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
width: 2,
|
||||||
|
},
|
||||||
|
areaStyle: {
|
||||||
|
color: {
|
||||||
|
type: "linear",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{
|
||||||
|
offset: 0,
|
||||||
|
color: "rgba(64,158,255,0.25)",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
offset: 1,
|
||||||
|
color: "rgba(54,207,251,0.05)",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
tooltip: {
|
||||||
|
trigger: "axis",
|
||||||
|
formatter: "{c}",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.9)",
|
||||||
|
borderColor: "#eee",
|
||||||
|
borderWidth: 1,
|
||||||
|
textStyle: {
|
||||||
|
color: "#666",
|
||||||
|
fontSize: 12,
|
||||||
|
},
|
||||||
|
padding: [4, 8],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// 监听卡片hover事件
|
||||||
|
const card = el.closest(".env-card");
|
||||||
|
if (card) {
|
||||||
|
card.addEventListener("mouseenter", () => {
|
||||||
|
chart.dispatchAction({
|
||||||
|
type: "showTip",
|
||||||
|
seriesIndex: 0,
|
||||||
|
dataIndex: data.length - 1,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
card.addEventListener("mouseleave", () => {
|
||||||
|
chart.dispatchAction({
|
||||||
|
type: "hideTip",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
// 初始化所有迷你图表
|
||||||
|
envStats.value.forEach((stat, index) => {
|
||||||
|
const el = document.getElementById(`miniChart${index}`);
|
||||||
|
if (el) {
|
||||||
|
initMiniChart(el, stat.trend);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// 原有的图表初始化
|
||||||
initChart();
|
initChart();
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="env-container">
|
<div class="env-container">
|
||||||
|
<!-- 环境指标卡片 -->
|
||||||
|
<el-row :gutter="20" class="mb-20">
|
||||||
|
<el-col :span="6" v-for="(item, index) in envStats" :key="index">
|
||||||
|
<div class="env-card">
|
||||||
|
<div class="env-header">
|
||||||
|
<span class="label">{{ item.label }}</span>
|
||||||
|
<el-tag :type="item.type" effect="plain">{{ item.status }}</el-tag>
|
||||||
|
</div>
|
||||||
|
<div class="env-value">{{ item.value }}</div>
|
||||||
|
<div class="env-chart" :id="`miniChart${index}`"></div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-card>
|
<el-card>
|
||||||
@ -169,54 +388,193 @@ onMounted(() => {
|
|||||||
@import "../../../styles/variables.scss";
|
@import "../../../styles/variables.scss";
|
||||||
|
|
||||||
.env-container {
|
.env-container {
|
||||||
|
.env-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
height: 140px;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.env-chart {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: $text-secondary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-tag {
|
||||||
|
border: none;
|
||||||
|
padding: 2px 8px;
|
||||||
|
font-size: 12px;
|
||||||
|
height: 22px;
|
||||||
|
line-height: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-value {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
line-height: 1;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.env-chart {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 50px;
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: opacity 0.3s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.el-card {
|
.el-card {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s;
|
||||||
box-shadow: $box-shadow;
|
box-shadow: $box-shadow;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
.el-tag {
|
||||||
display: flex;
|
transition: all 0.3s;
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
span {
|
&.el-tag--success {
|
||||||
font-size: 16px;
|
background-color: rgba(103, 194, 58, 0.1);
|
||||||
font-weight: 500;
|
border-color: rgba(103, 194, 58, 0.2);
|
||||||
color: $text-primary;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.mt-20 {
|
&.el-tag--warning {
|
||||||
margin-top: 20px;
|
background-color: rgba(230, 162, 60, 0.1);
|
||||||
}
|
border-color: rgba(230, 162, 60, 0.2);
|
||||||
|
}
|
||||||
:deep(.el-table) {
|
}
|
||||||
th.el-table__cell {
|
|
||||||
background-color: #fafafa;
|
|
||||||
color: $text-primary;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-table__cell {
|
.el-table {
|
||||||
padding: 12px 0;
|
:deep(tbody tr) {
|
||||||
}
|
transition: all 0.3s;
|
||||||
}
|
|
||||||
|
|
||||||
.dialog-footer {
|
&:hover {
|
||||||
.el-button {
|
background-color: rgba(64, 158, 255, 0.1) !important;
|
||||||
margin-left: 12px;
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
.el-card__body {
|
:deep(.el-checkbox-group) {
|
||||||
padding: 20px;
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
|
||||||
|
.el-checkbox {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: $text-primary;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: $primary-color;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-20 {
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-table) {
|
||||||
|
th.el-table__cell {
|
||||||
|
background-color: #fafafa;
|
||||||
|
color: $text-primary;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-table__cell {
|
||||||
|
padding: 12px 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.dialog-footer {
|
||||||
|
.el-button {
|
||||||
|
margin-left: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-card__body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-dialog) {
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.el-dialog__header {
|
||||||
|
margin: 0;
|
||||||
|
padding: 20px;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__body {
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-dialog__footer {
|
||||||
|
padding: 16px 20px;
|
||||||
|
border-top: 1px solid $border-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -39,19 +39,48 @@ const initChart = () => {
|
|||||||
const option = {
|
const option = {
|
||||||
title: {
|
title: {
|
||||||
text: "物种分布统计",
|
text: "物种分布统计",
|
||||||
|
textStyle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 500,
|
||||||
|
color: "#2c3e50",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: "axis",
|
trigger: "axis",
|
||||||
|
backgroundColor: "rgba(255,255,255,0.9)",
|
||||||
|
borderColor: "#eee",
|
||||||
|
textStyle: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
data: ["数量"],
|
data: ["数量"],
|
||||||
|
textStyle: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
xAxis: {
|
xAxis: {
|
||||||
type: "category",
|
type: "category",
|
||||||
data: ["东方白鹳", "黑鹳", "大天鹅", "小天鹅"],
|
data: ["东方白鹳", "黑鹳", "大天鹅", "小天鹅"],
|
||||||
|
axisLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#ddd",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
yAxis: {
|
yAxis: {
|
||||||
type: "value",
|
type: "value",
|
||||||
|
splitLine: {
|
||||||
|
lineStyle: {
|
||||||
|
color: "#eee",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
axisLabel: {
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
{
|
{
|
||||||
@ -59,7 +88,34 @@ const initChart = () => {
|
|||||||
type: "bar",
|
type: "bar",
|
||||||
data: [12, 8, 15, 10],
|
data: [12, 8, 15, 10],
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: "#409EFF",
|
color: {
|
||||||
|
type: "linear",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: "#83bff6" },
|
||||||
|
{ offset: 0.5, color: "#188df0" },
|
||||||
|
{ offset: 1, color: "#188df0" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
itemStyle: {
|
||||||
|
color: {
|
||||||
|
type: "linear",
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
x2: 0,
|
||||||
|
y2: 1,
|
||||||
|
colorStops: [
|
||||||
|
{ offset: 0, color: "#71a5eb" },
|
||||||
|
{ offset: 0.7, color: "#1677ff" },
|
||||||
|
{ offset: 1, color: "#1677ff" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
@ -91,6 +147,61 @@ const handleExportConfirm = () => {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="species-container">
|
<div class="species-container">
|
||||||
|
<!-- 统计卡片 -->
|
||||||
|
<el-row :gutter="20" class="mb-20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon">
|
||||||
|
<el-icon><Histogram /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="label">总物种数</div>
|
||||||
|
<div class="value">128</div>
|
||||||
|
<div class="trend up">
|
||||||
|
<el-icon><ArrowUp /></el-icon>
|
||||||
|
较上月增长 8%
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon warning">
|
||||||
|
<el-icon><Timer /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="label">本月新增</div>
|
||||||
|
<div class="value">12</div>
|
||||||
|
<div class="trend">较上月 +3</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon success">
|
||||||
|
<el-icon><Location /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="label">监测点位</div>
|
||||||
|
<div class="value">24</div>
|
||||||
|
<div class="trend">覆盖率 95%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<div class="stat-card">
|
||||||
|
<div class="stat-icon info">
|
||||||
|
<el-icon><Camera /></el-icon>
|
||||||
|
</div>
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="label">今日识别</div>
|
||||||
|
<div class="value">386</div>
|
||||||
|
<div class="trend">准确率 98%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-card>
|
<el-card>
|
||||||
@ -160,14 +271,121 @@ const handleExportConfirm = () => {
|
|||||||
.el-card {
|
.el-card {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border: none;
|
border: none;
|
||||||
border-radius: 4px;
|
border-radius: 8px;
|
||||||
|
transition: all 0.3s;
|
||||||
box-shadow: $box-shadow;
|
box-shadow: $box-shadow;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
.el-card__header {
|
.el-card__header {
|
||||||
padding: 16px 20px;
|
padding: 16px 20px;
|
||||||
border-bottom: 1px solid $border-color;
|
border-bottom: 1px solid $border-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.el-table {
|
||||||
|
:deep(tbody tr) {
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(64, 158, 255, 0.1) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.el-button) {
|
||||||
|
transition: all 0.3s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-20 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-card {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
transition: all 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.06);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: rgba(64, 158, 255, 0.1);
|
||||||
|
color: $primary-color;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 24px;
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: rgba(230, 162, 60, 0.1);
|
||||||
|
color: $warning-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background: rgba(103, 194, 58, 0.1);
|
||||||
|
color: $success-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.info {
|
||||||
|
background: rgba(144, 147, 153, 0.1);
|
||||||
|
color: $info-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.label {
|
||||||
|
color: $text-secondary;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
font-size: 24px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: $text-primary;
|
||||||
|
line-height: 1.2;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.trend {
|
||||||
|
font-size: 13px;
|
||||||
|
color: $text-secondary;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
|
||||||
|
&.up {
|
||||||
|
color: $success-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-icon {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-header {
|
.card-header {
|
||||||
@ -179,6 +397,20 @@ const handleExportConfirm = () => {
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
color: $text-primary;
|
color: $text-primary;
|
||||||
|
position: relative;
|
||||||
|
padding-left: 12px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: "";
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 4px;
|
||||||
|
height: 16px;
|
||||||
|
background: $primary-color;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue';
|
import { ref } from "vue";
|
||||||
|
|
||||||
// 系统参数配置
|
// 系统参数配置
|
||||||
const systemConfig = ref({
|
const systemConfig = ref({
|
||||||
dataCollectionInterval: 5, // 数据采集间隔(分钟)
|
dataCollectionInterval: 5, // 数据采集间隔(分钟)
|
||||||
dataRetentionDays: 90, // 数据保留天数
|
dataRetentionDays: 90, // 数据保留天数
|
||||||
autoBackupEnabled: true, // 自动备份开关
|
autoBackupEnabled: true, // 自动备份开关
|
||||||
backupInterval: 24 // 备份间隔(小时)
|
backupInterval: 24, // 备份间隔(小时)
|
||||||
});
|
});
|
||||||
|
|
||||||
// AI模型配置
|
// AI模型配置
|
||||||
const aiConfig = ref({
|
const aiConfig = ref({
|
||||||
modelVersion: 'v2.0.1',
|
modelVersion: "v2.0.1",
|
||||||
confidenceThreshold: 0.85,
|
confidenceThreshold: 0.85,
|
||||||
maxDetections: 100,
|
maxDetections: 100,
|
||||||
enabledModels: ['bird', 'fish', 'plant']
|
enabledModels: ["bird", "fish", "plant"],
|
||||||
});
|
});
|
||||||
|
|
||||||
// 预警阈值配置
|
// 预警阈值配置
|
||||||
@ -22,20 +22,24 @@ const alertConfig = ref({
|
|||||||
waterQuality: {
|
waterQuality: {
|
||||||
ph: { min: 6.5, max: 8.5 },
|
ph: { min: 6.5, max: 8.5 },
|
||||||
dissolvedOxygen: { min: 5, max: 9 },
|
dissolvedOxygen: { min: 5, max: 9 },
|
||||||
temperature: { min: 15, max: 30 }
|
temperature: { min: 15, max: 30 },
|
||||||
},
|
},
|
||||||
airQuality: {
|
airQuality: {
|
||||||
pm25: { max: 75 },
|
pm25: { max: 75 },
|
||||||
humidity: { min: 30, max: 70 }
|
humidity: { min: 30, max: 70 },
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// 保存配置
|
// 保存配置
|
||||||
const handleSave = (type: string) => {
|
const handleSave = (type: string) => {
|
||||||
// 这里模拟保存配置
|
// 这里模拟保存配置
|
||||||
console.log(`保存${type}配置:`,
|
console.log(
|
||||||
type === 'system' ? systemConfig.value :
|
`保存${type}配置:`,
|
||||||
type === 'ai' ? aiConfig.value : alertConfig.value
|
type === "system"
|
||||||
|
? systemConfig.value
|
||||||
|
: type === "ai"
|
||||||
|
? aiConfig.value
|
||||||
|
: alertConfig.value
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
@ -50,10 +54,14 @@ const handleSave = (type: string) => {
|
|||||||
<el-button type="primary" @click="handleSave('system')">保存配置</el-button>
|
<el-button type="primary" @click="handleSave('system')">保存配置</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-form :model="systemConfig" label-width="160px">
|
<el-form :model="systemConfig" label-width="160px">
|
||||||
<el-form-item label="数据采集间隔(分钟)">
|
<el-form-item label="数据采集间隔(分钟)">
|
||||||
<el-input-number v-model="systemConfig.dataCollectionInterval" :min="1" :max="60" />
|
<el-input-number
|
||||||
|
v-model="systemConfig.dataCollectionInterval"
|
||||||
|
:min="1"
|
||||||
|
:max="60"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="数据保留天数">
|
<el-form-item label="数据保留天数">
|
||||||
<el-input-number v-model="systemConfig.dataRetentionDays" :min="30" :max="365" />
|
<el-input-number v-model="systemConfig.dataRetentionDays" :min="30" :max="365" />
|
||||||
@ -75,13 +83,18 @@ const handleSave = (type: string) => {
|
|||||||
<el-button type="primary" @click="handleSave('ai')">保存配置</el-button>
|
<el-button type="primary" @click="handleSave('ai')">保存配置</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-form :model="aiConfig" label-width="160px">
|
<el-form :model="aiConfig" label-width="160px">
|
||||||
<el-form-item label="模型版本">
|
<el-form-item label="模型版本">
|
||||||
<el-input v-model="aiConfig.modelVersion" disabled />
|
<el-input v-model="aiConfig.modelVersion" disabled />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="置信度阈值">
|
<el-form-item label="置信度阈值">
|
||||||
<el-slider v-model="aiConfig.confidenceThreshold" :min="0" :max="1" :step="0.01" />
|
<el-slider
|
||||||
|
v-model="aiConfig.confidenceThreshold"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:step="0.01"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="最大检测数量">
|
<el-form-item label="最大检测数量">
|
||||||
<el-input-number v-model="aiConfig.maxDetections" :min="10" :max="500" />
|
<el-input-number v-model="aiConfig.maxDetections" :min="10" :max="500" />
|
||||||
@ -104,26 +117,40 @@ const handleSave = (type: string) => {
|
|||||||
<el-button type="primary" @click="handleSave('alert')">保存配置</el-button>
|
<el-button type="primary" @click="handleSave('alert')">保存配置</el-button>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<el-form :model="alertConfig" label-width="160px">
|
<el-form :model="alertConfig" label-width="160px">
|
||||||
<el-divider content-position="left">水质指标</el-divider>
|
<el-divider content-position="left">水质指标</el-divider>
|
||||||
<el-form-item label="pH值范围">
|
<el-form-item label="pH值范围">
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
<el-input-number v-model="alertConfig.waterQuality.ph.min" :precision="1" :step="0.1" />
|
<el-input-number
|
||||||
|
v-model="alertConfig.waterQuality.ph.min"
|
||||||
|
:precision="1"
|
||||||
|
:step="0.1"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="2" class="text-center">-</el-col>
|
<el-col :span="2" class="text-center">-</el-col>
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
<el-input-number v-model="alertConfig.waterQuality.ph.max" :precision="1" :step="0.1" />
|
<el-input-number
|
||||||
|
v-model="alertConfig.waterQuality.ph.max"
|
||||||
|
:precision="1"
|
||||||
|
:step="0.1"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="溶解氧范围(mg/L)">
|
<el-form-item label="溶解氧范围(mg/L)">
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
<el-input-number v-model="alertConfig.waterQuality.dissolvedOxygen.min" :precision="1" />
|
<el-input-number
|
||||||
|
v-model="alertConfig.waterQuality.dissolvedOxygen.min"
|
||||||
|
:precision="1"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="2" class="text-center">-</el-col>
|
<el-col :span="2" class="text-center">-</el-col>
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
<el-input-number v-model="alertConfig.waterQuality.dissolvedOxygen.max" :precision="1" />
|
<el-input-number
|
||||||
|
v-model="alertConfig.waterQuality.dissolvedOxygen.max"
|
||||||
|
:precision="1"
|
||||||
|
/>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
@ -131,7 +158,7 @@ const handleSave = (type: string) => {
|
|||||||
<el-form-item label="PM2.5上限(μg/m³)">
|
<el-form-item label="PM2.5上限(μg/m³)">
|
||||||
<el-input-number v-model="alertConfig.airQuality.pm25.max" />
|
<el-input-number v-model="alertConfig.airQuality.pm25.max" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item label="湿度范围(%)">
|
<el-form-item label="湿度范围(%)">
|
||||||
<el-col :span="11">
|
<el-col :span="11">
|
||||||
<el-input-number v-model="alertConfig.airQuality.humidity.min" />
|
<el-input-number v-model="alertConfig.airQuality.humidity.min" />
|
||||||
@ -177,4 +204,4 @@ const handleSave = (type: string) => {
|
|||||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user