finance-advisor-agent / src /views /Assessment.vue
Trae Agent
feat: upgrade with siliconflow and hf config
afd0ae6
<script setup lang="ts">
import { ref, computed } from 'vue'
import { useRouter } from 'vue-router'
import axios from 'axios'
import { useAuthStore } from '@/stores/auth'
import { ElMessage } from 'element-plus'
const router = useRouter()
const authStore = useAuthStore()
const questions = [
{
id: 1,
text: '您的投资主要目的是什么?',
options: [
{ text: '资产保值,避免损失', score: 1 },
{ text: '资产稳健增值', score: 3 },
{ text: '短期内获取高收益', score: 5 }
]
},
{
id: 2,
text: '您可以接受多长时间的投资期限?',
options: [
{ text: '1年以内', score: 1 },
{ text: '1-3年', score: 3 },
{ text: '3年以上', score: 5 }
]
},
{
id: 3,
text: '面对投资亏损,您的反应是?',
options: [
{ text: '无法接受任何亏损', score: 1 },
{ text: '感到焦虑,但能接受少量亏损', score: 3 },
{ text: '认为是市场正常波动,可以接受', score: 5 }
]
},
{
id: 4,
text: '您目前的家庭资产配置情况是?',
options: [
{ text: '大部分是储蓄和国债', score: 1 },
{ text: '有一定比例的理财产品和基金', score: 3 },
{ text: '持有较多股票或高风险资产', score: 5 }
]
},
{
id: 5,
text: '您对金融市场的了解程度?',
options: [
{ text: '完全不了解', score: 1 },
{ text: '有一定基础知识', score: 3 },
{ text: '非常专业,经常关注市场', score: 5 }
]
}
]
const currentStep = ref(0)
const answers = ref<number[]>(new Array(questions.length).fill(0))
const loading = ref(false)
const result = ref<any>(null)
const progress = computed(() => {
return ((currentStep.value + 1) / questions.length) * 100
})
const selectOption = (score: number) => {
answers.value[currentStep.value] = score
if (currentStep.value < questions.length - 1) {
currentStep.value++
}
}
const submitAssessment = async () => {
if (answers.value.some(a => a === 0)) {
ElMessage.warning('请回答所有问题')
return
}
loading.value = true
try {
const response = await axios.post('/api/assessment/submit', {
answers: answers.value
}, {
headers: { Authorization: `Bearer ${authStore.token}` }
})
if (response.data.status) {
result.value = response.data
await authStore.fetchUser() // Update user risk level in store
} else {
ElMessage.error('提交失败')
}
} catch (e) {
ElMessage.error('发生错误')
} finally {
loading.value = false
}
}
const prevStep = () => {
if (currentStep.value > 0) {
currentStep.value--
}
}
</script>
<template>
<div class="max-w-3xl mx-auto py-10 px-4 sm:px-6 lg:px-8">
<div v-if="!result" class="bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6">
<h3 class="text-lg leading-6 font-medium text-gray-900">风险承受能力评估</h3>
<p class="mt-1 max-w-2xl text-sm text-gray-500">请如实回答以下问题,以便我们为您提供更准确的建议。</p>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-6">
<div class="mb-6">
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div class="bg-blue-600 h-2.5 rounded-full transition-all duration-300" :style="{ width: progress + '%' }"></div>
</div>
<p class="text-right text-xs text-gray-500 mt-1">进度: {{ currentStep + 1 }}/{{ questions.length }}</p>
</div>
<div class="min-h-[300px]">
<h4 class="text-xl font-medium text-gray-900 mb-6">{{ questions[currentStep].id }}. {{ questions[currentStep].text }}</h4>
<div class="space-y-4">
<button
v-for="(option, index) in questions[currentStep].options"
:key="index"
@click="selectOption(option.score)"
class="w-full text-left px-4 py-3 border rounded-md hover:bg-blue-50 hover:border-blue-300 focus:outline-none focus:ring-2 focus:ring-blue-500 transition-colors"
:class="answers[currentStep] === option.score ? 'bg-blue-50 border-blue-500 ring-1 ring-blue-500' : 'border-gray-300'"
>
<div class="flex items-center">
<div class="flex-shrink-0 h-4 w-4 rounded-full border border-gray-300 flex items-center justify-center"
:class="answers[currentStep] === option.score ? 'border-blue-600' : ''"
>
<div v-if="answers[currentStep] === option.score" class="h-2 w-2 rounded-full bg-blue-600"></div>
</div>
<span class="ml-3 text-sm font-medium text-gray-900">{{ option.text }}</span>
</div>
</button>
</div>
</div>
<div class="mt-8 flex justify-between">
<button
@click="prevStep"
:disabled="currentStep === 0"
class="px-4 py-2 border border-gray-300 shadow-sm text-sm font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 focus:outline-none disabled:opacity-50"
>
上一步
</button>
<button
v-if="currentStep === questions.length - 1"
@click="submitAssessment"
:disabled="loading"
class="px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-900 hover:bg-blue-800 focus:outline-none disabled:opacity-50"
>
{{ loading ? '分析中...' : '提交评估' }}
</button>
</div>
</div>
</div>
<div v-else class="bg-white shadow overflow-hidden sm:rounded-lg">
<div class="px-4 py-5 sm:px-6 text-center">
<div class="mx-auto flex items-center justify-center h-16 w-16 rounded-full bg-green-100 mb-4">
<svg class="h-8 w-8 text-green-600" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
</div>
<h3 class="text-2xl leading-6 font-medium text-gray-900">评估完成</h3>
<p class="mt-2 text-sm text-gray-500">根据您的回答,您的风险偏好类型为:</p>
<div class="mt-4 inline-flex items-center px-6 py-2 rounded-full text-lg font-bold bg-blue-100 text-blue-800">
{{ result.risk_level === 'conservative' ? '保守型' : result.risk_level === 'balanced' ? '稳健型' : '进取型' }}
</div>
</div>
<div class="border-t border-gray-200 px-4 py-5 sm:p-6">
<div class="bg-gray-50 rounded-lg p-4 mb-6">
<h4 class="text-sm font-medium text-gray-900 uppercase tracking-wider mb-2">类型描述</h4>
<p class="text-gray-600">{{ result.description }}</p>
</div>
<div class="bg-blue-50 rounded-lg p-4 mb-6">
<h4 class="text-sm font-medium text-blue-900 uppercase tracking-wider mb-2">投资建议</h4>
<ul class="list-disc list-inside space-y-1 text-blue-800">
<li v-for="(suggestion, idx) in result.suggestions" :key="idx">{{ suggestion }}</li>
</ul>
</div>
<div class="flex justify-center space-x-4">
<button @click="router.push('/dashboard')" class="px-6 py-3 border border-transparent text-base font-medium rounded-md text-white bg-blue-900 hover:bg-blue-800 shadow-sm">
查看投资组合
</button>
<button @click="router.push('/chat')" class="px-6 py-3 border border-gray-300 text-base font-medium rounded-md text-gray-700 bg-white hover:bg-gray-50 shadow-sm">
AI 咨询详情
</button>
</div>
</div>
</div>
</div>
</template>