<template>
|
<view class="page">
|
<view class="custom-nav"><view class="safe-space" :style="'height:' + navTop + 'px'"></view><view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="back-button" hover-class="back-button-active" @tap="goBack"><image src="/static/news-assets/arrow-left.svg"></image></view><text class="nav-title">人才政策</text><view class="capsule-space" :style="'width:' + navWidth + 'px'"></view></view></view>
|
<scroll-view class="content" scroll-y>
|
<view v-if="initialLoading" class="detail-state"><view class="loading-spinner"></view><text>正在加载政策详情</text></view>
|
<view v-else-if="policyUnavailable" class="detail-state error-state"><text>政策已失效</text><text class="retry-text" @tap="goBack">返回政策列表</text></view>
|
<view v-else-if="initialError" class="detail-state error-state"><text>政策详情加载失败</text><text class="retry-text" @tap="loadPolicy">重新加载</text></view>
|
<view v-else>
|
<image class="detail-hero" :src="policyCover" mode="aspectFill" @error="useHeroFallback"></image>
|
<view class="policy-summary"><view class="summary-title-row"><text class="summary-title">{{policyTitle}}</text><text class="summary-tag">{{policyCategory}}</text></view><text class="summary-desc">{{policySummary}}</text></view>
|
<view class="explanation-card"><image v-for="(imageUrl, index) in policyContentImages" :key="imageUrl + index" class="content-image" :src="imageUrl" mode="aspectFill" @error="useContentImageFallback(index)"></image><text class="explanation-title" :class="policyContentImages.length == 0 ? 'explanation-title-first' : ''">政策说明</text><text v-if="policyContent.length > 0" class="explanation-text">{{policyContent}}</text><text v-else class="explanation-empty">暂无政策说明</text></view>
|
</view>
|
<view class="content-bottom"></view>
|
</scroll-view>
|
</view>
|
</template>
|
|
<script setup lang="uts">
|
import { getPolicyDetail } from '../../services/portal.uts'
|
import { resolveApiUrl } from '../../services/http.uts'
|
import { safeBack } from '../../utils/navigation.uts'
|
|
const navTop = ref(28)
|
const navHeight = ref(32)
|
const navWidth = ref(88)
|
const navRight = ref(10)
|
const policyTitle = ref('安家补助政策')
|
const policySummary = ref('最高30万安家补助,按人才类别分档申请。')
|
const policyCategory = ref('东区政策')
|
const policyContent = ref('')
|
const policyContentImages = ref<string[]>([])
|
const policyCover = ref('')
|
const policyId = ref('')
|
const initialLoading = ref(true)
|
const initialError = ref(false)
|
const policyUnavailable = ref(false)
|
const policyContentText = (data: any): string => {
|
const extracted = data != null && data.contentText != null ? (data.contentText as string).trim() : ''
|
if (extracted != '') return extracted
|
const raw = data != null && data.content != null ? data.content as string : ''
|
return raw.replace(/<[^>]+>/g, '').replace(/ /g, ' ').trim()
|
}
|
|
onLoad((options: any) => {
|
if (options != null && options.id != null) policyId.value = decodeURIComponent(options.id as string)
|
// #ifdef MP-WEIXIN
|
const capsule = uni.getMenuButtonBoundingClientRect()
|
const systemInfo = uni.getSystemInfoSync()
|
navTop.value = capsule.top
|
navHeight.value = capsule.height
|
navWidth.value = capsule.width
|
navRight.value = systemInfo.windowWidth - capsule.right
|
// #endif
|
loadPolicy()
|
})
|
const loadPolicy = () => {
|
if (policyId.value.length == 0) { initialLoading.value = false; initialError.value = true; return }
|
initialLoading.value = true
|
initialError.value = false
|
policyUnavailable.value = false
|
getPolicyDetail(policyId.value, (data: any) => {
|
policyTitle.value = data != null && data.title != null ? data.title as string : '人才政策'
|
policySummary.value = data != null && data.subtitle != null ? data.subtitle as string : ''
|
policyCategory.value = data != null && data.subtitle != null && (data.subtitle as string).trim() != '' ? data.subtitle as string : '人才政策'
|
// 正文直接取后台政策内容;contentText 为空时兼容后台仅返回富文本 content 的情况。
|
policyContent.value = policyContentText(data)
|
policyCover.value = ''
|
const heroImage = data != null && data.heroImage != null ? data.heroImage : (data != null ? data.coverUrl : null)
|
if (heroImage != null && (heroImage as string).length > 0) policyCover.value = resolveApiUrl(heroImage as string)
|
const images: string[] = []
|
if (data != null && data.contentImages instanceof Array) {
|
const sourceImages = data.contentImages as any[]
|
for (let index = 0; index < sourceImages.length; index++) {
|
if (sourceImages[index] != null && (sourceImages[index] as string).length > 0) images.push(resolveApiUrl(sourceImages[index] as string))
|
}
|
}
|
policyContentImages.value = images
|
initialLoading.value = false
|
}, (error: any) => {
|
initialLoading.value = false
|
policyUnavailable.value = error != null && error.statusCode == 404
|
initialError.value = !policyUnavailable.value
|
})
|
}
|
const useHeroFallback = () => { policyCover.value = '' }
|
const useContentImageFallback = (index: number) => {
|
const next = policyContentImages.value.slice()
|
next[index] = '/static/common/image-placeholder.png'
|
policyContentImages.value = next
|
}
|
const goBack = () => safeBack('/pages/policy/policy')
|
</script>
|
|
<style>
|
.page { display: flex; width: 100%; height: 100vh; background-color: #FFFFFF; flex-direction: column; }
|
.custom-nav { flex-shrink: 0; padding-bottom: 30rpx; background-image: linear-gradient(180deg, #FFE4BC 0%, #FFF1DA 100%); }
|
.safe-space { height: 28px; }
|
.nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 26rpx; padding-right: 10px; position: relative; flex-direction: row; align-items: center; justify-content: space-between; }
|
.back-button { display: flex; width: 60rpx; height: 60rpx; margin-left: -8rpx; justify-content: center; align-items: center; }
|
.back-button image { width: 38rpx; height: 38rpx; }
|
.back-button-active { opacity: .55; }
|
.nav-title { position: absolute; left: 50%; top: 50%; color: #181A1E; font-size: 36rpx; font-weight: 700; transform: translate(-50%, -50%); white-space: nowrap; }
|
.capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
|
.content { min-height: 0; flex: 1; background-color: #FFFFFF; }
|
.detail-state { display: flex; height: 520rpx; flex-direction: row; justify-content: center; align-items: center; color: #A0A4AA; font-size: 25rpx; }
|
.detail-state.error-state { flex-direction: column; }
|
.retry-text { margin-top: 22rpx; color: #F26A38; }
|
.loading-spinner { width: 28rpx; height: 28rpx; margin-right: 13rpx; box-sizing: border-box; border: 3rpx solid #FFD6C4; border-top-color: #FF6C2B; border-radius: 50%; animation: policy-detail-loading 720ms linear infinite; }
|
@keyframes policy-detail-loading { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }
|
.detail-hero { width: 100%; height: 320rpx; background-color: #8BB9CB; }
|
.policy-summary { padding: 70rpx 57rpx 43rpx; }
|
.summary-title-row { display: flex; flex-direction: row; align-items: center; justify-content: space-between; }
|
.summary-title { color: #24262A; font-size: 34rpx; font-weight: 700; }
|
.summary-tag { padding: 9rpx 18rpx; border-radius: 22rpx; background-color: #FFF4E8; color: #FF761D; font-size: 22rpx; }
|
.summary-desc { display: block; margin-top: 34rpx; color: #808794; font-size: 27rpx; line-height: 42rpx; }
|
.explanation-card { margin: 0 57rpx; padding: 34rpx 34rpx 50rpx; border-radius: 29rpx; background-color: #FFFFFF; box-shadow: 0 9rpx 28rpx rgba(64,73,86,.08); }
|
.content-image { display: block; width: 100%; height: 324rpx; margin-bottom: 22rpx; border-radius: 10rpx; background-color: #D3D7DA; }
|
.explanation-title { display: block; margin-top: 38rpx; color: #24262A; font-size: 30rpx; font-weight: 700; }
|
.explanation-title-first { margin-top: 0; }
|
.explanation-text { display: block; margin-top: 22rpx; color: #717987; font-size: 27rpx; line-height: 50rpx; white-space: pre-wrap; }
|
.explanation-empty { display: block; margin-top: 22rpx; color: #A5A8AD; font-size: 27rpx; line-height: 50rpx; }
|
.content-bottom { height: 48rpx; height: calc(48rpx + env(safe-area-inset-bottom)); }
|
</style>
|