2026-09-28 14:56:46 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<view class="page" :style="pageStyle()">
|
|
|
|
|
|
<AppHeader title="本次产品对比" :customBack="true" @back="back" />
|
|
|
|
|
|
<scroll-view scroll-y :style="contentStyle(96, false)">
|
|
|
|
|
|
<view class="content">
|
|
|
|
|
|
<text class="eyebrow">评估 #{{ assessmentId }} · 历史产品快照</text>
|
|
|
|
|
|
<text class="headline">并排看清,选择更有依据</text>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
<text class="intro">同次评估 · 金额单位为万元 · 条件判断不代表获批</text>
|
2026-09-28 14:56:46 +08:00
|
|
|
|
<view v-if="routeError.length > 0" class="panel"><text class="warning">{{ routeError }}</text><button class="secondary" @click="toMatches">返回本次匹配</button></view>
|
|
|
|
|
|
<template v-else>
|
|
|
|
|
|
<view class="toolbar"><view class="summary"><text class="summary-title">已选 {{ ids.length }} / 3 项</text><text class="hint">{{ complete ? '已读取同一版本的完整对比' : (ids.length < 2 ? '再选产品后开始对比' : '对比尚不完整,请查看各列状态') }}</text></view><button class="add" @click="toMatches">{{ ids.length < 3 ? '补选产品' : '调整选择' }}</button></view>
|
|
|
|
|
|
<view v-if="ids.length == 0" class="panel"><text class="summary-title">还没有选择产品</text><text class="intro">从本次匹配中选择2–3项,也可对照不符条件和待核实的产品。</text><button class="secondary" @click="toMatches">去选择产品</button></view>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
<template v-else>
|
|
|
|
|
|
<view class="overview-table">
|
|
|
|
|
|
<view class="overview-row">
|
|
|
|
|
|
<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">
|
2026-10-14 14:53:13 +08:00
|
|
|
|
<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>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view v-for="row in overviewRows" :key="row.id" class="overview-row">
|
|
|
|
|
|
<view class="overview-label"><text class="row-label">{{ row.label }}</text></view>
|
|
|
|
|
|
<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>
|
2026-10-14 14:53:13 +08:00
|
|
|
|
<text class="overview-note">{{ orderingNote }}</text>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
<view class="details-toggle" @click="detailsOpen = !detailsOpen"><text class="summary-title">详细条件与匹配依据</text><text class="detail-toggle-label">{{ detailsOpen ? '收起' : '展开' }}</text></view>
|
2026-10-22 11:24:15 +08:00
|
|
|
|
<text v-if="detailsOpen" class="scroll-hint">各产品并排展示 · 向下查看完整条件与依据</text>
|
|
|
|
|
|
<view v-if="detailsOpen" class="overview-table detail-table">
|
|
|
|
|
|
<view class="overview-row">
|
|
|
|
|
|
<view class="overview-label"><text class="row-label">对比指标</text><text class="hint">同次评估</text></view>
|
|
|
|
|
|
<view v-for="column in columns" :key="column.id" class="overview-value detail-value">
|
|
|
|
|
|
<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 class="overview-bank">{{ column.data!.item.product.bank }} · v{{ column.data!.item.product.version }}</text>
|
|
|
|
|
|
<text class="overview-link" @click="detail(column.id)">查看依据与详情 ›</text>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<template v-else><text class="overview-name">产品 #{{ column.id }}</text><text class="hint">{{ 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>
|
2026-09-28 14:56:46 +08:00
|
|
|
|
</view>
|
2026-10-22 11:24:15 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view v-for="row in rows" :key="row.id" class="overview-row">
|
|
|
|
|
|
<view class="overview-label"><text class="row-label">{{ row.label }}</text></view>
|
|
|
|
|
|
<view v-for="column in columns" :key="column.id" class="overview-value detail-value"><text :class="row.id == 'amount' ? 'cell-text cell-amount' : 'cell-text'">{{ cell(column, row.id) }}</text></view>
|
|
|
|
|
|
</view>
|
2026-09-28 14:56:46 +08:00
|
|
|
|
</view>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
</template>
|
2026-09-30 19:11:44 +08:00
|
|
|
|
<text class="footer-note">历史参数保留评估时版本;当前可用状态单独展示。缺失信息不会被推断为零。</text>
|
2026-09-28 17:50:46 +08:00
|
|
|
|
<button class="secondary" @click="consult">产品差异,咨询AI</button>
|
2026-09-28 14:56:46 +08:00
|
|
|
|
<button class="secondary" @click="toMatches">返回本次匹配</button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="uts">
|
|
|
|
|
|
import AppHeader from '@/components/AppHeader.uvue'
|
|
|
|
|
|
import { usePageLayout } from '@/services/layout.uts'
|
|
|
|
|
|
import { getSession } from '@/services/api.uts'
|
2026-09-28 17:50:46 +08:00
|
|
|
|
import { assistantRoute } from '@/packages/finance/services/assistant.uts'
|
2026-10-14 14:53:13 +08:00
|
|
|
|
import { MatchDetail, MatchReason, validRecordId, fetchMatchDetailRecord, hasProductValue, matchPercentage } from '@/packages/finance/services/matches.uts'
|
2026-09-28 14:56:46 +08:00
|
|
|
|
import { parseCompareIds, saveCompareSelection, compareQuery, sameMatchSnapshot, compareReasonText } from '@/packages/finance/services/compare.uts'
|
|
|
|
|
|
type CompareColumn = { id: string, loading: boolean, error: string, data: MatchDetail | null }
|
2026-10-22 11:24:15 +08:00
|
|
|
|
type CompareRow = { id: string, label: string }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const { pageStyle, contentStyle } = usePageLayout()
|
2026-09-30 19:11:44 +08:00
|
|
|
|
const revisionId = ref(''); const assessmentId = ref(''); const ids = ref<string[]>([]); const columns = ref<CompareColumn[]>([]); const routeError = ref('')
|
2026-09-29 17:27:25 +08:00
|
|
|
|
const detailsOpen = ref(false)
|
2026-10-22 11:24:15 +08:00
|
|
|
|
// 按指标组织整行,产品列均分可用宽度,长证据自然撑高行而不截断。
|
|
|
|
|
|
const overviewDefinitions: CompareRow[] = [{ id: 'score', label: '匹配度' }, { id: 'amount', label: '额度范围' }, { id: 'term', label: '期限范围' }, { id: 'rate', label: '年化利率' }, { id: 'availability', label: '当前状态' }]
|
2026-09-28 14:56:46 +08:00
|
|
|
|
let visible = false; let generation = 0
|
2026-10-07 10:01:05 +08:00
|
|
|
|
let owner = ''; let accountTimer = 0; let frozenDetail: MatchDetail | null = null
|
|
|
|
|
|
const stopAccountTimer = () => { if (accountTimer != 0) clearInterval(accountTimer); accountTimer = 0 }
|
|
|
|
|
|
const login = () => { uni.redirectTo({ url: '/packages/account/login/login?resumeMatch=1&matchPage=compare' + compareQuery(assessmentId.value, ids.value, revisionId.value).replace('?', '&') }) }
|
|
|
|
|
|
const current = (request: number, token: string): boolean => {
|
|
|
|
|
|
if (!visible || request != generation) return false
|
|
|
|
|
|
if (getSession()?.getString('token') == token) return true
|
|
|
|
|
|
generation++; columns.value = []; frozenDetail = null; detailsOpen.value = false; stopAccountTimer()
|
|
|
|
|
|
if (getSession() == null) login(); else { ids.value = []; routeError.value = '账户已切换,请返回本次匹配重新读取。' }
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
const updateUrl = () => {
|
|
|
|
|
|
// #ifdef WEB
|
|
|
|
|
|
history.replaceState(history.state, '', location.pathname + location.search + '#/packages/finance/compare/compare' + compareQuery(assessmentId.value, ids.value, revisionId.value))
|
|
|
|
|
|
// #endif
|
|
|
|
|
|
}
|
2026-10-14 14:53:13 +08:00
|
|
|
|
const detailRows: CompareRow[] = [
|
2026-10-22 11:24:15 +08:00
|
|
|
|
{ id: 'availability', label: '当前状态' },
|
|
|
|
|
|
{ id: 'score', label: '匹配度' },
|
|
|
|
|
|
{ id: 'amount', label: '额度范围' }, { id: 'term', label: '期限范围' }, { id: 'rate', label: '年化利率' },
|
|
|
|
|
|
{ id: 'objects', label: '适用对象' }, { id: 'regions', label: '适用地区' },
|
|
|
|
|
|
{ id: 'positive', label: '推荐与参考理由' }, { id: 'blocking', label: '不推荐原因' }, { id: 'cautions', label: '注意事项' },
|
|
|
|
|
|
{ id: 'conditions', label: '准入条件' }, { id: 'repayment', label: '还款方式' },
|
|
|
|
|
|
{ id: 'materials', label: '准备材料' }, { id: 'channel', label: '办理渠道' }, { id: 'source', label: '资料来源' }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
]
|
|
|
|
|
|
const problem = (column: CompareColumn): string => {
|
|
|
|
|
|
if (column.error.length > 0) return column.error
|
|
|
|
|
|
if (column.data == null) return '资料尚未读取'
|
|
|
|
|
|
const first = columns.value.find((c: CompareColumn): boolean => c.data != null)
|
|
|
|
|
|
if (first != null && !sameMatchSnapshot(first.data!, column.data!)) return '冻结结果版本不一致,请重试或返回重新选择。'
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
const usable = (column: CompareColumn): boolean => !column.loading && column.data != null && problem(column).length == 0
|
|
|
|
|
|
const complete = computed((): boolean => ids.value.length >= 2 && columns.value.length == ids.value.length && columns.value.every((c: CompareColumn): boolean => usable(c)))
|
2026-09-29 17:27:25 +08:00
|
|
|
|
const overviewClass = (column: CompareColumn, key: string): string => {
|
|
|
|
|
|
if (key == 'amount') return 'overview-text overview-amount'
|
2026-10-22 11:24:15 +08:00
|
|
|
|
return 'overview-text'
|
2026-09-29 17:27:25 +08:00
|
|
|
|
}
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const cell = (column: CompareColumn, key: string): string => {
|
|
|
|
|
|
if (!usable(column)) return column.loading ? '读取中…' : '本列未就绪'
|
|
|
|
|
|
const m = column.data!.item; const p = m.product
|
2026-10-22 11:24:15 +08:00
|
|
|
|
if (key == 'score') {
|
|
|
|
|
|
const percentage = matchPercentage(m.state, m.score)
|
|
|
|
|
|
return percentage.length > 0 ? percentage : '—'
|
|
|
|
|
|
}
|
2026-09-28 14:56:46 +08:00
|
|
|
|
if (key == 'availability') return m.availabilityLabel + ';不改变历史结论'
|
|
|
|
|
|
if (key == 'positive') return compareReasonText(m.positive, '暂无已确认满足的条件')
|
2026-10-19 16:11:59 +08:00
|
|
|
|
if (key == 'blocking') return compareReasonText(m.blocking, '未发现已知不符条件;结果仅供参考')
|
|
|
|
|
|
if (key == 'cautions') return compareReasonText(m.cautions, '暂无需要特别注意的已知条件;不代表审批结果')
|
2026-09-28 14:56:46 +08:00
|
|
|
|
if (key == 'conditions') return p.conditions.length > 0 ? p.conditions.join('\n\n') : '未提供'
|
2026-10-14 14:53:13 +08:00
|
|
|
|
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 : '—'
|
2026-09-28 14:56:46 +08:00
|
|
|
|
if (key == 'objects') return p.objects
|
|
|
|
|
|
if (key == 'regions') return p.regions
|
|
|
|
|
|
if (key == 'materials') return p.materials
|
2026-10-14 14:53:13 +08:00
|
|
|
|
if (key == 'repayment') return hasProductValue(p.repayment) ? p.repayment : '—'
|
|
|
|
|
|
if (key == 'channel') return hasProductValue(p.channel) ? p.channel : '—'
|
2026-10-22 11:24:15 +08:00
|
|
|
|
if (key == 'source') {
|
|
|
|
|
|
const sourceText = [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 sourceText.length > 0 ? sourceText : '—'
|
|
|
|
|
|
}
|
2026-09-28 14:56:46 +08:00
|
|
|
|
return '未提供'
|
|
|
|
|
|
}
|
2026-10-14 14:53:13 +08:00
|
|
|
|
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)))
|
2026-10-19 16:11:59 +08:00
|
|
|
|
const orderingNote = computed((): string => columns.value.some((column: CompareColumn): boolean => usable(column) && column.data!.orderingVersion == 'product-score-descending-v1') ? '同组产品按匹配度排序,此处按所选顺序对比。匹配度不代表获批概率。' : '按所选顺序对比,具体条件以机构核验为准。')
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const loadColumn = async (id: string) => {
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (!current(generation, owner)) return
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const column = columns.value.find((c: CompareColumn): boolean => c.id == id)
|
|
|
|
|
|
if (column == null || column.loading) return
|
|
|
|
|
|
const session = getSession()
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (session == null) { login(); return }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const token = session.getString('token') ?? ''; const request = generation
|
|
|
|
|
|
column.loading = true; column.error = ''; column.data = null
|
|
|
|
|
|
try {
|
2026-09-30 19:11:44 +08:00
|
|
|
|
const result = await fetchMatchDetailRecord(assessmentId.value, id, revisionId.value)
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (!current(request, token)) return
|
|
|
|
|
|
if (revisionId.value.length > 0 && revisionId.value != result.revisionId) throw new Error('评估版本已变化,请重新加载对比。')
|
|
|
|
|
|
if (frozenDetail != null && !sameMatchSnapshot(frozenDetail!, result)) throw new Error('冻结结果版本不一致,请重试或返回重新选择。')
|
|
|
|
|
|
if (frozenDetail == null) frozenDetail = result
|
|
|
|
|
|
revisionId.value = result.revisionId; column.data = result
|
|
|
|
|
|
saveCompareSelection(assessmentId.value, ids.value, revisionId.value); updateUrl()
|
2026-09-28 14:56:46 +08:00
|
|
|
|
} catch (err) {
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (current(request, token)) column.error = err instanceof Error ? err.message : '本列读取失败,请重试。'
|
2026-09-28 14:56:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
finally { if (request == generation) column.loading = false }
|
|
|
|
|
|
}
|
|
|
|
|
|
const toMatches = () => {
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (owner.length > 0 && routeError.value.length == 0 && !current(generation, owner)) return
|
2026-09-28 14:56:46 +08:00
|
|
|
|
if (!validRecordId(assessmentId.value)) { uni.redirectTo({ url: '/packages/finance/archive/archive' }); return }
|
2026-09-30 19:11:44 +08:00
|
|
|
|
uni.redirectTo({ url: '/packages/finance/matches/matches' + compareQuery(assessmentId.value, ids.value, revisionId.value) })
|
2026-09-28 14:56:46 +08:00
|
|
|
|
}
|
|
|
|
|
|
const back = () => { if (getCurrentPages().length > 1) uni.navigateBack(); else toMatches() }
|
2026-10-07 10:01:05 +08:00
|
|
|
|
const consult = () => { if (current(generation, owner) && complete.value) uni.navigateTo({ url: assistantRoute(assessmentId.value, ids.value, 'compare', '', revisionId.value) }); else if (routeError.value.length == 0) uni.showToast({ title: '请先读取完整对比后再咨询AI', icon: 'none' }) }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const remove = (id: string) => {
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (!current(generation, owner)) return
|
2026-09-28 14:56:46 +08:00
|
|
|
|
const next = ids.value.filter((value: string): boolean => value != id)
|
2026-09-30 19:11:44 +08:00
|
|
|
|
saveCompareSelection(assessmentId.value, next, revisionId.value)
|
2026-09-28 14:56:46 +08:00
|
|
|
|
// 用新路由替换当前页,使刷新/分享恢复实际选择,而不是已移除的旧ID。
|
2026-09-30 19:11:44 +08:00
|
|
|
|
uni.redirectTo({ url: '/packages/finance/compare/compare' + compareQuery(assessmentId.value, next, revisionId.value) })
|
2026-09-28 14:56:46 +08:00
|
|
|
|
}
|
2026-10-07 10:01:05 +08:00
|
|
|
|
const detail = (id: string) => { if (current(generation, owner)) uni.navigateTo({ url: '/packages/finance/product/product' + compareQuery(assessmentId.value, ids.value, revisionId.value) + '&productId=' + id + '&tab=explanation&from=compare' }) }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
onLoad((options) => {
|
2026-09-30 19:11:44 +08:00
|
|
|
|
revisionId.value = options?.['revisionId'] ?? '';
|
|
|
|
|
|
if (revisionId.value.length > 0 && !validRecordId(revisionId.value)) routeError.value = '版本编号无效,请返回重新选择。'
|
2026-09-28 14:56:46 +08:00
|
|
|
|
assessmentId.value = options?.['assessmentId'] ?? ''
|
|
|
|
|
|
if (!validRecordId(assessmentId.value)) { routeError.value = '评估编号无效,请从融资档案重新选择。'; return }
|
2026-09-30 19:11:44 +08:00
|
|
|
|
try { ids.value = parseCompareIds(options?.['productIds'] ?? ''); saveCompareSelection(assessmentId.value, ids.value, revisionId.value) }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
catch (err) { routeError.value = err instanceof Error ? err.message : '对比参数无效' }
|
|
|
|
|
|
})
|
|
|
|
|
|
onShow(() => {
|
2026-10-07 10:01:05 +08:00
|
|
|
|
visible = true; generation++; stopAccountTimer(); frozenDetail = null
|
2026-09-28 14:56:46 +08:00
|
|
|
|
if (routeError.value.length > 0) return
|
2026-10-07 10:01:05 +08:00
|
|
|
|
if (getSession() == null) { login(); return }
|
|
|
|
|
|
owner = getSession()?.getString('token') ?? ''
|
|
|
|
|
|
accountTimer = setInterval(() => { current(generation, owner) }, 1000)
|
2026-09-28 14:56:46 +08:00
|
|
|
|
// 依URL顺序固定列位;仅最多三个详情请求并行,单列失败不影响其他列。
|
|
|
|
|
|
columns.value = ids.value.map((id: string): CompareColumn => { return { id, loading: false, error: '', data: null } })
|
|
|
|
|
|
ids.value.forEach((id: string) => { loadColumn(id) })
|
|
|
|
|
|
})
|
2026-10-07 10:01:05 +08:00
|
|
|
|
onHide(() => { visible = false; stopAccountTimer(); generation++; columns.value = []; frozenDetail = null })
|
|
|
|
|
|
onUnload(() => { visible = false; stopAccountTimer(); generation++; columns.value = []; frozenDetail = null })
|
2026-09-28 14:56:46 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
<style>
|
2026-09-29 17:27:25 +08:00
|
|
|
|
.overview-table { border: 1rpx solid #dfe8f3; border-radius: 14rpx; overflow: hidden; background-color: #fff; }
|
|
|
|
|
|
.overview-row { flex-direction: row; border-bottom: 1rpx solid #e7edf5; }
|
|
|
|
|
|
.overview-label { width: 120rpx; flex-shrink: 0; padding: 22rpx 12rpx; background-color: #eef4fb; justify-content: center; }
|
|
|
|
|
|
.overview-value { flex: 1; width: 0; padding: 22rpx 12rpx; border-left: 1rpx solid #e7edf5; justify-content: center; }
|
|
|
|
|
|
.overview-head { height: 270rpx; padding-top: 18rpx; padding-bottom: 18rpx; }
|
|
|
|
|
|
.overview-product { padding: 0; }
|
|
|
|
|
|
.overview-name { font-size: 24rpx; line-height: 35rpx; font-weight: 700; color: #243f60; }
|
|
|
|
|
|
.overview-bank { color: #788ba2; font-size: 20rpx; line-height: 30rpx; margin-top: 10rpx; }
|
|
|
|
|
|
.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; }
|
2026-10-14 14:53:13 +08:00
|
|
|
|
.overview-logo { width: 56rpx; height: 56rpx; margin-bottom: 10rpx; flex-shrink: 0; }
|
2026-09-29 17:27:25 +08:00
|
|
|
|
.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; }
|
|
|
|
|
|
.detail-toggle-label { color: #0868f7; font-size: 24rpx; }
|
2026-09-28 14:56:46 +08:00
|
|
|
|
.content { padding: 28rpx 22rpx 60rpx; }
|
|
|
|
|
|
.eyebrow { color: #6d829c; font-size: 22rpx; }
|
|
|
|
|
|
.headline { color: #193653; font-size: 36rpx; font-weight: 700; margin-top: 14rpx; }
|
|
|
|
|
|
.intro { color: #76879c; font-size: 24rpx; line-height: 39rpx; margin-top: 14rpx; }
|
|
|
|
|
|
.panel { padding: 30rpx; background-color: #fff; border-radius: 18rpx; margin-top: 24rpx; }
|
|
|
|
|
|
.warning { color: #a6603e; font-size: 26rpx; line-height: 40rpx; }
|
|
|
|
|
|
.toolbar { flex-direction: row; align-items: center; padding: 24rpx 0; }
|
|
|
|
|
|
.summary { flex: 1; }
|
|
|
|
|
|
.summary-title { color: #243f60; font-size: 28rpx; font-weight: 700; }
|
|
|
|
|
|
.hint { color: #788ba2; font-size: 22rpx; line-height: 34rpx; margin-top: 8rpx; }
|
|
|
|
|
|
.add { color: #0868f7; background-color: #e6f0ff; font-size: 24rpx; border-radius: 12rpx; margin-left: 14rpx; }
|
|
|
|
|
|
.scroll-hint { font-size: 21rpx; color: #8494a7; margin-bottom: 16rpx; }
|
|
|
|
|
|
.row-label { font-size: 23rpx; line-height: 36rpx; color: #48627e; }
|
|
|
|
|
|
.state { font-size: 22rpx; margin-top: 12rpx; }
|
|
|
|
|
|
.good { color: #18805d; }
|
|
|
|
|
|
.blocked { color: #b26341; }
|
|
|
|
|
|
.pending { color: #9b751f; }
|
|
|
|
|
|
.demo { color: #8b97a8; font-size: 20rpx; margin-top: 6rpx; }
|
2026-10-22 11:24:15 +08:00
|
|
|
|
.detail-value { justify-content: flex-start; }
|
|
|
|
|
|
.cell-text { font-size: 23rpx; line-height: 36rpx; color: #455f7c; }
|
|
|
|
|
|
.cell-amount { color: #154779; font-weight: 700; }
|
|
|
|
|
|
/* #ifndef APP */
|
|
|
|
|
|
.detail-table .cell-text, .detail-table .overview-name, .detail-table .overview-bank { white-space: pre-wrap; word-break: break-all; }
|
|
|
|
|
|
/* #endif */
|
2026-09-28 14:56:46 +08:00
|
|
|
|
.footer-note { color: #8997a8; font-size: 22rpx; line-height: 36rpx; margin-top: 24rpx; }
|
|
|
|
|
|
.secondary { color: #0868f7; background-color: #eaf3ff; font-size: 26rpx; border-radius: 12rpx; margin-top: 20rpx; }
|
|
|
|
|
|
</style>
|