169 lines
14 KiB
Plaintext
169 lines
14 KiB
Plaintext
<template>
|
|
<view class="page page-white" :style="pageStyle()">
|
|
<AppHeader title="确认信息" />
|
|
<scroll-view scroll-y :style="contentStyle(96, false)">
|
|
<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="!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>
|
|
<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">{{ !hasInput ? '去填写信息' : (submitting ? '提交中…' : '确认提交资料') }}</button></view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
|
|
import { wanAmountValid } from '@/services/money.uts'
|
|
import { getSession } from '@/services/api.uts'
|
|
import { usePageLayout } from '@/services/layout.uts'
|
|
import AppHeader from '@/components/AppHeader.uvue'
|
|
import { submitAssessment } from '@/packages/finance/services/api.uts'
|
|
import { activeAssessmentType, getAssessmentInput, draftText, documentsFromInput, ownershipFromInput, formFromInput, assetValidationMessage, contributionValidationMessage, 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 hasInput = ref(false)
|
|
const assessmentType = ref('personal')
|
|
const sections = ref<InfoSection[]>([])
|
|
const payload = ref<UTSJSONObject>({ source: 'mobile', confirmed: false })
|
|
let visible = false; let generation = 0; let ownerToken = ''; let accountTimer = 0
|
|
const stopAccountTimer = () => { if (accountTimer != 0) clearInterval(accountTimer); accountTimer = 0 }
|
|
const clearView = () => { hasInput.value = false; sections.value = []; payload.value = { source: 'mobile', confirmed: false }; submitting.value = false }
|
|
const sameOwner = (): boolean => {
|
|
if (!visible) return false
|
|
if (ownerToken.length > 0 && getSession()?.getString('token') == ownerToken) return true
|
|
generation++; clearView(); clearAssessmentFlow(); stopAccountTimer()
|
|
if (getSession() == null) uni.redirectTo({ url: '/packages/account/login/login' })
|
|
else uni.showToast({ title: '账户已变化,请重新填写。', icon: 'none' })
|
|
return false
|
|
}
|
|
const mask = (value: string, left: number, right: number): string => { if (value.length <= left + right) return value; return value.substring(0, left) + '****' + value.substring(value.length - right) }
|
|
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 loadInput = () => {
|
|
assessmentType.value = activeAssessmentType()
|
|
const saved = getAssessmentInput(assessmentType.value)
|
|
if (saved == null) { clearView(); return }
|
|
hasInput.value = true
|
|
const enterprise = assessmentType.value == 'enterprise'
|
|
const basicRows: InfoRow[] = []
|
|
if (enterprise) {
|
|
appendRow(basicRows, '企业名称', displayValue(saved, 'enterpriseName'))
|
|
appendRow(basicRows, '信用代码', mask(displayValue(saved, 'businessCode'), 4, 4))
|
|
}
|
|
appendRow(basicRows, enterprise ? '联系人' : '姓名', displayValue(saved, 'name'))
|
|
appendRow(basicRows, '手机号', mask(displayValue(saved, 'phone'), 3, 4))
|
|
appendRow(basicRows, '所在城市', displayValue(saved, 'city'))
|
|
if (!enterprise) {
|
|
appendRow(basicRows, '身份证号', mask(displayValue(saved, 'idCard'), 4, 4))
|
|
appendRow(basicRows, '婚姻状况', displayValue(saved, 'maritalStatus'))
|
|
}
|
|
const incomeRows: InfoRow[] = enterprise ? [
|
|
{ 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: valueWithUnit(saved, 'socialSecurityBase', '万元') }, { label: '公积金缴纳', value: displayValue(saved, 'housingFund') }, { label: '公积金月缴存基数', value: valueWithUnit(saved, 'housingFundBase', '万元') }
|
|
]
|
|
const files = documentsFromInput(saved)
|
|
const fileNames = files.map((file) => file.name).join('、')
|
|
const grants = saved['authorizations'] as string[] | null
|
|
const grantNames: string[] = []
|
|
if (grants != null) { if (grants.includes('credit')) grantNames.push('征信'); if (grants.includes('business')) grantNames.push('经营'); if (grants.includes('fund')) grantNames.push('公积金') }
|
|
sections.value = [
|
|
{ title: '基本信息', step: 0, rows: basicRows, expanded: false },
|
|
{ title: enterprise ? '经营信息' : '职业与收入', step: 1, rows: incomeRows, expanded: false },
|
|
{ title: '资产与负债', step: 2, rows: [
|
|
{ label: '信用贷款笔数', value: valueWithUnit(saved, 'creditLoanCount', '笔') },
|
|
{ label: '欠款总余额', value: valueWithUnit(saved, 'totalDebtBalance', '万元') },
|
|
{ label: '贷款月还款金额', value: valueWithUnit(saved, 'monthlyLoanPayment', '万元') },
|
|
{ label: '银行信用卡张数', value: valueWithUnit(saved, 'creditCardCount', '张') },
|
|
{ label: '总额度', value: valueWithUnit(saved, 'creditCardTotalLimit', '万元') },
|
|
{ label: '已使用总额度', value: valueWithUnit(saved, 'creditCardUsedLimit', '万元') },
|
|
{ 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(() => {
|
|
visible = true; generation++; ownerToken = getSession()?.getString('token') ?? ''
|
|
loadInput(); stopAccountTimer()
|
|
if (sameOwner()) accountTimer = setInterval(() => { sameOwner() }, 1000)
|
|
})
|
|
onHide(() => { visible = false; generation++; stopAccountTimer(); clearView() })
|
|
onUnload(() => {
|
|
visible = false; generation++; stopAccountTimer(); clearView()
|
|
const pages = getCurrentPages()
|
|
if (!pages.some((page): boolean => page.route == 'packages/finance/assessment/assessment')) clearAssessmentFlow()
|
|
})
|
|
const edit = (step: number) => {
|
|
if (!sameOwner()) return
|
|
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 (!sameOwner()) return
|
|
if (!hasInput.value) { edit(0); return }
|
|
if (submitting.value) return
|
|
const saved = payload.value
|
|
const amount = draftText(saved, assessmentType.value == 'enterprise' ? 'annualRevenue' : 'monthlyIncome')
|
|
if (draftText(saved, 'name').length < 2 || !/^1[3-9][0-9]{9}$/.test(draftText(saved, 'phone')) || draftText(saved, 'city').length == 0) { uni.showToast({ title: '请先完善基本信息', icon: 'none' }); edit(0); return }
|
|
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 grants = saved['authorizations'] as string[] | null
|
|
if (grants != null && grants.length > 0 && saved['dataConsent'] != true) { uni.showToast({ title: '请确认授权选择', icon: 'none' }); edit(3); return }
|
|
const contributionError = assessmentType.value == 'personal' ? contributionValidationMessage(formFromInput(payload.value)) : ''
|
|
if (contributionError.length > 0) { uni.showToast({ title: contributionError, icon: 'none' }); edit(1); return }
|
|
const assetError = assetValidationMessage(formFromInput(saved))
|
|
if (assetError.length > 0) { uni.showToast({ title: assetError, icon: 'none' }); edit(2); return }
|
|
submitting.value = true
|
|
const request = ++generation
|
|
try {
|
|
const id = await submitAssessment(payload.value)
|
|
if (request != generation || !sameOwner()) return
|
|
clearAssessmentFlow()
|
|
// 清空本次填写流程和页面栈;修改须重新读取最新版本,防止返回旧表单重复提交。
|
|
uni.reLaunch({ url: '/packages/finance/analyzing/analyzing?assessmentId=' + encodeURIComponent(id) })
|
|
}
|
|
catch (error) { if (request == generation && sameOwner()) uni.showToast({ title: error instanceof Error ? error.message : '提交失败,请重试', icon: 'none' }) }
|
|
finally { if (request == generation && visible) submitting.value = false }
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.confirm-content { padding: 26rpx 30rpx 0; }
|
|
.confirm-progress { flex-direction: row; align-items: center; margin-bottom: 24rpx; }
|
|
.progress-line { flex: 1; flex-direction: row; }
|
|
.progress-segment { flex: 1; height: 8rpx; margin-right: 8rpx; border-radius: 8rpx; background-color: #0868f7; }
|
|
.progress-count { font-size: 24rpx; color: #758297; margin-left: 20rpx; }
|
|
.confirm-tip { padding: 24rpx; border-radius: 18rpx; background-color: #eff6ff; flex-direction: row; align-items: center; }
|
|
.tip-icon { width: 56rpx; height: 56rpx; border-radius: 28rpx; background-color: #0868f7; align-items: center; justify-content: center; margin-right: 18rpx; flex-shrink: 0; }
|
|
.tip-mark { color: #fff; font-size: 28rpx; }
|
|
.tip-copy { flex: 1; min-width: 0; }
|
|
.tip-title { color: #22324b; font-size: 29rpx; font-weight: 600; }
|
|
.tip-desc { color: #738198; font-size: 24rpx; line-height: 36rpx; margin-top: 7rpx; }
|
|
.empty-hint { font-size: 28rpx; line-height: 44rpx; color: #778499; margin-top: 40rpx; }
|
|
.info-section { margin-top: 18rpx; border-bottom: 1rpx solid #e9eef5; padding-bottom: 14rpx; }
|
|
.section-bar { height: 74rpx; flex-direction: row; align-items: center; justify-content: space-between; }
|
|
.section-name { font-size: 29rpx; font-weight: 600; color: #22324b; }
|
|
.edit { padding: 16rpx 0 16rpx 24rpx; color: #0868f7; font-size: 25rpx; }
|
|
.info-row { min-height: 62rpx; flex-direction: row; align-items: flex-start; padding: 9rpx 0; }
|
|
.info-label { color: #758297; font-size: 25rpx; line-height: 38rpx; width: 176rpx; flex-shrink: 0; margin-right: 16rpx; }
|
|
.info-value { flex: 1; min-width: 0; text-align: right; color: #26374f; font-size: 26rpx; line-height: 38rpx; }
|
|
.expand-link { color: #7387a3; font-size: 24rpx; padding: 15rpx 0 10rpx; }
|
|
.assessment-note { font-size: 23rpx; line-height: 36rpx; color: #8490a3; margin-top: 24rpx; }
|
|
.scroll-spacer { min-height: 152rpx; }
|
|
.submit-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 20rpx 30rpx; border-top: 1rpx solid #edf1f6; background-color: #fff; }
|
|
.submit-button { margin: 0; }
|
|
</style>
|