药品预测版本模型改列表展示

This commit is contained in:
LYFxiaoan 2025-07-23 10:58:20 +08:00
parent e4397c201a
commit 52cb6b74d5
3 changed files with 60 additions and 48 deletions

View File

@ -44,25 +44,7 @@
</el-row>
<el-row :gutter="20" v-if="form.model_type">
<el-col :span="5">
<el-form-item label="模型版本">
<el-select
v-model="form.version"
placeholder="选择版本"
style="width: 100%"
:disabled="!availableVersions.length"
:loading="versionsLoading"
>
<el-option
v-for="version in availableVersions"
:key="version"
:label="version"
:value="version"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="5">
<el-col :span="6">
<el-form-item label="预测天数">
<el-input-number
v-model="form.future_days"
@ -72,7 +54,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-col :span="6">
<el-form-item label="历史天数">
<el-input-number
v-model="form.history_lookback_days"
@ -82,7 +64,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="5">
<el-col :span="6">
<el-form-item label="起始日期">
<el-date-picker
v-model="form.start_date"
@ -95,7 +77,7 @@
/>
</el-form-item>
</el-col>
<el-col :span="4">
<el-col :span="6">
<el-form-item label="预测分析">
<el-switch
v-model="form.analyze_result"
@ -108,17 +90,45 @@
</el-form>
</div>
<div class="prediction-actions">
<el-button
type="primary"
size="large"
@click="startPrediction"
:loading="predicting"
:disabled="!canPredict"
>
<el-icon><TrendCharts /></el-icon>
开始预测
</el-button>
<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">
<template #default="{ row }">
{{ new Date(row.created_at).toLocaleString() }}
</template>
</el-table-column>
<el-table-column prop="metrics.mae" label="MAE">
<template #default="{ row }">
{{ row.metrics && row.metrics.mae ? row.metrics.mae.toFixed(4) : 'N/A' }}
</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">
<template #default="{ row }">
<el-button
type="primary"
size="small"
@click="startPrediction(row.version)"
:loading="predicting && form.version === row.version"
>
<el-icon><TrendCharts /></el-icon>
开始预测
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
@ -162,9 +172,7 @@ const form = reactive({
analyze_result: true
})
const canPredict = computed(() => {
return form.product_id && form.model_type && form.version
})
const fetchModelTypes = async () => {
try {
@ -188,9 +196,7 @@ const fetchAvailableVersions = async () => {
const response = await axios.get(url)
if (response.data.status === 'success') {
availableVersions.value = response.data.data.versions || []
if (response.data.data.latest_version) {
form.version = response.data.data.latest_version
}
// No longer setting a default version, the user will choose from the list.
}
} catch (error) {
availableVersions.value = []
@ -203,26 +209,29 @@ const handleProductChange = () => {
form.model_type = ''
form.version = ''
availableVersions.value = []
predictionResult.value = null;
}
const handleModelTypeChange = () => {
form.version = ''
availableVersions.value = []
predictionResult.value = null;
fetchAvailableVersions()
}
const startPrediction = async () => {
const startPrediction = async (version) => {
form.version = version; // Keep track of which version is running
try {
predicting.value = true
const payload = {
product_id: form.product_id,
model_type: form.model_type,
version: form.version,
version: version,
future_days: form.future_days,
history_lookback_days: form.history_lookback_days,
start_date: form.start_date,
include_visualization: form.analyze_result,
}
// Corrected API endpoint from /api/predict to /api/prediction
const response = await axios.post('/api/prediction', payload)
if (response.data.status === 'success') {
// The backend response may have history_data and prediction_data at the top level
@ -382,13 +391,14 @@ watch([() => form.product_id, () => form.model_type], () => {
.model-selection-section h4 {
margin-bottom: 16px;
}
.prediction-actions {
display: flex;
justify-content: center;
.versions-list-section {
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ebeef5;
}
.versions-list-section h4 {
margin-bottom: 16px;
}
.prediction-chart {
margin-top: 20px;
}

Binary file not shown.

View File

@ -3536,15 +3536,17 @@ def get_model_versions_api(product_id, model_type):
result = model_manager.list_models(product_id=product_id, model_type=model_type)
models = result.get('models', [])
versions = sorted(list(set(m['version'] for m in models)), key=lambda v: (v != 'best', v))
latest_version = versions[0] if versions else None
# Sort models: 'best' version first, then by creation date descending
models.sort(key=lambda x: (x.get('version') != 'best', x.get('created_at', '')), reverse=True)
latest_version = models[0]['version'] if models else None
return jsonify({
"status": "success",
"data": {
"product_id": product_id,
"model_type": model_type,
"versions": versions,
"versions": models, # Return the full list of model details
"latest_version": latest_version
}
})