shinnosuke
2026-07-31 82141e1b9e46a154418295aa95b34cb842c001c3
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
<template>
    <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>
                <view class="hero-gradient"></view>
                <view class="play-button" @tap="playVideo"><image src="/static/home-assets/play.svg"></image><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>
            </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 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-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>
            <view class="content-bottom"></view>
        </scroll-view>
 
        <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>
 
        <view class="tab-bar">
            <view class="tab active"><view class="tab-icon-wrap active-icon"><image src="/static/home-assets/home.svg"></image></view><text>首页</text></view>
            <view class="tab" @tap="goBenefits"><view class="tab-icon-wrap"><image src="/static/home-assets/benefits.svg"></image></view><text>人才权益</text></view>
            <view class="tab card-tab" @tap="goCard"><view class="card-icon"><image src="/static/home-assets/card.png"></image><text class="card-label">人才卡</text></view></view>
            <view class="tab" @tap="goPolicy"><view class="tab-icon-wrap"><image src="/static/home-assets/policy.svg"></image></view><text>人才政策</text></view>
            <view class="tab" @tap="goProfile"><view class="tab-icon-wrap"><image src="/static/home-assets/profile.svg"></image></view><text>个人中心</text></view>
        </view>
    </view>
</template>
 
<script setup lang="uts">
    import { isApiConfigured } from '../../config/api.uts'
    import { getAppHome } from '../../services/portal.uts'
 
    const navTop = ref(28)
    const navHeight = ref(32)
    const navWidth = ref(88)
    const navRight = ref(10)
    const isStickyNav = ref(false)
    const homeLoading = ref(false)
    const homeError = ref('')
 
    onLoad(() => {
        // #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
        loadHome()
    })
 
    const featuredNews = ref({ id: 'news-20260603-senior-talent', title: '攀枝花市东区事业单位2026年直接考核招聘副高职称及以上人才公告', date: '2026-06-03 09:19' })
    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 },
        { id: 'news-20260522-talent-notice', title: '关于攀枝花市东区2026年春季综合类、教育系统事业单位引才有关事宜的公告', date: '2026-05-22 11:01', isNew: false }
    ])
 
    const formatNoticeDate = (value: any): string => {
        if (value == null) return ''
        const text = '' + value
        return text.length >= 16 ? text.substring(0, 16).replace('T', ' ') : text.replace('T', ' ')
    }
    const loadHome = () => {
        if (!isApiConfigured()) return
        homeLoading.value = true
        homeError.value = ''
        getAppHome((data: any) => {
            homeLoading.value = false
            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) }
            const nextItems: any[] = []
            for (let index = 1; index < articles.length; index++) {
                const item: any = articles[index]
                nextItems.push({ id: '' + item.id, title: item.title != null ? item.title as string : '人才资讯', date: formatNoticeDate(item.publishTime), isNew: index == 1 })
            }
            news.value = nextItems
        }, (error) => {
            homeLoading.value = false
            homeError.value = error.message
        })
    }
 
    const goBenefits = () => uni.reLaunch({ url: '/pages/benefits/benefits' })
    const goCard = () => uni.reLaunch({ url: '/pages/card/card' })
    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 openNews = (newsId: string) => uni.navigateTo({ url: '/pages/news-detail/news-detail?newsId=' + encodeURIComponent(newsId) })
    onPullDownRefresh(() => {
        loadHome()
        setTimeout(() => uni.stopPullDownRefresh(), 300)
    })
    const handleScroll = (event: any) => {
        const shouldShow = event.detail.scrollTop > 64
        if (isStickyNav.value != shouldShow) {
            isStickyNav.value = shouldShow
        }
    }
</script>
 
<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%); }
    .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; }
    .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; }
 
    .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-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); }
 
    .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; }
    .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-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; }
    /* 人才资讯:提高标题与时间的阅读层级 */
    .featured-copy text:first-child { font-size: 28rpx; }
    .featured-copy text:last-child { font-size: 22rpx; }
    .news-name { font-size: 30rpx; line-height: 42rpx; }
    .news-meta { margin-top: 14rpx; font-size: 22rpx; }
    .new-badge { font-size: 20rpx; }
</style>