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

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>
<el-row :gutter="20" v-if="form.model_type"> <el-row :gutter="20" v-if="form.model_type">
<el-col :span="5"> <el-col :span="6">
<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-form-item label="预测天数"> <el-form-item label="预测天数">
<el-input-number <el-input-number
v-model="form.future_days" v-model="form.future_days"
@ -72,7 +54,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="6">
<el-form-item label="历史天数"> <el-form-item label="历史天数">
<el-input-number <el-input-number
v-model="form.history_lookback_days" v-model="form.history_lookback_days"
@ -82,7 +64,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="6">
<el-form-item label="起始日期"> <el-form-item label="起始日期">
<el-date-picker <el-date-picker
v-model="form.start_date" v-model="form.start_date"
@ -95,7 +77,7 @@
/> />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="4"> <el-col :span="6">
<el-form-item label="预测分析"> <el-form-item label="预测分析">
<el-switch <el-switch
v-model="form.analyze_result" v-model="form.analyze_result"
@ -108,17 +90,45 @@
</el-form> </el-form>
</div> </div>
<div class="prediction-actions"> <div v-if="availableVersions.length > 0" class="versions-list-section">
<el-button <h4>📦 可用模型版本</h4>
type="primary" <el-table :data="availableVersions" style="width: 100%" v-loading="versionsLoading">
size="large" <el-table-column prop="product_name" label="药品名称" width="220"></el-table-column>
@click="startPrediction" <el-table-column prop="version" label="版本号" width="180"></el-table-column>
:loading="predicting" <el-table-column prop="created_at" label="创建时间" width="200">
:disabled="!canPredict" <template #default="{ row }">
> {{ new Date(row.created_at).toLocaleString() }}
<el-icon><TrendCharts /></el-icon> </template>
开始预测 </el-table-column>
</el-button> <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> </div>
</el-card> </el-card>
@ -162,9 +172,7 @@ const form = reactive({
analyze_result: true analyze_result: true
}) })
const canPredict = computed(() => {
return form.product_id && form.model_type && form.version
})
const fetchModelTypes = async () => { const fetchModelTypes = async () => {
try { try {
@ -188,9 +196,7 @@ const fetchAvailableVersions = async () => {
const response = await axios.get(url) const response = await axios.get(url)
if (response.data.status === 'success') { if (response.data.status === 'success') {
availableVersions.value = response.data.data.versions || [] availableVersions.value = response.data.data.versions || []
if (response.data.data.latest_version) { // No longer setting a default version, the user will choose from the list.
form.version = response.data.data.latest_version
}
} }
} catch (error) { } catch (error) {
availableVersions.value = [] availableVersions.value = []
@ -203,26 +209,29 @@ const handleProductChange = () => {
form.model_type = '' form.model_type = ''
form.version = '' form.version = ''
availableVersions.value = [] availableVersions.value = []
predictionResult.value = null;
} }
const handleModelTypeChange = () => { const handleModelTypeChange = () => {
form.version = '' form.version = ''
availableVersions.value = []
predictionResult.value = null;
fetchAvailableVersions() fetchAvailableVersions()
} }
const startPrediction = async () => { const startPrediction = async (version) => {
form.version = version; // Keep track of which version is running
try { try {
predicting.value = true predicting.value = true
const payload = { const payload = {
product_id: form.product_id, product_id: form.product_id,
model_type: form.model_type, model_type: form.model_type,
version: form.version, version: version,
future_days: form.future_days, future_days: form.future_days,
history_lookback_days: form.history_lookback_days, history_lookback_days: form.history_lookback_days,
start_date: form.start_date, start_date: form.start_date,
include_visualization: form.analyze_result, include_visualization: form.analyze_result,
} }
// Corrected API endpoint from /api/predict to /api/prediction
const response = await axios.post('/api/prediction', payload) const response = await axios.post('/api/prediction', payload)
if (response.data.status === 'success') { if (response.data.status === 'success') {
// The backend response may have history_data and prediction_data at the top level // 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 { .model-selection-section h4 {
margin-bottom: 16px; margin-bottom: 16px;
} }
.prediction-actions { .versions-list-section {
display: flex;
justify-content: center;
margin-top: 20px; margin-top: 20px;
padding-top: 20px; padding-top: 20px;
border-top: 1px solid #ebeef5; border-top: 1px solid #ebeef5;
} }
.versions-list-section h4 {
margin-bottom: 16px;
}
.prediction-chart { .prediction-chart {
margin-top: 20px; 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) result = model_manager.list_models(product_id=product_id, model_type=model_type)
models = result.get('models', []) models = result.get('models', [])
versions = sorted(list(set(m['version'] for m in models)), key=lambda v: (v != 'best', v)) # Sort models: 'best' version first, then by creation date descending
latest_version = versions[0] if versions else None 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({ return jsonify({
"status": "success", "status": "success",
"data": { "data": {
"product_id": product_id, "product_id": product_id,
"model_type": model_type, "model_type": model_type,
"versions": versions, "versions": models, # Return the full list of model details
"latest_version": latest_version "latest_version": latest_version
} }
}) })