feat: 重构评估流程为内存状态管理并接入真实提交与报告接口

本次重构将评估草稿改为内存状态管理,移除本地存储草稿,新增评估流程内存状态模块;接入真实的账户资料获取、头像上传接口,新增账户缓存清理逻辑;重构评估提交逻辑,接入真实提交接口并返回评估ID;新增分析页和报告页的真实状态查询、报告获取功能;更新README文档,记录开发进度和接口状态。
This commit is contained in:
hujun
2026-09-24 11:50:07 +08:00
parent dcf12a4471
commit dbfd637388
22 changed files with 1468 additions and 434 deletions
+62 -41
View File
@@ -1,47 +1,68 @@
<template>
<view class="page" :style="pageStyle()">
<AppHeader title="融资需求报告" />
<scroll-view scroll-y class="report-scroll" :style="contentStyle(96, false)">
<view class="report-content">
<view class="score-panel"><text class="score-label">综合评分</text><view class="score-row"><text class="big-score">85</text><text class="score-unit">分</text><text class="score-badge">良好</text></view><text class="score-caption">从五个维度了解融资资质</text></view>
<view class="radar-panel"><image class="radar-image" src="/packages/finance/static/finance-radar.png" mode="aspectFit"></image><text class="radar-label top">还款能力</text><text class="radar-label left">收入稳定</text><text class="radar-label right">信用状况</text><text class="radar-label bottom-left">资产条件</text><text class="radar-label bottom-right">负债情况</text></view>
<view class="conclusion"><text class="section-title">评估结论</text><text class="conclusion-text">可进一步比较信用融资产品的额度、期限与还款安排,结合自身需求选择合适方案。</text><view class="suggest-row"><text class="suggest-label">建议期限</text><text class="suggest-value">6–36个月</text></view><text class="report-note">报告内容仅供参考,实际融资结果以授权资料和机构审核为准。</text></view>
<button class="primary-button" @click="viewMatches">查看匹配方案</button>
</view>
</scroll-view>
</view>
<view class="page" :style="pageStyle()">
<AppHeader title="融资资料分析报告" />
<scroll-view scroll-y :style="contentStyle(0, false)">
<view class="report-content">
<text class="report-note">评估记录 · {{ assessmentId }}</text>
<text v-if="loading" class="body-text">正在加载报告…</text>
<view v-if="error.length > 0" class="panel"><text class="body-text">{{ error }}</text><text class="action" @click="loadReport">重新加载</text><text class="action" @click="history">前往融资档案</text></view>
<template v-if="report != null">
<text class="report-note">分析模型:{{ report!.modelCode }}</text>
<template v-if="report!.version == 2">
<view class="panel"><text class="section-title">需求概况</text><view class="fact-row" v-for="fact in report!.demand" :key="fact.label"><text class="fact-label">{{ fact.label }}</text><text class="fact-value">{{ fact.value }}</text></view></view>
<view class="panel"><text class="section-title">资料完整度</text><text class="readiness-value">{{ report!.completenessText }}</text><text class="report-note">仅统计当前适用的自填字段,不代表信用评分或真实性。</text><view class="fact-row" v-for="item in report!.completenessItems" :key="item.label"><text class="fact-label">{{ item.label }}</text><text class="fact-value">{{ item.value }}</text></view></view>
<view class="panel"><text class="section-title">融资准备状态</text><text class="readiness-value">{{ report!.readinessLabel }}</text><text class="body-text" v-for="(item, index) in report!.readinessBasis" :key="index">{{ item }}</text><text class="report-note">资料未经核验;即使完整度达到100%,仍需核对机构要求的材料。</text></view>
</template>
<view class="panel"><text class="section-title">自填资料分析</text><text class="body-text">{{ report!.summary }}</text><text class="report-note">{{ report!.disclaimer }}</text></view>
<view class="panel"><text class="section-title">本次提交的事实</text><text class="report-note">来源:用户自填,未经外部核验;未填写不等于零。</text><view class="fact-row" v-for="fact in report!.facts" :key="fact.label"><text class="fact-label">{{ fact.label }}</text><text class="fact-value">{{ fact.value }}</text></view></view>
<view class="panel"><text class="section-title">{{ report!.version == 2 ? '主要优势' : '资料解读' }}</text><text v-if="report!.observations.length == 0" class="report-note">暂无有充分依据的优势说明。</text><text class="body-text" v-for="(item, index) in report!.observations" :key="index">{{ item }}</text></view>
<view v-if="report!.suggestions.length > 0" class="panel"><text class="section-title">完善建议</text><text class="body-text" v-for="(item, index) in report!.suggestions" :key="index">{{ item }}</text></view>
<view class="panel"><text class="section-title">{{ report!.version == 2 ? '风险提示' : '待补充信息' }}</text><text class="body-text" v-for="(item, index) in report!.warnings" :key="'system-' + index">{{ item }}</text><text class="body-text" v-for="(item, index) in report!.dataGaps" :key="index">{{ item }}</text></view>
<view class="panel"><text class="section-title">产品匹配</text><text class="body-text">{{ report!.matchingMessage }}</text></view>
<text class="action" @click="history">查看历史评估</text>
</template>
</view>
</scroll-view>
</view>
</template>
<script setup lang="uts">
import { usePageLayout } from '@/services/layout.uts'
const { contentStyle, pageStyle } = usePageLayout()
import AppHeader from '@/components/AppHeader.uvue'
const viewMatches = () => { uni.navigateTo({ url: '/packages/finance/matches/matches' }) }
import AppHeader from '@/components/AppHeader.uvue'
import { usePageLayout } from '@/services/layout.uts'
import { getSession } from '@/services/api.uts'
import { AnalysisReport, fetchAnalysisReport } from '@/packages/finance/services/records.uts'
const { contentStyle, pageStyle } = usePageLayout()
const assessmentId = ref(''); const loading = ref(false); const error = ref('')
const report = ref<AnalysisReport | null>(null)
let generation = 0; let visible = false
const loadReport = async () => {
const request = ++generation
report.value = null; error.value = ''
if (assessmentId.value.length == 0) { error.value = '请从融资档案选择已完成的评估记录。'; return }
const session = getSession()
if (session == null) { uni.redirectTo({ url: '/packages/account/login/login' }); return }
const owner = session['token'] as string
loading.value = true
try {
const result = await fetchAnalysisReport(assessmentId.value)
if (visible && request == generation && getSession()?.getString('token') == owner) report.value = result
} catch (err) { if (visible && request == generation) error.value = err instanceof Error ? err.message : '报告加载失败,请重试' }
finally { if (request == generation) loading.value = false }
}
const history = () => { uni.navigateTo({ url: '/packages/finance/archive/archive' }) }
onLoad((options) => { if (options != null) assessmentId.value = options['assessmentId'] ?? '' })
onShow(() => { visible = true; loadReport() })
// 报告只留当前可见页面,账户切换或离开后不能由迟到请求恢复旧资料。
onHide(() => { visible = false; generation++; report.value = null; error.value = '' })
onUnload(() => { visible = false; generation++ })
</script>
<style>
.report-scroll { }
.report-content { padding: 28rpx 30rpx 80rpx; }
.score-panel { background-color: #fff; border-radius: 18rpx; padding: 30rpx; border: 1rpx solid #e7edf6; }
.score-label { font-size: 24rpx; color: #6f7d91; }
.score-row { flex-direction: row; align-items: flex-end; margin-top: 8rpx; }
.big-score { color: #102557; font-size: 82rpx; line-height: 90rpx; font-weight: 900; }
.score-unit { font-size: 28rpx; margin: 0 16rpx 14rpx 5rpx; }
.score-badge { padding: 8rpx 17rpx; border-radius: 10rpx; background-color: #e5f8ee; color: #0ba868; font-size: 22rpx; margin-bottom: 15rpx; }
.score-caption { color: #8793a6; font-size: 22rpx; margin-top: 6rpx; }
.radar-panel { height: 370rpx; margin-top: 20rpx; border-radius: 18rpx; background-color: #fff; border: 1rpx solid #e7edf6; align-items: center; justify-content: center; position: relative; overflow: hidden; }
.radar-image { width: 340rpx; height: 310rpx; }
.radar-core { position: absolute; width: 92rpx; height: 92rpx; left: 50%; top: 50%; margin-left: -46rpx; margin-top: -46rpx; border-radius: 50%; background-color: #0868f7; align-items: center; justify-content: center; color: white; font-size: 34rpx; font-weight: 800; box-shadow: 0 0 32rpx rgba(8,104,247,.28); }
.radar-label { position: absolute; color: #58667b; font-size: 21rpx; }
.top { top: 28rpx; }
.left { left: 27rpx; top: 162rpx; }
.right { right: 27rpx; top: 162rpx; }
.bottom-left { left: 95rpx; bottom: 32rpx; }
.bottom-right { right: 86rpx; bottom: 32rpx; }
.conclusion { margin: 24rpx 0 32rpx; background-color: #fff; border-radius: 18rpx; padding: 28rpx; border: 1rpx solid #e7edf6; }
.conclusion-text { margin-top: 18rpx; font-size: 24rpx; color: #59677b; line-height: 42rpx; }
.suggest-row { margin-top: 24rpx; padding-top: 22rpx; border-top: 1rpx solid #edf1f6; flex-direction: row; justify-content: space-between; font-size: 25rpx; }
.suggest-value { color: #0868f7; font-size: 27rpx; font-weight: 700; }
.suggest-label { color: #53667f; font-size: 25rpx; }
.report-note { font-size: 21rpx; line-height: 34rpx; color: #78889e; margin-top: 20rpx; }
.report-content { padding: 28rpx 30rpx 80rpx; }
.panel { margin-top: 24rpx; padding: 28rpx; border-radius: 18rpx; background-color: #fff; border: 1rpx solid #e7edf6; }
.body-text { margin-top: 20rpx; font-size: 27rpx; color: #42536b; line-height: 44rpx; }
.report-note { font-size: 23rpx; line-height: 38rpx; color: #78889e; margin-top: 18rpx; }
.fact-row { flex-direction: row; padding: 22rpx 0; border-bottom: 1rpx solid #edf1f6; }
.fact-label { flex: 1; font-size: 25rpx; color: #738198; }
.fact-value { flex: 1; text-align: right; font-size: 26rpx; color: #23344e; }
.action { padding: 26rpx 0; color: #0868f7; font-size: 27rpx; }
.readiness-value { margin-top: 20rpx; font-size: 32rpx; line-height: 48rpx; font-weight: bold; color: #23344e; }
</style>