图表颜色统一、修复店铺预测图表标题显示错误、统一模型时间显示格式
This commit is contained in:
parent
3aaddcd658
commit
08b26b5fa0
@ -47,7 +47,7 @@
|
|||||||
<el-table :data="paginatedModelList" style="width: 100%" v-loading="modelsLoading">
|
<el-table :data="paginatedModelList" style="width: 100%" v-loading="modelsLoading">
|
||||||
<el-table-column prop="model_type" label="模型类型" sortable />
|
<el-table-column prop="model_type" label="模型类型" sortable />
|
||||||
<el-table-column prop="version" label="版本" />
|
<el-table-column prop="version" label="版本" />
|
||||||
<el-table-column prop="created_at" label="创建时间" />
|
<el-table-column prop="created_at" label="创建时间" :formatter="formatDateTime" />
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
@ -136,6 +136,20 @@ const handlePageChange = (page) => {
|
|||||||
pagination.currentPage = page
|
pagination.currentPage = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (row, column, cellValue) => {
|
||||||
|
if (!cellValue) return ''
|
||||||
|
const date = new Date(cellValue)
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
}).replace(/\//g, '-')
|
||||||
|
}
|
||||||
|
|
||||||
const fetchModelTypes = async () => {
|
const fetchModelTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/model_types')
|
const response = await axios.get('/api/model_types')
|
||||||
@ -263,10 +277,15 @@ const renderChart = () => {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: 'white' // 图例文字颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: `全局模型 (${predictionResult.value.model_type}) - 销量预测趋势图`,
|
text: `全局模型 (${predictionResult.value.model_type}) - 销量预测趋势图`,
|
||||||
color: '#303133',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 20,
|
size: 20,
|
||||||
weight: 'bold',
|
weight: 'bold',
|
||||||
@ -275,7 +294,7 @@ const renderChart = () => {
|
|||||||
subtitle: {
|
subtitle: {
|
||||||
display: true,
|
display: true,
|
||||||
text: subtitleText,
|
text: subtitleText,
|
||||||
color: '#606266',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 14,
|
size: 14,
|
||||||
},
|
},
|
||||||
@ -288,7 +307,11 @@ const renderChart = () => {
|
|||||||
x: {
|
x: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '日期'
|
text: '日期',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // X轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
@ -297,10 +320,14 @@ const renderChart = () => {
|
|||||||
y: {
|
y: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '销量'
|
text: '销量',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // Y轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
color: '#e9e9e9',
|
color: 'rgba(255, 255, 255, 0.2)', // 网格线颜色
|
||||||
drawBorder: false,
|
drawBorder: false,
|
||||||
},
|
},
|
||||||
beginAtZero: true
|
beginAtZero: true
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<el-table-column prop="product_name" label="药品名称" sortable />
|
<el-table-column prop="product_name" label="药品名称" sortable />
|
||||||
<el-table-column prop="model_type" label="模型类型" sortable />
|
<el-table-column prop="model_type" label="模型类型" sortable />
|
||||||
<el-table-column prop="version" label="版本" />
|
<el-table-column prop="version" label="版本" />
|
||||||
<el-table-column prop="created_at" label="创建时间" />
|
<el-table-column prop="created_at" label="创建时间" :formatter="formatDateTime" />
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
@ -146,6 +146,20 @@ const handlePageChange = (page) => {
|
|||||||
pagination.currentPage = page
|
pagination.currentPage = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (row, column, cellValue) => {
|
||||||
|
if (!cellValue) return ''
|
||||||
|
const date = new Date(cellValue)
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
}).replace(/\//g, '-')
|
||||||
|
}
|
||||||
|
|
||||||
const fetchModelTypes = async () => {
|
const fetchModelTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/model_types')
|
const response = await axios.get('/api/model_types')
|
||||||
@ -273,10 +287,15 @@ const renderChart = () => {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: 'white' // 图例文字颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: `${predictionResult.value.product_name} - 销量预测趋势图`,
|
text: `${predictionResult.value.product_name} - 销量预测趋势图`,
|
||||||
color: '#303133',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 20,
|
size: 20,
|
||||||
weight: 'bold',
|
weight: 'bold',
|
||||||
@ -285,7 +304,7 @@ const renderChart = () => {
|
|||||||
subtitle: {
|
subtitle: {
|
||||||
display: true,
|
display: true,
|
||||||
text: subtitleText,
|
text: subtitleText,
|
||||||
color: '#606266',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 14,
|
size: 14,
|
||||||
},
|
},
|
||||||
@ -298,7 +317,11 @@ const renderChart = () => {
|
|||||||
x: {
|
x: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '日期'
|
text: '日期',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // X轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
@ -307,10 +330,14 @@ const renderChart = () => {
|
|||||||
y: {
|
y: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '销量'
|
text: '销量',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // Y轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
color: '#e9e9e9',
|
color: 'rgba(255, 255, 255, 0.2)', // 网格线颜色
|
||||||
drawBorder: false,
|
drawBorder: false,
|
||||||
},
|
},
|
||||||
beginAtZero: true
|
beginAtZero: true
|
||||||
|
@ -56,7 +56,7 @@
|
|||||||
<el-table-column prop="store_name" label="店铺名称" sortable />
|
<el-table-column prop="store_name" label="店铺名称" sortable />
|
||||||
<el-table-column prop="model_type" label="模型类型" sortable />
|
<el-table-column prop="model_type" label="模型类型" sortable />
|
||||||
<el-table-column prop="version" label="版本" />
|
<el-table-column prop="version" label="版本" />
|
||||||
<el-table-column prop="created_at" label="创建时间" />
|
<el-table-column prop="created_at" label="创建时间" :formatter="formatDateTime" />
|
||||||
<el-table-column label="操作">
|
<el-table-column label="操作">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<el-button
|
<el-button
|
||||||
@ -161,6 +161,20 @@ const handlePageChange = (page) => {
|
|||||||
pagination.currentPage = page
|
pagination.currentPage = page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const formatDateTime = (row, column, cellValue) => {
|
||||||
|
if (!cellValue) return ''
|
||||||
|
const date = new Date(cellValue)
|
||||||
|
return date.toLocaleString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
second: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
}).replace(/\//g, '-')
|
||||||
|
}
|
||||||
|
|
||||||
const fetchModelTypes = async () => {
|
const fetchModelTypes = async () => {
|
||||||
try {
|
try {
|
||||||
const response = await axios.get('/api/model_types')
|
const response = await axios.get('/api/model_types')
|
||||||
@ -300,10 +314,15 @@ const renderChart = () => {
|
|||||||
responsive: true,
|
responsive: true,
|
||||||
maintainAspectRatio: false,
|
maintainAspectRatio: false,
|
||||||
plugins: {
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
labels: {
|
||||||
|
color: 'white' // 图例文字颜色
|
||||||
|
}
|
||||||
|
},
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: `${predictionResult.value.store_name} - 销量预测趋势图`,
|
text: `${predictionResult.value.store_name} - 销量预测趋势图`,
|
||||||
color: '#303133',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 20,
|
size: 20,
|
||||||
weight: 'bold',
|
weight: 'bold',
|
||||||
@ -312,7 +331,7 @@ const renderChart = () => {
|
|||||||
subtitle: {
|
subtitle: {
|
||||||
display: true,
|
display: true,
|
||||||
text: subtitleText,
|
text: subtitleText,
|
||||||
color: '#606266',
|
color: 'white',
|
||||||
font: {
|
font: {
|
||||||
size: 14,
|
size: 14,
|
||||||
},
|
},
|
||||||
@ -325,7 +344,11 @@ const renderChart = () => {
|
|||||||
x: {
|
x: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '日期'
|
text: '日期',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // X轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
display: false
|
display: false
|
||||||
@ -334,10 +357,14 @@ const renderChart = () => {
|
|||||||
y: {
|
y: {
|
||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: '销量'
|
text: '销量',
|
||||||
|
color: 'white'
|
||||||
|
},
|
||||||
|
ticks: {
|
||||||
|
color: 'white' // Y轴刻度文字颜色
|
||||||
},
|
},
|
||||||
grid: {
|
grid: {
|
||||||
color: '#e9e9e9',
|
color: 'rgba(255, 255, 255, 0.2)', // 网格线颜色
|
||||||
drawBorder: false,
|
drawBorder: false,
|
||||||
},
|
},
|
||||||
beginAtZero: true
|
beginAtZero: true
|
||||||
|
@ -324,3 +324,30 @@
|
|||||||
2. **修正数据访问**: 在 `startPrediction` 方法中,将API响应的核心数据 `response.data.data` 赋值给 `predictionResult`。
|
2. **修正数据访问**: 在 `startPrediction` 方法中,将API响应的核心数据 `response.data.data` 赋值给 `predictionResult`。
|
||||||
3. **标准化日期**: 在 `renderChart` 方法的开头,增加了一个 `formatDate` 辅助函数,并在处理数据时立即调用它,将所有日期都统一转换为 `'YYYY-MM-DD'` 格式的字符串,从而一举解决了数据点丢失和标题格式错误的双重问题。
|
3. **标准化日期**: 在 `renderChart` 方法的开头,增加了一个 `formatDate` 辅助函数,并在处理数据时立即调用它,将所有日期都统一转换为 `'YYYY-MM-DD'` 格式的字符串,从而一举解决了数据点丢失和标题格式错误的双重问题。
|
||||||
- **最终结论**: 至此,所有预测视图的前后端数据链路和UI展示功能均已修复,系统功能恢复正常。
|
- **最终结论**: 至此,所有预测视图的前后端数据链路和UI展示功能均已修复,系统功能恢复正常。
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2025-07-24:UI/UX 优化与后端逻辑统一
|
||||||
|
**开发者**: Roo (AI Assistant) & lyf
|
||||||
|
|
||||||
|
### 14:30 - 统一预测图表颜色风格
|
||||||
|
- **任务**: 根据用户反馈,将所有预测结果图表的文字颜色(包括标题、副标题、图例、坐标轴)统一修改为白色,以适应深色UI背景。
|
||||||
|
- **实施**:
|
||||||
|
- 逐一修改了 `ProductPredictionView.vue`, `StorePredictionView.vue`, 和 `GlobalPredictionView.vue`。
|
||||||
|
- 在 `Chart.js` 的 `options` 配置中,将所有相关的 `color` 属性设置为 `'white'`,并将网格线颜色调整为半透明白色 `rgba(255, 255, 255, 0.2)`。
|
||||||
|
|
||||||
|
### 14:50 - 修复“按店铺预测”图表标题显示错误
|
||||||
|
- **问题**: “按店铺预测”的图表标题显示为 `undefined`。
|
||||||
|
- **根本原因**: 后端 `server/api.py` 在处理店铺预测时,没有查询并返回真实的店铺名称,导致前端无法获取该数据。
|
||||||
|
- **修复方案 (后端优先)**:
|
||||||
|
1. 在 `server/api.py` 中新增了 `get_store_name` 辅助函数,用于根据 `store_id` 查询店铺名称。
|
||||||
|
2. 修改了 `/api/prediction` 接口,在 `training_mode` 为 `'store'` 时,调用新函数获取店铺名,并将其以 `store_name` 字段返回给前端。
|
||||||
|
- **结论**: 通过统一后端逻辑,确保了数据源的正确性,从根本上解决了问题。
|
||||||
|
|
||||||
|
### 15:05 - 统一模型列表时间显示格式
|
||||||
|
- **任务**: 根据用户要求,将所有预测页面模型列表中的“创建时间”从ISO格式统一为 `YYYY-MM-DD HH:MM:SS` 的24小时制格式。
|
||||||
|
- **实施**:
|
||||||
|
- 逐一修改了 `ProductPredictionView.vue`, `StorePredictionView.vue`, 和 `GlobalPredictionView.vue`。
|
||||||
|
- 在 `<script setup>` 中添加了 `formatDateTime` 辅助函数。
|
||||||
|
- 在 `<el-table-column>` 中使用 `:formatter="formatDateTime"` 属性来应用该格式化函数,实现了UI显示的统一。
|
||||||
|
Binary file not shown.
@ -1389,7 +1389,9 @@ def predict():
|
|||||||
if not store_id:
|
if not store_id:
|
||||||
return jsonify({"status": "error", "error": "店铺模式需要 store_id"}), 400
|
return jsonify({"status": "error", "error": "店铺模式需要 store_id"}), 400
|
||||||
model_identifier = f"store_{store_id}"
|
model_identifier = f"store_{store_id}"
|
||||||
product_name = f"店铺 {store_id} 整体"
|
# 修复:调用 get_store_name 获取真实的店铺名称
|
||||||
|
store_name = get_store_name(store_id) or f"店铺 {store_id}"
|
||||||
|
product_name = store_name # 保持product_name字段用于日志和历史记录
|
||||||
else: # 默认为 'product' 模式
|
else: # 默认为 'product' 模式
|
||||||
if not product_id:
|
if not product_id:
|
||||||
return jsonify({"status": "error", "error": "药品模式需要 product_id"}), 400
|
return jsonify({"status": "error", "error": "药品模式需要 product_id"}), 400
|
||||||
@ -1466,6 +1468,10 @@ def predict():
|
|||||||
'prediction_data': prediction_result.get('prediction_data', [])
|
'prediction_data': prediction_result.get('prediction_data', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# 修复:如果是在店铺模式下,确保返回结果中包含 store_name
|
||||||
|
if training_mode == 'store':
|
||||||
|
response_data['data']['store_name'] = store_name
|
||||||
|
|
||||||
# 调试日志
|
# 调试日志
|
||||||
print("=== 预测API响应数据结构 (v3) ===")
|
print("=== 预测API响应数据结构 (v3) ===")
|
||||||
print(f"history_data 长度: {len(response_data['history_data'])}")
|
print(f"history_data 长度: {len(response_data['history_data'])}")
|
||||||
@ -2556,6 +2562,20 @@ def get_product_name(product_id):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"获取产品名称失败: {str(e)}")
|
print(f"获取产品名称失败: {str(e)}")
|
||||||
return None
|
return None
|
||||||
|
# 获取店铺名称的辅助函数
|
||||||
|
def get_store_name(store_id):
|
||||||
|
"""根据店铺ID获取店铺名称"""
|
||||||
|
try:
|
||||||
|
from utils.multi_store_data_utils import get_available_stores
|
||||||
|
stores = get_available_stores()
|
||||||
|
for store in stores:
|
||||||
|
if store['store_id'] == store_id:
|
||||||
|
return store['store_name']
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"获取店铺名称失败: {str(e)}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
# run_prediction 函数已被移除,因为其逻辑已完全整合到 /api/prediction 路由处理函数中
|
# run_prediction 函数已被移除,因为其逻辑已完全整合到 /api/prediction 路由处理函数中
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user