Spaces:
Sleeping
Sleeping
| <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> |