Files
cms/src/views/AiCaseGenerateView.vue
T
2026-06-14 08:32:08 +08:00

199 lines
6.4 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>
<div class="page-stack">
<section class="page-toolbar">
<div>
<h1>AI病例生成</h1>
<p>输入病例长描述并选择病例类型生成结构化传统病例或教学病例</p>
</div>
<div class="toolbar-actions">
<el-button :icon="Refresh" @click="resetForm">重置</el-button>
<el-button :icon="Cpu" :loading="generating" type="primary" @click="submitGenerate">开始生成</el-button>
</div>
</section>
<section class="ai-case-workbench">
<div class="data-section">
<div class="section-header">
<div>
<h2>生成配置</h2>
<p>填写病例描述后生成结构化内容可直接用于后续草稿录入</p>
</div>
</div>
<el-form ref="formRef" class="ai-case-form" :model="form" :rules="rules" label-position="top">
<el-form-item label="病例类型" prop="case_type">
<el-radio-group v-model="form.case_type">
<el-radio-button label="traditional">传统病例</el-radio-button>
<el-radio-button label="teaching">教学病例</el-radio-button>
</el-radio-group>
</el-form-item>
<el-form-item label="病例长描述" prop="prompt">
<el-input
v-model="form.prompt"
type="textarea"
:rows="10"
maxlength="2000"
show-word-limit
placeholder="请生成一个儿科急性上呼吸道感染传统病例,患儿4岁男孩,发热咳嗽3天。"
/>
</el-form-item>
</el-form>
</div>
<div class="data-section ai-result-section">
<div class="section-header">
<div>
<h2>生成结果</h2>
<p>{{ resultStatusText }}</p>
</div>
<el-tag v-if="result" type="success">{{ caseTypeLabel(result.caseType || form.case_type) }}</el-tag>
</div>
<el-empty v-if="!result" description="暂无生成结果" />
<template v-else>
<div v-if="hasResultMetrics" class="ai-result-kpis">
<div>
<span>输入消耗</span>
<strong>{{ formatNumber(result.aiUsage.promptTokens) }}</strong>
</div>
<div>
<span>输出消耗</span>
<strong>{{ formatNumber(result.aiUsage.completionTokens) }}</strong>
</div>
<div>
<span>生成耗时</span>
<strong>{{ formatSeconds(result.generatingSeconds) }}</strong>
</div>
<div>
<span>解析耗时</span>
<strong>{{ formatSeconds(result.parsingSeconds) }}</strong>
</div>
</div>
<el-descriptions :column="2" border>
<el-descriptions-item label="标题">{{ result.data.title || '-' }}</el-descriptions-item>
<el-descriptions-item label="科室">{{ result.data.department_name || '-' }}</el-descriptions-item>
<el-descriptions-item label="主诉" :span="2">{{ result.data.chief_complaint || '-' }}</el-descriptions-item>
<el-descriptions-item label="患者年龄">{{ formatValue(result.data.patient_age) }}</el-descriptions-item>
<el-descriptions-item label="患者性别">{{ patientGenderLabel(result.data.patient_gender) }}</el-descriptions-item>
<el-descriptions-item label="Prompt版本" :span="2">{{ result.promptVersion || '-' }}</el-descriptions-item>
</el-descriptions>
</template>
</div>
</section>
</div>
</template>
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { ElMessage } from 'element-plus'
import type { FormInstance, FormRules } from 'element-plus'
import { Cpu, Refresh } from '@element-plus/icons-vue'
import { generateCaseWithAi, type AiGenerateCaseResult, type DraftCaseType } from '@/api/cases'
import { useAppStore } from '@/stores/app'
const appStore = useAppStore()
const formRef = ref<FormInstance>()
const generating = ref(false)
const result = ref<AiGenerateCaseResult | null>(null)
const form = reactive({
case_type: 'traditional' as DraftCaseType,
prompt: ''
})
const rules: FormRules = {
case_type: [{ required: true, message: '请选择病例类型', trigger: 'change' }],
prompt: [{ required: true, message: '请输入病例长描述', trigger: 'blur' }]
}
const resultStatusText = computed(() => {
if (!result.value) {
return '等待生成结果返回。'
}
return result.value.parseId ? `解析ID${result.value.parseId}` : '接口已返回生成结果'
})
const hasResultMetrics = computed(() => {
if (!result.value) {
return false
}
return [
result.value.aiUsage.promptTokens,
result.value.aiUsage.completionTokens,
result.value.generatingSeconds,
result.value.parsingSeconds
].some(value => value !== null)
})
async function submitGenerate() {
if (!appStore.token) {
ElMessage.warning('缺少登录信息,请重新登录')
return
}
const isValid = await formRef.value?.validate().catch(() => false)
if (!isValid) {
return
}
try {
generating.value = true
result.value = await generateCaseWithAi({
token: appStore.token,
payload: {
case_type: form.case_type,
prompt: form.prompt.trim()
}
})
ElMessage.success('AI病例生成完成')
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : 'AI病例生成失败')
} finally {
generating.value = false
}
}
function resetForm() {
form.case_type = 'traditional'
form.prompt = ''
result.value = null
formRef.value?.clearValidate()
}
function caseTypeLabel(type: string) {
const labels: Record<string, string> = {
traditional: '传统病例',
teaching: '教学病例'
}
return labels[type] || type || '-'
}
function patientGenderLabel(value: unknown) {
if (value === 'male' || value === '男') return '男'
if (value === 'female' || value === '女') return '女'
if (value === 'unknown') return '未知'
return formatValue(value)
}
function formatNumber(value: number | null) {
return value === null ? '-' : value.toLocaleString()
}
function formatSeconds(value: number | null) {
return value === null ? '-' : `${value}s`
}
function formatValue(value: unknown) {
if (value === undefined || value === null || value === '') {
return '-'
}
return String(value)
}
</script>