shinnosuke
5 天以前 e52934f6527027bd6008c2ade61c3083159efd4a
rencaika/pages/index/index.uvue
@@ -2,27 +2,25 @@
   <view class="page">
      <scroll-view class="scroll-page" scroll-y @scroll="handleScroll">
         <view class="hero">
            <image class="top-image" src="/static/home-assets/home-banner-v2.jpg" mode="aspectFill"></image>
            <image class="hero-image" :src="homeImage('home-top.png')" mode="aspectFill"></image>
            <view class="hero-gradient"></view>
            <view class="play-button" @tap="playVideo"><image src="/static/home-assets/play.svg"></image><text>向东看</text></view>
            <view v-if="homeVideoUrl != ''" class="play-button" @tap="playVideo"><text>向东看</text></view>
         </view>
         <view class="city-panel">
            <image class="city-mascot" src="/static/home-assets/talent-east.png" mode="aspectFit"></image>
            <image class="city-flower" src="/static/home-assets/talent-east-flower.png" mode="aspectFit"></image>
            <view class="city-heading"><text>才聚东区,共富未来</text><view class="city-label" @tap="openIntro"><text>幸福东区</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
            <view class="city-grid">
               <view class="city-card city-intro" @tap="openIntro"><image class="city-photo" src="/static/home-assets/city-intro.jpg" mode="aspectFill"></image><view class="city-shade"></view><text class="intro-name">东区简介</text><view class="intro-button"><text>查看更多</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
               <view class="city-side"><view class="city-card city-potential"><image class="city-photo" src="/static/home-assets/city-potential.jpg" mode="aspectFill"></image><view class="city-shade"></view><text>潜力城</text></view><view class="city-card city-golden"><image class="city-photo" src="/static/home-assets/city-golden.webp" mode="aspectFill"></image><view class="city-shade"></view><text>黄金城</text></view></view>
               <view class="city-side"><view class="city-card city-industry"><image class="city-photo" src="/static/home-assets/city-industry.webp" mode="aspectFill"></image><view class="city-shade"></view><text>兴业城</text></view><view class="city-card city-livable"><image class="city-photo" src="/static/home-assets/city-livable.jpg" mode="aspectFill"></image><view class="city-shade"></view><text>宜居城</text></view></view>
               <view class="city-card city-intro" @tap="openIntro"><image class="city-bg" :src="homeImage('city-intro.jpg')" mode="aspectFill"></image><text class="intro-name">东区简介</text><view class="intro-button"><text>查看更多</text><image src="/static/home-assets/arrow-right.svg"></image></view></view>
               <view class="city-side"><view class="city-card city-potential"><image class="city-bg" :src="homeImage('city-potential.jpg')" mode="aspectFill"></image><text>潜力城</text></view><view class="city-card city-golden"><image class="city-bg" :src="homeImage('city-golden.jpg')" mode="aspectFill"></image><text>黄金城</text></view></view>
               <view class="city-side"><view class="city-card city-industry"><image class="city-bg" :src="homeImage('city-industry.jpg')" mode="aspectFill"></image><text>兴业城</text></view><view class="city-card city-livable"><image class="city-bg" :src="homeImage('city-livable.png')" mode="aspectFill"></image><text>宜居城</text></view></view>
            </view>
         </view>
         <view class="section-head"><view class="section-title"><image src="/static/home-assets/news-mascot-x3.png"></image><text>人才资讯</text></view><text class="more" @tap="goNews">查看更多</text></view>
         <view class="section-head"><view class="section-title"><text>人才资讯</text></view><text class="more" @tap="goNews">查看更多</text></view>
         <view v-if="homeLoading" class="news-state"><text>资讯加载中...</text></view>
         <view v-else-if="homeError" class="news-state" @tap="loadHome"><text>{{homeError}}</text><text class="news-retry">点击重试</text></view>
         <view v-else>
            <view class="news-featured" @tap="openNews(featuredNews.id)"><image src="/static/home-assets/home-banner-v2.jpg" mode="aspectFill"></image><view class="news-shade"></view><view class="top-badge"><text>置顶</text></view><view class="featured-copy"><text>{{featuredNews.title}}</text><text>{{featuredNews.date}}</text></view></view>
            <view class="news-featured" @tap="openNews(featuredNews.id)"><image v-if="featuredNews.coverUrl != ''" :src="featuredNews.coverUrl" mode="aspectFill"></image><view class="news-shade"></view><view class="top-badge"><text>置顶</text></view><view class="featured-copy"><text>{{featuredNews.title}}</text><text>{{featuredNews.date}}</text></view></view>
            <view class="news-list"><view class="news-row" v-for="item in news" :key="item.id" @tap="openNews(item.id)"><text class="news-name">{{item.title}}</text><view class="news-meta"><text v-if="item.isNew" class="new-badge">最新</text><text>{{item.date}}</text></view></view></view>
         </view>
         <view class="news-more" @tap="goNews"><text>查看更多</text></view>
@@ -32,7 +30,7 @@
      <view class="sticky-nav" :class="isStickyNav ? 'is-solid' : ''">
         <view class="sticky-nav-bg"></view>
         <view class="safe-space" :style="'height:' + navTop + 'px'"></view>
         <view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="nav-title"><image src="/static/home-assets/home-title-left-v3.png" mode="aspectFit"></image></view><view class="capsule-reserve" :style="'width:' + navWidth + 'px'"></view></view>
         <view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'"><view class="nav-title"><text>攀枝花市东区人才服务平台</text></view><view class="capsule-reserve" :style="'width:' + navWidth + 'px'"></view></view>
      </view>
      <view class="tab-bar">
@@ -48,6 +46,13 @@
<script setup lang="uts">
   import { isApiConfigured } from '../../config/api.uts'
   import { getAppHome } from '../../services/portal.uts'
   import { resolveApiUrl } from '../../services/http.uts'
   /**
    * 首页图片放在服务器上(`/uploads/home/…`),代码里只存相对路径:
    * 换图不用改代码、也不用重新上传体验版,和后台域名一起走环境配置。
    */
   const homeImage = (name: string): string => resolveApiUrl('/uploads/home/' + name)
   const navTop = ref(28)
   const navHeight = ref(32)
@@ -56,6 +61,11 @@
   const isStickyNav = ref(false)
   const homeLoading = ref(false)
   const homeError = ref('')
   const homeVideoUrl = ref('')
   const miniProgramUrl = ref('')
   // 后台可配置目标小程序 AppID,配置后"幸福东区"等入口直接用小程序跳转,不再走 web-view。
   const miniProgramAppId = ref('')
   const miniProgramPath = ref('')
   onLoad(() => {
      // #ifdef MP-WEIXIN
@@ -69,7 +79,7 @@
      loadHome()
   })
   const featuredNews = ref({ id: 'news-20260603-senior-talent', title: '攀枝花市东区事业单位2026年直接考核招聘副高职称及以上人才公告', date: '2026-06-03 09:19' })
   const featuredNews = ref({ id: 'news-20260603-senior-talent', title: '攀枝花市东区事业单位2026年直接考核招聘副高职称及以上人才公告', date: '2026-06-03 09:19', coverUrl: '' })
   const news = ref([
      { id: 'news-20260601-score-review', title: '关于攀枝花市东区事业单位2026年上半年公开考试招聘工作人员东区岗位加分情况、笔试总成绩及岗位排名、面试资格复审有关事宜的公告', date: '2026-06-01 16:36', isNew: true },
      { id: 'news-20260529-teacher-review', title: '攀枝花市2026年上半年公开考试招聘中小学教师东区岗位面试资格复审及领取面试通知书公告', date: '2026-05-29 10:42', isNew: false },
@@ -87,10 +97,14 @@
      homeError.value = ''
      getAppHome((data: any) => {
         homeLoading.value = false
         homeVideoUrl.value = data != null && data.homeVideoUrl != null ? data.homeVideoUrl as string : ''
         miniProgramUrl.value = data != null && data.miniProgramUrl != null ? data.miniProgramUrl as string : ''
         miniProgramAppId.value = data != null && data.miniProgramAppId != null ? data.miniProgramAppId as string : ''
         miniProgramPath.value = data != null && data.miniProgramPath != null ? data.miniProgramPath as string : ''
         const articles: any[] = data != null && data.news instanceof Array ? data.news as any[] : []
         if (articles.length == 0) return
         const first: any = articles[0]
         featuredNews.value = { id: '' + first.id, title: first.title != null ? first.title as string : '人才资讯', date: formatNoticeDate(first.publishTime) }
         featuredNews.value = { id: '' + first.id, title: first.title != null ? first.title as string : '人才资讯', date: formatNoticeDate(first.publishTime), coverUrl: first.coverUrl != null && first.coverUrl != '' ? resolveApiUrl(first.coverUrl as string) : '' }
         const nextItems: any[] = []
         for (let index = 1; index < articles.length; index++) {
            const item: any = articles[index]
@@ -108,8 +122,30 @@
   const goPolicy = () => uni.reLaunch({ url: '/pages/policy/policy' })
   const goNews = () => uni.navigateTo({ url: '/pages/news/news' })
   const goProfile = () => uni.reLaunch({ url: '/pages/profile/profile' })
   const playVideo = () => uni.showToast({ title: '东区宣传视频即将播放', icon: 'none' })
   const openIntro = () => uni.showToast({ title: '东区简介 PDF 请在正式版查看', icon: 'none' })
   const playVideo = () => {
      if (homeVideoUrl.value == '') {
         uni.showToast({ title: '首页视频暂未配置', icon: 'none' })
         return
      }
      uni.navigateTo({ url: '/pages/home-video/home-video?url=' + encodeURIComponent(resolveApiUrl(homeVideoUrl.value)) })
   }
   const openIntro = () => {
      if (miniProgramAppId.value != '') {
         uni.navigateToMiniProgram({
            appId: miniProgramAppId.value,
            path: miniProgramPath.value,
            fail: () => {
               uni.showToast({ title: '跳转小程序失败,请稍后重试', icon: 'none' })
            }
         })
         return
      }
      if (miniProgramUrl.value == '') {
         uni.showToast({ title: '首页跳转链接暂未配置', icon: 'none' })
         return
      }
      uni.navigateTo({ url: '/pages/external-webview/external-webview?source=homeMiniProgram' })
   }
   const openNews = (newsId: string) => uni.navigateTo({ url: '/pages/news-detail/news-detail?newsId=' + encodeURIComponent(newsId) })
   onPullDownRefresh(() => {
      loadHome()
@@ -126,32 +162,32 @@
<style>
   .page { height: 100%; background-color: #FFFFFF; }
   .scroll-page { height: calc(100% - 126rpx); height: calc(100% - 126rpx - env(safe-area-inset-bottom)); background-color: #FFFFFF; }
   .hero { height: 468rpx; background-color: #FFFFFF; position: relative; overflow: hidden; }
   .top-image { position: absolute; z-index: 0; top: 0; left: 0; width: 100%; height: 468rpx; border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; }
   .hero-gradient { position: absolute; z-index: 1; top: 0; left: 0; right: 0; height: 300rpx; background-image: linear-gradient(180deg, rgba(239,81,39,.92) 0%, rgba(239,81,39,.68) 42%, rgba(239,81,39,0) 100%); }
   .hero { height: 468rpx; background-color: #FFFFFF; position: relative; overflow: hidden; border-bottom-left-radius: 48rpx; border-bottom-right-radius: 48rpx; }
   .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 468rpx; }
   .hero-gradient { position: absolute; z-index: 1; top: 0; left: 0; right: 0; height: 300rpx; background-image: linear-gradient(180deg, rgba(244,90,42,.88) 0%, rgba(255,130,60,.42) 62%, rgba(255,166,80,0) 100%); }
   .safe-space { height: 28px; position: relative; z-index: 2; }
   .nav-row { height: 32px; padding: 0 32rpx; position: relative; z-index: 2; flex-direction: row; align-items: center; justify-content: space-between; }
   .sticky-nav { position: absolute; z-index: 30; top: 0; left: 0; right: 0; padding-bottom: 20rpx; }
   .sticky-nav-bg { position: absolute; z-index: 0; top: 0; left: 0; right: 0; bottom: 0; opacity: 0; background-image: linear-gradient(180deg, #F26A38 0%, #EE542B 100%); transition: opacity 280ms ease-out; }
   .sticky-nav.is-solid .sticky-nav-bg { opacity: 1; }
   .nav-title { position: absolute; z-index: 1; left: 0; top: 10rpx; width: 750rpx; height: 100%; padding: 0; flex-direction: row; align-items: center; justify-content: flex-start; }
   .nav-title image { width: 750rpx; height: 66rpx; }
   .nav-title { position: absolute; z-index: 1; left: 30rpx; top: 10rpx; height: 100%; flex-direction: row; align-items: center; justify-content: flex-start; }
   .nav-title text { color: #FFFFFF; font-family: 'FoodCalligraphyFull', 'FoodCalligraphy'; font-size: 38rpx; font-weight: 700; opacity: .95; }
   .capsule-reserve { width: 88px; height: 100%; flex-shrink: 0; }
   .play-button { position: absolute; z-index: 2; bottom: 20rpx; left: 50%; margin-left: -78rpx; width: 156rpx; height: 56rpx; border-radius: 28rpx; background-image: linear-gradient(180deg, #FFB75A 0%, #FF6A24 100%); border-width: 2rpx; border-color: rgba(255,255,255,.82); box-shadow: 0 7rpx 14rpx rgba(234,92,26,.26); flex-direction: row; justify-content: center; align-items: center; }
   .play-button image { width: 24rpx; height: 24rpx; margin-right: 9rpx; } .play-button text { color: #FFFFFF; font-size: 23rpx; }
   .play-button text { color: #FFFFFF; font-size: 23rpx; }
   .city-panel { margin: -10rpx 16rpx 38rpx; padding: 22rpx 0 0; overflow: visible; background-color: #FFFFFF; border-radius: 26rpx; position: relative; box-shadow: 0 -8rpx 14rpx rgba(255, 103, 39, .27); }
   .city-mascot { position: absolute; z-index: 2; top: 45rpx; right: 174rpx; width: 112rpx; height: 86rpx; } .city-flower { position: absolute; z-index: 2; top: 6rpx; right: 6rpx; width: 46rpx; height: 72rpx; }
   .city-heading { height: 58rpx; margin: 0 22rpx; flex-direction: row; justify-content: space-between; align-items: center; }
   .city-heading > text { color: #F16624; font-size: 35rpx; font-weight: 700; letter-spacing: 1rpx; background-image: linear-gradient(180deg, #F45A2A 0%, #FFB257 100%); background-clip: text; -webkit-background-clip: text; -webkit-text-fill-color: transparent; }
   .city-label { margin-right: 48rpx; padding: 10rpx 13rpx; border-radius: 18rpx; background-image: linear-gradient(180deg, #FFB85E 0%, #FF6726 100%); box-shadow: 0 5rpx 10rpx rgba(239,101,34,.22); flex-direction: row; align-items: center; } .city-label text { color: #FFFFFF; font-size: 20rpx; } .city-label image { width: 20rpx; height: 20rpx; margin-left: 7rpx; }
   .city-grid { box-sizing: border-box; height: 292rpx; margin-top: 18rpx; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-color: #455A71; } .city-photo { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .city-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 92rpx; background-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.62) 100%); }
   .city-grid { box-sizing: border-box; height: 292rpx; margin-top: 18rpx; padding: 22rpx; overflow: visible; background-image: linear-gradient(180deg, #FF5F2D 0%, #FFB36B 100%); border-bottom-left-radius: 26rpx; border-bottom-right-radius: 26rpx; flex-direction: row; } .city-card { overflow: hidden; border-radius: 17rpx; position: relative; justify-content: flex-end; background-image: linear-gradient(135deg, #E96C3D 0%, #F8AA56 100%); }
   .city-bg { position: absolute; left: 0; top: 0; width: 100%; height: 100%; }
   .city-intro { width: 242rpx; height: 308rpx; box-sizing: border-box; margin-right: 14rpx; padding: 22rpx; } .intro-name { position: absolute; z-index: 1; top: 50%; left: 0; right: 0; transform: translateY(-50%); color: #FFFFFF; font-size: 37rpx; font-weight: 700; text-align: center; } .intro-button { position: relative; z-index: 1; align-self: center; box-sizing: border-box; width: 146rpx; height: 42rpx; margin-top: 13rpx; padding: 0 12rpx; border-radius: 21rpx; background-image: linear-gradient(90deg, rgba(255,207,138,.92) 0%, rgba(255,101,36,.68) 100%); border-width: 1rpx; border-color: rgba(255,255,255,.42); flex-direction: row; align-items: center; justify-content: space-between; } .intro-button text { color: #FFFFFF; font-size: 19rpx; } .intro-button image { width: 19rpx; height: 19rpx; margin-left: 8rpx; }
   .city-side { height: 308rpx; flex: 1; margin-right: 14rpx; justify-content: space-between; } .city-side:last-child { margin-right: 0; } .city-side .city-card { height: 147rpx; padding: 0; } .city-side .city-card > text { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; padding: 10rpx 12rpx; background-image: linear-gradient(90deg, rgba(236,76,118,.96) 0%, rgba(247,94,135,.82) 45%, rgba(255,169,187,0) 72%, rgba(255,169,187,0) 100%); color: #FFFFFF; font-size: 22rpx; font-weight: 700; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.35); }
   .city-side { height: 308rpx; flex: 1; margin-right: 14rpx; justify-content: space-between; } .city-side:last-child { margin-right: 0; } .city-side .city-card { height: 147rpx; padding: 0; } .city-side .city-card > text { position: absolute; z-index: 1; left: 0; right: 0; bottom: 0; padding: 10rpx 12rpx; color: #FFFFFF; font-size: 22rpx; font-weight: 700; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.5); }
   .section-head { height: 93rpx; padding: 0 31rpx; flex-direction: row; align-items: center; justify-content: space-between; } .section-title { flex-direction: row; align-items: center; } .section-title image { width: 40rpx; height: 40rpx; margin-right: 12rpx; } .section-title text { color: #353535; font-size: 31rpx; font-weight: 700; } .more { color: #5C5C5C; font-size: 22rpx; }
   .section-head { height: 93rpx; padding: 0 31rpx; flex-direction: row; align-items: center; justify-content: space-between; } .section-title { flex-direction: row; align-items: center; } .section-title text { color: #353535; font-size: 31rpx; font-weight: 700; } .more { color: #5C5C5C; font-size: 22rpx; }
   .news-state { min-height: 150rpx; margin: 0 31rpx; justify-content: center; align-items: center; color: #8B8B8B; font-size: 24rpx; } .news-retry { margin-top: 12rpx; color: #F16624; }
   .news-featured { height: 205rpx; margin: 0 19rpx; border-radius: 18rpx; overflow: hidden; position: relative; } .news-featured > image { width: 100%; height: 100%; } .news-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 118rpx; background-color: rgba(0,0,0,.44); } .top-badge { position: absolute; left: 20rpx; top: 18rpx; padding: 7rpx 14rpx; border-radius: 12rpx; background-color: #FFF3D4; } .top-badge text { color: #F2722E; font-size: 20rpx; font-weight: 700; } .featured-copy { position: absolute; left: 20rpx; right: 20rpx; bottom: 17rpx; } .featured-copy text:first-child { display: block; color: #FFFFFF; font-size: 24rpx; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .featured-copy text:last-child { display: block; margin-top: 8rpx; color: #FFFFFF; opacity: .85; font-size: 19rpx; }
   .news-featured { height: 205rpx; margin: 0 19rpx; border-radius: 18rpx; overflow: hidden; position: relative; background-image: linear-gradient(135deg, #F26A38 0%, #F9A551 100%); } .news-featured > image { width: 100%; height: 100%; } .news-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 118rpx; background-color: rgba(0,0,0,.44); } .top-badge { position: absolute; left: 20rpx; top: 18rpx; padding: 7rpx 14rpx; border-radius: 12rpx; background-color: #FFF3D4; } .top-badge text { color: #F2722E; font-size: 20rpx; font-weight: 700; } .featured-copy { position: absolute; left: 20rpx; right: 20rpx; bottom: 17rpx; } .featured-copy text:first-child { display: block; color: #FFFFFF; font-size: 24rpx; font-weight: 700; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } .featured-copy text:last-child { display: block; margin-top: 8rpx; color: #FFFFFF; opacity: .85; font-size: 19rpx; }
   .news-list { margin: 0 31rpx; } .news-row { min-height: 116rpx; padding: 22rpx 0 16rpx; border-bottom-width: 1rpx; border-bottom-color: #EEEEEE; } .news-name { color: #363636; font-size: 25rpx; line-height: 36rpx; } .news-meta { margin-top: 12rpx; flex-direction: row; align-items: center; color: #98A2B3; font-size: 19rpx; } .new-badge { margin-right: 14rpx; padding: 4rpx 10rpx; border-radius: 12rpx; background-color: #FFF0D9; color: #FE7930; font-size: 18rpx; }
   .news-more { height: 87rpx; justify-content: center; align-items: center; color: #545454; font-size: 22rpx; } .content-bottom { height: 22rpx; }
   .tab-bar { height: 126rpx; height: calc(126rpx + constant(safe-area-inset-bottom)); height: calc(126rpx + env(safe-area-inset-bottom)); padding: 8rpx 14rpx; padding-bottom: calc(8rpx + constant(safe-area-inset-bottom)); padding-bottom: calc(8rpx + env(safe-area-inset-bottom)); box-sizing: border-box; overflow: visible; background-color: #FFFFFF; border-top-width: 1rpx; border-top-color: #F0F0F0; flex-direction: row; justify-content: space-between; } .tab { width: 20%; padding-top: 4rpx; align-items: center; justify-content: flex-start; color: #708099; font-size: 24rpx; } .tab-icon-wrap { width: 66rpx; height: 66rpx; margin-bottom: 13rpx; border-radius: 33rpx; justify-content: center; align-items: center; } .tab-icon-wrap image { width: 42rpx; height: 42rpx; } .active-icon { background-color: #FFF1F1; } .tab.active { color: #FF6030; font-weight: 700; } .card-tab { margin-top: -16rpx; padding-top: 0; justify-content: flex-start; } .card-icon { width: 160rpx; height: 160rpx; position: relative; justify-content: center; align-items: center; } .card-icon image { width: 160rpx; height: 160rpx; } .card-label { position: absolute; left: 0; right: 0; bottom: 42rpx; z-index: 1; color: #FFFFFF; font-size: 18rpx; font-weight: 700; text-align: center; }