All files / src/views PipelineView.vue

0% Statements 0/33
0% Branches 0/15
0% Functions 0/8
0% Lines 0/30

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                                                                                                                                                         
<script setup lang="ts">
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import ApiLoader from '@/components/common/ApiLoader.vue'
import PipelineCard from '@/components/feature/PipelineCard.vue'
import { fetchPipeline } from '@/api/modules/pipeline'
import type { PipelineItem } from '@/api/types'
import { filterPipeline, payoutMonths } from '@/utils/pipelineFilter'
 
const { t } = useI18n()
const loading = ref(true)
const error = ref<string | null>(null)
const items = ref<PipelineItem[]>([])
 
const policyQuery = ref('')
const selectedMonth = ref('')
 
const months = computed(() => payoutMonths(items.value))
 
const filtered = computed(() =>
  filterPipeline(items.value, { query: policyQuery.value, month: selectedMonth.value })
)
 
async function load() {
  loading.value = true
  error.value = null
  try {
    items.value = await fetchPipeline()
    // 重新拉取后原选中的月份可能已不存在,否则会停在一个永远为空的筛选条件上
    if (selectedMonth.value && !months.value.includes(selectedMonth.value)) {
      selectedMonth.value = ''
    }
  } catch (e) {
    error.value = e instanceof Error ? e.message : 'error'
  } finally {
    loading.value = false
  }
}
 
onMounted(load)
</script>
 
<template>
  <div class="space-y-4">
    <div class="rounded-2xl border border-amber-100 bg-amber-50 px-4 py-3 text-xs font-medium text-amber-700">{{ t('pipeline.adjustNote') }}</div>
 
    <div class="grid grid-cols-2 gap-3">
      <label class="block">
        <span class="mb-1 block text-xs text-slate-500">{{ t('pipeline.filterPolicy') }}</span>
        <input
          v-model="policyQuery"
          type="search"
          :placeholder="t('pipeline.filterPolicyPlaceholder')"
          class="w-full rounded-xl border border-silver-200 bg-white px-3 py-2 text-sm text-slate-900 outline-none transition-colors placeholder:text-silver-400 focus:border-brand-200"
        >
      </label>
      <label class="block">
        <span class="mb-1 block text-xs text-slate-500">{{ t('pipeline.filterMonth') }}</span>
        <select
          v-model="selectedMonth"
          class="w-full rounded-xl border border-silver-200 bg-white px-3 py-2 text-sm text-slate-900 outline-none transition-colors focus:border-brand-200"
        >
          <option value="">{{ t('pipeline.allMonths') }}</option>
          <option v-for="month in months" :key="month" :value="month">{{ month }}</option>
        </select>
      </label>
    </div>
 
    <ApiLoader :loading="loading" :error="error" :empty="!loading && !error && items.length === 0" @retry="load">
      <div v-if="filtered.length" class="space-y-3">
        <PipelineCard v-for="item in filtered" :key="item.id" :item="item" />
      </div>
      <div v-else class="py-10 text-center text-sm text-slate-400">{{ t('pipeline.noResult') }}</div>
    </ApiLoader>
  </div>
</template>