Files
shop-toy/src/pages/me/loan-application-records.vue
2025-12-26 18:13:42 +08:00

789 lines
20 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script lang="ts" setup>
import { mockLoanApplicationRecords } from '@/mock/loan-application'
import type { LoanApplicationRecord, LoanApplicationStatus } from '@/typings/mall'
definePage({
style: {
navigationBarTitleText: '助贷申请记录',
navigationBarBackgroundColor: '#fff',
navigationBarTextStyle: 'black',
},
})
// 申请记录列表
const applicationRecords = ref<LoanApplicationRecord[]>(mockLoanApplicationRecords)
// 详情弹窗相关
const showDetailModal = ref(false)
const selectedRecord = ref<LoanApplicationRecord | null>(null)
// 打开详情弹窗
function openDetailModal(record: LoanApplicationRecord) {
selectedRecord.value = record
showDetailModal.value = true
}
// 关闭详情弹窗
function closeDetailModal() {
showDetailModal.value = false
selectedRecord.value = null
}
// 处理按钮点击
function handleActionClick(record: LoanApplicationRecord, action: any) {
if (action.code === 'VIEW_DETAIL' || action.code === 'VIEW_RESULT' || action.code === 'CONTINUE_FILL') {
openDetailModal(record)
} else if (action.code === 'DOWNLOAD_CONTRACT') {
uni.showToast({
title: '合同下载中...',
icon: 'loading'
})
setTimeout(() => {
uni.showToast({
title: '合同已下载',
icon: 'success'
})
}, 1500)
} else if (action.code === 'CANCEL') {
uni.showModal({
title: '提示',
content: '确定要取消该申请吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '申请已取消',
icon: 'success'
})
}
}
})
} else if (action.code === 'DELETE') {
uni.showModal({
title: '提示',
content: '确定要删除该申请记录吗?',
success: (res) => {
if (res.confirm) {
uni.showToast({
title: '记录已删除',
icon: 'success'
})
}
}
})
}
}
// 拨打银行受理人电话
function handleCallPhone(phone: string) {
uni.makePhoneCall({
phoneNumber: phone
})
}
// 获取状态颜色
function getStatusColor(status: LoanApplicationStatus) {
switch (status) {
case 'PROCESSING':
return {
bg: 'rgba(59, 130, 246, 0.1)',
text: '#3B82F6'
}
case 'COMPLETED':
return {
bg: 'rgba(16, 185, 129, 0.1)',
text: '#10B981'
}
case 'PENDING':
return {
bg: 'rgba(245, 158, 11, 0.1)',
text: '#F59E0B'
}
default:
return {
bg: '#f5f5f5',
text: '#999'
}
}
}
// 获取按钮样式
function getButtonStyle(style: string) {
switch (style) {
case 'primary-blue':
return 'background: #3B82F6; color: #fff;'
case 'primary-green':
return 'background: #10B981; color: #fff;'
case 'primary-yellow':
return 'background: #F59E0B; color: #fff;'
case 'text-link':
return 'background: transparent; color: #666; border: none;'
default:
return 'background: #f5f5f5; color: #333;'
}
}
</script>
<template>
<view class="loan-application-records-page">
<!-- 申请记录列表 -->
<view class="records-list">
<view
v-for="record in applicationRecords"
:key="record.applicationId"
class="record-card"
>
<!-- 头部区域 -->
<view class="card-header">
<view class="title-section">
<text class="title">{{ record.loanTitle }}</text>
<view
class="status-badge"
:style="{
background: getStatusColor(record.status).bg,
color: getStatusColor(record.status).text
}"
>
{{ record.statusText }}
</view>
</view>
<view class="info-section">
<text class="application-id">申请编号{{ record.applicationId }}</text>
<text class="date-info">{{ record.dateLabel }}{{ record.dateValue }}</text>
</view>
</view>
<!-- 进度条区域 -->
<view v-if="record.progress && record.progress.show" class="progress-section">
<view class="steps-container">
<view
v-for="(step, index) in record.progress.steps"
:key="index"
class="step-item"
:class="{
'completed': index < record.progress.currentStepIndex,
'current': index === record.progress.currentStepIndex,
'future': index > record.progress.currentStepIndex
}"
>
<view class="step-circle">
<text v-if="index < record.progress.currentStepIndex" class="i-carbon-checkmark"></text>
<text v-else>{{ index + 1 }}</text>
</view>
<text class="step-text">{{ step }}</text>
</view>
</view>
</view>
<!-- 提示信息区域 -->
<view v-if="record.alertInfo && record.alertInfo.show" class="alert-section">
<view
class="alert-box"
:class="record.alertInfo.type"
>
<text
v-if="record.alertInfo.type === 'info'"
class="i-carbon-information alert-icon"
></text>
<text
v-else-if="record.alertInfo.type === 'warning'"
class="i-carbon-warning-alt alert-icon"
></text>
<text class="alert-content">{{ record.alertInfo.content }}</text>
</view>
</view>
<!-- 银行受理人信息仅处理中状态显示 -->
<view v-if="record.status === 'PROCESSING' && record.handlerName" class="handler-section">
<view class="handler-info">
<text class="i-carbon-user handler-icon"></text>
<view class="handler-details">
<text class="handler-label">银行受理人</text>
<text class="handler-name">{{ record.handlerName }}</text>
<view class="handler-phone-wrapper" @click="handleCallPhone(record.handlerPhone)">
<text class="i-carbon-phone handler-phone-icon"></text>
<text class="handler-phone">{{ record.handlerPhone }}</text>
</view>
</view>
</view>
</view>
<!-- 底部操作区 -->
<view class="actions-section">
<view
v-for="action in record.actions"
:key="action.code"
class="action-btn"
:style="getButtonStyle(action.style)"
@click="handleActionClick(record, action)"
>
{{ action.text }}
</view>
</view>
</view>
</view>
<!-- 详情弹窗 -->
<view v-if="showDetailModal" class="modal-overlay" @click="closeDetailModal">
<view class="modal-content" @click.stop>
<view class="modal-header">
<text class="modal-title">申请详情</text>
<text class="i-carbon-close modal-close" @click="closeDetailModal"></text>
</view>
<view v-if="selectedRecord" class="modal-body">
<view class="detail-item">
<text class="detail-label">申请编号</text>
<text class="detail-value">{{ selectedRecord.applicationId }}</text>
</view>
<view class="detail-item">
<text class="detail-label">贷款类型</text>
<text class="detail-value">{{ selectedRecord.loanTitle }}</text>
</view>
<view class="detail-item">
<text class="detail-label">申请状态</text>
<text
class="detail-value status-text"
:style="{ color: getStatusColor(selectedRecord.status).text }"
>
{{ selectedRecord.statusText }}
</text>
</view>
<view class="detail-item">
<text class="detail-label">{{ selectedRecord.dateLabel }}</text>
<text class="detail-value">{{ selectedRecord.dateValue }}</text>
</view>
<!-- 进度详情 -->
<view v-if="selectedRecord.progress && selectedRecord.progress.show" class="progress-detail">
<text class="detail-label">申请进度</text>
<view class="progress-steps">
<view
v-for="(step, index) in selectedRecord.progress.steps"
:key="index"
class="progress-step"
:class="{
'completed': index < selectedRecord.progress.currentStepIndex,
'current': index === selectedRecord.progress.currentStepIndex,
'future': index > selectedRecord.progress.currentStepIndex
}"
>
<view class="step-dot"></view>
<text class="step-name">{{ step }}</text>
</view>
</view>
</view>
<!-- 银行受理人信息仅处理中状态显示 -->
<view v-if="selectedRecord.status === 'PROCESSING' && selectedRecord.handlerName" class="handler-detail">
<text class="detail-label">银行受理人</text>
<view class="handler-detail-info">
<view class="handler-detail-row">
<text class="i-carbon-user handler-detail-icon"></text>
<text class="handler-detail-name">{{ selectedRecord.handlerName }}</text>
</view>
<view class="handler-detail-row handler-detail-phone" @click="handleCallPhone(selectedRecord.handlerPhone)">
<text class="i-carbon-phone handler-detail-icon"></text>
<text class="handler-detail-phone-number">{{ selectedRecord.handlerPhone }}</text>
</view>
</view>
</view>
<!-- 提示信息 -->
<view v-if="selectedRecord.alertInfo && selectedRecord.alertInfo.show" class="alert-detail">
<view
class="alert-detail-box"
:class="selectedRecord.alertInfo.type"
>
<text
v-if="selectedRecord.alertInfo.type === 'info'"
class="i-carbon-information alert-icon"
></text>
<text
v-else-if="selectedRecord.alertInfo.type === 'warning'"
class="i-carbon-warning-alt alert-icon"
></text>
<text>{{ selectedRecord.alertInfo.content }}</text>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<style lang="scss" scoped>
.loan-application-records-page {
min-height: 100vh;
background: #f5f5f5;
padding: 20rpx;
}
.records-list {
.record-card {
background: #fff;
border-radius: 16rpx;
padding: 30rpx;
margin-bottom: 20rpx;
.card-header {
margin-bottom: 24rpx;
.title-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.status-badge {
padding: 8rpx 16rpx;
border-radius: 20rpx;
font-size: 24rpx;
font-weight: 500;
}
}
.info-section {
display: flex;
flex-direction: column;
gap: 8rpx;
.application-id, .date-info {
font-size: 26rpx;
color: #666;
}
}
}
.progress-section {
margin-bottom: 24rpx;
.steps-container {
display: flex;
justify-content: space-between;
position: relative;
padding: 0 10rpx;
&::before {
content: '';
position: absolute;
top: 30rpx;
left: 50rpx;
right: 50rpx;
height: 2rpx;
background: #e5e5e5;
z-index: 1;
}
.step-item {
display: flex;
flex-direction: column;
align-items: center;
position: relative;
z-index: 2;
flex: 1;
.step-circle {
width: 60rpx;
height: 60rpx;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.step-text {
font-size: 22rpx;
text-align: center;
color: #666;
}
&.completed {
.step-circle {
background: #10B981;
color: #fff;
}
.step-text {
color: #10B981;
}
}
&.current {
.step-circle {
background: #3B82F6;
color: #fff;
transform: scale(1.1);
}
.step-text {
color: #3B82F6;
font-weight: 500;
}
}
&.future {
.step-circle {
background: #f5f5f5;
color: #999;
}
.step-text {
color: #999;
}
}
}
}
}
.alert-section {
margin-bottom: 24rpx;
.alert-box {
padding: 20rpx;
border-radius: 12rpx;
display: flex;
align-items: flex-start;
gap: 12rpx;
&.info {
background: rgba(59, 130, 246, 0.1);
.alert-icon {
color: #3B82F6;
}
.alert-content {
color: #3B82F6;
}
}
&.warning {
background: rgba(245, 158, 11, 0.1);
.alert-icon {
color: #F59E0B;
}
.alert-content {
color: #F59E0B;
}
}
.alert-icon {
font-size: 32rpx;
margin-top: 2rpx;
}
.alert-content {
flex: 1;
font-size: 26rpx;
line-height: 1.5;
}
}
}
.handler-section {
margin-bottom: 24rpx;
.handler-info {
background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
border-radius: 12rpx;
padding: 20rpx;
display: flex;
align-items: center;
gap: 16rpx;
.handler-icon {
font-size: 48rpx;
color: #fff;
}
.handler-details {
flex: 1;
display: flex;
flex-direction: column;
gap: 8rpx;
.handler-label {
font-size: 22rpx;
color: rgba(255, 255, 255, 0.8);
}
.handler-name {
font-size: 28rpx;
font-weight: 600;
color: #fff;
}
.handler-phone-wrapper {
display: flex;
align-items: center;
gap: 8rpx;
.handler-phone-icon {
font-size: 24rpx;
color: #fff;
}
.handler-phone {
font-size: 26rpx;
color: #fff;
}
}
}
}
}
.actions-section {
display: flex;
justify-content: flex-end;
gap: 20rpx;
.action-btn {
padding: 16rpx 32rpx;
border-radius: 8rpx;
font-size: 28rpx;
font-weight: 500;
border: 1rpx solid #e5e5e5;
&:active {
opacity: 0.8;
}
}
}
}
}
// 弹窗样式
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
padding: 40rpx;
.modal-content {
background: #fff;
border-radius: 16rpx;
width: 100%;
max-width: 600rpx;
max-height: 80vh;
overflow: hidden;
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 30rpx;
border-bottom: 1rpx solid #f5f5f5;
.modal-title {
font-size: 32rpx;
font-weight: 600;
color: #333;
}
.modal-close {
font-size: 36rpx;
color: #999;
&:active {
opacity: 0.8;
}
}
}
.modal-body {
padding: 30rpx;
max-height: 60vh;
overflow-y: auto;
.detail-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-bottom: 1rpx solid #f5f5f5;
&:last-child {
border-bottom: none;
}
.detail-label {
font-size: 28rpx;
color: #666;
}
.detail-value {
font-size: 28rpx;
color: #333;
font-weight: 500;
&.status-text {
font-weight: 600;
}
}
}
.progress-detail {
padding: 20rpx 0;
.detail-label {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
display: block;
}
.progress-steps {
.progress-step {
display: flex;
align-items: center;
margin-bottom: 20rpx;
&:last-child {
margin-bottom: 0;
}
.step-dot {
width: 24rpx;
height: 24rpx;
border-radius: 50%;
margin-right: 20rpx;
}
.step-name {
font-size: 28rpx;
}
&.completed {
.step-dot {
background: #10B981;
}
.step-name {
color: #10B981;
}
}
&.current {
.step-dot {
background: #3B82F6;
transform: scale(1.2);
}
.step-name {
color: #3B82F6;
font-weight: 600;
}
}
&.future {
.step-dot {
background: #f5f5f5;
border: 2rpx solid #e5e5e5;
}
.step-name {
color: #999;
}
}
}
}
}
.handler-detail {
padding: 20rpx 0;
.detail-label {
font-size: 28rpx;
color: #666;
margin-bottom: 20rpx;
display: block;
}
.handler-detail-info {
background: linear-gradient(135deg, #3B82F6 0%, #60A5FA 100%);
border-radius: 12rpx;
padding: 20rpx;
display: flex;
flex-direction: column;
gap: 16rpx;
.handler-detail-row {
display: flex;
align-items: center;
gap: 12rpx;
.handler-detail-icon {
font-size: 32rpx;
color: #fff;
}
.handler-detail-name {
font-size: 28rpx;
font-weight: 600;
color: #fff;
}
&.handler-detail-phone {
cursor: pointer;
.handler-detail-phone-number {
font-size: 26rpx;
color: #fff;
}
}
}
}
}
.alert-detail {
padding: 20rpx 0;
.alert-detail-box {
padding: 20rpx;
border-radius: 12rpx;
display: flex;
align-items: flex-start;
gap: 12rpx;
font-size: 26rpx;
line-height: 1.5;
&.info {
background: rgba(59, 130, 246, 0.1);
color: #3B82F6;
.alert-icon {
color: #3B82F6;
}
}
&.warning {
background: rgba(245, 158, 11, 0.1);
color: #F59E0B;
.alert-icon {
color: #F59E0B;
}
}
.alert-icon {
font-size: 32rpx;
margin-top: 2rpx;
}
}
}
}
}
}
</style>