492 lines
11 KiB
Vue
492 lines
11 KiB
Vue
<script lang="ts" setup>
|
||
import type { ClaimApplication } from '@/api/types/insurance'
|
||
import { getClaimApplicationDetail, reviewClaimApplication } from '@/api/insurance'
|
||
|
||
definePage({
|
||
style: {
|
||
navigationBarTitleText: '理赔审核详情',
|
||
},
|
||
})
|
||
|
||
const claimId = ref('')
|
||
const claim = ref<ClaimApplication | null>(null)
|
||
const loading = ref(false)
|
||
const reviewing = ref(false)
|
||
const rejectionReason = ref('')
|
||
const payoutAmount = ref('')
|
||
|
||
// 状态映射
|
||
const statusMap: Record<string, { text: string, color: string }> = {
|
||
pending: { text: '待审核', color: '#F59E0B' },
|
||
approved: { text: '已通过', color: '#00c05a' },
|
||
rejected: { text: '已拒绝', color: '#fa4350' },
|
||
}
|
||
|
||
const statusInfo = computed(() => {
|
||
if (!claim.value)
|
||
return null
|
||
return statusMap[claim.value.status] || { text: claim.value.status, color: '#666' }
|
||
})
|
||
|
||
async function loadDetail() {
|
||
loading.value = true
|
||
try {
|
||
const res = await getClaimApplicationDetail(claimId.value)
|
||
claim.value = res
|
||
if (res.payoutAmount) {
|
||
payoutAmount.value = res.payoutAmount.toString()
|
||
}
|
||
}
|
||
finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 预览图片
|
||
function handlePreviewImage(url: string) {
|
||
uni.previewImage({
|
||
urls: [url],
|
||
})
|
||
}
|
||
|
||
// 理赔审核通过
|
||
async function handleApprove() {
|
||
if (!payoutAmount.value || Number(payoutAmount.value) <= 0) {
|
||
uni.showToast({ title: '请输入赔付金额', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
reviewing.value = true
|
||
try {
|
||
await reviewClaimApplication(claimId.value, {
|
||
approved: true,
|
||
payoutAmount: Number(payoutAmount.value),
|
||
})
|
||
uni.showToast({ title: '审核通过', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
finally {
|
||
reviewing.value = false
|
||
}
|
||
}
|
||
|
||
// 理赔审核拒绝
|
||
async function handleReject() {
|
||
if (!rejectionReason.value) {
|
||
uni.showToast({ title: '请填写拒绝原因', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
reviewing.value = true
|
||
try {
|
||
await reviewClaimApplication(claimId.value, {
|
||
approved: false,
|
||
rejectionReason: rejectionReason.value,
|
||
})
|
||
uni.showToast({ title: '已拒绝', icon: 'success' })
|
||
setTimeout(() => {
|
||
uni.navigateBack()
|
||
}, 1500)
|
||
}
|
||
finally {
|
||
reviewing.value = false
|
||
}
|
||
}
|
||
|
||
onLoad((options) => {
|
||
if (options?.id) {
|
||
claimId.value = options.id
|
||
loadDetail()
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<view class="claim-review-detail-page">
|
||
<view v-if="loading" class="loading-state">
|
||
加载中...
|
||
</view>
|
||
|
||
<template v-else-if="claim">
|
||
<!-- 状态卡片 -->
|
||
<view class="status-card" :style="{ background: statusInfo?.color }">
|
||
<text class="status-text">{{ statusInfo?.text }}</text>
|
||
</view>
|
||
|
||
<!-- 银行信息 -->
|
||
<view class="section-card">
|
||
<view class="card-header">
|
||
银行信息
|
||
</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="label">银行名称</text>
|
||
<text class="value">{{ claim.bankName }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">保险单号</text>
|
||
<text class="value">{{ claim.policyNumber }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 保险公司信息 -->
|
||
<view class="section-card">
|
||
<view class="card-header">
|
||
保险公司信息
|
||
</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="label">保险公司</text>
|
||
<text class="value">{{ claim.companyName }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 理赔信息 -->
|
||
<view class="section-card">
|
||
<view class="card-header">
|
||
理赔信息
|
||
</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="label">理赔金额</text>
|
||
<text class="value amount">{{ claim.claimAmount }}元</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">理赔原因</text>
|
||
<text class="value reason">{{ claim.claimReason }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">提交时间</text>
|
||
<text class="value">{{ claim.submittedAt }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 理赔材料 -->
|
||
<view class="section-card">
|
||
<view class="card-header">
|
||
理赔材料
|
||
</view>
|
||
<view class="materials-list">
|
||
<view
|
||
v-for="material in claim.materials"
|
||
:key="material.id"
|
||
class="material-item"
|
||
@click="handlePreviewImage(material.url)"
|
||
>
|
||
<image :src="material.url" mode="aspectFill" />
|
||
<view class="material-info">
|
||
<text class="material-name">{{ material.name }}</text>
|
||
<text class="upload-time">{{ material.uploadTime }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 审核操作 -->
|
||
<view v-if="claim.status === 'pending'" class="action-card">
|
||
<view class="card-header">
|
||
理赔审核
|
||
</view>
|
||
<view class="action-buttons">
|
||
<button
|
||
class="btn approve"
|
||
:disabled="reviewing"
|
||
@click="handleApprove"
|
||
>
|
||
<text v-if="reviewing">处理中...</text>
|
||
<text v-else>通过</text>
|
||
</button>
|
||
<button
|
||
class="btn reject"
|
||
:disabled="reviewing"
|
||
@click="handleReject"
|
||
>
|
||
<text v-if="reviewing">处理中...</text>
|
||
<text v-else>拒绝</text>
|
||
</button>
|
||
</view>
|
||
<view class="payout-input">
|
||
<text class="label">赔付金额(元)</text>
|
||
<input
|
||
v-model="payoutAmount"
|
||
type="digit"
|
||
class="input"
|
||
placeholder="请输入赔付金额"
|
||
>
|
||
</view>
|
||
<view v-if="!reviewing" class="reject-reason">
|
||
<textarea
|
||
v-model="rejectionReason"
|
||
class="textarea"
|
||
placeholder="请输入拒绝原因..."
|
||
:maxlength="500"
|
||
/>
|
||
<text class="char-count">{{ rejectionReason.length }}/500</text>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 已审核信息 -->
|
||
<view v-if="claim.status !== 'pending'" class="section-card">
|
||
<view class="card-header">
|
||
审核信息
|
||
</view>
|
||
<view class="info-list">
|
||
<view class="info-item">
|
||
<text class="label">审核时间</text>
|
||
<text class="value">{{ claim.reviewedAt }}</text>
|
||
</view>
|
||
<view class="info-item">
|
||
<text class="label">审核人员</text>
|
||
<text class="value">{{ claim.reviewedBy }}</text>
|
||
</view>
|
||
<view v-if="claim.payoutAmount" class="info-item">
|
||
<text class="label">赔付金额</text>
|
||
<text class="value payout">{{ claim.payoutAmount }}元</text>
|
||
</view>
|
||
<view v-if="claim.payoutDate" class="info-item">
|
||
<text class="label">赔付日期</text>
|
||
<text class="value">{{ claim.payoutDate }}</text>
|
||
</view>
|
||
<view v-if="claim.rejectionReason" class="info-item full">
|
||
<text class="label">拒绝原因</text>
|
||
<text class="value reason">{{ claim.rejectionReason }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
</template>
|
||
|
||
<style lang="scss" scoped>
|
||
.claim-review-detail-page {
|
||
min-height: 100vh;
|
||
background: #f5f7fa;
|
||
padding: 20rpx;
|
||
padding-bottom: 120rpx;
|
||
}
|
||
|
||
.loading-state {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 100rpx 0;
|
||
color: #999;
|
||
}
|
||
|
||
.status-card {
|
||
background: linear-gradient(135deg, #00c05a 0%, #34d19d 100%);
|
||
border-radius: 16rpx;
|
||
padding: 40rpx 30rpx;
|
||
color: #fff;
|
||
margin-bottom: 20rpx;
|
||
text-align: center;
|
||
|
||
.status-text {
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.section-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 0 30rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.card-header {
|
||
padding: 30rpx 0;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.info-list {
|
||
padding: 20rpx 0;
|
||
|
||
.info-item {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
padding: 16rpx 0;
|
||
font-size: 26rpx;
|
||
border-bottom: 1rpx solid #f9f9f9;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.label {
|
||
color: #666;
|
||
}
|
||
|
||
.value {
|
||
color: #333;
|
||
text-align: right;
|
||
|
||
&.amount {
|
||
color: #ff8f0d;
|
||
font-weight: 600;
|
||
}
|
||
|
||
&.reason {
|
||
text-align: left;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
&.payout {
|
||
color: #00c05a;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
&.full {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
|
||
.value {
|
||
margin-top: 8rpx;
|
||
width: 100%;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.materials-list {
|
||
padding: 20rpx 0;
|
||
display: grid;
|
||
grid-template-columns: repeat(2, 1fr);
|
||
gap: 16rpx;
|
||
|
||
.material-item {
|
||
position: relative;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
background: #f8f9fa;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 200rpx;
|
||
display: block;
|
||
}
|
||
|
||
.material-info {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background: rgba(0, 0, 0, 0.7);
|
||
padding: 12rpx;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 4rpx;
|
||
|
||
.material-name {
|
||
font-size: 22rpx;
|
||
color: #fff;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.upload-time {
|
||
font-size: 20rpx;
|
||
color: rgba(255, 255, 255, 0.8);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.action-card {
|
||
background: #fff;
|
||
border-radius: 16rpx;
|
||
padding: 0 30rpx;
|
||
margin-bottom: 20rpx;
|
||
|
||
.card-header {
|
||
padding: 30rpx 0;
|
||
border-bottom: 1rpx solid #f5f5f5;
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #333;
|
||
}
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 20rpx;
|
||
padding: 20rpx 0;
|
||
|
||
.btn {
|
||
flex: 1;
|
||
font-size: 28rpx;
|
||
height: 80rpx;
|
||
line-height: 80rpx;
|
||
border-radius: 40rpx;
|
||
|
||
&.approve {
|
||
background: #00c05a;
|
||
color: #fff;
|
||
}
|
||
|
||
&.reject {
|
||
background: #fa4350;
|
||
color: #fff;
|
||
}
|
||
|
||
&:disabled {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
}
|
||
|
||
.payout-input {
|
||
padding: 0 0 20rpx 0;
|
||
|
||
.label {
|
||
display: block;
|
||
font-size: 26rpx;
|
||
color: #666;
|
||
margin-bottom: 12rpx;
|
||
}
|
||
|
||
.input {
|
||
width: 100%;
|
||
padding: 16rpx;
|
||
background: #f8f9fa;
|
||
border-radius: 8rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.reject-reason {
|
||
padding: 0 0 20rpx 0;
|
||
|
||
.textarea {
|
||
width: 100%;
|
||
min-height: 150rpx;
|
||
padding: 16rpx;
|
||
background: #f8f9fa;
|
||
border-radius: 8rpx;
|
||
font-size: 26rpx;
|
||
color: #333;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
.char-count {
|
||
font-size: 22rpx;
|
||
color: #999;
|
||
text-align: right;
|
||
margin-top: 8rpx;
|
||
display: block;
|
||
}
|
||
}
|
||
}
|
||
</style>
|