wenminjie
2026-08-06 8898f00c2150048a03ca561b7ed98dbf9c80732b
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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
<template>
    <view class="page">
        <view class="custom-nav" :class="(isSolidNav ? 'is-solid ' : '') + (isHotelMerchant ? 'is-hotel' : '')">
            <view class="custom-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="back-button" @tap="goBack"><image src="/static/food-assets/arrow-left-white.svg"></image></view>
                <text class="nav-title">{{merchantName}}</text>
                <view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
            </view>
        </view>
 
        <scroll-view :class="'content' + (isMedicalMerchant ? ' content-full' : '')" scroll-y @scroll="handleScroll">
            <view class="hero">
                <image class="hero-image" :src="merchantHero" mode="aspectFill" @error="replaceHeroImage"></image>
                <view class="hero-shade"></view>
            </view>
 
            <view class="merchant-panel">
                <view v-if="merchantLoading" class="merchant-detail-state"><view class="loading-spinner"></view><text>正在加载商户详情</text></view>
                <view v-else-if="merchantError" class="merchant-detail-state error-state"><text>商户详情加载失败</text><text class="retry-text" @tap="retryMerchantDetail">重新加载</text></view>
                <view v-else class="merchant-info-card">
                    <view v-if="!merchantAvailable" class="merchant-unavailable"><text>{{merchantUnavailableReason}}</text></view>
                    <view v-if="isParkingMerchant" class="parking-title-block">
                        <view class="parking-title-row"><text class="merchant-title">{{merchantName}}</text><view class="round-action" @tap="callMerchant"><image src="/static/merchant-assets/phone.svg"></image></view></view>
                        <text class="parking-free">{{parkingBenefitText}}</text>
                    </view>
                    <view v-else-if="isMedicalMerchant" class="merchant-title-row">
                        <text class="merchant-title">{{merchantName}}</text>
                        <text class="medical-free-tag">免费</text>
                    </view>
                    <view v-else class="merchant-title-row">
                        <text class="merchant-title">{{merchantName}}</text>
                        <view class="merchant-tags"><text class="coupon-tag">{{merchantPrimaryTag}}</text><text v-if="merchantDiscount != ''" class="discount-tag">{{merchantDiscount}}</text></view>
                    </view>
                    <view class="service-tags"><text v-if="!isParkingMerchant && !isMedicalMerchant">{{merchantServiceLabel}}</text><text>{{applicableCards}}</text></view>
                    <view v-if="!isParkingMerchant" class="merchant-address-row">
                        <image class="address-icon" src="/static/benefits-assets/map-pin.svg"></image>
                        <text>{{merchantAddress}}</text>
                        <view class="merchant-actions"><view :class="merchantLocation.latitude > 0 && merchantLocation.longitude > 0 ? 'round-action' : 'round-action is-disabled'" @tap="openMap"><image src="/static/merchant-assets/navigation.svg"></image></view><view :class="merchantPhone != '' ? 'round-action' : 'round-action is-disabled'" @tap="callMerchant"><image src="/static/merchant-assets/phone.svg"></image></view></view>
                    </view>
                </view>
 
                <view v-if="!merchantError" class="tabs">
                    <view class="tab-item" :class="currentTab == 'primary' ? 'active' : ''" @tap="changeTab('primary')"><text>{{isMedicalMerchant ? '预约' : (groupBuyingEnabled ? '消费购买' : '使用说明')}}</text><view class="tab-line"></view></view>
                    <view class="tab-item" :class="currentTab == 'rights' ? 'active' : ''" @tap="changeTab('rights')"><text>{{isParkingMerchant ? '停车场地址' : (isTransportMerchant ? '适用门店' : '权益详情')}}</text><view class="tab-line"></view></view>
                </view>
 
                <view v-if="!merchantError && currentTab == 'primary'">
                    <view v-if="isMedicalMerchant" class="medical-package-list">
                        <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="loadMerchantLists">重新加载</text></view>
                        <view v-else-if="listLoaded && visibleMedicalPackages.length == 0" class="list-state"><text>暂无可预约体检套餐</text></view>
                        <view class="medical-package-item" v-for="item in visibleMedicalPackages" :key="item.id" hover-class="package-item-active" @tap="openMedicalPackage(item)">
                            <image class="medical-package-image" :src="item.image" mode="aspectFill" @error="replaceImage(item)"></image>
                            <view class="medical-package-info">
                                <text class="medical-package-name">{{item.name}}</text>
                                <view class="medical-package-bottom">
                                    <view class="medical-open-row"><text class="medical-open-label">开放时间</text><text class="medical-open-value">{{item.openTime}}</text></view>
                                    <view class="medical-view-button"><text>查看</text></view>
                                </view>
                            </view>
                        </view>
                    </view>
                    <view v-else-if="groupBuyingEnabled" class="package-list">
                        <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="loadMerchantLists">重新加载</text></view>
                        <view v-else-if="listLoaded && visiblePackages.length == 0" class="list-state"><text>{{isHotelMerchant ? '暂无可预约房型' : '暂无可购买套餐'}}</text></view>
                        <view class="package-item" :class="item.available === false ? 'is-sold-out' : ''" v-for="item in visiblePackages" :key="item.id" hover-class="package-item-active" @tap="handlePackage(item)">
                            <image class="package-image" :src="item.image" mode="aspectFill" @error="replaceImage(item)"></image>
                            <view class="package-info">
                                <text class="package-name">{{item.name}}</text>
                                <view class="package-tags"><text class="coupon-tag">券</text><text class="discount-tag">{{item.discount}}</text></view>
                                <view class="package-bottom"><view class="price-row"><text class="price-symbol">¥</text><text class="price">{{item.price}}</text><text v-if="!isHotelMerchant" class="old-price">¥{{item.originalPrice}}</text></view><view class="buy-button"><text>{{item.available === false ? '已售罄' : (isHotelMerchant ? '预约' : '购买')}}</text></view></view>
                            </view>
                        </view>
                    </view>
                    <view v-else class="usage-panel"><text>{{usageInstructions}}</text></view>
                </view>
                <view v-else-if="!merchantError && isTransportMerchant" class="store-panel">
                    <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="loadMerchantLists">重新加载</text></view>
                    <view v-else-if="listLoaded && applicableStores.length == 0" class="list-state"><text>暂无适用门店</text></view>
                    <view v-if="listLoaded && applicableStores.length > 0" class="store-table-header"><view class="store-head-name"><text>适用门店名称</text></view><view class="store-head-address"><text>详细地址</text></view><view class="store-head-action"></view></view>
                    <view class="store-row" v-for="item in applicableStores" :key="item.id">
                        <view class="store-name-cell"><text>{{item.name}}</text></view>
                        <view class="store-address-cell"><text>{{item.address}}</text></view>
                        <view :class="item.canNavigate === false ? 'store-location is-disabled' : 'store-location'" @tap="openStoreLocation(item)"><image src="/static/merchant-assets/store-pin-blue.svg" mode="aspectFit"></image></view>
                    </view>
                </view>
                <view v-else-if="!merchantError && isMedicalMerchant" class="medical-usage-panel"><text>{{usageInstructions}}</text></view>
                <view v-else-if="!merchantError" class="rights-panel">
                    <text class="rights-title">人才卡专属权益</text>
                    <text class="rights-text">{{merchantRightsText}}</text>
                    <view class="rule-row"><text>适用卡种</text><text>{{applicableCards}}</text></view>
                    <view class="rule-row"><text>使用方式</text><text>到店出示人才码</text></view>
                </view>
            </view>
            <view class="content-bottom"></view>
        </scroll-view>
 
        <view v-if="!isMedicalMerchant && merchantAvailable" class="action-bar">
            <view v-if="isParkingMerchant && parkingActionMode == 'talentCode'" class="talent-code-button" @tap="showBottomVoucher"><image src="/static/merchant-assets/qr-code.svg"></image><text>出示人才码</text></view>
            <view v-else-if="isParkingMerchant" class="talent-code-button" @tap="openPlatePage"><text>填写车牌</text></view>
            <view v-else-if="isTransportMerchant" class="talent-code-button" @tap="openMobileDialog"><text>填写手机号</text></view>
            <view v-else class="talent-code-button" @tap="showBottomVoucher"><image :src="hasFreeVoucher ? '/static/merchant-assets/coupon.svg' : '/static/merchant-assets/qr-code.svg'"></image><text>{{hasFreeVoucher ? '出示免费券' : '出示人才码'}}</text></view>
        </view>
 
        <view v-if="showMobileDialog" class="mobile-dialog-mask" @tap="closeMobileDialog">
            <view class="mobile-dialog" @tap.stop>
                <view class="mobile-dialog-close" @tap="closeMobileDialog"><text>×</text></view>
                <text class="mobile-dialog-title">填写手机号</text>
                <text class="mobile-dialog-desc">提交后工作人员将根据加油权益与您联系</text>
                <view class="mobile-input-box"><input v-model="mobile" type="number" maxlength="11" placeholder="请输入手机号" placeholder-class="mobile-placeholder" /></view>
                <text v-if="mobileError != ''" class="mobile-error">{{mobileError}}</text>
                <view class="mobile-submit" @tap="submitMobile"><text>提交</text></view>
            </view>
        </view>
 
    </view>
</template>
 
<script setup lang="uts">
    import { apiEnvironment, isApiConfigured } from '../../config/api.uts'
    import { getBenefitQuota, getFuelMerchantDetail, getMedicalPackages, getMerchantDetail, getParkingMerchantDetail, getProducts, submitFuelMobileRegistration } from '../../services/portal.uts'
    import { resolveApiUrl } from '../../services/http.uts'
    import { getSession } from '../../services/session.uts'
    import { safeBack } from '../../utils/navigation.uts'
    import { submitBusiness } from '../../services/business.uts'
 
    const navTop = ref(28)
    const navHeight = ref(32)
    const navWidth = ref(88)
    const navRight = ref(10)
    const isSolidNav = ref(false)
    const currentTab = ref('primary')
    const isHotelMerchant = ref(false)
    const isTransportMerchant = ref(false)
    const isFuelMerchant = ref(false)
    const isParkingMerchant = ref(false)
    const isMedicalMerchant = ref(false)
    // 后台停车权益配置:plate=填写车牌,talentCode=出示人才码。
    const parkingActionMode = ref('plate')
    // 后台车牌表单配置:withParking=需搜索停车场,simple=仅填写车牌。
    const parkingPlateMode = ref('simple')
    const showMobileDialog = ref(false)
    const mobile = ref('')
    const mobileError = ref('')
    const isSubmittingMobile = ref(false)
    const hasFreeVoucher = ref(false)
    const merchantLoading = ref(false)
    const merchantError = ref(false)
    const merchantAvailable = ref(true)
    const merchantUnavailableReason = ref('该商户已停业或下架,暂不可购买和核销')
    const merchantPhone = ref('08123335555')
    const merchantId = ref('merchant-food-default')
    const merchantType = ref('food')
    const merchantName = ref('百杏厨房(中环天地店)')
    const merchantHero = ref('')
    const fallbackImage = ''
    const replaceHeroImage = () => { merchantHero.value = '' }
    const replaceImage = (item: any) => { if (item.image != '') item.image = '' }
    const merchantPrimaryTag = ref('券')
    const merchantDiscount = ref('8.8折')
    const merchantAddress = ref('四川省攀枝花市东区炳三区新福路20号中环天地负一层')
    const merchantServiceLabel = ref('到店消费')
    const applicableCards = ref('卓越卡可用')
    const fuelBenefitId = ref('')
    const parkingBenefitId = ref('')
    const parkingBenefitText = ref('免费')
    const talentId = ref('')
    const fuelRegistrationKey = ref('')
    const merchantRightsText = ref('持卓越人才卡到店消费可享指定菜品及套餐 8.8 折优惠,具体可用时段、适用门店和使用规则以商户现场说明为准。')
    // 后台商户配置:false 时不展示套餐团购,主标签切换为“使用说明”。
    const groupBuyingEnabled = ref(true)
    const usageInstructions = ref('所有菜品8.8折优惠;就餐后亮码,商家扫码确认订单后在本小程序中完成支付。')
    const merchantLocation = ref({
        latitude: 26.5856,
        longitude: 101.7184,
        name: '百杏厨房(中环天地店)',
        address: '四川省攀枝花市东区炳三区新福路20号中环天地负一层'
    })
    const allApplicableStores = [
        { id: 1, name: '盐源居小区停车场', address: '盐华路38号2栋', latitude: 26.5856, longitude: 101.7184 },
        { id: 2, name: '东林巷停车场', address: '东林巷29号1栋、2栋、3栋、东林巷31号1栋、2栋', latitude: 26.5862, longitude: 101.7191 },
        { id: 3, name: '东区政府第2号停车场', address: '东阳巷19号', latitude: 26.5849, longitude: 101.7177 },
        { id: 4, name: '东区政府第3号停车场', address: '原环保局办公楼', latitude: 26.5844, longitude: 101.7169 },
        { id: 5, name: '东区政府第4号停车场', address: '东阳巷8号', latitude: 26.5838, longitude: 101.7180 },
        { id: 6, name: '东区机关食堂外停车场', address: '东阳巷23号旁红砖房', latitude: 26.5832, longitude: 101.7190 },
        { id: 7, name: '攀枝花大道558号停车场', address: '攀枝花大道中段558号6栋', latitude: 26.5827, longitude: 101.7201 },
        { id: 8, name: '五十四交通银行旁停车场', address: '供销社背后金星巷3号', latitude: 26.5819, longitude: 101.7210 },
        { id: 9, name: '五十四供销社幼儿园停车场', address: '供销社背后金星巷3号', latitude: 26.5812, longitude: 101.7218 }
    ]
    const applicableStores = ref<any[]>([])
 
    const packages = ref([
        { id: 'food-set-001', name: '【川味盛宴】川菜12人餐', discount: '6折', price: '138', originalPrice: '266', image: '/static/food-assets/food-1.jpg' },
        { id: 'food-set-002', name: '【川香四溢】川菜4人餐', discount: '6折', price: '118', originalPrice: '198', image: '/static/food-assets/food-2.jpg' },
        { id: 'food-set-003', name: '【欢聚时光】招牌菜6人餐', discount: '6.8折', price: '168', originalPrice: '248', image: '/static/food-assets/food-1.jpg' },
        { id: 'food-set-004', name: '【暖心小聚】特色菜双人餐', discount: '7折', price: '88', originalPrice: '126', image: '/static/food-assets/food-2.jpg' },
        { id: 'food-set-005', name: '【家庭聚餐】经典川菜8人餐', discount: '6.5折', price: '238', originalPrice: '366', image: '/static/food-assets/food-1.jpg' },
        { id: 'food-set-006', name: '【人气必点】招牌菜4人餐', discount: '6.8折', price: '128', originalPrice: '188', image: '/static/food-assets/food-2.jpg' },
        { id: 'food-set-007', name: '【周末欢聚】特色菜10人餐', discount: '6折', price: '298', originalPrice: '498', image: '/static/food-assets/food-1.jpg' },
        { id: 'food-set-008', name: '【一人食】暖心工作餐', discount: '8折', price: '39', originalPrice: '49', image: '/static/food-assets/food-2.jpg' }
    ])
    const hotelPackages = [
        { id: 101, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
        { id: 102, name: '山景云窗商务双床房', discount: '6折', price: '388-600', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
        { id: 103, name: '都市视野大床房', discount: '6折', price: '588-800', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
        { id: 104, name: '行政景观大床房', discount: '6折', price: '688-920', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
        { id: 105, name: '亲子主题家庭房', discount: '6.5折', price: '628-860', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
        { id: 106, name: '高层城景双床房', discount: '6折', price: '468-680', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' },
        { id: 107, name: '豪华行政套房', discount: '6.8折', price: '988-1280', originalPrice: '', image: '/static/stay-assets/hotel-hampton.jpg' },
        { id: 108, name: '舒适大床房', discount: '6折', price: '428-620', originalPrice: '', image: '/static/stay-assets/hotel-vienna.jpg' }
    ]
    // 体检医院演示套餐:图片按体验版素材规则复用“享”专题两张演示图。
    const medicalPackages = [
        { id: 1, name: 'A套餐(脑血管系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-hampton.jpg' },
        { id: 2, name: 'B套餐(心血管系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-vienna.jpg' },
        { id: 3, name: 'C套餐(消化系统筛查)', openTime: '5-6月', image: '/static/stay-assets/hotel-hampton.jpg' }
    ]
    const visiblePackages = ref<any[]>([])
    const visibleMedicalPackages = ref<any[]>([])
    const listLoading = ref(true)
    const listLoaded = ref(false)
    const listError = ref(false)
    const failOnce = ref(false)
    const forceEmpty = ref(false)
    let parkingDetailLoaded = false
 
    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
        if (options != null && options['previewState'] != null) {
            const previewState = options['previewState'] as string
            failOnce.value = previewState == 'initialError'
            forceEmpty.value = previewState == 'empty'
        }
        if (options != null && options['merchantId'] != null && options['merchantId'] != '') merchantId.value = options['merchantId'] as string
        if (options != null && options['name'] != null) {
            const name = decodeURIComponent(options['name'] as string)
            const routedMerchantType = options['merchantType'] != null ? decodeURIComponent(options['merchantType'] as string) : ''
            const routedImage = options['image'] != null ? decodeURIComponent(options['image'] as string) : ''
            const routedAddress = options['address'] != null ? decodeURIComponent(options['address'] as string) : ''
            const routedDiscount = options['discount'] != null ? decodeURIComponent(options['discount'] as string) : ''
            merchantType.value = routedMerchantType == '' ? 'food' : routedMerchantType
            if (options['action'] != null && options['action'] == 'talentCode') parkingActionMode.value = 'talentCode'
            if (options['plateMode'] != null && options['plateMode'] == 'withParking') parkingPlateMode.value = 'withParking'
            if (options['type'] == 'medical') {
                merchantType.value = 'medical'
                isMedicalMerchant.value = true
                merchantName.value = name
                merchantHero.value = ''
                merchantPrimaryTag.value = '免费'
                merchantDiscount.value = ''
                merchantAddress.value = '攀枝花市东区益康街34号4号综合3楼体检中心'
                merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: name, address: merchantAddress.value }
                groupBuyingEnabled.value = false
                usageInstructions.value = '①预约时间每年度5-6月,体检时间每年度6月1日-10月31日。\n②体检时请带上本人二代居民身份证。\n③检查前三天请注意休息,勿熬夜,以清淡饮食为宜,勿饮酒、忌高脂、高胆固醇食物、不吃保健品。检查前一日晚上十二点以后,请完全禁食(包括饮水)。\n④体检当天请着宽松衣物,不要穿连衣裙、连裤袜。做X线检查时,宜穿棉布内衣,勿穿带有金属纽扣的衣服、文胸;请摘去项链、手机、钢笔、钥匙等金属物品。\n⑤自费项目:体检当天,体检对象与体检医院沟通后确定,体检医院给予八折收费优惠。'
            } else if (options['type'] == 'transport') {
                merchantType.value = 'transport'
                isTransportMerchant.value = true
                isFuelMerchant.value = routedMerchantType == 'fuel' || name == '加油服务'
                if (routedMerchantType == 'parking' || name.indexOf('停车') >= 0) {
                    isParkingMerchant.value = true
                    merchantName.value = '停车服务'
                merchantHero.value = ''
                    merchantPrimaryTag.value = '免费'
                    merchantDiscount.value = ''
                    merchantAddress.value = '攀枝花市东区渡口公司管辖停车场'
                    merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: merchantName.value, address: merchantAddress.value }
                    groupBuyingEnabled.value = false
                    usageInstructions.value = '登记信息后可享受渡口公司管辖的49处停车场免费停车服务'
                } else {
                    merchantName.value = name == '加油服务' ? '中国石油东区新福路加油站' : name
                merchantHero.value = ''
                    merchantPrimaryTag.value = '券'
                    merchantDiscount.value = '9.4折'
                    merchantAddress.value = '四川省攀枝花市东区新福路20号附3-21号'
                    merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: merchantName.value, address: merchantAddress.value }
                    groupBuyingEnabled.value = false
                    usageInstructions.value = '持人才卡到店加油,出示人才码后按当日油价享受专属折扣;具体适用油品、活动库存和使用规则以加油站现场为准。'
                    merchantServiceLabel.value = '加油服务'
                    merchantRightsText.value = '持卓越人才卡到站加油并出示人才码,可享指定油品 9.4 折优惠,具体适用油品、优惠上限、可用时段和活动库存以加油站现场说明为准。'
                }
            } else if (routedMerchantType == 'hotel' || routedMerchantType == '酒店服务' || name.indexOf('酒店') >= 0) {
                isHotelMerchant.value = true
                merchantType.value = 'hotel'
                merchantName.value = name
                merchantHero.value = routedImage
                merchantDiscount.value = routedDiscount == '' ? '6折' : routedDiscount
                hasFreeVoucher.value = merchantDiscount.value.indexOf('免费') >= 0
                merchantPrimaryTag.value = hasFreeVoucher.value ? '券' : '惠'
                merchantAddress.value = routedAddress == '' ? '西区清香坪北街51号' : routedAddress
                merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: name, address: merchantAddress.value }
                packages.value = hotelPackages
                merchantServiceLabel.value = '住宿服务'
                merchantRightsText.value = '持卓越人才卡入住可享商户配置的住宿权益,具体房型、价格、可用日期和预约规则以酒店实际说明为准。'
            } else {
                merchantName.value = name
                if (routedImage != '') merchantHero.value = routedImage
                if (routedAddress != '') merchantAddress.value = routedAddress
                if (routedDiscount != '') merchantDiscount.value = routedDiscount
                merchantLocation.value = { latitude: 26.5856, longitude: 101.7184, name: merchantName.value, address: merchantAddress.value }
 
                if (routedMerchantType == 'scenic') {
                    merchantPrimaryTag.value = '权益'
                    groupBuyingEnabled.value = false
                    merchantServiceLabel.value = '景区服务'
                    usageInstructions.value = '到达景区后出示人才码,根据景区后台配置的权益规则核验使用;开放时间、适用区域及预约要求以景区现场说明为准。'
                    merchantRightsText.value = '持卓越人才卡可享该景区配置的专属游览权益,具体优惠内容、开放时间、预约方式和使用限制以景区说明为准。'
                } else if (routedMerchantType == 'apartment') {
                    merchantPrimaryTag.value = '惠'
                    groupBuyingEnabled.value = false
                    merchantServiceLabel.value = '住宿服务'
                    usageInstructions.value = '到店后出示人才码并按人才公寓或驿站的后台配置提交入住申请,房源、入住时长及审核要求以服务方说明为准。'
                    merchantRightsText.value = '持卓越人才卡可申请该人才公寓或驿站配置的住宿权益,具体房源、租金、入住期限和申请条件以服务方说明为准。'
                } else if (routedMerchantType != '' && routedMerchantType != 'food') {
                    merchantPrimaryTag.value = '权益'
                    groupBuyingEnabled.value = false
                    merchantServiceLabel.value = '人才服务'
                    usageInstructions.value = '到店后出示人才码,根据服务机构后台配置的权益规则进行核验使用。'
                    merchantRightsText.value = '持卓越人才卡可享该服务机构配置的人才专属权益,具体内容与使用规则以服务机构说明为准。'
                } else {
                    merchantType.value = 'food'
                    merchantPrimaryTag.value = '券'
                    merchantServiceLabel.value = '到店消费'
                    merchantRightsText.value = '持卓越人才卡到店消费可享商户配置的餐饮优惠,具体可用时段、适用菜品和使用规则以商户现场说明为准。'
                }
            }
        }
        const session = getSession()
        if (session != null && session.talentId != null) talentId.value = session.talentId as string
        if (talentId.value == '' && apiEnvironment == 'development') talentId.value = '2'
        if (isFuelMerchant.value || isParkingMerchant.value) {
            loadTransportMerchantDetail()
        } else {
            loadRemoteMerchant()
            loadMerchantLists()
        }
    })
    const loadTransportMerchantDetail = () => {
        if (isFuelMerchant.value) { loadFuelMerchant(); return }
        if (isParkingMerchant.value) { loadParkingMerchant(); return }
        loadRemoteMerchant()
    }
    const loadFuelMerchant = () => {
        if (!isApiConfigured() || !/^\d+$/.test(merchantId.value)) {
            merchantLoading.value = false
            merchantError.value = true
            return
        }
        merchantLoading.value = true
        merchantError.value = false
        listLoading.value = true
        listLoaded.value = false
        listError.value = false
        getFuelMerchantDetail(merchantId.value, (data: any) => {
            merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : ''
            merchantAddress.value = data != null && data.address != null ? data.address as string : ''
            merchantPhone.value = data != null && data.contactMobile != null ? data.contactMobile as string : ''
            merchantAvailable.value = data != null && (data.available === true || Number(data.available) == 1)
            merchantDiscount.value = data != null && data.discountText != null ? data.discountText as string : '请咨询商户'
            merchantServiceLabel.value = data != null && data.serviceLabel != null ? data.serviceLabel as string : '请咨询商户'
            applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : '请咨询商户'
            usageInstructions.value = data != null && data.usageInstructions != null ? data.usageInstructions as string : '请咨询商户'
            merchantRightsText.value = data != null && data.rightsText != null ? data.rightsText as string : '请咨询商户'
            groupBuyingEnabled.value = false
            merchantType.value = 'fuel'
            const heroes: any[] = data != null && data.heroImages instanceof Array ? data.heroImages as any[] : []
            if (heroes.length > 0 && heroes[0].imageUrl != null && heroes[0].imageUrl != '') merchantHero.value = resolveApiUrl(heroes[0].imageUrl as string)
            fuelBenefitId.value = data != null && data.benefit != null && data.benefit.benefitId != null ? '' + data.benefit.benefitId : ''
            merchantLocation.value = {
                latitude: data != null && data.latitude != null ? Number(data.latitude) : 0,
                longitude: data != null && data.longitude != null ? Number(data.longitude) : 0,
                name: merchantName.value,
                address: merchantAddress.value
            }
            const stores: any[] = data != null && data.applicableStores instanceof Array ? data.applicableStores as any[] : []
            applicableStores.value = stores.map((item: any) => ({
                id: '' + item.id,
                name: item.storeName != null ? item.storeName as string : '',
                address: item.address != null ? item.address as string : '',
                latitude: item.latitude != null ? Number(item.latitude) : 0,
                longitude: item.longitude != null ? Number(item.longitude) : 0,
                canNavigate: item.canNavigate === true || Number(item.canNavigate) == 1
            }))
            merchantLoading.value = false
            listLoading.value = false
            listLoaded.value = true
            if (!merchantAvailable.value) currentTab.value = 'primary'
        }, () => {
            merchantLoading.value = false
            merchantError.value = true
            listLoading.value = false
            listError.value = true
        })
    }
    const loadParkingMerchant = () => {
        if (!isApiConfigured() || !/^\d+$/.test(merchantId.value)) {
            merchantLoading.value = false
            merchantError.value = true
            return
        }
        merchantLoading.value = true
        merchantError.value = false
        listLoading.value = true
        listLoaded.value = false
        listError.value = false
        getParkingMerchantDetail(merchantId.value, (data: any) => {
            merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : ''
            merchantHero.value = data != null && data.coverUrl != null && data.coverUrl != '' ? resolveApiUrl(data.coverUrl as string) : fallbackImage
            merchantAddress.value = data != null && data.address != null ? data.address as string : ''
            merchantPhone.value = data != null && data.contactMobile != null ? data.contactMobile as string : ''
            merchantAvailable.value = data != null && (data.available === true || Number(data.available) == 1)
            merchantUnavailableReason.value = data != null && data.unavailableReason != null && data.unavailableReason != '' ? data.unavailableReason as string : '该停车权益暂不可用'
            merchantServiceLabel.value = data != null && data.serviceLabel != null ? data.serviceLabel as string : '停车服务'
            parkingBenefitText.value = data != null && data.benefitText != null ? data.benefitText as string : '免费停车'
            applicableCards.value = data != null && data.applicableCards != null ? data.applicableCards as string : '请咨询商户'
            usageInstructions.value = data != null && data.usageInstructions != null ? data.usageInstructions as string : '请咨询商户'
            merchantRightsText.value = data != null && data.rightsText != null ? data.rightsText as string : '请咨询商户'
            parkingActionMode.value = data != null && data.actionMode == 'talentCode' ? 'talentCode' : 'plate'
            parkingPlateMode.value = data != null && data.plateFormMode == 'withParking' ? 'withParking' : 'simple'
            parkingBenefitId.value = data != null && data.benefit != null && data.benefit.benefitId != null ? '' + data.benefit.benefitId : ''
            groupBuyingEnabled.value = false
            merchantType.value = 'parking'
            merchantLocation.value = {
                latitude: data != null && data.latitude != null ? Number(data.latitude) : 0,
                longitude: data != null && data.longitude != null ? Number(data.longitude) : 0,
                name: merchantName.value,
                address: merchantAddress.value
            }
            const stores: any[] = data != null && data.applicableStores instanceof Array ? data.applicableStores as any[] : []
            applicableStores.value = stores.map((item: any) => ({
                id: '' + item.id,
                name: item.storeName != null ? item.storeName as string : '',
                address: item.address != null ? item.address as string : '',
                latitude: item.latitude != null ? Number(item.latitude) : 0,
                longitude: item.longitude != null ? Number(item.longitude) : 0,
                canNavigate: item.canNavigate === true || Number(item.canNavigate) == 1
            }))
            merchantLoading.value = false
            listLoading.value = false
            listLoaded.value = true
            parkingDetailLoaded = true
        }, () => {
            merchantLoading.value = false
            merchantError.value = true
            listLoading.value = false
            listError.value = true
        })
    }
    onShow(() => {
        if (isParkingMerchant.value && parkingDetailLoaded) loadParkingMerchant()
    })
    const loadRemoteMerchant = () => {
        if (isFuelMerchant.value || isParkingMerchant.value) { loadTransportMerchantDetail(); return }
        if (!isApiConfigured() || !/^\d+$/.test(merchantId.value)) return
        merchantLoading.value = true
        merchantError.value = false
        getMerchantDetail(merchantId.value, (data: any) => {
            merchantName.value = data != null && data.merchantName != null ? data.merchantName as string : merchantName.value
            merchantHero.value = data != null && data.coverUrl != null && data.coverUrl != '' ? resolveApiUrl(data.coverUrl as string) : merchantHero.value
            merchantAddress.value = data != null && data.address != null ? data.address as string : merchantAddress.value
            const rawType = data != null && data.merchantType != null ? data.merchantType as string : merchantType.value
            merchantType.value = rawType == '餐饮服务' ? 'food' : rawType
            merchantAvailable.value = data == null || data.available !== false
            merchantPhone.value = data != null && data.contactMobile != null ? data.contactMobile as string : ''
            merchantDiscount.value = data != null && data.discountText != null ? data.discountText as string : merchantDiscount.value
            merchantServiceLabel.value = data != null && data.serviceLabel != null && data.serviceLabel != '' ? data.serviceLabel as string : merchantServiceLabel.value
            merchantRightsText.value = data != null && data.rightsText != null && data.rightsText != '' ? data.rightsText as string : merchantRightsText.value
            usageInstructions.value = data != null && data.usageInstructions != null && data.usageInstructions != '' ? data.usageInstructions as string : usageInstructions.value
            groupBuyingEnabled.value = data == null || data.groupBuyingEnabled == null ? groupBuyingEnabled.value : data.groupBuyingEnabled === true || Number(data.groupBuyingEnabled) == 1
            isHotelMerchant.value = merchantType.value == 'hotel' || merchantType.value == '酒店服务'
            hasFreeVoucher.value = merchantDiscount.value.indexOf('免费') >= 0
            if (isHotelMerchant.value && /^\d+$/.test(merchantId.value) && talentId.value != '') {
                getBenefitQuota('HOTEL', talentId.value, '', merchantId.value, (quota: any) => {
                    hasFreeVoucher.value = quota != null && Number(quota.quotaTotal) > 0
                }, () => {})
            }
            merchantLocation.value = {
                latitude: data != null && data.latitude != null ? Number(data.latitude) : 0,
                longitude: data != null && data.longitude != null ? Number(data.longitude) : 0,
                name: merchantName.value,
                address: merchantAddress.value
            }
            merchantLoading.value = false
            if (!merchantAvailable.value) {
                visiblePackages.value = []
                currentTab.value = 'rights'
            }
        }, () => {
            merchantLoading.value = false
            merchantError.value = true
        })
    }
    const retryMerchantDetail = () => { if (isFuelMerchant.value || isParkingMerchant.value) loadTransportMerchantDetail(); else loadRemoteMerchant() }
    const loadMerchantLists = () => {
        if (isFuelMerchant.value || isParkingMerchant.value) { loadTransportMerchantDetail(); return }
        listLoading.value = true
        listLoaded.value = false
        listError.value = false
        if (isApiConfigured() && /^\d+$/.test(merchantId.value) && isMedicalMerchant.value) {
            getMedicalPackages(merchantId.value, (data: any) => {
                const records: any[] = data instanceof Array ? data as any[] : []
                const packages: any[] = []
                for (let index = 0; index < records.length; index++) {
                    const item: any = records[index]
                    packages.push({ id: '' + item.id, name: item.name || '体检套餐', openTime: item.openTime || '以医院通知为准', image: item.heroImage != null && item.heroImage != '' ? resolveApiUrl(item.heroImage as string) : fallbackImage, available: item.available === true, unavailableReason: item.unavailableReason || '' })
                }
                visibleMedicalPackages.value = packages
                visiblePackages.value = []
                applicableStores.value = []
                listLoading.value = false
                listLoaded.value = true
            }, () => { listLoading.value = false; listError.value = true })
            return
        }
        if (isApiConfigured() && /^\d+$/.test(merchantId.value) && !isMedicalMerchant.value && !isTransportMerchant.value) {
            getProducts(merchantId.value, 1, 20, (data: any) => {
                const records: any[] = data != null && data.records instanceof Array ? data.records as any[] : []
                const remotePackages: any[] = []
                for (let index = 0; index < records.length; index++) {
                    const item: any = records[index]
                    const sale = item.salePrice != null ? Number(item.salePrice) : 0
                    const original = item.originalPrice != null ? Number(item.originalPrice) : 0
                    const stock = item.stock != null ? Number(item.stock) : -1
                    remotePackages.push({ id: '' + item.id, name: item.productName != null ? item.productName as string : '人才专享套餐', discount: sale > 0 && original > sale ? '' + Math.round(sale / original * 100) / 10 + '折' : '人才专享', price: '' + sale, originalPrice: original > 0 ? '' + original : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: stock != 0 })
                }
                visiblePackages.value = remotePackages
                visibleMedicalPackages.value = []
                applicableStores.value = []
                listLoading.value = false
                listLoaded.value = true
            }, () => {
                listLoading.value = false
                listError.value = true
            })
            return
        }
        if (apiEnvironment != 'development') {
            listLoading.value = false
            listError.value = true
            return
        }
        setTimeout(() => {
            if (failOnce.value) { failOnce.value = false; listLoading.value = false; listError.value = true; return }
            visiblePackages.value = forceEmpty.value ? [] : packages.value
            visibleMedicalPackages.value = forceEmpty.value ? [] : medicalPackages
            applicableStores.value = forceEmpty.value ? [] : allApplicableStores
            listLoading.value = false
            listLoaded.value = true
        }, 450)
    }
 
    const goBack = () => safeBack('/pages/benefits/benefits')
    const handleScroll = (event: any) => {
        const shouldShow = event.detail.scrollTop > 64
        if (isSolidNav.value != shouldShow) isSolidNav.value = shouldShow
    }
    const changeTab = (name: string) => { currentTab.value = name }
    const openMap = () => {
        if (merchantLocation.value.latitude <= 0 || merchantLocation.value.longitude <= 0) {
            uni.showToast({ title: '商户暂未配置位置', icon: 'none' })
            return
        }
        uni.openLocation({
            latitude: merchantLocation.value.latitude,
            longitude: merchantLocation.value.longitude,
            name: merchantLocation.value.name,
            address: merchantLocation.value.address,
            scale: 18,
            fail: () => uni.showToast({ title: '暂时无法打开地图导航', icon: 'none' })
        })
    }
    const callMerchant = () => {
        if (merchantPhone.value.trim().length == 0) {
            uni.showToast({ title: '商户暂未配置联系电话', icon: 'none' })
            return
        }
        uni.makePhoneCall({ phoneNumber: merchantPhone.value })
    }
    const openStoreLocation = (item: any) => {
        if (item.canNavigate === false || Number(item.latitude) <= 0 || Number(item.longitude) <= 0) {
            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 handlePackage = (item: any) => {
        if (!merchantAvailable.value) {
            uni.showToast({ title: '该商户已停业或下架', icon: 'none' })
            return
        }
        if (item.available === false) {
            uni.showToast({ title: '该套餐已售罄', icon: 'none' })
            return
        }
        if (isHotelMerchant.value) {
            uni.navigateTo({
                url: '/pages/hotel-booking/hotel-booking?hotelName='
                    + encodeURIComponent(merchantName.value)
                    + '&hotelAddress=' + encodeURIComponent(merchantAddress.value)
                    + '&roomName=' + encodeURIComponent(item.name)
            })
            return
        }
        uni.navigateTo({
            url: '/pages/product-detail/product-detail?merchantId=' + encodeURIComponent(merchantId.value)
                + '&productId=' + encodeURIComponent(item.id)
                + '&name=' + encodeURIComponent(item.name)
                + '&image=' + encodeURIComponent(item.image)
                + '&price=' + encodeURIComponent(item.price)
                + '&originalPrice=' + encodeURIComponent(item.originalPrice)
                + '&discount=' + encodeURIComponent(item.discount)
        })
    }
    const openMedicalPackage = (item: any) => {
        if (item.available === false) { uni.showToast({ title: item.unavailableReason || '该套餐暂不可预约', icon: 'none' }); return }
        uni.navigateTo({ url: '/pages/medical-package/medical-package?packageId=' + encodeURIComponent(item.id) + '&hospitalId=' + encodeURIComponent(merchantId.value) + '&name=' + encodeURIComponent(item.name) + '&hospital=' + encodeURIComponent(merchantName.value) })
    }
    const showBottomVoucher = () => {
        if (hasFreeVoucher.value) {
            uni.navigateTo({ url: '/pages/free-benefit/free-benefit?merchantId=' + encodeURIComponent(merchantId.value) })
            return
        }
        uni.navigateTo({ url: '/pages/talent-code/talent-code?talentId=' + encodeURIComponent(talentId.value) + '&merchantId=' + encodeURIComponent(merchantId.value) + '&scene=' + encodeURIComponent(isParkingMerchant.value ? 'parking' : 'merchant') })
    }
    const openMobileDialog = () => {
        mobileError.value = ''
        if (isFuelMerchant.value) fuelRegistrationKey.value = 'fuel-registration-' + merchantId.value + '-' + Date.now()
        showMobileDialog.value = true
    }
    const closeMobileDialog = () => { showMobileDialog.value = false; mobileError.value = '' }
    const submitMobile = () => {
        if (isSubmittingMobile.value) return
        const value = mobile.value.trim()
        if (!/^1[3-9][0-9]{9}$/.test(value)) {
            mobileError.value = '请输入正确的 11 位手机号'
            return
        }
        isSubmittingMobile.value = true
        if (isFuelMerchant.value) {
            if (talentId.value == '' || fuelBenefitId.value == '') {
                isSubmittingMobile.value = false
                mobileError.value = talentId.value == '' ? '请登录后提交手机号' : '加油权益暂未配置'
                return
            }
            if (fuelRegistrationKey.value == '') fuelRegistrationKey.value = 'fuel-registration-' + merchantId.value + '-' + Date.now()
            submitFuelMobileRegistration(merchantId.value, talentId.value, fuelBenefitId.value, value, fuelRegistrationKey.value, (data: any) => {
                isSubmittingMobile.value = false
                closeMobileDialog()
                mobile.value = ''
                uni.navigateTo({
                    url: '/pages/transport-submit-success/transport-submit-success?type=transport&merchantType=fuel'
                        + '&merchantId=' + encodeURIComponent(merchantId.value)
                        + '&name=' + encodeURIComponent(merchantName.value)
                        + '&registrationId=' + encodeURIComponent(data != null && data.registrationId != null ? '' + data.registrationId : '')
                })
            }, (error) => {
                isSubmittingMobile.value = false
                mobileError.value = error.message
            })
            return
        }
        submitBusiness('transportMobileRegistration', { merchantId: merchantId.value, merchantType: merchantType.value, mobile: value }, () => {
            isSubmittingMobile.value = false
            closeMobileDialog()
            mobile.value = ''
            uni.navigateTo({ url: '/pages/transport-submit-success/transport-submit-success' })
        }, (error) => {
            isSubmittingMobile.value = false
            mobileError.value = error.message
        })
    }
    const openPlatePage = () => {
        if (parkingBenefitId.value == '') {
            uni.showToast({ title: '停车权益暂未配置', icon: 'none' })
            return
        }
        uni.navigateTo({
            url: '/pages/plate-info/plate-info?merchantId=' + encodeURIComponent(merchantId.value)
                + '&benefitId=' + encodeURIComponent(parkingBenefitId.value)
                + '&mode=' + encodeURIComponent(parkingPlateMode.value)
                + '&name=' + encodeURIComponent(merchantName.value)
        })
    }
</script>
 
<style>
    .page { width: 100%; height: 100vh; background-color: #F6F6F6; }
    .content { height: calc(100% - 150rpx); height: calc(100% - 150rpx - env(safe-area-inset-bottom)); background-color: #F6F6F6; }
    .hero { height: 445rpx; position: relative; overflow: hidden; background-color: #3B3028; }
    .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 445rpx; }
    .hero-shade { position: absolute; left: 0; right: 0; top: 0; height: 190rpx; background-image: linear-gradient(180deg, rgba(0,0,0,.58) 0%, rgba(0,0,0,0) 100%); }
    .custom-nav { position: fixed; z-index: 20; left: 0; right: 0; top: 0; padding-bottom: 20rpx; }
    .custom-nav-bg { position: absolute; z-index: 0; left: 0; right: 0; top: 0; bottom: 0; opacity: 0; background-image: linear-gradient(180deg, #F26A38 0%, #EE542B 100%); transition: opacity 280ms ease-out; }
    .custom-nav.is-solid .custom-nav-bg { opacity: 1; }
    .safe-space { height: 28px; position: relative; z-index: 2; }
    .nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 24rpx; padding-right: 10px; position: relative; z-index: 2; flex-direction: row; align-items: center; justify-content: space-between; }
    .back-button { display: flex; width: 54rpx; height: 54rpx; position: relative; z-index: 2; border-radius: 50%; background-color: rgba(45,45,45,.52); transition: background-color 280ms ease-out; justify-content: center; align-items: center; }
    .custom-nav.is-solid .back-button { background-color: rgba(45,45,45,0); }
    .back-button image { width: 34rpx; height: 34rpx; }
    .nav-title { position: absolute; z-index: 1; left: 105rpx; right: 105rpx; opacity: 0; color: #FFFFFF; font-size: 29rpx; font-weight: 600; text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; transition: opacity 280ms ease-out; }
    .custom-nav.is-solid .nav-title { opacity: 1; }
    .custom-nav.is-hotel .nav-title { opacity: 1; }
    .capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
    .merchant-panel { position: relative; z-index: 3; margin-top: -22rpx; border-top-left-radius: 27rpx; border-top-right-radius: 27rpx; overflow: hidden; background-color: #F6F6F6; }
    .merchant-info-card { padding: 25rpx 38rpx 20rpx; background-color: #F6F6F6; }
    .merchant-detail-state { display: flex; height: 280rpx; color: #969CA5; font-size: 25rpx; flex-direction: row; justify-content: center; align-items: center; }
    .merchant-unavailable { margin-bottom: 18rpx; box-sizing: border-box; padding: 14rpx 18rpx; border-radius: 10rpx; background-color: #FFF0EC; }
    .merchant-unavailable text { color: #E55C3D; font-size: 24rpx; line-height: 34rpx; }
    .merchant-title-row { display: flex; min-height: 50rpx; flex-direction: row; align-items: center; justify-content: space-between; }
    .parking-title-block { display: flex; flex-direction: column; }
    .parking-title-row { display: flex; min-height: 54rpx; flex-direction: row; align-items: center; justify-content: space-between; }
    .parking-title-row .round-action { margin-left: 20rpx; }
    .parking-free { margin-top: 5rpx; color: #FF5A36; font-size: 24rpx; line-height: 34rpx; }
    .merchant-title { min-width: 0; flex: 1; color: #292929; font-size: 35rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .merchant-tags { display: flex; margin-left: 15rpx; flex-direction: row; align-items: center; flex-shrink: 0; }
    .coupon-tag { padding: 6rpx 8rpx; border-radius: 5rpx; background-color: #FFF0EC; color: #FF5D37; font-size: 24rpx; }
    .discount-tag { margin-left: 10rpx; padding: 6rpx 9rpx; border-radius: 5rpx; background-color: #FFF5E7; color: #FF7C2B; font-size: 24rpx; }
    .service-tags { display: flex; margin-top: 16rpx; flex-direction: row; }
    .service-tags text { margin-right: 10rpx; padding: 6rpx 11rpx; border-width: 1rpx; border-style: solid; border-color: #FF9B70; border-radius: 5rpx; color: #EF7046; font-size: 24rpx; }
    .merchant-address-row { display: flex; min-height: 72rpx; margin-top: 16rpx; flex-direction: row; align-items: flex-start; }
    .address-icon { width: 27rpx; height: 27rpx; margin-top: 4rpx; margin-right: 8rpx; flex-shrink: 0; }
    .merchant-address-row > text { width: 420rpx; min-width: 0; max-width: 420rpx; color: #7B8491; font-size: 24rpx; line-height: 34rpx; }
    .merchant-actions { display: flex; margin-left: auto; flex-direction: row; align-items: flex-start; flex-shrink: 0; }
    .round-action { display: flex; width: 52rpx; height: 52rpx; border-radius: 50%; background-color: #F2F3F5; flex-shrink: 0; justify-content: center; align-items: center; }
    .round-action.is-disabled { opacity: .4; }
    .merchant-actions .round-action:last-child { margin-left: 30rpx; }
    .round-action image { width: 27rpx; height: 27rpx; }
    .tabs { display: flex; height: 85rpx; padding: 0 38rpx; background-color: #F6F6F6; flex-direction: row; align-items: center; }
    .tab-item { display: flex; height: 85rpx; margin-right: 54rpx; position: relative; color: #82868D; font-size: 32rpx; justify-content: center; align-items: center; }
    .tab-item.active { color: #303030; font-weight: 600; }
    .tab-line { position: absolute; left: 50%; bottom: 0; width: 59rpx; height: 7rpx; margin-left: -30rpx; border-radius: 4rpx; opacity: 0; background-image: linear-gradient(90deg, #FF4D36 0%, #FFBF3A 100%); }
    .tab-item.active .tab-line { opacity: 1; }
    .package-list { margin: 16rpx 25rpx 0; background-color: transparent; }
    .list-state { display:flex; height:310rpx; flex-direction:row; justify-content:center; align-items:center; color:#969CA5; font-size:25rpx; }
    .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:merchant-list-loading 720ms linear infinite; }
    @keyframes merchant-list-loading { from { transform:rotate(0deg); } to { transform:rotate(360deg); } }
    .usage-panel { min-height: 548rpx; margin: 16rpx 25rpx 0; box-sizing: border-box; padding: 31rpx 27rpx; border-radius: 15rpx; background-color: #FFFFFF; }
    .usage-panel text { color: #777F8B; font-size: 27rpx; line-height: 43rpx; white-space: pre-wrap; }
    .package-item { display: flex; min-height: 218rpx; margin-bottom: 20rpx; box-sizing: border-box; padding: 20rpx 18rpx; border-radius: 15rpx; background-color: #FFFFFF; flex-direction: row; }
    .package-item.is-sold-out { opacity: .56; }
    .package-item.is-sold-out .buy-button { background-color: #A8ADB5; background-image: none; }
    .package-item:last-child { margin-bottom: 0; }
    .package-item-active { opacity: .78; }
    .package-image { width: 168rpx; height: 168rpx; border-radius: 14rpx; flex-shrink: 0; }
    .package-info { display: flex; min-width: 0; flex: 1; padding-left: 23rpx; flex-direction: column; }
    .package-name { color: #292929; font-size: 31rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
    .package-tags { display: flex; margin-top: 13rpx; flex-direction: row; align-items: center; }
    .package-bottom { display: flex; height: 57rpx; margin-top: auto; box-sizing: border-box; padding-left: 10rpx; border-radius: 29rpx; background-image: linear-gradient(90deg, rgba(255,247,240,.18) 0%, rgba(255,235,218,.72) 58%, rgba(255,222,195,.96) 100%); flex-direction: row; align-items: center; justify-content: space-between; }
    .price-row { display: flex; height: 52rpx; flex-direction: row; align-items: center; }
    .price-symbol { color: #FF4C2B; font-size: 28rpx; }
    .price { margin-left: 3rpx; color: #FF4C2B; font-size: 38rpx; font-weight: 700; }
    .old-price { margin-left: 12rpx; color: #B2B2B2; font-size: 24rpx; text-decoration: line-through; }
    .buy-button { display: flex; width: 132rpx; height: 57rpx; border-radius: 29rpx; background-image: linear-gradient(90deg, #FF5733 0%, #FF8A20 100%); justify-content: center; align-items: center; }
    .buy-button text { color: #FFFFFF; font-size: 27rpx; }
    .rights-panel { margin: 16rpx 25rpx 0; padding: 28rpx 26rpx; border-radius: 15rpx; background-color: #FFFFFF; }
    .rights-title { display: block; color: #303030; font-size: 32rpx; font-weight: 700; }
    .rights-text { display: block; margin-top: 18rpx; color: #777F8B; font-size: 27rpx; line-height: 43rpx; }
    .rule-row { display: flex; min-height: 70rpx; margin-top: 9rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #F0F0F0; flex-direction: row; align-items: center; justify-content: space-between; color: #7D838C; font-size: 26rpx; }
    .rule-row text:last-child { color: #3C424A; }
    .store-panel { margin: 16rpx 25rpx 0; overflow: hidden; border-width: 2rpx; border-style: solid; border-color: #DCE7FA; border-radius: 16rpx; background-color: #FFFFFF; }
    .store-table-header { display: flex; min-height: 67rpx; background-color: #EEF4FF; flex-direction: row; align-items: center; }
    .store-head-name, .store-head-address { display: flex; height: 67rpx; box-sizing: border-box; padding: 0 13rpx; justify-content: flex-start; align-items: center; }
    .store-head-name { width: 250rpx; }
    .store-head-address { min-width: 0; flex: 1; }
    .store-head-action { width: 72rpx; height: 67rpx; flex-shrink: 0; }
    .store-head-name text, .store-head-address text { width: 100%; color: #26374F; font-size: 23rpx; font-weight: 700; text-align: left; }
    .store-row { display: flex; min-height: 92rpx; border-top-width: 1rpx; border-top-style: solid; border-top-color: #E7ECF4; flex-direction: row; align-items: stretch; }
    .store-name-cell { display: flex; width: 250rpx; box-sizing: border-box; padding: 17rpx 13rpx; border-right-width: 1rpx; border-right-style: solid; border-right-color: #E7ECF4; align-items: center; justify-content: flex-start; }
    .store-address-cell { display: flex; min-width: 0; flex: 1; box-sizing: border-box; padding: 17rpx 13rpx; align-items: center; justify-content: flex-start; }
    .store-name-cell text, .store-address-cell text { width: 100%; color: #48566A; font-size: 21rpx; line-height: 31rpx; text-align: left; }
    .store-location { display: flex; width: 72rpx; flex-shrink: 0; justify-content: center; align-items: center; }
    .store-location image { width: 34rpx; height: 34rpx; box-sizing: border-box; padding: 4rpx; border-width: 2rpx; border-style: solid; border-color: #8BB9FF; border-radius: 50%; background-color: #EEF6FF; }
    .store-location.is-disabled { opacity: .4; }
    .content-bottom { height: 35rpx; }
    .content-full { height: 100%; }
    .medical-free-tag { margin-left: 15rpx; color: #FF7C2B; font-size: 26rpx; flex-shrink: 0; }
    .medical-package-list { margin: 16rpx 25rpx 0; }
    .medical-package-item { display: flex; min-height: 218rpx; margin-bottom: 20rpx; box-sizing: border-box; padding: 20rpx 18rpx; border-radius: 15rpx; background-color: #FFFFFF; flex-direction: row; }
    .medical-package-item:last-child { margin-bottom: 0; }
    .medical-package-image { width: 168rpx; height: 168rpx; border-radius: 14rpx; flex-shrink: 0; }
    .medical-package-info { display: flex; min-width: 0; flex: 1; padding-left: 23rpx; flex-direction: column; }
    .medical-package-name { color: #292929; font-size: 31rpx; font-weight: 700; }
    .medical-package-bottom { display: flex; margin-top: auto; flex-direction: row; align-items: center; justify-content: space-between; }
    .medical-open-row { display: flex; flex-direction: row; align-items: center; }
    .medical-open-label { color: #8A919C; font-size: 24rpx; }
    .medical-open-value { margin-left: 14rpx; color: #FF7C2B; font-size: 26rpx; font-weight: 600; }
    .medical-view-button { display: flex; width: 132rpx; height: 57rpx; border-radius: 29rpx; background-image: linear-gradient(90deg, #FF5733 0%, #FF8A20 100%); justify-content: center; align-items: center; }
    .medical-view-button text { color: #FFFFFF; font-size: 27rpx; }
    .medical-usage-panel { min-height: 548rpx; margin: 16rpx 25rpx 0; box-sizing: border-box; padding: 31rpx 27rpx; border-radius: 15rpx; background-color: #EFF1F4; }
    .medical-usage-panel text { color: #777F8B; font-size: 27rpx; line-height: 43rpx; white-space: pre-wrap; }
    .action-bar { display: flex; height: 150rpx; height: calc(150rpx + env(safe-area-inset-bottom)); box-sizing: border-box; padding: 20rpx 34rpx 2rpx; padding-bottom: calc(2rpx + env(safe-area-inset-bottom)); background-color: #FFFFFF; box-shadow: 0 -5rpx 15rpx rgba(0,0,0,.06); flex-direction: column; justify-content: flex-end; }
    .talent-code-button { display: flex; width: 100%; height: 90rpx; flex-shrink: 0; border-radius: 46rpx; background-image: linear-gradient(90deg, #FF4D32 0%, #FF8A20 100%); flex-direction: row; justify-content: center; align-items: center; }
    .talent-code-button image { width: 29rpx; height: 29rpx; margin-right: 14rpx; }
    .talent-code-button text { color: #FFFFFF; font-size: 31rpx; font-weight: 500; }
    .mobile-dialog-mask { display: flex; position: fixed; z-index: 100; left: 0; right: 0; top: 0; bottom: 0; background-color: rgba(24,29,38,.48); justify-content: center; align-items: center; }
    .mobile-dialog { display: flex; width: 664rpx; min-height: 430rpx; position: relative; box-sizing: border-box; padding: 61rpx 48rpx 38rpx; border-radius: 34rpx; background-color: #FFFFFF; flex-direction: column; align-items: center; }
    .mobile-dialog-close { display: flex; width: 64rpx; height: 64rpx; position: absolute; right: 28rpx; top: 22rpx; justify-content: center; align-items: center; }
    .mobile-dialog-close text { color: #AAB2BF; font-size: 43rpx; line-height: 48rpx; font-weight: 300; }
    .mobile-dialog-title { color: #212633; font-size: 36rpx; line-height: 50rpx; font-weight: 700; }
    .mobile-dialog-desc { margin-top: 14rpx; color: #A9AFBA; font-size: 23rpx; line-height: 34rpx; }
    .mobile-input-box { display: flex; width: 100%; height: 86rpx; margin-top: 28rpx; box-sizing: border-box; padding: 0 24rpx; border-width: 2rpx; border-style: solid; border-color: #E8EBEF; border-radius: 13rpx; background-color: #F8F9FB; align-items: center; }
    .mobile-input-box input { width: 100%; height: 82rpx; color: #30343A; font-size: 30rpx; }
    .mobile-placeholder { color: #B3B8C0; font-size: 27rpx; }
    .mobile-error { align-self: flex-start; margin-top: 9rpx; color: #F05C3A; font-size: 22rpx; }
    .mobile-submit { display: flex; width: 100%; height: 76rpx; margin-top: 25rpx; border-radius: 39rpx; background-image: linear-gradient(90deg, #FF4434 0%, #FF8C21 100%); justify-content: center; align-items: center; }
    .mobile-submit text { color: #FFFFFF; font-size: 28rpx; font-weight: 600; }
</style>