预测页面UI优化与同步

This commit is contained in:
LYFxiaoan 2025-07-23 15:06:42 +08:00
parent 01d3cd0aac
commit dc4cc34f11
5 changed files with 67 additions and 49 deletions

View File

@ -74,33 +74,23 @@
<div v-if="availableVersions.length > 0" class="versions-list-section">
<h4>📦 可用模型版本</h4>
<el-table :data="availableVersions" style="width: 100%" v-loading="versionsLoading">
<el-table-column prop="aggregation_method" label="聚合方法" width="180">
<el-table-column prop="aggregation_method" label="聚合方法">
<template #default="{ row }">
{{ row.aggregation_method || 'N/A' }}
</template>
</el-table-column>
<el-table-column prop="version" label="版本号" width="180"></el-table-column>
<el-table-column prop="created_at" label="创建时间" width="200">
<el-table-column prop="version" label="版本号"></el-table-column>
<el-table-column prop="created_at" label="创建时间">
<template #default="{ row }">
{{ new Date(row.created_at).toLocaleString() }}
</template>
</el-table-column>
<el-table-column prop="metrics.mae" label="MAE">
<el-table-column prop="model_type" label="算法类型">
<template #default="{ row }">
{{ row.metrics && row.metrics.mae ? row.metrics.mae.toFixed(4) : 'N/A' }}
{{ getModelTypeName(row.model_type) }}
</template>
</el-table-column>
<el-table-column prop="metrics.rmse" label="RMSE">
<template #default="{ row }">
{{ row.metrics && row.metrics.rmse ? row.metrics.rmse.toFixed(4) : 'N/A' }}
</template>
</el-table-column>
<el-table-column prop="metrics.mape" label="MAPE (%)">
<template #default="{ row }">
{{ row.metrics && row.metrics.mape ? (row.metrics.mape * 100).toFixed(2) : 'N/A' }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="primary"
@ -166,6 +156,11 @@ const fetchModelTypes = async () => {
}
}
const getModelTypeName = (typeId) => {
const model = modelTypes.value.find(m => m.id === typeId);
return model ? model.name : typeId;
};
const fetchAvailableVersions = async () => {
if (!form.model_type) {
availableVersions.value = []
@ -366,10 +361,14 @@ watch(() => form.model_type, () => {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
padding-bottom: 20px;
}
.versions-list-section h4 {
margin-bottom: 16px;
}
.versions-list-section .el-table {
--el-table-bg-color: #19202e;
}
.prediction-chart {
margin-top: 20px;
}

View File

@ -83,30 +83,20 @@
<div v-if="availableVersions.length > 0" class="versions-list-section">
<h4>📦 可用模型版本</h4>
<el-table :data="availableVersions" style="width: 100%" v-loading="versionsLoading">
<el-table-column prop="product_name" label="药品名称" width="220"></el-table-column>
<el-table-column prop="version" label="版本号" width="180"></el-table-column>
<el-table-column prop="created_at" label="创建时间" width="200">
<el-table :data="availableVersions" style="width: 100%;" v-loading="versionsLoading">
<el-table-column prop="product_name" label="药品名称"></el-table-column>
<el-table-column prop="version" label="版本号"></el-table-column>
<el-table-column prop="created_at" label="创建时间">
<template #default="{ row }">
{{ new Date(row.created_at).toLocaleString() }}
</template>
</el-table-column>
<el-table-column prop="metrics.mae" label="MAE">
<el-table-column prop="model_type" label="算法类型">
<template #default="{ row }">
{{ row.metrics && row.metrics.mae ? row.metrics.mae.toFixed(4) : 'N/A' }}
{{ getModelTypeName(row.model_type) }}
</template>
</el-table-column>
<el-table-column prop="metrics.rmse" label="RMSE">
<template #default="{ row }">
{{ row.metrics && row.metrics.rmse ? row.metrics.rmse.toFixed(4) : 'N/A' }}
</template>
</el-table-column>
<el-table-column prop="metrics.mape" label="MAPE (%)">
<template #default="{ row }">
{{ row.metrics && row.metrics.mape ? (row.metrics.mape * 100).toFixed(2) : 'N/A' }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="primary"
@ -175,6 +165,11 @@ const fetchModelTypes = async () => {
}
}
const getModelTypeName = (typeId) => {
const model = modelTypes.value.find(m => m.id === typeId);
return model ? model.name : typeId;
};
const fetchAvailableVersions = async () => {
if (!form.product_id || !form.model_type) {
availableVersions.value = []
@ -384,10 +379,14 @@ watch([() => form.product_id, () => form.model_type], () => {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
padding-bottom: 20px;
}
.versions-list-section h4 {
margin-bottom: 16px;
}
.versions-list-section .el-table {
--el-table-bg-color: #19202e;
}
.prediction-chart {
margin-top: 20px;
}

View File

@ -84,29 +84,19 @@
<div v-if="availableVersions.length > 0" class="versions-list-section">
<h4>📦 可用模型版本</h4>
<el-table :data="availableVersions" style="width: 100%" v-loading="versionsLoading">
<el-table-column prop="store_name" label="店铺名称" width="220"></el-table-column>
<el-table-column prop="version" label="版本号" width="180"></el-table-column>
<el-table-column prop="created_at" label="创建时间" width="200">
<el-table-column prop="store_name" label="店铺名称"></el-table-column>
<el-table-column prop="version" label="版本号"></el-table-column>
<el-table-column prop="created_at" label="创建时间">
<template #default="{ row }">
{{ new Date(row.created_at).toLocaleString() }}
</template>
</el-table-column>
<el-table-column prop="metrics.mae" label="MAE">
<el-table-column prop="model_type" label="算法类型">
<template #default="{ row }">
{{ row.metrics && row.metrics.mae ? row.metrics.mae.toFixed(4) : 'N/A' }}
{{ getModelTypeName(row.model_type) }}
</template>
</el-table-column>
<el-table-column prop="metrics.rmse" label="RMSE">
<template #default="{ row }">
{{ row.metrics && row.metrics.rmse ? row.metrics.rmse.toFixed(4) : 'N/A' }}
</template>
</el-table-column>
<el-table-column prop="metrics.mape" label="MAPE (%)">
<template #default="{ row }">
{{ row.metrics && row.metrics.mape ? (row.metrics.mape * 100).toFixed(2) : 'N/A' }}
</template>
</el-table-column>
<el-table-column label="操作" width="150">
<el-table-column label="操作">
<template #default="{ row }">
<el-button
type="primary"
@ -174,6 +164,11 @@ const fetchModelTypes = async () => {
}
}
const getModelTypeName = (typeId) => {
const model = modelTypes.value.find(m => m.id === typeId);
return model ? model.name : typeId;
};
const fetchAvailableVersions = async () => {
if (!form.store_id || !form.model_type) {
availableVersions.value = []
@ -382,10 +377,14 @@ watch([() => form.store_id, () => form.model_type], () => {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
padding-bottom: 20px;
}
.versions-list-section h4 {
margin-bottom: 16px;
}
.versions-list-section .el-table {
--el-table-bg-color: #19202e;
}
.prediction-chart {
margin-top: 20px;
}

View File

@ -355,3 +355,24 @@
- **根本原因**: 模型训练后保存的评估指标 (`metrics`) 是NumPy的 `float32` 数据类型标准的Flask JSON编码器无法处理。
- **修复方案**: 在 `server/api.py``get_model_versions_api`, `get_store_model_versions_api`, 和 `get_global_model_versions_api` 三个接口中增加了一个处理步骤。在返回JSON响应之前显式地遍历 `metrics` 字典将所有值从NumPy类型`float32`强制转换为标准的Python `float` 类型,从而彻底解决了该序列化问题。
- **最终结论**: 至此所有预测页面的UI重构和功能升级已全部完成相关的数据链路和bug也已修复。系统现在能够为用户提供更加清晰、详细的模型版本信息并支持基于这些信息直接发起预测。
---
## 2025-07-23预测页面UI二次优化与同步
**开发者**: lyf
### 14:00 - 功能优化:简化模型版本列表
- **任务目标**: 根据最新需求,对所有预测页面(“按药品”、“按店铺”、“全局模型”)的模型版本列表进行界面简化和美化,提升信息的可读性和界面的整洁度。
- **实现方案**:
1. **精简列信息**: 在 `ProductPredictionView.vue`, `StorePredictionView.vue`, 和 `GlobalPredictionView.vue` 三个文件中,统一移除了原有的 `MAE`, `RMSE`, `MAPE` 等性能指标列。
2. **核心信息补充**: 新增了“算法类型”列,让用户能更直观地了解每个模型版本所使用的算法。
3. **特殊化处理**: 针对“全局模型”页面的独特性,特别保留了“聚合方法”列,确保了其业务信息的完整性。
### 14:30 - 样式美化:提升表格视觉体验
- **任务目标**: 解决之前版本中表格样式存在的问题,包括宽度不匹配、边距过近以及背景色区分度不高等。
- **实现方案**:
1. **动态列宽**: 移除了所有 `el-table-column` 的固定 `width` 属性,使表格能够根据内容自动调整列宽,并完美地铺满其父容器。
2. **优化间距**: 为包裹表格的 `.versions-list-section` 容器增加了 `padding-bottom` 样式,解决了表格底部边框线与后续内容紧贴的问题。
3. **增强层次感**: 通过CSS变量 `--el-table-bg-color``el-table` 组件设置了独立的背景色使其在深色主题下能与主背景形成微妙而有效的区分提升了整体UI的质感。
- **最终结论**: 本次优化后所有预测页面的模型版本列表在信息呈现上更加简洁、核心同时视觉样式也更加美观、专业符合了最新的UI/UX要求。

Binary file not shown.