shinnosuke
2026-09-10 65b57875700bf42ebd17eafa01a265e5690ce271
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
<template>
    <view class="page">
        <image class="hero-image" :src="heroImage" mode="aspectFill" @error="replaceHero"></image>
        <view class="hero-shade"></view>
        <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" @tap="goBack"><image src="/static/food-assets/arrow-left-white.svg" mode="aspectFit"></image></view>
                <view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
            </view>
        </view>
 
        <view class="content-sheet">
            <view class="title-block"><text class="page-title">{{benefitTitle}}</text><view v-if="applicableCards != ''" class="talent-tag"><text>{{applicableCards}}</text></view></view>
            <view class="tab-bar">
                <view class="tab" :class="activeTab == 0 ? 'active' : ''" @tap="switchTab(0)">
                    <text>使用说明</text><view class="tab-line"></view>
                </view>
                <view class="tab" :class="activeTab == 1 ? 'active' : ''" @tap="switchTab(1)">
                    <text>学校地址</text><view class="tab-line"></view>
                </view>
            </view>
            <view class="tab-content">
                <view v-if="activeTab == 0" class="desc-card"><view v-if="benefitLoading" class="benefit-state"><view class="loading-spinner"></view><text>正在加载使用说明</text></view><view v-else-if="benefitError" class="benefit-state error-state"><text>使用说明加载失败</text><text class="retry-text" @tap="loadBenefit">重新加载</text></view><text v-else>{{description}}</text></view>
                <scroll-view v-else class="school-scroll" scroll-y="true" :show-scrollbar="false">
                    <view v-if="schoolLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载学校信息</text></view>
                    <view v-else-if="schoolError" class="list-state error-state"><text>学校信息加载失败</text><text class="retry-text" @tap="loadSchools">重新加载</text></view>
                    <view v-else-if="schoolLoaded && schools.length == 0" class="list-state"><text>暂无适用学校</text></view>
                    <view v-if="schoolLoaded && schools.length > 0" class="school-panel">
                        <view class="school-table-header"><view class="school-head-name"><text>学校名称</text></view><view class="school-head-action"></view></view>
                    <view class="school-row" v-for="item in schools" :key="item.id">
                        <view :class="item.detailAvailable ? 'school-name-cell' : 'school-name-cell is-disabled'" hover-class="school-name-cell-active" @tap="openSchoolDetail(item)"><text class="school-name-text">{{item.name}}</text></view>
                        <view class="school-actions">
                            <view :class="canNavigate(item) ? 'school-action' : 'school-action is-disabled'" @tap="openLocation(item)"><image src="/static/premium-assets/school-location.svg" mode="aspectFit"></image></view>
                            <view v-if="item.phone != ''" class="school-action" @tap="callSchool(item)"><image src="/static/premium-assets/school-phone.svg" mode="aspectFit"></image></view>
                        </view>
                    </view>
                    </view>
                    <view class="school-bottom-space"></view>
                </scroll-view>
            </view>
        </view>
 
        <view class="bottom-actions">
            <view class="btn-outline" hover-class="btn-outline-active" @tap="showTalentCode">
                <image src="/static/premium-assets/qr-code.png" mode="aspectFit"></image><text>出示人才码</text>
            </view>
            <view class="btn-fill" hover-class="btn-fill-active" @tap="goChildrenInfo">
                <image src="/static/premium-assets/children-info.svg" mode="aspectFit"></image><text>子女信息</text>
            </view>
        </view>
    </view>
</template>
 
<script setup lang="uts">
    import { getChildrenEntrustmentBenefit, getChildrenEntrustmentSchools } from '../../services/portal.uts'
    import { apiEnvironment } from '../../config/api.uts'
    import { resolveApiUrl } from '../../services/http.uts'
    import { getSession } from '../../services/session.uts'
    import { safeBack } from '../../utils/navigation.uts'
 
    const navTop = ref(28)
    const navHeight = ref(32)
    const navWidth = ref(88)
    const navRight = ref(10)
    const activeTab = ref(0)
    const serviceId = ref('')
    const talentId = ref('')
    const heroImage = ref('')
    const benefitTitle = ref('子女托管')
    const applicableCards = ref('')
    const description = ref('')
    const benefitLoading = ref(true)
    const benefitError = ref(false)
    const schools = ref<any[]>([])
    const schoolLoading = ref(true)
    const schoolLoaded = ref(false)
    const schoolError = ref(false)
 
    onLoad((options: any) => {
        // #ifdef MP-WEIXIN
        const capsule = uni.getMenuButtonBoundingClientRect()
        const info = uni.getSystemInfoSync()
        navTop.value = capsule.top
        navHeight.value = capsule.height
        navWidth.value = capsule.width
        navRight.value = info.windowWidth - capsule.right
        // #endif
        serviceId.value = options != null && options['serviceId'] != null ? options['serviceId'] as string : ''
        const session = getSession()
        if (session != null && session.talentId != null) talentId.value = session.talentId as string
        if (talentId.value == '' && apiEnvironment == 'development') talentId.value = '2'
        loadBenefit()
        loadSchools()
    })
    const loadBenefit = () => {
        if (serviceId.value == '') { benefitLoading.value = false; benefitError.value = true; return }
        benefitLoading.value = true
        benefitError.value = false
        getChildrenEntrustmentBenefit(serviceId.value, talentId.value, (data: any) => {
            benefitTitle.value = data.title || '子女托管'
            applicableCards.value = data.applicableCards != null ? data.applicableCards as string : ''
            heroImage.value = data.heroImage != null && data.heroImage != '' ? resolveApiUrl(data.heroImage as string) : ''
            description.value = data.rightsText || '暂无使用说明'
            benefitLoading.value = false
        }, () => { benefitLoading.value = false; benefitError.value = true })
    }
    const loadSchools = () => {
        schoolLoading.value = true
        schoolLoaded.value = false
        schoolError.value = false
        if (serviceId.value == '') { schoolLoading.value = false; schoolError.value = true; return }
        getChildrenEntrustmentSchools(serviceId.value, (data: any) => { schools.value = data instanceof Array ? data as any[] : []; schoolLoading.value = false; schoolLoaded.value = true }, () => { schoolLoading.value = false; schoolError.value = true })
    }
    const goBack = () => safeBack('/pages/premium-benefits/premium-benefits')
    const switchTab = (index: number) => { activeTab.value = index }
    const replaceHero = () => { heroImage.value = '' }
    const openSchoolDetail = (item: any) => {
        if (item.detailAvailable !== true) { uni.showToast({ title: '学校介绍暂未开放', icon: 'none' }); return }
        uni.navigateTo({ url: '/pages/school-detail/school-detail?serviceId=' + encodeURIComponent(serviceId.value) + '&id=' + encodeURIComponent('' + item.id) + '&name=' + encodeURIComponent(item.name) + '&address=' + encodeURIComponent(item.address) })
    }
    const canNavigate = (item: any): boolean => Number(item.latitude) > 0 && Number(item.longitude) > 0
    const openLocation = (item: any) => { if (!canNavigate(item)) { uni.showToast({ title: '学校暂未配置位置', icon: 'none' }); return }; uni.openLocation({ latitude: Number(item.latitude), longitude: Number(item.longitude), name: item.name, address: item.address, scale: 18, fail: () => uni.showToast({ title: '暂时无法打开地图导航', icon: 'none' }) }) }
    const callSchool = (item: any) => { if (item.phone == null || item.phone == '') return; uni.makePhoneCall({ phoneNumber: item.phone, fail: () => uni.showToast({ title: '暂时无法拨打电话', icon: 'none' }) }) }
    const showTalentCode = () => { const session = getSession(); const talentId = session != null && session.talentId != null ? session.talentId as string : (apiEnvironment == 'development' ? '2' : ''); uni.navigateTo({ url: '/pages/talent-code/talent-code?talentId=' + encodeURIComponent(talentId) + '&merchantId=' + encodeURIComponent(serviceId.value) + '&scene=children' }) }
    const goChildrenInfo = () => uni.navigateTo({ url: '/pages/children-info/children-info' })
</script>
 
<style>
    .page { width:100%; height:100vh; position:relative; overflow:hidden; background-color:#F5F5F5; }
    .hero-image { position:absolute; left:0; right:0; top:0; width:100%; height:425rpx; background-color:#D4C8BE; }
    .hero-shade { position:absolute; z-index:1; left:0; right:0; top:0; height:210rpx; background-image:linear-gradient(180deg,rgba(0,0,0,.45),rgba(0,0,0,0)); }
    .custom-nav { position:absolute; z-index:10; left:0; right:0; top:0; }
    .safe-space { height:28px; }
    .nav-row { display:flex; box-sizing:border-box; padding-left:24rpx; flex-direction:row; align-items:center; justify-content:space-between; }
    .back-button { display:flex; width:54rpx; height:54rpx; border-radius:50%; background-color:rgba(45,45,45,.48); justify-content:center; align-items:center; }
    .back-button image { width:38rpx; height:38rpx; }
    .capsule-space { height:100%; flex-shrink:0; }
    .content-sheet { display:flex; position:absolute; z-index:3; left:0; right:0; top:405rpx; bottom:128rpx; bottom:calc(128rpx + env(safe-area-inset-bottom)); border-radius:30rpx 30rpx 0 0; background-color:#F5F5F5; flex-direction:column; }
    .title-block { display:flex; box-sizing:border-box; padding:28rpx 32rpx 0; flex-direction:column; align-items:flex-start; flex-shrink:0; }
    .page-title { color:#252525; font-size:35rpx; line-height:48rpx; font-weight:700; }
    .talent-tag { display:flex; height:42rpx; margin-top:13rpx; box-sizing:border-box; padding:0 14rpx; border:1rpx solid #FFB98E; border-radius:5rpx; justify-content:center; align-items:center; }
    .talent-tag text { color:#D87842; font-size:23rpx; line-height:30rpx; }
    .tab-bar { display:flex; box-sizing:border-box; padding:0 32rpx; flex-direction:row; align-items:flex-start; flex-shrink:0; }
    .tab { display:flex; margin-right:48rpx; padding-top:24rpx; flex-direction:column; align-items:center; flex-shrink:0; }
    .tab text { color:#9A9A9A; font-size:30rpx; line-height:42rpx; font-weight:500; }
    .tab.active text { color:#252525; font-weight:600; }
    .tab-line { width:0; height:7rpx; margin-top:10rpx; border-radius:4rpx; background-image:linear-gradient(90deg,#FF4C56,#FFD43A); transition:width 180ms ease-out; }
    .tab.active .tab-line { width:61rpx; }
    .tab-content { display:flex; min-height:0; flex:1; flex-direction:column; }
    .desc-card { display:flex; height:650rpx; margin:20rpx 26rpx 0; box-sizing:border-box; padding:28rpx 32rpx; border-radius:15rpx; background-color:#FFFFFF; flex-direction:column; flex-shrink:0; }
    .desc-card text { color:#727983; font-size:25rpx; line-height:40rpx; }
    .benefit-state { display:flex; min-height:0; flex:1; color:#9299A4; font-size:24rpx; justify-content:center; align-items:center; }.benefit-state.error-state { flex-direction:column; }
    .school-scroll { min-height:0; flex:1; }
    .list-state { display:flex; height:360rpx; flex-direction:row; justify-content:center; align-items:center; color:#9299A4; font-size:24rpx; }
    .error-state { flex-direction:column; }
    .retry-text { margin-top:20rpx; 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:school-loading 720ms linear infinite; }
    @keyframes school-loading { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
    .school-panel { margin: 16rpx 36rpx 0; overflow: hidden; border-width: 2rpx; border-style: solid; border-color: #DCE7FA; border-radius: 16rpx; background-color: #FFFFFF; }
    .school-table-header { display: flex; min-height: 67rpx; background-color: #EEF4FF; flex-direction: row; align-items: center; }
    .school-head-name { display: flex; min-width: 0; height: 67rpx; flex: 1; box-sizing: border-box; padding: 0 22rpx; justify-content: flex-start; align-items: center; }
    .school-head-action { width: 126rpx; height: 67rpx; flex-shrink: 0; }
    .school-head-name text { display:block; width:100%; height:67rpx; color:#26374F; font-size:23rpx; line-height:67rpx; font-weight:700; text-align:left; }
    .school-row { display: flex; height: 92rpx; min-height: 92rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #E7ECF4; flex-direction: row; align-items: center; }
    .school-name-cell { display: flex; min-width: 0; height: 92rpx; flex: 1; box-sizing: border-box; padding: 0 22rpx; align-items: center; justify-content: flex-start; }
    .school-name-text { display:block; width:100%; min-width:0; height:92rpx; flex:1; color:#48566A; font-size:21rpx; line-height:92rpx; text-align:left; font-weight:600; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
    .school-name-cell-active { background-color:#F5F8FD; }
    .school-actions { display: flex; width: 126rpx; height: 92rpx; flex-direction: row; align-items: center; justify-content: space-evenly; flex-shrink: 0; }
    .school-action { display: flex; width: 46rpx; height: 46rpx; box-sizing: border-box; border-width: 2rpx; border-style: solid; border-color: #B7D6FF; border-radius: 50%; background-color: #F1F7FF; justify-content: center; align-items: center; }
    .school-action image { width: 27rpx; height: 27rpx; }
    .is-disabled { opacity:.42; }
    .school-bottom-space { height:calc(30rpx + env(safe-area-inset-bottom)); }
    .bottom-actions { display:flex; position:absolute; z-index:8; left:0; right:0; bottom:0; height:128rpx; height:calc(128rpx + env(safe-area-inset-bottom)); box-sizing:border-box; padding:17rpx 31rpx 13rpx; padding-bottom:calc(13rpx + env(safe-area-inset-bottom)); background-color:#FFFFFF; flex-direction:row; justify-content:space-between; align-items:flex-start; }
    .btn-outline { display:flex; flex:1; height:82rpx; margin-right:16rpx; box-sizing:border-box; border-radius:43rpx; background-image:linear-gradient(90deg,#FF4B32,#FF7E21); flex-direction:row; justify-content:center; align-items:center; }
    .btn-outline image { width:32rpx; height:32rpx; margin-right:10rpx; }
    .btn-outline text { color:#FFFFFF; font-size:28rpx; font-weight:600; }
    .btn-outline-active { opacity:.7; }
    .btn-fill { display:flex; flex:1; height:82rpx; margin-left:16rpx; border-radius:43rpx; background-image:linear-gradient(90deg,#FF4B32,#FF8A1E); flex-direction:row; justify-content:center; align-items:center; }
    .btn-fill image { width:32rpx; height:32rpx; margin-right:10rpx; }
    .btn-fill text { color:#FFFFFF; font-size:28rpx; font-weight:600; }
    .btn-fill-active { opacity:.8; transform:scale(.99); }
</style>