CI/CD Pipeline / Backend Build & Test (push) Canceled after 0s
CI/CD Pipeline / Admin Web Build (push) Canceled after 0s
CI/CD Pipeline / Mini Program Compile Check (push) Canceled after 0s
CI/CD Pipeline / Migration Validation (push) Canceled after 0s
CI/CD Pipeline / Test Shell Guard (Phase 23 Gate) (push) Canceled after 0s
CI/CD Pipeline / UI Contracts (source-structure gates) (push) Canceled after 0s
CI/CD Pipeline / E2E (live stack) (push) Canceled after 0s
218 lines
6.0 KiB
Vue
218 lines
6.0 KiB
Vue
<template>
|
||
<PageLayout title="本地资格">
|
||
<div class="le">
|
||
<!-- 状态卡 -->
|
||
<UiLoading v-if="loading" text="正在读取资格状态…" />
|
||
<UiErrorState v-else-if="loadError" message="状态加载失败,请重试" @retry="checkStatus" />
|
||
|
||
<template v-else>
|
||
<div class="le__status" :class="`le__status--${statusTone}`">
|
||
<span class="le__status-label">当前状态</span>
|
||
<UiTag :type="statusTag">{{ statusText }}</UiTag>
|
||
</div>
|
||
|
||
<div class="le__header">
|
||
<div class="le__title">为什么需要确认本地资格?</div>
|
||
<div class="le__desc">
|
||
善缘丹阳主要服务在丹阳工作、居住或拥有丹阳户籍的适龄单身人士。
|
||
本地资格确认帮助我们更好地匹配适合你身边的伴侣。
|
||
</div>
|
||
</div>
|
||
|
||
<div class="le__section">
|
||
<div class="le__section-title">请选择你的本地资格</div>
|
||
<div class="le__types">
|
||
<div
|
||
v-for="t in eligibilityTypes"
|
||
:key="t.value"
|
||
class="le__type"
|
||
:class="{ 'le__type--active': eligibilityType === t.value }"
|
||
@click="onTypeSelect(t.value)"
|
||
>
|
||
<div class="le__type-icon">{{ t.icon }}</div>
|
||
<div class="le__type-label">{{ t.label }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="le__notice">
|
||
只需声明一种资格即可。如需补充佐证材料,系统会在审核后告知你。
|
||
</div>
|
||
|
||
<div class="le__actions">
|
||
<van-button plain block @click="goToMyEligibility">查看我已有的资格</van-button>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</PageLayout>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { computed, onMounted, ref } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import { localEligibilityService } from '@/api/services'
|
||
import { useAppStore } from '@/stores/app'
|
||
import PageLayout from '@/layouts/PageLayout.vue'
|
||
import UiLoading from '@/components/UiLoading.vue'
|
||
import UiErrorState from '@/components/UiErrorState.vue'
|
||
import UiTag from '@/components/UiTag.vue'
|
||
|
||
const router = useRouter()
|
||
const appStore = useAppStore()
|
||
|
||
const loading = ref(true)
|
||
const loadError = ref(false)
|
||
const status = ref('not_declared')
|
||
const eligibilityType = ref('')
|
||
|
||
const eligibilityTypes = [
|
||
{ value: 'household_registration', label: '丹阳户籍', icon: '户' },
|
||
{ value: 'employment', label: '在丹阳工作', icon: '工' },
|
||
{ value: 'residence', label: '在丹阳长期居住', icon: '居' },
|
||
{ value: 'other', label: '其他符合服务条件', icon: '其' },
|
||
]
|
||
|
||
const STATUS_TEXT: Record<string, string> = {
|
||
not_declared: '尚未声明',
|
||
declared: '声明已保存',
|
||
needs_evidence: '需要补充佐证',
|
||
pending_review: '审核中',
|
||
verified: '已核验',
|
||
rejected: '未通过',
|
||
}
|
||
|
||
const statusText = computed(() => STATUS_TEXT[status.value] || '尚未声明')
|
||
const statusTag = computed<'primary' | 'success' | 'warning' | 'danger' | 'info'>(() => {
|
||
if (status.value === 'verified') return 'success'
|
||
if (status.value === 'rejected') return 'danger'
|
||
if (status.value === 'pending_review' || status.value === 'needs_evidence') return 'warning'
|
||
if (status.value === 'declared') return 'primary'
|
||
return 'info'
|
||
})
|
||
const statusTone = computed(() => (status.value === 'verified' ? 'ok' : status.value === 'rejected' ? 'bad' : 'warn'))
|
||
|
||
async function checkStatus() {
|
||
loading.value = true
|
||
loadError.value = false
|
||
try {
|
||
const result: any = await localEligibilityService.getStatus()
|
||
status.value = result?.verificationStatus || 'not_declared'
|
||
eligibilityType.value = result?.eligibilityType || ''
|
||
} catch {
|
||
loadError.value = true
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function onTypeSelect(type: string) {
|
||
router.push({ path: '/local-eligibility/supplement', query: { type } })
|
||
}
|
||
|
||
function goToMyEligibility() {
|
||
if (status.value === 'needs_evidence' || status.value === 'rejected' || status.value === 'declared') {
|
||
router.push({ path: '/local-eligibility/supplement', query: { type: eligibilityType.value || '' } })
|
||
return
|
||
}
|
||
appStore.toast(statusText.value)
|
||
}
|
||
|
||
onMounted(checkStatus)
|
||
</script>
|
||
|
||
<style scoped>
|
||
.le {
|
||
padding: 16px;
|
||
}
|
||
.le__status {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
border-radius: var(--mini-radius);
|
||
padding: 12px 14px;
|
||
margin-bottom: 14px;
|
||
background: var(--mini-surface);
|
||
box-shadow: var(--mini-shadow);
|
||
}
|
||
.le__status--ok {
|
||
background: rgba(47, 179, 109, 0.1);
|
||
}
|
||
.le__status--warn {
|
||
background: rgba(245, 166, 35, 0.12);
|
||
}
|
||
.le__status--bad {
|
||
background: rgba(240, 72, 62, 0.1);
|
||
}
|
||
.le__status-label {
|
||
font-size: 13px;
|
||
color: var(--mini-text-secondary);
|
||
}
|
||
.le__header {
|
||
padding: 4px 4px 12px;
|
||
}
|
||
.le__title {
|
||
font-size: 17px;
|
||
font-weight: 600;
|
||
color: var(--mini-text-strong);
|
||
}
|
||
.le__desc {
|
||
font-size: 13px;
|
||
color: var(--mini-text-secondary);
|
||
line-height: 1.8;
|
||
margin-top: 8px;
|
||
}
|
||
.le__section-title {
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
color: var(--mini-text-strong);
|
||
margin-bottom: 10px;
|
||
}
|
||
.le__types {
|
||
display: grid;
|
||
grid-template-columns: 1fr 1fr;
|
||
gap: 10px;
|
||
}
|
||
.le__type {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 10px;
|
||
background: var(--mini-surface);
|
||
border-radius: var(--mini-radius);
|
||
padding: 14px;
|
||
box-shadow: var(--mini-shadow);
|
||
border: 1px solid transparent;
|
||
}
|
||
.le__type--active {
|
||
border-color: var(--mini-tabbar-active);
|
||
}
|
||
.le__type-icon {
|
||
width: 36px;
|
||
height: 36px;
|
||
border-radius: 10px;
|
||
background: var(--mini-brand-gradient);
|
||
color: #fff;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
flex: none;
|
||
}
|
||
.le__type-label {
|
||
font-size: 13px;
|
||
color: var(--mini-text-strong);
|
||
}
|
||
.le__notice {
|
||
font-size: 12px;
|
||
color: var(--mini-text-secondary);
|
||
background: rgba(79, 140, 255, 0.06);
|
||
border-radius: var(--mini-radius);
|
||
padding: 12px 14px;
|
||
margin-top: 14px;
|
||
line-height: 1.7;
|
||
}
|
||
.le__actions {
|
||
margin-top: 18px;
|
||
}
|
||
</style>
|