All files / src/views PoliciesView.vue

93.87% Statements 46/49
76.47% Branches 13/17
86.66% Functions 13/15
93.33% Lines 42/45

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>