feat(融资): 新增产品对比页面及相关接口实现

新增产品对比页面 compare/compare,支持同一评估下最多3个产品的横向对比,展示额度、期限、利率、适用条件等核心指标及匹配详情。
新增 matches.uts 服务层,封装匹配列表、详情、公共产品查询接口。
新增 account 分包的 latest-report.uts 服务,用于查找用户最新成功报告。
调整个人中心页面,将“评估记录”改为“开始评估”,新增最近报告直达功能。
更新文档,标记匹配相关接口及原型07、09页面已实现,调整页面数量至26页。
移除 finance 分包中旧的 fetchMatches、fetchProduct 模拟数据接口,统一使用正式API。
This commit is contained in:
hujun
2026-09-28 14:56:46 +08:00
parent 26da91b337
commit 7c252fc730
20 changed files with 1605 additions and 403 deletions
+44 -3
View File
@@ -4,7 +4,7 @@
<scroll-view scroll-y :style="contentStyle(226, false)">
<view class="profile-content">
<view class="profile-card" @click="openAccount"><view class="avatar"><image class="profile-avatar" :src="avatar.length > 0 ? avatar : DEFAULT_AVATAR" mode="aspectFill" @error="avatar = ''" /></view><view class="profile-main"><text class="profile-name">{{ displayName }}</text><text class="profile-phone">{{ loggedIn ? maskedPhone : '登录后管理您的融资信息' }}</text></view><text v-if="!loggedIn" class="account-state">未登录</text><AppIcon v-if="loggedIn" name="chevron-right" :size="28" /></view>
<view class="stat-row"><view class="stat-item" @click="go('/packages/finance/archive/archive')"><text class="stat-value">查看</text><text class="stat-label">评估记录</text></view><view class="stat-item" @click="openReport"><text class="stat-value">查看</text><text class="stat-label">评估报告</text></view><view class="stat-item" @click="go('/packages/account/points/points')"><AppIcon name="points" :size="42" /><text class="stat-label">积分中心</text></view></view>
<view class="stat-row"><view class="stat-item" @click="startAssessment"><AppIcon name="edit-file" :size="42" /><text class="stat-label">开始评估</text></view><view class="stat-item" @click="openReport"><AppIcon name="file-text" :size="42" /><text class="stat-label">{{ reportLoading ? '正在查找…' : '最近报告' }}</text></view><view class="stat-item" @click="go('/packages/account/points/points')"><AppIcon name="points" :size="42" /><text class="stat-label">积分中心</text></view></view>
<view class="menu-list"><view class="menu-row" v-for="item in menus" :key="item.name" @click="open(item.key)"><AppIcon :name="item.icon" :size="46" /><text class="menu-name">{{ item.name }}</text><text v-if="item.note.length > 0" class="menu-note">{{ item.note }}</text><AppIcon name="chevron-right" :size="28" /></view></view>
<button class="logout" @click="logout">{{ loggedIn ? '退出登录' : '登录 / 注册' }}</button>
<text class="version-note">AI融资 · 让融资更简单</text>
@@ -23,6 +23,7 @@
import AppIcon from '@/components/AppIcon.uvue'
import { getSession } from '@/services/api.uts'
import { logoutAccount } from '@/packages/account/services/auth.uts'
import { findLatestReportEntry } from '@/packages/account/services/latest-report.uts'
import { showAgreementNotice } from '@/services/assessment.uts'
import { DEFAULT_AVATAR, getAccountProfile, fetchAccountProfile, maskPhone } from '@/packages/account/services/account.uts'
const { contentStyle, pageStyle } = usePageLayout()
@@ -32,7 +33,10 @@
const displayName = ref('登录 / 注册')
const maskedPhone = ref('')
const avatar = ref('')
const reportLoading = ref(false)
let profileVisible = false; let reportRequest = 0
onShow(() => {
profileVisible = true
const session = getSession()
loggedIn.value = session != null
const profile = getAccountProfile()
@@ -43,7 +47,45 @@
}).catch(() => { /* 读取失败保留已归属当前账户的缓存,不显示其他账户资料。 */ })
})
const go = (url: string) => { uni.navigateTo({ url }) }
const openReport = () => { go('/packages/finance/archive/archive') }
const startAssessment = () => {
const session = getSession()
if (session == null) { go('/packages/account/login/login'); return }
const token = session.getString('token') ?? ''
uni.showActionSheet({ itemList: ['个人评估', '企业评估'], success: (result) => {
if (profileVisible && getSession()?.getString('token') == token) go('/packages/finance/entry/entry?type=' + (result.tapIndex == 0 ? 'personal' : 'enterprise'))
} })
}
const openReport = async () => {
if (reportLoading.value) return
const session = getSession()
if (session == null) { go('/packages/account/login/login'); return }
const token = session.getString('token') ?? ''; const request = ++reportRequest
const current = (): boolean => profileVisible && request == reportRequest && getSession()?.getString('token') == token
reportLoading.value = true
try {
const entry = await findLatestReportEntry()
if (!current()) return
if (entry != null && entry.status == 'succeeded') {
go('/packages/finance/report/report?assessmentId=' + entry.id)
} else if (entry != null) {
const id = entry.id
uni.showModal({ title: '报告生成中', content: '您有一份评估正在排队或分析,可查看当前进度。', confirmText: '查看进度', cancelText: '暂不查看', success: (result) => {
if (result.confirm && current()) go('/packages/finance/analyzing/analyzing?assessmentId=' + id)
} })
} else {
uni.showModal({ title: '暂无报告', content: '暂无已完成的评估报告。您可以开始评估,已提交的记录可在“我的融资档案”查看。', confirmText: '开始评估', cancelText: '暂不评估', success: (result) => {
if (result.confirm && current()) startAssessment()
} })
}
} catch (err) {
if (!profileVisible || request != reportRequest) return
if (getSession() == null) { go('/packages/account/login/login'); return }
if (current()) uni.showToast({ title: err instanceof Error ? err.message : '报告查找失败,请重试', icon: 'none' })
} finally { if (request == reportRequest) reportLoading.value = false }
}
// 离页或退出后,旧查找结果及弹窗回调不得把用户带到前一个账户的评估。
onHide(() => { profileVisible = false; reportRequest++; reportLoading.value = false })
onUnload(() => { profileVisible = false; reportRequest++; reportLoading.value = false })
const open = (key: string) => {
if (key == 'archive') go('/packages/finance/archive/archive')
else if (key == 'help') go('/packages/content/help/help')
@@ -72,7 +114,6 @@
.account-state { padding: 8rpx 12rpx; border-radius: 8rpx; background-color: #eef4fd; color: #6c84a5; font-size: 22rpx; flex-shrink: 0; margin-left: 10rpx; }
.stat-row { min-height: 136rpx; background-color: #fff; border-top: 1rpx solid #edf1f6; border-radius: 0 0 18rpx 18rpx; flex-direction: row; }
.stat-item { flex: 1; align-items: center; justify-content: center; }
.stat-value { font-size: 31rpx; font-weight: 700; color: #173864; line-height: 44rpx; }
.stat-label { color: #7d8ca1; font-size: 24rpx; margin-top: 10rpx; }
.menu-list { margin-top: 24rpx; padding: 0 24rpx; border-radius: 18rpx; background-color: #fff; }
.menu-row { min-height: 106rpx; flex-direction: row; align-items: center; border-bottom: 1rpx solid #edf1f6; }
@@ -0,0 +1,20 @@
import { requestJson } from '@/services/api.uts'
export type LatestReportEntry = { id: string, status: string }
async function latestAssessment(status: string): Promise<LatestReportEntry | null> {
const result = await requestJson('/assessments', 'GET', { status, limit: '1' })
const items = result['items'] as UTSJSONObject[] | null
if (items == null) throw new Error('评估记录响应不完整')
if (items.length == 0) return null
const id = items[0].getString('assessmentId') ?? ''
if (!/^[1-9][0-9]{0,9}$/.test(id) || items[0].getString('status') != status) throw new Error('评估记录格式错误,请稍后重试')
return { id, status }
}
// 服务端先按成功状态筛选再取最新提交,避免最近一页中的处理中任务遮住更早的报告。
// 账户分包只读取共享API,不导入融资分包代码,也不缓存报告或个人资料。
export async function findLatestReportEntry(): Promise<LatestReportEntry | null> {
const report = await latestAssessment('succeeded')
return report != null ? report : await latestAssessment('processing')
}