yeg
2026-09-28 85bfb9351803d97a33329d507f30a2f78bf1fc75
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
<template>
    <view class="page">
        <view class="page-header">
            <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" mode="aspectFit"></image></view>
                <text class="nav-title">解除绑定</text>
                <view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
            </view>
        </view>
 
        <scroll-view class="content" scroll-y="true" show-scrollbar="false">
            <view class="content-inner">
                <view v-if="listLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载已绑定家属</text></view>
                <view v-else-if="listError" class="list-state error-state"><text>家属列表加载失败</text><text class="retry-text" @tap="loadMembers">重新加载</text></view>
                <view v-else-if="listLoaded && familyMembers.length == 0" class="list-state"><text>暂无可解绑家属</text></view>
                <view class="member-card" v-for="member in familyMembers" :key="member.id" hover-class="member-card-active" @tap="toggleMember(member.id)">
                    <view class="member-copy"><text class="member-title">已绑定家属</text><view class="member-info"><text>{{member.name}}</text><text>{{member.mobile}}</text><text class="relation-mark">♡</text><text>{{member.relationship}}</text></view></view>
                    <view class="select-circle" :class="isSelected(member.id) ? 'selected' : ''"><text v-if="isSelected(member.id)">✓</text></view>
                </view>
                <view v-if="listLoaded && familyMembers.length > 0" class="unbind-button" hover-class="unbind-button-active" @tap="submitUnbind"><text>解除绑定</text></view>
                <view class="safe-bottom"></view>
            </view>
        </scroll-view>
    </view>
</template>
 
<script setup lang="uts">
    import { apiEnvironment } from '../../config/api.uts'
    import { getFamilyCards, getTalentCardProfile, unbindFamilyCards } from '../../services/portal.uts'
    import { safeBack } from '../../utils/navigation.uts'
 
    const navTop = ref(28)
    const navHeight = ref(32)
    const navWidth = ref(88)
    const navRight = ref(10)
    const selectedIds = ref<string[]>([])
    const isSubmitting = ref(false)
    const familyMembers = ref<any[]>([])
    const listLoading = ref(true)
    const listLoaded = ref(false)
    const listError = ref(false)
    const talentId = ref('')
    const talentCardId = ref('')
 
    // 模板上的返回按钮一直挂着 @tap="goBack" 却没定义处理函数,点了没有任何反应。
    const goBack = () => safeBack('/pages/family-card/family-card')
 
    onLoad((options: any) => {
        // #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
        talentId.value = options != null && options['talentId'] != null ? options['talentId'] as string : (apiEnvironment == 'development' ? '2' : '')
        resolveTalentCardId()
    })
    const loadMembers = () => {
        listLoading.value = true
        listLoaded.value = false
        listError.value = false
        if (talentCardId.value == '') { listLoading.value = false; listError.value = true; return }
        getFamilyCards(talentCardId.value, (data: any) => {
            const items = (data as any[]) || []
            familyMembers.value = items.filter((item: any) => item.status === 1).map((item: any) => ({
                id: '' + item.id,
                name: item.dependentName != null ? item.dependentName : '',
                mobile: item.dependentMobile != null ? item.dependentMobile : '',
                relationship: item.relation != null ? item.relation : ''
            }))
            selectedIds.value = selectedIds.value.filter((id: string) => familyMembers.value.some((item: any) => item.id == id))
            listLoading.value = false
            listLoaded.value = true
        }, () => { listLoading.value = false; listError.value = true })
    }
 
    const resolveTalentCardId = () => {
        if (talentId.value == '') { listLoading.value = false; listError.value = true; return }
        getTalentCardProfile(talentId.value, (data: any) => {
            const card = data != null ? data.card : null
            talentCardId.value = card != null && card.id != null ? '' + card.id : ''
            if (talentCardId.value == '') { listLoading.value = false; listError.value = true; return }
            loadMembers()
        }, () => { listLoading.value = false; listError.value = true })
    }
    const isSelected = (id: string) => selectedIds.value.indexOf(id) >= 0
    const toggleMember = (id: string) => {
        const next = selectedIds.value.slice()
        const index = next.indexOf(id)
        if (index >= 0) next.splice(index, 1)
        else next.push(id)
        selectedIds.value = next
    }
    const submitUnbind = () => {
        if (isSubmitting.value) return
        if (selectedIds.value.length == 0) {
            uni.showToast({ title: '请选择需要解绑的家属', icon: 'none' })
            return
        }
        uni.showModal({
            title: '解除绑定',
            content: '确认解除所选家属卡绑定吗?解除后相关权益将不可继续使用。',
            confirmText: '解除绑定',
            confirmColor: '#FF5935',
            success: (result) => {
                if (!result.confirm) return
                if (isSubmitting.value) return
                isSubmitting.value = true
                unbindSelected(selectedIds.value.slice())
            }
        })
    }
    const unbindSelected = (ids: string[]) => {
        unbindFamilyCards(ids, talentCardId.value, () => { isSubmitting.value = false; uni.showToast({ title: '已解除绑定', icon: 'success' }); setTimeout(() => safeBack('/pages/family-card/family-card'), 700) }, (error) => { isSubmitting.value = false; uni.showToast({ title: error.message, icon: 'none' }) })
    }
</script>
 
<style>
    .page { display: flex; width: 100%; height: 100vh; background-color: #FFFFFF; background-image: linear-gradient(180deg, #FFF0D7 0%, #FFF4E9 205rpx, #FFFFFF 420rpx); flex-direction: column; }
    .page-header { position: relative; z-index: 10; flex-shrink: 0; }
    .safe-space { height: 28px; }
    .nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 20rpx; padding-right: 10px; position: relative; flex-direction: row; align-items: center; justify-content: space-between; }
    .back-button { display: flex; width: 70rpx; height: 64rpx; margin-left: -4rpx; justify-content: center; align-items: center; }
    .back-button image { width: 36rpx; height: 36rpx; }
    .back-button-active { opacity: .55; }
    .nav-title { position: absolute; left: 50%; top: 50%; color: #171B22; font-family: sans-serif; font-size: 37rpx; line-height: 48rpx; 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; }
    .content-inner { padding: 91rpx 57rpx 0; }
    .list-state { display:flex; height:420rpx; flex-direction:row; justify-content:center; align-items:center; color:#9199A5; font-size:25rpx; }
    .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:member-loading 720ms linear infinite; }
    @keyframes member-loading { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
    .member-card { display: flex; min-height: 157rpx; margin-bottom: 24rpx; box-sizing: border-box; padding: 27rpx 30rpx 27rpx 39rpx; border-radius: 22rpx; background-color: #FFFFFF; flex-direction: row; align-items: center; box-shadow: 0 9rpx 28rpx rgba(69,80,94,.055); }
    .member-card-active { background-color: #FFF9F5; }
    .member-copy { min-width: 0; flex: 1; }
    .member-title { display: block; color: #26303A; font-size: 27rpx; line-height: 38rpx; font-weight: 700; }
    .member-info { display: flex; margin-top: 16rpx; color: #788290; font-size: 23rpx; line-height: 32rpx; flex-direction: row; align-items: center; }
    .member-info text + text { margin-left: 17rpx; }
    .member-info .relation-mark { margin-left: 26rpx; color: #9BA5B1; font-size: 26rpx; }
    .select-circle { display: flex; width: 40rpx; height: 40rpx; margin-left: 20rpx; flex-shrink: 0; box-sizing: border-box; border-width: 2rpx; border-style: solid; border-color: #D4DAE1; border-radius: 20rpx; justify-content: center; align-items: center; }
    .select-circle.selected { border-color: #FF6330; background-color: #FF6330; }
    .select-circle text { color: #FFFFFF; font-size: 25rpx; line-height: 28rpx; font-weight: 700; }
    .unbind-button { display: flex; height: 84rpx; margin-top: 46rpx; border-radius: 44rpx; background-image: linear-gradient(90deg, #FF4E35 0%, #FF8318 100%); justify-content: center; align-items: center; }
    .unbind-button text { color: #FFFFFF; font-size: 28rpx; line-height: 40rpx; font-weight: 700; }
    .unbind-button-active { opacity: .8; transform: scale(.99); }
    .safe-bottom { height: 40rpx; height: calc(40rpx + env(safe-area-inset-bottom)); }
</style>