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
+39 -20
View File
@@ -5,29 +5,29 @@
<view class="confirm-content">
<view class="confirm-progress"><view class="progress-line"><view class="progress-segment" v-for="i in 5" :key="i"></view></view><text class="progress-count">5/5</text></view>
<view class="confirm-tip"><view class="tip-icon"><text class="tip-mark">✓</text></view><view class="tip-copy"><text class="tip-title">{{ assessmentType == 'enterprise' ? '企业融资信息' : '个人融资信息' }}</text><text class="tip-desc">请确认后再开始评估,可随时返回修改。</text></view></view>
<text v-if="!hasDraft" class="empty-hint">还没有评估信息,请先完成信息填写。</text>
<text v-if="!hasInput" class="empty-hint">还没有评估信息,请先完成信息填写。</text>
<view class="info-section" v-for="section in sections" :key="section.title"><view class="section-bar"><text class="section-name">{{ section.title }}</text><text class="edit" @click="edit(section.step)">编辑</text></view><template v-for="(row, index) in section.rows" :key="row.label"><view v-if="index < 3 || section.expanded" class="info-row"><text class="info-label">{{ row.label }}</text><text class="info-value">{{ row.value }}</text></view></template><text v-if="section.rows.length > 3" class="expand-link" @click="section.expanded = !section.expanded">{{ section.expanded ? '收起详情' : '查看其余信息' }}</text></view>
<text class="assessment-note">提交信息用于融资评估,不代表已向金融机构提交贷款申请。</text>
<text class="assessment-note">提交后保存评估资料并进行自填资料分析。融资需求不代表授信承诺。资料上传及外部查询暂未开放;如已选资料或授权,请先移除。不会向金融机构提交贷款申请。</text>
<view class="scroll-spacer" :style="bottomStyle(24)"></view>
</view>
</scroll-view>
<view class="submit-bar" :style="bottomStyle(20)"><button class="primary-button submit-button" :disabled="submitting" @click="submit">{{ !hasDraft ? '去填写信息' : (submitting ? '提交中…' : '确认并开始评估') }}</button></view>
<view class="submit-bar" :style="bottomStyle(20)"><button class="primary-button submit-button" :disabled="submitting" @click="submit">{{ !hasInput ? '去填写信息' : (submitting ? '提交中…' : '确认提交资料') }}</button></view>
</view>
</template>
<script setup lang="uts">
import { setUserStorage } from '@/services/storage.uts'
import { wanAmountValid } from '@/services/money.uts'
import { demandValidationMessage } from '@/services/assessment-demand.uts'
import { usePageLayout } from '@/services/layout.uts'
import AppHeader from '@/components/AppHeader.uvue'
import { submitAssessment } from '@/packages/finance/services/api.uts'
import { activeAssessmentType, loadAssessmentDraft, draftText, documentsFromDraft, ownershipFromDraft, formFromDraft, assetValidationMessage } from '@/services/assessment.uts'
import { activeAssessmentType, getAssessmentInput, draftText, documentsFromInput, ownershipFromInput, formFromInput, assetValidationMessage, clearAssessmentFlow } from '@/services/assessment.uts'
const { pageStyle, contentStyle, bottomStyle } = usePageLayout()
type InfoRow = { label: string, value: string }
type InfoSection = { title: string, step: number, rows: InfoRow[], expanded: boolean }
const submitting = ref(false)
const hasDraft = ref(false)
const hasInput = ref(false)
const assessmentType = ref('personal')
const sections = ref<InfoSection[]>([])
const payload = ref<UTSJSONObject>({ source: 'mobile', confirmed: false })
@@ -35,11 +35,11 @@
const displayValue = (saved: UTSJSONObject, key: string): string => { const value = draftText(saved, key); return value.length > 0 ? value : '未填写' }
const appendRow = (rows: InfoRow[], label: string, value: string) => { const row: InfoRow = { label, value }; rows.push(row) }
const valueWithUnit = (saved: UTSJSONObject, key: string, unit: string): string => { const value = draftText(saved, key); return value.length > 0 ? value + ' ' + unit : '未填写' }
const loadDraft = () => {
const loadInput = () => {
assessmentType.value = activeAssessmentType()
const saved = loadAssessmentDraft(assessmentType.value)
if (saved == null) { hasDraft.value = false; sections.value = []; return }
hasDraft.value = true
const saved = getAssessmentInput(assessmentType.value)
if (saved == null) { hasInput.value = false; sections.value = []; return }
hasInput.value = true
const enterprise = assessmentType.value == 'enterprise'
const basicRows: InfoRow[] = []
if (enterprise) {
@@ -54,11 +54,17 @@
appendRow(basicRows, '婚姻状况', displayValue(saved, 'maritalStatus'))
}
const incomeRows: InfoRow[] = enterprise ? [
{ label: '经营行业', value: displayValue(saved, 'occupation') }, { label: '经营年限', value: displayValue(saved, 'operatingYears') }, { label: '年营业收入', value: draftText(saved, 'annualRevenue').length > 0 ? draftText(saved, 'annualRevenue') + ' 万元' : '未填写' }
{ label: '经营行业', value: displayValue(saved, 'occupation') }, { label: '经营月数', value: valueWithUnit(saved, 'operatingMonths', '个月') }, { label: '年营业收入', value: draftText(saved, 'annualRevenue').length > 0 ? draftText(saved, 'annualRevenue') + ' 万元' : '未填写' }
] : [
{ label: '职业类型', value: displayValue(saved, 'occupation') }, { label: '月收入', value: draftText(saved, 'monthlyIncome').length > 0 ? draftText(saved, 'monthlyIncome') + ' 万元' : '未填写' }, { label: '工作单位', value: displayValue(saved, 'company') }, { label: '社保缴纳', value: displayValue(saved, 'socialSecurity') }, { label: '公积金缴纳', value: displayValue(saved, 'housingFund') }
]
const files = documentsFromDraft(saved)
const files = documentsFromInput(saved)
const demandRows: InfoRow[] = [
{ label: '融资金额', value: valueWithUnit(saved, 'requestedAmountWan', '万元') },
{ label: '融资期限', value: valueWithUnit(saved, 'requestedTermMonths', '个月') },
{ label: '资金用途', value: displayValue(saved, 'purposeLabel') }
]
if (draftText(saved, 'purposeCode') == 'other') appendRow(demandRows, '用途说明', displayValue(saved, 'purposeDetail'))
const fileNames = files.map((file) => file.name).join('、')
const grants = saved['authorizations'] as string[] | null
const grantNames: string[] = []
@@ -66,6 +72,7 @@
sections.value = [
{ title: '基本信息', step: 0, rows: basicRows, expanded: false },
{ title: enterprise ? '经营信息' : '职业与收入', step: 1, rows: incomeRows, expanded: false },
{ title: '资金需求', step: 1, rows: demandRows, expanded: true },
{ title: '资产与负债', step: 2, rows: [
{ label: '信用贷款笔数', value: valueWithUnit(saved, 'creditLoanCount', '笔') },
{ label: '欠款总余额', value: valueWithUnit(saved, 'totalDebtBalance', '万元') },
@@ -73,22 +80,27 @@
{ label: '银行信用卡张数', value: valueWithUnit(saved, 'creditCardCount', '张') },
{ label: '总额度', value: valueWithUnit(saved, 'creditCardTotalLimit', '万元') },
{ label: '已使用总额度', value: valueWithUnit(saved, 'creditCardUsedLimit', '万元') },
{ label: '是否有房', value: ownershipFromDraft(saved, 'house').length > 0 ? ownershipFromDraft(saved, 'house') : '未填写' },
{ label: '是否有车', value: ownershipFromDraft(saved, 'car').length > 0 ? ownershipFromDraft(saved, 'car') : '未填写' }
{ label: '是否有房', value: ownershipFromInput(saved, 'house').length > 0 ? ownershipFromInput(saved, 'house') : '未填写' },
{ label: '是否有车', value: ownershipFromInput(saved, 'car').length > 0 ? ownershipFromInput(saved, 'car') : '未填写' }
], expanded: false },
{ title: '资料与授权', step: 3, rows: [{ label: '本地资料', value: fileNames.length > 0 ? fileNames : '未选择' }, { label: '授权意向', value: grantNames.length > 0 ? grantNames.join('、') : '未选择' }, { label: '当前状态', value: '资料未上传 · 数据未查询' }], expanded: false }
]
payload.value = { ...saved, source: 'mobile', confirmed: true }
}
onShow(() => loadDraft())
onShow(() => loadInput())
onUnload(() => {
const pages = getCurrentPages()
if (!pages.some((page): boolean => page.route == 'packages/finance/assessment/assessment')) clearAssessmentFlow()
})
const edit = (step: number) => {
setUserStorage('assessmentEditStep', step)
const input = getAssessmentInput(assessmentType.value)
if (input != null) input['step'] = step
const pages = getCurrentPages()
if (pages.length > 1 && pages[pages.length - 2].route == 'packages/finance/assessment/assessment') uni.navigateBack({ delta: 1 })
else uni.navigateTo({ url: '/packages/finance/assessment/assessment?type=' + assessmentType.value })
}
const submit = async () => {
if (!hasDraft.value) { edit(0); return }
if (!hasInput.value) { edit(0); return }
if (submitting.value) return
const saved = payload.value
const amount = draftText(saved, assessmentType.value == 'enterprise' ? 'annualRevenue' : 'monthlyIncome')
@@ -96,12 +108,19 @@
if (assessmentType.value == 'personal' && !/(^[0-9]{15}$)|(^[0-9]{17}[0-9Xx]$)/.test(draftText(saved, 'idCard'))) { uni.showToast({ title: '请补全身份证信息', icon: 'none' }); edit(0); return }
if (assessmentType.value == 'enterprise' && (draftText(saved, 'enterpriseName').length < 2 || !/^[0-9A-Z]{18}$/.test(draftText(saved, 'businessCode').toUpperCase()))) { uni.showToast({ title: '请补全企业信息', icon: 'none' }); edit(0); return }
if (!wanAmountValid(amount) || parseFloat(amount) <= 0) { uni.showToast({ title: '请先完善收入信息', icon: 'none' }); edit(1); return }
const demandError = demandValidationMessage(draftText(saved, 'requestedAmountWan'), draftText(saved, 'requestedTermMonths'), draftText(saved, 'purposeCode'), draftText(saved, 'purposeDetail'))
if (demandError.length > 0) { uni.showToast({ title: demandError, icon: 'none' }); edit(1); return }
const grants = saved['authorizations'] as string[] | null
if ((documentsFromDraft(saved).length > 0 && saved['documentConsent'] != true) || (grants != null && grants.length > 0 && saved['dataConsent'] != true)) { uni.showToast({ title: '请确认资料与授权选择', icon: 'none' }); edit(3); return }
const assetError = assetValidationMessage(formFromDraft(saved))
if ((documentsFromInput(saved).length > 0 && saved['documentConsent'] != true) || (grants != null && grants.length > 0 && saved['dataConsent'] != true)) { uni.showToast({ title: '请确认资料与授权选择', icon: 'none' }); edit(3); return }
const assetError = assetValidationMessage(formFromInput(saved))
if (assetError.length > 0) { uni.showToast({ title: assetError, icon: 'none' }); edit(2); return }
submitting.value = true
try { await submitAssessment(payload.value); uni.redirectTo({ url: '/packages/finance/analyzing/analyzing' }) }
try {
const id = await submitAssessment(payload.value)
clearAssessmentFlow()
// 已提交记录只读;清空页面栈,防止返回旧表单再次提交。
uni.reLaunch({ url: '/packages/finance/analyzing/analyzing?assessmentId=' + encodeURIComponent(id) })
}
catch (error) { uni.showToast({ title: error instanceof Error ? error.message : '提交失败,请重试', icon: 'none' }) }
finally { submitting.value = false }
}