feat: 实现协议全文与同意记录功能
新增协议全文后台管理、版本化发布及本人同意记录功能,支持可撤销授权;完善资料与授权相关边界提示,调整评估提交时的资料与授权校验逻辑,同步更新相关页面文案与路由。
This commit is contained in:
@@ -16,7 +16,7 @@
|
||||
<view class="overview-label overview-head"><text class="row-label">核心参数</text></view>
|
||||
<scroll-view v-for="column in columns" :key="column.id" scroll-y class="overview-value overview-head">
|
||||
<view class="overview-product">
|
||||
<template v-if="usable(column)"><text class="overview-name">{{ column.data!.item.product.name }}</text><text class="overview-bank">{{ column.data!.item.product.bank }}</text><text class="overview-link" @click="detail(column.id)">产品详情 ›</text></template>
|
||||
<template v-if="usable(column)"><image class="overview-logo" :src="column.data!.item.product.logo.length > 0 ? column.data!.item.product.logo : '/static/mobile/icons-generated/building.png'" mode="aspectFit" @error="column.data!.item.product.logo = ''" /><text class="overview-name">{{ column.data!.item.product.name }}</text><text v-if="column.data!.item.product.bank.length > 0" class="overview-bank">{{ column.data!.item.product.bank }}</text><text class="overview-link" @click="detail(column.id)">产品详情 ›</text></template>
|
||||
<template v-else><text class="overview-name">产品 #{{ column.id }}</text><text class="overview-bank">{{ column.loading ? '读取中…' : problem(column) }}</text><text v-if="!column.loading" class="overview-link" @click="loadColumn(column.id)">重试此列</text></template>
|
||||
<text class="overview-remove" @click="remove(column.id)">移除</text>
|
||||
</view>
|
||||
@@ -27,7 +27,7 @@
|
||||
<view v-for="column in columns" :key="column.id" class="overview-value"><text :class="overviewClass(column, row.id)">{{ cell(column, row.id) }}</text></view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="overview-note">展示顺序不代表优劣,具体条件以机构核验为准。</text>
|
||||
<text class="overview-note">{{ orderingNote }}</text>
|
||||
<view class="details-toggle" @click="detailsOpen = !detailsOpen"><text class="summary-title">详细条件与匹配依据</text><text class="detail-toggle-label">{{ detailsOpen ? '收起' : '展开' }}</text></view>
|
||||
<text v-if="detailsOpen" class="scroll-hint">左右滑动查看各产品 · 长内容可在单元格内上下滑动</text>
|
||||
<view v-if="detailsOpen" class="table">
|
||||
@@ -37,6 +37,7 @@
|
||||
<view v-for="column in columns" :key="column.id" class="column">
|
||||
<scroll-view scroll-y class="product-head"><view class="head-content">
|
||||
<template v-if="usable(column)">
|
||||
<image class="overview-logo" :src="column.data!.item.product.logo.length > 0 ? column.data!.item.product.logo : '/static/mobile/icons-generated/building.png'" mode="aspectFit" @error="column.data!.item.product.logo = ''" />
|
||||
<text class="product-name">{{ column.data!.item.product.name }}</text>
|
||||
<text class="hint">{{ column.data!.item.product.bank }} · v{{ column.data!.item.product.version }}</text>
|
||||
<text :class="column.data!.item.state == 'eligible' ? 'state good' : (column.data!.item.state == 'ineligible' ? 'state blocked' : 'state pending')">{{ column.data!.item.stateLabel }}</text>
|
||||
@@ -66,7 +67,7 @@
|
||||
import { usePageLayout } from '@/services/layout.uts'
|
||||
import { getSession } from '@/services/api.uts'
|
||||
import { assistantRoute } from '@/packages/finance/services/assistant.uts'
|
||||
import { MatchDetail, MatchReason, validRecordId, fetchMatchDetailRecord } from '@/packages/finance/services/matches.uts'
|
||||
import { MatchDetail, MatchReason, validRecordId, fetchMatchDetailRecord, hasProductValue, matchPercentage } from '@/packages/finance/services/matches.uts'
|
||||
import { parseCompareIds, saveCompareSelection, compareQuery, sameMatchSnapshot, compareReasonText } from '@/packages/finance/services/compare.uts'
|
||||
type CompareColumn = { id: string, loading: boolean, error: string, data: MatchDetail | null }
|
||||
type CompareRow = { id: string, label: string, height: number }
|
||||
@@ -74,7 +75,7 @@
|
||||
const revisionId = ref(''); const assessmentId = ref(''); const ids = ref<string[]>([]); const columns = ref<CompareColumn[]>([]); const routeError = ref('')
|
||||
const detailsOpen = ref(false)
|
||||
// 首屏并排展示短参数;长证据保留原宽表,展开后读取完整内容,不截断判断依据。
|
||||
const overviewRows: CompareRow[] = [{ id: 'state', label: '匹配状态', height: 0 }, { id: 'amount', label: '额度范围', height: 0 }, { id: 'term', label: '期限范围', height: 0 }, { id: 'rate', label: '年化利率', height: 0 }]
|
||||
const overviewDefinitions: CompareRow[] = [{ id: 'state', label: '匹配状态', height: 0 }, { id: 'score', label: '匹配度', height: 0 }, { id: 'amount', label: '额度范围', height: 0 }, { id: 'term', label: '期限范围', height: 0 }, { id: 'rate', label: '年化利率', height: 0 }]
|
||||
let visible = false; let generation = 0
|
||||
let owner = ''; let accountTimer = 0; let frozenDetail: MatchDetail | null = null
|
||||
const stopAccountTimer = () => { if (accountTimer != 0) clearInterval(accountTimer); accountTimer = 0 }
|
||||
@@ -91,13 +92,14 @@
|
||||
history.replaceState(history.state, '', location.pathname + location.search + '#/packages/finance/compare/compare' + compareQuery(assessmentId.value, ids.value, revisionId.value))
|
||||
// #endif
|
||||
}
|
||||
const rows: CompareRow[] = [
|
||||
const detailRows: CompareRow[] = [
|
||||
{ id: 'availability', label: '当前状态', height: 160 },
|
||||
{ id: 'score', label: '匹配度', height: 120 },
|
||||
{ id: 'amount', label: '额度范围', height: 120 }, { id: 'term', label: '期限范围', height: 120 }, { id: 'rate', label: '年化利率', height: 120 },
|
||||
{ id: 'objects', label: '适用对象', height: 120 }, { id: 'regions', label: '适用地区', height: 140 },
|
||||
{ id: 'positive', label: '已满足条件', height: 360 }, { id: 'blocking', label: '不符条件', height: 300 }, { id: 'cautions', label: '待核实与提示', height: 300 },
|
||||
{ id: 'positive', label: '推荐与参考理由', height: 360 }, { id: 'blocking', label: '不推荐原因', height: 300 }, { id: 'cautions', label: '注意与补充资料', height: 300 },
|
||||
{ id: 'conditions', label: '准入条件', height: 300 }, { id: 'repayment', label: '还款方式', height: 120 },
|
||||
{ id: 'materials', label: '准备材料', height: 260 }, { id: 'channel', label: '办理渠道', height: 120 }, { id: 'valid', label: '产品有效期', height: 160 }, { id: 'source', label: '资料来源', height: 260 }
|
||||
{ id: 'materials', label: '准备材料', height: 260 }, { id: 'channel', label: '办理渠道', height: 120 }, { id: 'source', label: '资料来源', height: 260 }
|
||||
]
|
||||
const rowStyle = (row: CompareRow): string => 'height:' + row.height + 'rpx;'
|
||||
const columnsStyle = (): string => 'width:' + (columns.value.length * 350) + 'rpx;'
|
||||
@@ -120,21 +122,27 @@
|
||||
const m = column.data!.item; const p = m.product
|
||||
// 核心表也保留当前不可用提示,不能让历史符合条件被误读为现在仍可办理。
|
||||
if (key == 'state') return m.stateLabel + (m.availability != 'available' ? ' · ' + m.availabilityLabel : '')
|
||||
if (key == 'score') return matchPercentage(m.state, m.score) || '—'
|
||||
if (key == 'availability') return m.availabilityLabel + ';不改变历史结论'
|
||||
if (key == 'positive') return compareReasonText(m.positive, '暂无已确认满足的条件')
|
||||
if (key == 'blocking') return compareReasonText(m.blocking, '未发现已知不符条件;仍以核验为准')
|
||||
if (key == 'cautions') return compareReasonText(m.cautions, '暂无待核实提示;不代表已获审批')
|
||||
if (key == 'conditions') return p.conditions.length > 0 ? p.conditions.join('\n\n') : '未提供'
|
||||
if (key == 'amount') return p.amount
|
||||
if (key == 'term') return p.term
|
||||
if (key == 'rate') return p.rate
|
||||
if (key == 'amount') return hasProductValue(p.amount) ? p.amount : '—'
|
||||
if (key == 'term') return hasProductValue(p.term) ? p.term : '—'
|
||||
if (key == 'rate') return hasProductValue(p.rate) ? p.rate : '—'
|
||||
if (key == 'objects') return p.objects
|
||||
if (key == 'regions') return p.regions
|
||||
if (key == 'materials') return p.materials
|
||||
if (key == 'valid') return p.valid
|
||||
if (key == 'source') return p.source + '\n引用:' + p.sourceRef + '\n核对日期:' + p.checkedOn
|
||||
if (key == 'repayment') return hasProductValue(p.repayment) ? p.repayment : '—'
|
||||
if (key == 'channel') return hasProductValue(p.channel) ? p.channel : '—'
|
||||
if (key == 'source') return [hasProductValue(p.source) ? p.source : '', hasProductValue(p.sourceRef) ? '引用:' + p.sourceRef : '', hasProductValue(p.checkedOn) ? '核对日期:' + p.checkedOn : ''].filter((value: string): boolean => value.length > 0).join('\n') || '—'
|
||||
return '未提供'
|
||||
}
|
||||
const rowHasValue = (row: CompareRow): boolean => columns.value.some((column: CompareColumn): boolean => !usable(column) || hasProductValue(cell(column, row.id)))
|
||||
const overviewRows = computed((): CompareRow[] => overviewDefinitions.filter((row: CompareRow): boolean => !['score', 'amount', 'term', 'rate'].includes(row.id) || rowHasValue(row)))
|
||||
const rows = computed((): CompareRow[] => detailRows.filter((row: CompareRow): boolean => !['score', 'amount', 'term', 'rate', 'repayment', 'channel', 'source'].includes(row.id) || rowHasValue(row)))
|
||||
const orderingNote = computed((): string => columns.value.some((column: CompareColumn): boolean => usable(column) && column.data!.orderingVersion == 'product-score-descending-v1') ? '匹配列表按匹配度排序,此处按所选顺序对比。匹配度不代表获批概率。' : '按所选顺序对比,具体条件以机构核验为准。')
|
||||
const loadColumn = async (id: string) => {
|
||||
if (!current(generation, owner)) return
|
||||
const column = columns.value.find((c: CompareColumn): boolean => c.id == id)
|
||||
@@ -204,6 +212,7 @@
|
||||
.overview-link, .overview-remove { font-size: 22rpx; line-height: 34rpx; color: #0868f7; padding-top: 12rpx; }
|
||||
.overview-remove { color: #788ba2; }
|
||||
.overview-text { font-size: 23rpx; line-height: 36rpx; color: #455f7c; }
|
||||
.overview-logo { width: 56rpx; height: 56rpx; margin-bottom: 10rpx; flex-shrink: 0; }
|
||||
.overview-amount { font-weight: 700; color: #154779; }
|
||||
.overview-note { color: #8494a7; font-size: 21rpx; line-height: 34rpx; margin-top: 14rpx; }
|
||||
.details-toggle { flex-direction: row; align-items: center; justify-content: space-between; padding: 28rpx 0; }
|
||||
|
||||
Reference in New Issue
Block a user