Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 | 5x 5x 5x 5x 5x 5x 5x 5x 5x 6x 6x 5x 5x 5x 5x 5x 4x 4x 4x 4x 1x 4x 4x 4x 4x 4x 4x 5x 1x 1x 1x 5x 1x 1x 1x 4x 1x 2x 1x 1x 2x 13x | <script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import ApiLoader from '@/components/common/ApiLoader.vue'
import Modal from '@/components/common/Modal.vue'
import PolicyCard from '@/components/feature/PolicyCard.vue'
import { fetchPolicies, fetchPolicyCommissions } from '@/api/modules/policy'
import type { Policy, PolicyCommissionYear } from '@/api/types'
import { formatDetailMoney } from '@/utils/money'
const { t } = useI18n()
const loading = ref(true)
const error = ref<string | null>(null)
const policies = ref<Policy[]>([])
const modalOpen = ref(false)
const modalLoading = ref(false)
const modalError = ref<string | null>(null)
const commissions = ref<PolicyCommissionYear[]>([])
const selectedPolicyNo = ref('')
const inforce = computed(() => policies.value.filter((p) => p.status === 'INFORCE'))
const inactive = computed(() => policies.value.filter((p) => p.status !== 'INFORCE'))
async function load() {
loading.value = true
error.value = null
try {
policies.value = await fetchPolicies()
} catch (e) {
error.value = e instanceof Error ? e.message : 'error'
} finally {
loading.value = false
}
}
async function loadCommissions(policyNo: string) {
modalLoading.value = true
modalError.value = null
try {
commissions.value = await fetchPolicyCommissions(policyNo)
} catch (e) {
// 原实现没有 catch:请求失败时 modalOpen 永不置 true,点击保单毫无反馈且抛 unhandled rejection
modalError.value = e instanceof Error ? e.message : 'error'
} finally {
modalLoading.value = false
}
}
function openCommissions(policyNo: string) {
selectedPolicyNo.value = policyNo
commissions.value = []
modalError.value = null
// 先开弹窗再加载,避免请求慢时点击后长时间无任何视觉反馈
modalOpen.value = true
void loadCommissions(policyNo)
}
onMounted(load)
</script>
<template>
<ApiLoader :loading="loading" :error="error" :empty="!loading && !error && policies.length === 0" @retry="load">
<div class="space-y-6">
<section>
<h2 class="mb-3 text-[13px] font-semibold tracking-wide text-silver-600">{{ t('policies.inforce') }}</h2>
<div class="space-y-3">
<PolicyCard v-for="policy in inforce" :key="policy.policyNo" :policy="policy" @select="openCommissions" />
</div>
</section>
<section v-if="inactive.length">
<h2 class="mb-3 text-[13px] font-semibold tracking-wide text-silver-600">{{ t('policies.inactive') }}</h2>
<div class="space-y-3">
<PolicyCard v-for="policy in inactive" :key="policy.policyNo" :policy="policy" @select="openCommissions" />
</div>
</section>
</div>
</ApiLoader>
<Modal :open="modalOpen" :title="selectedPolicyNo" @close="modalOpen = false">
<ApiLoader
:loading="modalLoading"
:error="modalError"
:empty="!modalLoading && !modalError && commissions.length === 0"
@retry="loadCommissions(selectedPolicyNo)"
>
<div class="space-y-2">
<div
v-for="(item, index) in commissions"
:key="`${item.year}-${item.status}-${index}`"
class="flex items-center justify-between gap-3 text-sm"
>
<span>{{ item.year }} · {{ item.type }}</span>
<span class="text-right">
<span class="tnum">{{ formatDetailMoney(item.amount, item.currency) }}</span> · {{ t(`stage.${item.status}`) }}
<span v-if="item.statusDesc" class="block text-xs text-slate-400">{{ item.statusDesc }}</span>
</span>
</div>
</div>
</ApiLoader>
</Modal>
</template>
|