Files
pig-farm-controller-fe/src/components/feed/AIRecipeReviewDialog.vue
2025-12-02 16:20:46 +08:00

284 lines
6.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
v-model="dialogVisible"
title="AI点评配方"
width="80%"
:before-close="handleClose"
destroy-on-close
align-center
>
<el-form label-width="100px">
<el-form-item label="配方名称:">
<span>{{ recipe ? recipe.name : 'N/A' }}</span>
</el-form-item>
<el-form-item label="目标猪类型:">
<el-select
v-model="selectedPigTypeId"
placeholder="请选择目标猪类型"
filterable
:loading="loadingPigTypes"
:disabled="loadingAIReview"
style="width: 100%;"
>
<el-option
v-for="item in pigTypes"
:key="item.id"
:label="item.label"
:value="item.id"
></el-option>
</el-select>
</el-form-item>
</el-form>
<!-- 将AI点评结果移出el-form -->
<div v-if="aiReviewResult || error" style="margin-top: 20px;">
<label class="el-form-item__label" style="width: 100px;">AI点评结果:</label>
<div class="ai-review-content-wrapper" style="margin-top: 10px;">
<el-alert
v-if="error"
:title="error"
type="error"
show-icon
:closable="false"
></el-alert>
<div v-else-if="aiReviewResult" class="ai-review-content">
<p style="text-align: center; margin-bottom: 20px; color: #888;">
<strong>{{ aiReviewResult.ai_model }}</strong> 生成
</p>
<div v-html="renderedReview" class="markdown-body"></div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="handleClose" :disabled="loadingAIReview">取消</el-button>
<el-button
type="primary"
@click="handleConfirm"
:loading="loadingAIReview"
:disabled="!selectedPigTypeId || loadingPigTypes"
>
{{ aiReviewResult ? '重新点评' : '开始点评' }}
</el-button>
</span>
</template>
</el-dialog>
</template>
<script>
import { getPigTypes, aiDiagnoseRecipe } from '../../api/feed';
import { ElMessage } from 'element-plus';
import { marked } from 'marked';
export default {
name: 'AIRecipeReviewDialog',
props: {
visible: {
type: Boolean,
default: false,
},
recipe: {
type: Object,
default: null,
},
},
emits: ['update:visible', 'cancel'],
data() {
return {
dialogVisible: this.visible,
pigTypes: [], // 格式化后的猪类型列表
selectedPigTypeId: null,
aiReviewResult: null,
loadingPigTypes: false,
loadingAIReview: false,
error: null,
};
},
computed: {
renderedReview() {
if (this.aiReviewResult && this.aiReviewResult.review_message) {
return marked(this.aiReviewResult.review_message);
}
return '';
},
},
watch: {
visible(newVal) {
this.dialogVisible = newVal;
if (newVal) {
this.resetState();
this.loadPigTypes();
}
},
dialogVisible(newVal) {
this.$emit('update:visible', newVal);
},
},
methods: {
/**
* 重置组件内部状态
*/
resetState() {
this.selectedPigTypeId = null;
this.aiReviewResult = null;
this.error = null;
this.pigTypes = [];
},
/**
* 加载猪类型列表
*/
async loadPigTypes() {
this.loadingPigTypes = true;
this.error = null;
try {
// 获取所有猪类型,假设一次性获取足够的数据
const response = await getPigTypes({ page: 1, page_size: 1000 });
if (response.code === 2000 && response.data && response.data.list) {
this.pigTypes = response.data.list.map(type => ({
id: type.id,
label: `${type.breed_name} - ${type.age_stage_name}`,
}));
} else {
this.error = response.msg || '获取猪类型失败';
ElMessage.error(this.error);
}
} catch (err) {
this.error = '加载猪类型失败: ' + (err.message || '未知错误');
ElMessage.error(this.error);
console.error('加载猪类型失败:', err);
} finally {
this.loadingPigTypes = false;
}
},
/**
* 处理确认按钮点击调用AI点评接口
*/
async handleConfirm() {
if (!this.recipe || !this.recipe.id) {
ElMessage.warning('未选择配方');
return;
}
if (!this.selectedPigTypeId) {
ElMessage.warning('请选择目标猪类型');
return;
}
this.loadingAIReview = true;
this.aiReviewResult = null;
this.error = null;
try {
const response = await aiDiagnoseRecipe(this.recipe.id, this.selectedPigTypeId);
if (response.code === 2000 && response.data) {
this.aiReviewResult = response.data;
ElMessage.success('AI点评成功');
} else {
this.error = response.msg || 'AI点评失败';
ElMessage.error(this.error);
}
} catch (err) {
this.error = 'AI点评请求失败: ' + (err.message || '未知错误');
ElMessage.error(this.error);
console.error('AI点评请求失败:', err);
} finally {
this.loadingAIReview = false;
}
},
/**
* 处理对话框关闭
*/
handleClose() {
this.dialogVisible = false;
this.$emit('cancel');
},
},
};
</script>
<style scoped>
.ai-review-content {
background-color: #f5f5f5;
padding: 15px;
border-radius: 4px;
max-height: 60vh;
overflow-y: auto;
}
.markdown-body {
line-height: 1.6;
}
.markdown-body :deep(h1),
.markdown-body :deep(h2),
.markdown-body :deep(h3) {
margin-top: 24px;
margin-bottom: 16px;
font-weight: 600;
line-height: 1.25;
}
.markdown-body :deep(h1) {
font-size: 2em;
border-bottom: 1px solid #eaecef;
padding-bottom: .3em;
}
.markdown-body :deep(h2) {
font-size: 1.5em;
border-bottom: 1px solid #eaecef;
padding-bottom: .3em;
}
.markdown-body :deep(h3) {
font-size: 1.25em;
}
.markdown-body :deep(p) {
margin-bottom: 16px;
}
.markdown-body :deep(ul),
.markdown-body :deep(ol) {
padding-left: 2em;
margin-bottom: 16px;
}
.markdown-body :deep(li) {
margin-bottom: .5em;
}
.markdown-body :deep(table) {
width: 100%;
border-collapse: collapse;
margin-bottom: 16px;
}
.markdown-body :deep(th),
.markdown-body :deep(td) {
border: 1px solid #dfe2e5;
padding: 8px 12px;
}
.markdown-body :deep(th) {
background-color: #f6f8fa;
font-weight: 600;
}
.markdown-body :deep(blockquote) {
color: #6a737d;
border-left: .25em solid #dfe2e5;
padding: 0 1em;
margin-left: 0;
margin-bottom: 16px;
}
.markdown-body :deep(code) {
padding: .2em .4em;
margin: 0;
font-size: 85%;
background-color: rgba(27,31,35,.05);
border-radius: 3px;
}
</style>