Files
airz-uniapp/packages/finance/product/product.uvue
T
hujun 046a03e5e9 refactor: 优化跨端导航适配与页面文案体验
- 新增微信小程序胶囊区域避让布局逻辑
- 统一首页导航与标题对齐规则
- 更新多处演示提示文案为正式表述
- 调整个人中心账户状态显示
- 优化产品页分享按钮条件渲染
2026-09-20 14:18:55 +08:00

104 lines
7.2 KiB
Plaintext

<template>
<view class="page" :style="pageStyle()">
<AppHeader title="产品详情">
<!-- #ifndef MP-WEIXIN -->
<text v-if="available" class="share" @click="shareProduct">分享</text>
<!-- #endif -->
</AppHeader>
<scroll-view scroll-y :style="contentStyle(224, false)">
<view class="product-content">
<view v-if="loading" class="state"><text class="state-text">正在加载产品…</text></view>
<view v-else-if="!available" class="state"><text class="state-title">{{ loadFailed ? '产品加载失败' : '暂未找到该产品' }}</text><text class="state-text">请重试或返回方案列表选择其他产品</text><button class="retry-button" @click="loadProduct">重新加载</button></view>
<view v-else>
<!-- #ifdef MP-WEIXIN -->
<view class="product-actions"><text class="share" @click="shareProduct">分享产品</text></view>
<!-- #endif -->
<view class="bank-head"><view class="bank-mark" :style="'background-color:' + product.color"><text class="bank-initial">{{ product.initials }}</text></view><view class="bank-copy"><text class="product-name">{{ product.bank }}</text><text class="product-tags">个人信用融资 · 产品信息</text></view></view>
<view class="product-metrics"><view class="metric"><text class="metric-label">最高额度</text><text class="amount">{{ product.amount }}万</text></view><view class="metric"><text class="metric-label">年化利率(起)</text><text class="rate">{{ product.rate.toFixed(2) }}%</text></view><view class="metric last-metric"><text class="metric-label">贷款期限</text><text class="term">{{ product.term }}</text></view></view>
<view class="detail-section"><text class="section-title">产品优势</text><view class="advantage-grid"><view class="advantage" v-for="item in product.advantages" :key="item"><AppIcon name="check-circle" :size="30" /><text class="advantage-label">{{ item }}</text></view></view></view>
<view class="detail-section"><text class="section-title">申请条件</text><view class="condition"><text class="condition-label">年龄要求</text><text class="condition-value">{{ product.age }}</text></view><view class="condition"><text class="condition-label">工作要求</text><text class="condition-value">{{ product.work }}</text></view><view class="condition"><text class="condition-label">信用要求</text><text class="condition-value">{{ product.credit }}</text></view></view>
<view class="detail-section"><text class="section-title">所需材料</text><text class="material">{{ product.materials }}</text></view>
<text class="product-note">额度、利率及申请条件仅供参考,以机构实际审批为准。{{ isDemo ? '当前可准备申请资料,尚未向机构提交。' : '' }}</text>
</view>
</view>
</scroll-view>
<view v-if="available" class="apply-bar" :style="bottomStyle(18)"><button class="primary-button apply-button" @click="apply">准备申请</button></view>
</view>
</template>
<script setup lang="uts">
import { usePageLayout } from '@/services/layout.uts'
import { FinanceProduct, demoProducts } from '@/packages/finance/services/finance.uts'
import { fetchProduct, API_BASE_URL } from '@/packages/finance/services/api.uts'
import AppHeader from '@/components/AppHeader.uvue'
import AppIcon from '@/components/AppIcon.uvue'
const { bottomStyle, contentStyle, pageStyle } = usePageLayout()
const product = ref<FinanceProduct>(demoProducts[0])
const available = ref(false)
const productId = ref('')
const loading = ref(true)
const loadFailed = ref(false)
const isDemo = API_BASE_URL.length == 0
const loadProduct = async () => {
loading.value = true
loadFailed.value = false
available.value = false
try {
const selected = await fetchProduct(productId.value)
if (selected != null) { product.value = selected; available.value = true }
} catch (error) { loadFailed.value = true }
loading.value = false
}
onLoad((options) => {
productId.value = options?.['id'] ?? ''
loadProduct()
})
const shareProduct = () => {
const item = product.value
if (!available.value) return
uni.setClipboardData({ data: item.bank + '|最高' + item.amount + '万元|年化利率' + item.rate.toFixed(2) + '%起|' + item.term + '。展示信息仅供参考,以机构审批为准。' })
}
const apply = () => {
uni.showModal({
title: '准备申请资料',
content: '先完善融资档案,核对申请所需材料。当前尚未开通机构申请,资料不会提交至银行。',
confirmText: '完善档案',
success: (result) => { if (result.confirm) uni.navigateTo({ url: '/packages/finance/archive/archive' }) }
})
}
</script>
<style>
.product-actions { flex-direction: row; justify-content: flex-end; margin-bottom: 12rpx; }
.share { color: #0868f7; font-size: 25rpx; padding: 16rpx 0 16rpx 16rpx; }
.product-content { padding: 24rpx 28rpx 60rpx; }
.bank-head { padding: 26rpx; border-radius: 18rpx; background-color: #fff; border: 1rpx solid #e6edf6; flex-direction: row; align-items: center; }
.bank-mark { width: 80rpx; height: 80rpx; border-radius: 24rpx; align-items: center; justify-content: center; margin-right: 20rpx; flex-shrink: 0; }
.bank-initial { color: #fff; font-size: 36rpx; font-weight: 700; }
.bank-copy { flex: 1; }
.product-name { font-size: 30rpx; font-weight: 700; color: #20324d; }
.product-tags { color: #75859b; font-size: 22rpx; margin-top: 10rpx; }
.product-metrics { margin-top: 20rpx; padding: 30rpx 12rpx; border-radius: 18rpx; background-color: #fff; border: 1rpx solid #e6edf6; flex-direction: row; }
.metric { flex: 1; align-items: center; border-right: 1rpx solid #edf1f6; }
.last-metric { border-right: 0; }
.metric-label { color: #75859b; font-size: 21rpx; }
.amount, .rate, .term { margin-top: 14rpx; font-size: 36rpx; line-height: 46rpx; font-weight: 800; color: #20324d; }
.rate { color: #0868f7; }
.term { font-size: 27rpx; }
.detail-section { margin-top: 20rpx; padding: 26rpx; background-color: #fff; border-radius: 18rpx; border: 1rpx solid #e6edf6; }
.advantage-grid { margin-top: 24rpx; flex-direction: row; flex-wrap: wrap; }
.advantage { width: 50%; flex-direction: row; align-items: center; margin-bottom: 16rpx; }
.advantage-label { margin-left: 12rpx; font-size: 25rpx; color: #435168; }
.condition { min-height: 78rpx; padding: 14rpx 0; flex-direction: row; justify-content: space-between; align-items: center; border-bottom: 1rpx solid #edf1f6; }
.condition-label { color: #748399; font-size: 24rpx; flex-shrink: 0; margin-right: 20rpx; }
.condition-value { flex: 1; text-align: right; color: #273b54; font-size: 24rpx; line-height: 36rpx; }
.material { margin-top: 18rpx; font-size: 24rpx; line-height: 38rpx; color: #53667f; }
.product-note { margin: 22rpx 6rpx 0; color: #7a899e; font-size: 21rpx; line-height: 34rpx; }
.apply-bar { position: fixed; left: 0; right: 0; bottom: 0; padding: 18rpx 28rpx; background-color: #fff; border-top: 1rpx solid #e7edf6; }
.apply-button { margin: 0; }
.state { padding: 80rpx 24rpx; align-items: center; }
.state-title { font-size: 30rpx; color: #20324d; font-weight: 600; margin-bottom: 14rpx; }
.state-text { font-size: 25rpx; line-height: 40rpx; text-align: center; color: #75859b; }
.retry-button { margin-top: 24rpx; color: #0868f7; background-color: #e8f2ff; font-size: 25rpx; }
</style>