Files
xk-client-wx/pages/home/home.vue
李琦 2d2a8abfd4 feat(home/chat): 列表骨架屏、聊天室己方头像与协议/医生详情优化
- 新增分包组件 ListSkeleton(doctor/message/product/article/category/zone)
- 首页/消息/资讯首次加载改用骨架屏,刷新不再清空旧数据避免闪空
  · 去掉 home getList、消息 onShow 中的数组清空
  · 三页 pages.json 增加 easycom 与 componentPlaceholder 主包异步引用
- 聊天室己方头像改为读取本地 userinfo.avatarurl(登录后微信头像),
  mycollection 上传头像成功后同步写回 storage
- 修复协议抽屉点不开:page-container 改为 v-if + :show;
  协议名单独可点节点,匹配放宽 name/desc 双向 includes
- 医生气泡头像可点击进入 doctor-detail?readonly=1 只读医生详情,
  隐藏预约挂号区
2026-07-19 16:08:04 +08:00

1382 lines
50 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="content safe-area-inset-bottom">
<!-- 全局消息通知组件 -->
<MessageNotification />
<image-compress-popup ref="imageCompressPopup" />
<!-- 1. 自定义吸顶导航栏 -->
<view class="custom-nav"
:class="[
(storeType === '1' || storeType === 1) ? 'header-pharmacy' : 'header-clinic',
{ 'nav-sticky': isSticky }
]"
:style="{ paddingTop: statusBarHeight + 'px', height: (statusBarHeight + 44) + 'px' }">
<view class="nav-content">
<view class="store-info" @click="toStore">
<text class="store-name u-line-1">{{infoList.store['name'] || storeinfo[0].store['name'] || info.name || '请选择门店'}}</text>
<view v-if="!isGuestMode()" class="switch-badge">
<text class="iconfont icon-qiehuan"></text> 切换
</view>
</view>
</view>
</view>
<!-- 2. 头部背景与定位区域 -->
<view class="header" :class="(storeType === '1' || storeType === 1) ? 'header-pharmacy' : 'header-clinic'"
:style="{ paddingTop: (statusBarHeight + 44 + 10) + 'px' }">
<view class="bg-circle"></view>
<view class="location-bar">
<text class="iconfont icon-dizhi"></text>
<text class="u-line-1">{{infoList.store['position'] || storeinfo[0].store.position || info.position || '定位获取中...'}}</text>
</view>
</view>
<!-- 3. 轮播图区域 -->
<view class="banner-section">
<view class="banner-card">
<u-swiper
:list="list"
:img-mode="'aspectFill'"
:height="300"
:interval="4000"
@click="toBanner"
border-radius="16"
mode="rect"
indicator-pos="bottomRight"
></u-swiper>
</view>
</view>
<!-- 2.5 金刚区 (Zone) - 支持滑动/换行切换 -->
<view v-if="homeZonesInitialLoading" class="zone-wrapper">
<ListSkeleton type="zone" :rows="5" />
</view>
<view v-else-if="homeZones.length > 0" class="zone-wrapper" :class="zoneLayoutMode === 'scroll' ? 'zone-scroll-mode' : 'zone-wrap-mode'">
<view v-for="(item, index) in homeZones" :key="index" class="zone-item" @click="goZoneList(item)">
<view class="zone-icon-box">
<image
:src="item.icon || ((storeType === '1' || storeType === 1) ? 'https://img.icons8.com/fluency/48/pill.png' : 'https://img.icons8.com/fluency/48/doctor-male.png')"
class="zone-img"
mode="aspectFit"
></image>
</view>
<text class="zone-text">{{ item.title }}</text>
</view>
</view>
<!-- 3. 核心功能导航 (Nav) -->
<view class="nav-grid">
<!-- 诊所模式2列布局 -->
<view v-if="storeType === '0' || storeType === 0" class="nav-clinic-grid">
<view class="nav-card" @click="goYard(0)">
<view class="nav-content">
<view class="nav-title" style="color:#4175EE">到店挂号</view>
<view class="nav-desc">预约专家 不排队</view>
</view>
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/home/bg1.png" class="nav-icon-img-64" mode="aspectFit"></image>
</view>
<view class="nav-card" @click="goYard(2)">
<view class="nav-content">
<view class="nav-title" style="color:#00B578">在线复诊</view>
<view class="nav-desc">在线选药 极速达</view>
</view>
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/home/bg2.png" class="nav-icon-img-64" mode="aspectFit"></image>
</view>
</view>
<!-- 药店模式单列铺满 -->
<view v-if="storeType === '1' || storeType === 1" class="nav-pharmacy-grid">
<view class="nav-card" @click="golist(3)">
<view class="nav-content">
<view class="nav-title" style="color:#298DFF">预约购药</view>
<view class="nav-desc">正品保障 · 种类齐全 · 快速配送</view>
</view>
<image src="https://img.icons8.com/3d-fluency/94/delivery-scooter.png" class="nav-icon-img" style="width:140rpx; height:140rpx; right:20rpx; bottom:10rpx;" mode="aspectFit"></image>
</view>
</view>
</view>
<!-- 待支付挂号提醒 -->
<view
v-if="unpaidRegisterCount > 0"
class="unpaid-banner unpaid-banner--register"
@click="onUnpaidRegisterBannerClick"
>
<text class="unpaid-banner-text">您有 {{ unpaidRegisterCount }} 个待支付挂号订单点击前往查看</text>
<text class="iconfont icon-jiantou1 unpaid-banner-arrow"></text>
</view>
<!-- 待支付商品订单提醒 -->
<view
v-if="unpaidProductOrderCount > 0"
class="unpaid-banner unpaid-banner--product"
@click="onUnpaidProductOrderBannerClick"
>
<text class="unpaid-banner-text">您有 {{ unpaidProductOrderCount }} 个待支付商品订单点击前往查看</text>
<text class="iconfont icon-jiantou1 unpaid-banner-arrow"></text>
</view>
<!-- 4. 诊所模式内容特色药方 -->
<view v-if="(storeType === '0' || storeType === 0) && specialPrescription.enabled" class="tea-section-wrap">
<!-- 特色药方异形卡片模块 -->
<view class="tea-section">
<view class="irregular-card">
<!-- 左侧凸起 Tab右侧带下坡过渡 -->
<view class="card-tab">
<view class="card-tab-slope"></view>
</view>
<!-- 悬浮艺术字标题位于凸起与卡片的衔接处 -->
<image
:src="specialPrescription.title_image"
class="main-title"
mode="widthFix"
></image>
<!-- 主体区域 -->
<view class="card-body">
<!-- 悬浮 IP 小熊 -->
<image
:src="specialPrescription.mascot_image"
class="main-mascot"
mode="widthFix"
></image>
<view class="tea-subtitle">
<view class="subtitle-icon"></view>
<text>{{ specialPrescription.subtitle || '精选时令养生茶饮' }}</text>
</view>
<!-- 横向滑动列表 -->
<scroll-view scroll-x class="tea-scroll" :show-scrollbar="false" :enable-passive="false">
<view class="tea-list">
<navigator
v-if="detailUrl(tea)"
class="tea-item"
v-for="(tea, tIndex) in specialPrescription.items"
:key="tIndex"
:url="detailUrl(tea)"
hover-class="tea-item--active"
>
<view class="tea-image-wrap">
<image
v-if="tea.cover_image"
:src="tea.cover_image"
class="tea-image"
mode="aspectFill"
></image>
<text v-else class="tea-image-placeholder">特色方</text>
</view>
<view class="tea-info-wrap">
<view class="tea-name u-line-1">{{ tea.name }}</view>
<view class="tea-tags">
<text class="tea-tag" v-for="(tag, gIndex) in (tea.tags || []).slice(0, 2)" :key="gIndex">{{ tag }}</text>
</view>
<view class="tea-price" v-if="tea.price_available !== false">
<text class="tea-price-symbol">¥</text>{{ tea.price_per_dose }}
</view>
<view class="tea-price tea-price--unavailable" v-else>暂无报价</view>
</view>
</navigator>
</view>
</scroll-view>
<!-- 底部操作区 -->
<view class="tea-bottom-area">
<view class="days-left-tag">热卖养生茶</view>
<view class="tea-action-button" @click="goTeaList">查看全部药方</view>
</view>
</view>
</view>
</view>
</view>
<!-- 诊所模式医生列表 -->
<view v-if="storeType === '0' || storeType === 0">
<!-- 医生列表 -->
<view class="section-header">
<view class="section-title">医生推荐</view>
<view class="section-more" @click="goYard(0)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
</view>
<!-- 首次加载展示骨架刷新时保留旧列表避免闪空 -->
<ListSkeleton v-if="doctorListInitialLoading" type="doctor" :rows="3" />
<template v-else>
<view class="doctor-card" @click="goLookDoctor(item.id)" v-for="(item,index) in doctorList" :key="item.id">
<view class="doc-main">
<view class="doc-avatar-box">
<image :src="item.avatar || 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png'" mode="aspectFill" class="doc-avatar"></image>
<view class="status-dot"></view>
</view>
<view class="doc-info">
<view class="doc-name-row">
<text class="doc-name">{{item.name}}</text>
<text class="doc-title">{{item.title}}</text>
</view>
<view class="doc-dept">{{item.depart}} | {{item.store}}</view>
<view class="doc-skill">
<text class="skill-tag">擅长</text>
{{item.good_at}}
</view>
</view>
<!-- <view class="consult-btn">咨询</view>-->
<!-- <view class="consult-btn">查看</view>-->
</view>
<view class="doc-stats-bar">
<view class="stat-item">
<text class="stat-num">{{item.inquiry_num || '0'}}</text>
<view class="stat-label">问诊量</view>
</view>
<view class="stat-item">
<text class="stat-num">{{item.accept_percent || '100%'}}</text>
<view class="stat-label">接诊率</view>
</view>
<view class="stat-item">
<text class="stat-num">{{item.average_response_time || 5}}分钟</text>
<view class="stat-label">平均响应</view>
</view>
</view>
</view>
</template>
</view>
<!-- 5. 药店模式内容热销好药 -->
<view v-if="storeType === '1' || storeType === 1">
<view class="section-header">
<view class="section-title">{{ topProductsTitle || '热销好药' }}</view>
<view class="section-more" @click="golist(3)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
</view>
<!-- 优化后的药品列表使用计算属性 displayProductList 解决循环报错 -->
<view class="product-list-container">
<ListSkeleton v-if="productListInitialLoading" type="product" :rows="3" />
<template v-else>
<view class="product-card" @click="goProductDetail(item)" v-for="(item, index) in displayProductList" :key="index">
<view class="prod-img-box">
<image :src="(item.drug && item.drug.image) ? item.drug.image : 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png'" mode="aspectFill" class="prod-img" :class="{ 'prescription-blur': item.drug && item.drug.type == 2 && (item.drug.is_otc === 0 || item.drug.is_otc === '0') }"></image>
</view>
<view class="prod-info">
<view>
<view class="prod-title">{{ item.drug ? item.drug.drug_name : '' }}</view>
<view class="tag-row">
<!-- 只有西药type == 2才显示OTC/Rx标签 -->
<text v-if="item.drug && item.drug.type == 2" class="otc-tag" :class="item.drug.is_otc ? 'otc-green' : 'otc-red'">{{item.drug.is_otc ? 'OTC' : 'Rx'}}</text>
</view>
<view class="prod-spec">{{ item.drug ? item.drug.specification : '' }}</view>
</view>
<view class="prod-bottom">
<view class="price-box">
<text class="price-symbol">¥</text>
<text>{{ item.price || '0.00' }}</text>
</view>
<!-- 简化点击事件防止 template 编译错误 -->
<view class="buy-btn" @click.stop="handleAddToCart(item)">
<u-icon name="shopping-cart" color="#ffffff" size="32"></u-icon>
</view>
</view>
</view>
</view>
</template>
</view>
<!-- 6. 资质证明 -->
<view class="qualifications-section">
<view class="section-header" style="margin: 0 0 20rpx 0; padding: 0;">
<view class="section-title">店铺资质</view>
<view class="section-more" @click="goPlatformInfo">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
</view>
<view class="qualifications-row">
<view class="qualification-card" v-for="(item, index) in qualifications" :key="index" @click="previewQualification(item)">
<image
:src="item.image || (index === 0 ? 'https://img.icons8.com/fluency/48/certificate.png' : 'https://img.icons8.com/fluency/48/contract.png')"
class="qual-icon"
mode="aspectFit">
</image>
<text class="qual-name">{{ item.name }}</text>
</view>
</view>
</view>
</view>
<!-- 全局组件 -->
<view class="bg-" v-if="bgShow" @click="bgShow=false">
<image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/home/bg.png" mode="widthFix"></image>
</view>
<u-toast ref="uToast" />
</view>
</template>
<script>
import {
doctorList,
getBanner,
getToken,
storeChange,
storeInfo,
storeList,
fetchUnpaidProductOrderListApi,
} from '../../request/api/api'
import { getPlatformQualificationsApi } from '../../request/api/platform'
import { getStoreDrugListBySalespersonApi, getHomeTopProductsApi, getHomeZonesApi, checkOnlineConsultationConfigApi } from "@/request/api/product";
import { getSpecialPrescriptionHomeApi } from '@/request/api/specialPrescription';
import { fetchUnpaidRegisterListApi } from '@/request/api/register';
import request from "@/request/api/request";
import MessageNotification from "@/components/MessageNotification/MessageNotification.vue";
import ImageCompressPopup from '@/components/image-compress-popup/image-compress-popup.vue';
import { registerImageCompressPopup } from '@/utils/image-compress-modal.js';
import { setGuestMode, isGuestMode, isLoggedIn } from '@/common/utils/auth.js';
import {silentLogin} from "@/utils/login";
export default {
components: {
MessageNotification,
ImageCompressPopup,
},
data() {
return {
storeId: 0,
storeType: 0,
storeinfo: [],
doctorList: [],
productList: [],
homeZones: [],
// 首次加载骨架标记:仅第一次请求结束前为 true刷新时保留旧数据不闪空
doctorListInitialLoading: true,
productListInitialLoading: true,
homeZonesInitialLoading: true,
topProductsTitle: '热销商品',
topProductsList: [],
qualifications: [],
show: true,
showed: false,
shopList: {},
flag: false,
headerTitle: "",
infoList: "",
list: [],
info: [],
bgShow: false,
isShow_envVersion: true,
defaultDoctorId: '39',
statusBarHeight: 20,
isSticky: false,
// 控制金刚区布局模式: 'scroll' (滑动) 或 'wrap' (换行)
zoneLayoutMode: 'scroll',
unpaidRegisterCount: 0,
unpaidRegisterList: [],
unpaidProductOrderCount: 0,
unpaidProductOrderList: [],
// 特色药方API
specialPrescription: {
enabled: false,
doctor_id: 0,
title_image: '',
mascot_image: '',
subtitle: '',
items: [],
},
}
},
computed: {
// 解决 template 中复杂表达式导致的渲染错误
displayProductList() {
return this.topProductsList.length > 0 ? this.topProductsList : this.productList;
},
// 判断是否为guest模式用于模板
isGuestMode() {
return isGuestMode;
}
},
onLoad() {
this.isShow_envVersion = uni.getStorageSync('isShow_envVersion')
if (this.isShow_envVersion === false) {
this.getList();
}
try {
const systemInfo = uni.getSystemInfoSync();
this.statusBarHeight = systemInfo.statusBarHeight || 20;
} catch (e) {
console.error(e);
}
},
onReady() {
if (this.$refs.imageCompressPopup) {
registerImageCompressPopup(this.$refs.imageCompressPopup);
}
// console.log('调试静默登陆', isLoggedIn());
// 判断是否已登录
if (!isLoggedIn()) {
// console.log('调试静默登陆2', isLoggedIn());
// 未登录时设置guest模式标记和store_id
// setGuestMode();
// silentLogin().then((loginResult) => {
// if (loginResult.success) {
// } else {
// console.error('静默登录失败:', loginResult.message);
// }
// }).catch((error) => {
// console.error('静默登录异常:', error);
// });
}
// 未登录时也允许访问使用默认store_id=11001
if (!uni.getStorageSync('store_id')) {
// uni.setStorageSync('store_id', '11001');
}
if (uni.getStorageSync('store_id')) {
this.getStore();
} else {
this.goNewlist()
}
},
onPageScroll(e) {
if (e.scrollTop > 50) {
this.isSticky = true;
} else {
this.isSticky = false;
}
},
methods: {
async fetchUnpaidCounts() {
if (!isLoggedIn()) {
this.unpaidRegisterCount = 0;
this.unpaidRegisterList = [];
this.unpaidProductOrderCount = 0;
this.unpaidProductOrderList = [];
return;
}
try {
const [registerList, productList] = await Promise.all([
fetchUnpaidRegisterListApi(),
fetchUnpaidProductOrderListApi(),
]);
this.unpaidRegisterList = registerList;
this.unpaidRegisterCount = registerList.length;
this.unpaidProductOrderList = productList;
this.unpaidProductOrderCount = productList.length;
} catch (e) {
console.error('fetchUnpaidCounts', e);
this.unpaidRegisterCount = 0;
this.unpaidRegisterList = [];
this.unpaidProductOrderCount = 0;
this.unpaidProductOrderList = [];
}
},
onUnpaidRegisterBannerClick() {
if (this.unpaidRegisterCount === 1 && this.unpaidRegisterList[0]) {
const id = this.unpaidRegisterList[0].id;
uni.navigateTo({
url: `/subPackages/doctor/doctor-userinfo?step=2&register_id=${id}`,
});
return;
}
uni.navigateTo({
url: '/subPackages/my/myappiont',
});
},
onUnpaidProductOrderBannerClick() {
if (this.unpaidProductOrderCount === 1 && this.unpaidProductOrderList[0]) {
const id = this.unpaidProductOrderList[0].id;
uni.navigateTo({
url: `/subPackages/my/my-drug/drug-info?id=${id}`,
});
return;
}
uni.navigateTo({
url: '/subPackages/my/my-drug/drug-list?index=1',
});
},
// 处理购物车按钮点击
handleAddToCart(item) {
if(item && item.drug) {
this.goProductDetail(item);
}
},
async goYard(type) {
// 当 type === 2 (诊所在线问诊) 时,直接获取委托诊所和医生信息并跳转到挂号页面
if (type === 2) {
try {
const storeId = uni.getStorageSync('store_id') || '11001';
const { getClinicOnlineConsultationInfoApi } = await import('@/request/api/product');
const configRes = await getClinicOnlineConsultationInfoApi({ store_id: storeId });
if (configRes.data?.code === 0 || configRes.data?.code === '0') {
const result = configRes.data?.result;
if (result?.can_use && result?.delegate_doctor_id) {
// 直接跳转到就诊人选择页面传递医生ID和挂号类型
uni.navigateTo({
url: `/subPackages/doctor/doctor-userinfo?id=${result.delegate_doctor_id}&r_type=2&delegate_store_id=${result.delegate_store_id || storeId}`
});
return;
} else {
uni.showToast({
title: result?.message || '该门店暂不支持在线问诊功能',
icon: 'none',
duration: 2000
});
return;
}
} else {
uni.showToast({
title: configRes.data?.message || '获取在线问诊信息失败',
icon: 'none',
duration: 2000
});
return;
}
} catch (error) {
console.error('获取在线问诊信息失败:', error);
uni.showToast({
title: '获取在线问诊信息失败',
icon: 'none',
duration: 2000
});
return;
}
}
// 当 type === 3 (在线复诊) 时,需要先检查门店是否支持在线问诊功能
if (type === 3) {
try {
const storeId = uni.getStorageSync('store_id') || '11001';
const configRes = await checkOnlineConsultationConfigApi({
store_id: storeId,
guest: 1 // 使用 Guest 接口
});
// 直接使用新接口格式code + result
if (configRes.data?.code === 0 || configRes.data?.code === '0') {
// 获取结果数据,直接使用 result返回格式{ code: 0, result: { can_use: false, message: "..." } }
const result = configRes.data?.result;
// 检查是否可以使用在线问诊功能
if (!result?.can_use) {
// 显示后端返回的提示信息(使用 result 中的 message
uni.showToast({
title: result?.message || '该门店暂不支持在线问诊功能',
icon: 'none',
duration: 2000
});
return; // 阻止跳转
}
} else {
// API调用失败显示错误信息
uni.showToast({
title: configRes.data?.message || '检查在线问诊配置失败',
icon: 'none',
duration: 2000
});
return;
}
} catch (error) {
console.error('检查在线问诊配置失败:', error);
uni.showToast({
title: '检查在线问诊配置失败',
icon: 'none',
duration: 2000
});
return;
}
}
// 检查通过或非在线复诊类型,正常跳转
uni.navigateTo({
url: '/pages/Inquiries/Inquiries?type=' + type
})
},
async golist(type) {
if (type === 3) {
// 药店模式直接进入商品页,不检查配置
return uni.navigateTo({
url: '/subPackages/product/product?register_type=3'
});
}
uni.navigateTo({
url: '/pages/Inquiries/Inquiries?type=' + type
})
},
getH5() {
getToken({
method: "post",
data: {
user_id: uni.getStorageSync('user_id'),
avatarurl: uni.getStorageSync('userinfo')['avatarurl'],
mobile: uni.getStorageSync('userinfo')['mobile'],
nickname: uni.getStorageSync('userinfo')['nickname'],
openid: uni.getStorageSync('userinfo')['openid'],
session_key: uni.getStorageSync('userinfo')['session_key'],
store_id: uni.getStorageSync('store_id') || '11001',
},
}).then((res) => {
if (res.data.errcode == 0) {
uni.setStorageSync('token_platform_id', res.data.data.token)
} else {
this.$refs.uToast.show({ title: res.data.msg, type: 'default', icon: false })
}
})
},
tosearch() {
uni.navigateTo({ url: '/subPackages/shop/search-home' })
},
toStore() {
uni.navigateTo({ url: '/subPackages/my/mystore' })
},
goLookDoctor(id, sts_id) {
if (!sts_id) { let sts_id = uni.getStorageSync('store_id') }
setTimeout(() => {
uni.requestSubscribeMessage({
tmplIds: ['JdSs08cMSiL17xwwHZ8fZ5UBUqeY0eKAuvyW7n4Fl20', 'OERMugiahk-aKRVH9GHmdJCRBE3yldzEKTzbMCJhY6U', 'p-Ix0uIuwPKxm-_yKDlI8mkI-niUgPtaMUZyAaa5vvA'],
complete: (res) => {
uni.navigateTo({ url: '/subPackages/doctor/doctor-detail?store_id=' + sts_id + '&id=' + id })
}
})
}, 200)
},
getStore() {
if (uni.getStorageSync('store_id')) {
this.show = false
storeInfo({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
if (responseData && responseData.store) {
this.storeType = responseData.store['type']
uni.setStorageSync('store_type', responseData.store['type']) // 存储门店类型
this.infoList = responseData
this.headerTitle = responseData.store['name']
this.getBannered();
}
}
})
} else {
this.goNewlist()
}
},
goNewlist() {
storeList({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
if (res.data.errcode == 0) {
this.show = res.data.data.open_issue
this.showed = this.show
if (this.isShow_envVersion) {
this.shopList = res.data.data.list
this.getBannered();
} else {
this.storeinfo = res.data.data.list.filter((item) => item.is_online == 1)
uni.setStorageSync('store_id', this.storeinfo[0].store.id)
this.headerTitle = this.storeinfo[0].store['name']
this.getBannered();
}
} else {
// 未登录时不跳转使用默认store_id=11001
uni.setStorageSync('store_id', '11001');
this.getBannered();
}
})
},
getList() {
// 不再请求前清空 doctorList避免 onShow 刷新时出现「先空后满」
doctorList({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
this.doctorList = (responseData.list || []).slice(0, 5)
}
}).finally(() => {
this.doctorListInitialLoading = false
})
},
toChang(e) {
storeChange({ method: "post", data: { store_id: e } }).then((res) => {
if (res.data.errcode == 0) {
this.getStore()
this.getList();
}
})
},
getBannered() {
getBanner({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
// 使用 guest_mode 判断 + code/errcode 双重保险
const isGuest = isGuestMode();
const isNewFormat = isGuest && (res.data.code === 0 || res.data.code === '0');
const isOldFormat = !isGuest && (res.data.errcode === 0 || res.data.errcode === '0');
if (isNewFormat || isOldFormat) {
// 新接口格式:从 result 取值
// 老接口格式:从 data 取值
const responseData = isGuest && res.data.result ? res.data.result : res.data.data;
if (responseData) {
this.info = responseData
this.urlInfo = responseData.nav || []
this.storeType = responseData.type
this.list = (responseData.nav || []).map((item) => { return item.pic })
}
}
})
},
toBanner(e) {
if (!this.urlInfo[e].external_link) return
if (this.urlInfo[e].link_type == 1 && this.isShow_envVersion == false) {
uni.navigateTo({ url: this.urlInfo[e].external_link })
} else {
uni.navigateTo({ url: '/pages/home/home-banner?id=' + this.urlInfo[e].external_link })
}
},
getProductList() {
getStoreDrugListBySalespersonApi({
store_id: uni.getStorageSync('store_id') || '11001',
page: 1,
limit: 3
}).then((res) => {
if (res.data.code == 0) {
this.productList = res.data.result.records;
}
}).finally(() => {
// top 与 product 任一返回即可结束首次骨架(见 getHomeTopProducts
this._markProductInitialLoaded()
})
},
getHomeTopProducts() {
getHomeTopProductsApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data.code == 0 && res.data.result) {
const result = res.data.result;
this.topProductsTitle = result.title || '热销商品';
this.topProductsList = result.list || [];
}
}).finally(() => {
this._markProductInitialLoaded()
})
},
// 商品区依赖 topProducts 或 productList两者都结束首次请求后再关骨架
_markProductInitialLoaded() {
this._productFetchDone = (this._productFetchDone || 0) + 1
if (this._productFetchDone >= 2) {
this.productListInitialLoading = false
}
},
getHomeZones() {
getHomeZonesApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data.code == 0) {
this.homeZones = res.data.result || res.data.data || [];
}
}).catch(() => {
// 仅尚无数据时清空,避免刷新失败抹掉已有专区
if (!this.homeZones || this.homeZones.length === 0) {
this.homeZones = [];
}
}).finally(() => {
this.homeZonesInitialLoading = false
})
},
getPlatformQualifications() {
getPlatformQualificationsApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data.code == 0 || res.data.errcode == 0) {
const result = res.data.data || res.data.result || {};
// 适配新的返回格式(包含 store_name 和 list
if (result.store_name !== undefined) {
this.qualifications = result.list || [];
} else {
// 兼容旧格式(直接返回数组)
this.qualifications = Array.isArray(result) ? result : [];
}
}
})
},
previewQualification(item) {
// 获取所有有图片的资质,支持滑动预览
const allImages = this.qualifications
.filter(q => q.image)
.map(q => q.image);
if (allImages.length > 0 && item.image) {
uni.previewImage({
urls: allImages,
current: item.image
});
}
},
goProductDetail(item) {
if(!item || !item.drug) return;
uni.navigateTo({
url: `/subPackages/product/symptoms?id=${item.drug.id}&drug_name=${item.drug.drug_name}&is_otc=${item.drug.is_otc}&image=${item.drug.image}&price=${item.price}`
});
},
goZoneList(zone) {
if (!zone || !zone.type) return;
uni.navigateTo({ url: `/subPackages/product/product?zone_type=${zone.type}` });
},
goPlatformInfo() {
uni.navigateTo({ url: '/pages/index/platform-info' });
},
// 跳转到全部药方 / 养生茶饮列表
goTeaList() {
uni.navigateTo({
url: '/subPackages/special-prescription/list',
});
},
goTeaDetail(tea) {
const id = Number(tea?.id);
if (!id) return;
uni.navigateTo({
url: `/subPackages/special-prescription/detail?id=${id}`,
});
},
detailUrl(tea) {
const id = Number(tea?.id);
if (!id) return '';
return `/subPackages/special-prescription/detail?id=${id}`;
},
fetchSpecialPrescriptionHome() {
getSpecialPrescriptionHomeApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data?.code === 0 || res.data?.code === '0') {
const result = res.data.result || {};
this.specialPrescription = {
enabled: !!result.enabled,
doctor_id: result.doctor_id || 0,
title_image: result.title_image || '',
mascot_image: result.mascot_image || '',
subtitle: result.subtitle || '',
items: result.items || [],
};
}
}).catch(() => {
this.specialPrescription.enabled = false;
});
},
async getDefaultDoctorId() {
try {
const storeId = uni.getStorageSync('store_id');
if (!storeId) return;
const res = await request('/xkApi/online-consultation/get-default-doctor-id', {
method: 'GET',
data: { store_id: storeId }
}, 0);
if (res.data && res.data.result && res.data.result.doctor_id) {
this.defaultDoctorId = res.data.result.doctor_id;
}
} catch (error) {
console.error('获取默认医生ID失败:', error);
}
}
},
onShow() {
this.storeId = uni.getStorageSync('store_id');
this.toChang(this.storeId)
this.getStore();
this.getList();
this.getProductList();
this.getHomeTopProducts();
this.getHomeZones();
this.getPlatformQualifications(); // 已补回
this.getDefaultDoctorId();
this.fetchUnpaidCounts();
this.fetchSpecialPrescriptionHome();
},
}
</script>
<style lang="scss" scoped>
$primary-color: #4175EE;
$secondary-color: #00B578;
$text-main: #222222;
$text-sub: #999999;
$bg-body: #F5F7FA;
$card-radius: 24rpx;
$card-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.04);
.content {
background-color: #e0e5ec;
min-height: 100vh;
width: 750rpx;
background-color: $bg-body;
padding-bottom: 30rpx;
/* --- 1. 自定义吸顶导航栏 --- */
.custom-nav {
position: fixed;
top: 0; left: 0; width: 100%; z-index: 999;
padding-left: 32rpx; padding-right: 200rpx;
box-sizing: border-box;
background: transparent;
transition: all 0.4s ease;
&.nav-sticky {
box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.1);
&.header-clinic { background: linear-gradient(160deg, #298DFF 0%, #6BAFFF 100%); }
&.header-pharmacy { background: linear-gradient(160deg, #00B578 0%, #8BE3C3 100%); }
}
.nav-content {
display: flex; align-items: center; height: 100%;
.store-info {
display: flex; align-items: center;
.store-name { font-size: 36rpx; font-weight: 800; color: #fff; margin-right: 20rpx; text-shadow: 0 2rpx 4rpx rgba(0,0,0,0.1); max-width: 400rpx; }
.switch-badge { font-size: 20rpx; background: rgba(255,255,255,0.9); color: $primary-color; padding: 6rpx 16rpx; border-radius: 24rpx; font-weight: bold; display: flex; align-items: center; .iconfont { font-size: 20rpx; margin-right: 4rpx; } }
}
}
}
/* --- 2. 头部区域 --- */
.header {
position: relative;
padding-left: 32rpx; padding-right: 32rpx; padding-bottom: 160rpx;
border-radius: 0 0 60rpx 60rpx; overflow: hidden; transition: all 0.5s ease;
&.header-clinic { background: linear-gradient(180deg, #298DFF 0%, #6BAFFF 80%, #F5F7FA 100%); }
&.header-pharmacy { background: linear-gradient(180deg, #00B578 0%, #8BE3C3 80%, #F5F7FA 100%); }
.bg-circle { position: absolute; top: -100rpx; right: -100rpx; width: 400rpx; height: 400rpx; background: rgba(255,255,255,0.15); border-radius: 50%; filter: blur(80rpx); }
.location-bar { display: inline-flex; align-items: center; background: rgba(255,255,255,0.3); backdrop-filter: blur(16rpx); padding: 10rpx 24rpx; border-radius: 40rpx; border: 2rpx solid rgba(255,255,255,0.4); color: #fff; font-size: 26rpx; max-width: 600rpx; margin-top: 20rpx; .iconfont { font-size: 26rpx; margin-right: 12rpx; } }
}
/* --- 3. 轮播图 --- */
.banner-section {
margin-top: -120rpx; padding: 0 32rpx; position: relative; z-index: 10; margin-bottom: 40rpx;
.banner-card { width: 100%; height: 300rpx; border-radius: 32rpx; box-shadow: 0 30rpx 70rpx rgba(41, 141, 255, 0.25); overflow: hidden; }
}
/* --- 2.5 金刚区 (Zone) --- */
.zone-wrapper {
padding: 0 32rpx 20rpx;
display: flex;
/* 默认样式,会被下方 modifier 覆盖 */
/* 模式1: 滑动 */
&.zone-scroll-mode {
flex-wrap: nowrap;
overflow-x: auto;
.zone-item { width: 140rpx; margin-right: 10rpx; }
}
/* 模式2: 换行 */
&.zone-wrap-mode {
flex-wrap: wrap;
justify-content: flex-start;
.zone-item { width: 20%; margin-bottom: 24rpx; }
}
.zone-item {
flex-shrink: 0; text-align: center; display: flex; flex-direction: column; align-items: center;
&:active { transform: scale(0.95); }
.zone-icon-box {
width: 100rpx; height: 100rpx;
//background: linear-gradient(180deg, #FFFFFF 0%, #F5F9FF 100%);
border-radius: 36rpx;
display: flex; align-items: center; justify-content: center;
//box-shadow: 0 8rpx 24rpx rgba(65, 117, 238, 0.08);
margin-bottom: 12rpx;
.zone-img { width: 100rpx; height: 100rpx; }
}
.zone-text { font-size: 24rpx; color: #555; font-weight: 500; }
}
}
.nav-grid {
padding: 32rpx;
.nav-clinic-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24rpx; }
.nav-pharmacy-grid { display: block; .nav-card { width: 100%; height: 200rpx; } }
.nav-card {
height: 180rpx; border-radius: 32rpx; background-color: #fff; position: relative; overflow: hidden;
box-shadow: 0 8rpx 30rpx rgba(0,0,0,0.03); display: flex; flex-direction: column; justify-content: center; padding-left: 32rpx;
&:active { transform: scale(0.98); }
&::before { content: ''; position: absolute; top: 0; bottom: 0; left: 0; right: 0; background: linear-gradient(90deg, #FDFEFF 0%, rgba(255,255,255,0) 100%); z-index: 1; }
.nav-content { position: relative; z-index: 2; .nav-title { font-size: 36rpx; font-weight: 800; margin-bottom: 8rpx; } .nav-desc { font-size: 22rpx; color: #888; } }
.nav-icon-img { position: absolute; right: 0; bottom: 0; width: 128rpx; height: 128rpx; z-index: 1; }
.nav-icon-img-64 { position: absolute; right: 30rpx; top: 30rpx; width: 84rpx; height: 84rpx; z-index: 1; }
}
}
.unpaid-banner {
display: flex;
align-items: center;
justify-content: space-between;
margin: 16rpx 32rpx 0;
padding: 24rpx 28rpx;
border-radius: 16rpx;
&--register {
background: linear-gradient(90deg, rgba(65, 117, 238, 0.08) 0%, rgba(65, 117, 238, 0.04) 100%);
border: 1rpx solid rgba(65, 117, 238, 0.2);
.unpaid-banner-text,
.unpaid-banner-arrow {
color: #4175EE;
}
}
&--product {
background: linear-gradient(90deg, rgba(0, 181, 120, 0.08) 0%, rgba(0, 181, 120, 0.04) 100%);
border: 1rpx solid rgba(0, 181, 120, 0.25);
.unpaid-banner-text,
.unpaid-banner-arrow {
color: #00B578;
}
}
.unpaid-banner-text {
flex: 1;
font-size: 26rpx;
line-height: 1.5;
}
.unpaid-banner-arrow {
font-size: 24rpx;
margin-left: 12rpx;
}
}
.section-header {
padding: 0 32rpx; margin: 20rpx 0 32rpx; display: flex; justify-content: space-between; align-items: center;
.section-title { font-size: 36rpx; font-weight: 800; color: #333; position: relative; padding-left: 24rpx; &::before { content: ''; position: absolute; left: 0; top: 8rpx; bottom: 8rpx; width: 8rpx; background: $primary-color; border-radius: 4rpx; } }
.section-more { font-size: 26rpx; color: #999; display: flex; align-items: center; }
}
/* --- 特色药方 / 养生茶饮异形卡片 --- */
.tea-section {
padding: 57rpx 32rpx 12rpx;
.irregular-card {
position: relative;
display: flex;
flex-direction: column;
align-items: flex-start;
}
/* 左侧凸起 Tab */
.card-tab {
width: 220rpx;
height: 40rpx;
background: #FFFFFF;
border-radius: 24rpx 24rpx 0 0;
position: relative;
z-index: 2;
margin-bottom: -1rpx;
}
/* 凸起右侧的下坡过渡(凹角) */
.card-tab-slope {
position: absolute;
right: -40rpx;
bottom: 0;
width: 40rpx;
height: 40rpx;
/* 使用径向渐变模拟内凹的下坡曲线 */
background-image: radial-gradient(circle at 100% 0%, transparent 40rpx, #FFFFFF 40rpx);
}
/* 悬浮艺术字标题:坐落在凸起与卡片衔接处 */
.main-title {
position: absolute;
left: 8rpx;
top: -120rpx;
width: 210rpx;
height: 128rpx;
z-index: 6;
pointer-events: none;
}
/* 主体区域 */
.card-body {
background: #FFFFFF;
width: 100%;
padding: 24rpx 24rpx 24rpx;
border-radius: 0 24rpx 24rpx 24rpx;
position: relative;
z-index: 1;
box-sizing: border-box;
box-shadow: 0 12rpx 32rpx rgba(65, 117, 238, 0.16);
}
/* 悬浮 IP 小熊 */
.main-mascot {
position: absolute;
right: 12rpx;
top: -140rpx;
width: 217rpx;
height: 168rpx;
z-index: 10;
pointer-events: none;
}
.tea-subtitle {
font-size: 28rpx;
color: #333;
font-weight: bold;
margin-bottom: 20rpx;
padding-right: 150rpx;
display: flex;
align-items: center;
.subtitle-icon {
width: 6rpx;
height: 26rpx;
background: #4175EE;
border-radius: 3rpx;
margin-right: 12rpx;
}
}
/* 横向滑动列表 */
.tea-scroll {
width: 100%;
white-space: nowrap;
margin: 0 -24rpx;
box-sizing: border-box;
position: relative;
z-index: 5;
}
.tea-list {
display: inline-flex;
padding: 4rpx 24rpx 12rpx;
position: relative;
z-index: 5;
}
.tea-item {
display: inline-flex;
flex-direction: column;
width: 176rpx;
margin-right: 16rpx;
background: #FFFFFF;
border-radius: 16rpx;
overflow: hidden;
border: 1rpx solid #F0F0F0;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
position: relative;
z-index: 5;
&:last-child { margin-right: 0; }
&:active,
&.tea-item--active { transform: scale(0.97); }
.tea-image-wrap {
width: 100%;
height: 132rpx;
background: linear-gradient(135deg, #F5F7FA 0%, rgba(65, 117, 238, 0.08) 100%);
display: flex;
align-items: center;
justify-content: center;
.tea-image { width: 100%; height: 100%; }
.tea-image-placeholder { font-size: 22rpx; color: #4175EE; }
}
.tea-info-wrap {
padding: 12rpx;
display: flex;
flex-direction: column;
}
.tea-name {
font-size: 24rpx;
color: #333;
font-weight: 600;
margin-bottom: 8rpx;
}
.tea-tags {
display: flex;
margin-bottom: 8rpx;
.tea-tag {
font-size: 18rpx;
color: #4175EE;
background: rgba(65, 117, 238, 0.1);
padding: 1rpx 8rpx;
border-radius: 4rpx;
margin-right: 6rpx;
}
}
.tea-price {
color: #FF5722;
font-size: 28rpx;
font-weight: 800;
.tea-price-symbol { font-size: 20rpx; margin-right: 2rpx; }
&--unavailable {
color: #999;
font-size: 22rpx;
font-weight: 500;
}
}
}
/* 底部操作区 */
.tea-bottom-area {
position: relative;
margin-top: 16rpx;
.days-left-tag {
position: absolute;
top: -20rpx;
right: 20rpx;
background: #FFC107;
color: #8C6A00;
font-size: 20rpx;
font-weight: bold;
padding: 6rpx 18rpx;
border-radius: 16rpx 16rpx 16rpx 0;
z-index: 2;
border: 2rpx solid #fff;
box-shadow: 0 4rpx 12rpx rgba(255, 193, 7, 0.3);
}
.tea-action-button {
width: 100%;
text-align: center;
padding: 20rpx 0;
border-radius: 40rpx;
background: linear-gradient(90deg, #4175EE 0%, #298DFF 100%);
color: #fff;
font-size: 28rpx;
font-weight: bold;
box-sizing: border-box;
box-shadow: 0 8rpx 24rpx rgba(65, 117, 238, 0.3);
&:active { transform: scale(0.98); }
}
}
}
.doctor-card {
background: #fff; margin: 0 32rpx 32rpx; border-radius: $card-radius; box-shadow: $card-shadow; overflow: hidden;
&:active { transform: scale(0.99); }
.doc-main {
padding: 32rpx; display: flex; position: relative;
.doc-avatar-box { position: relative; margin-right: 24rpx; .doc-avatar { width: 112rpx; height: 112rpx; border-radius: 50%; background: #eee; border: 4rpx solid #fff; box-shadow: 0 4rpx 16rpx rgba(0,0,0,0.1); } .status-dot { position: absolute; bottom: 4rpx; right: 4rpx; width: 24rpx; height: 24rpx; background: $secondary-color; border: 4rpx solid #fff; border-radius: 50%; } }
.doc-info { flex: 1; .doc-name-row { display: flex; align-items: baseline; margin-bottom: 8rpx; .doc-name { font-size: 34rpx; font-weight: 700; color: #333; margin-right: 16rpx; } .doc-title { font-size: 22rpx; color: #666; background: #F0F2F5; padding: 4rpx 12rpx; border-radius: 8rpx; } } .doc-dept { font-size: 24rpx; color: #888; margin-bottom: 16rpx; } .doc-skill { font-size: 24rpx; color: #666; line-height: 1.4; display: -webkit-box; -webkit-line-clamp: 1; -webkit-box-orient: vertical; overflow: hidden; .skill-tag { color: $primary-color; background: rgba(65, 117, 238, 0.1); padding: 0 8rpx; border-radius: 4rpx; margin-right: 8rpx; } } }
.consult-btn { position: absolute; top: 32rpx; right: 32rpx; background: $primary-color; color: #fff; font-size: 24rpx; padding: 12rpx 28rpx; border-radius: 40rpx; box-shadow: 0 8rpx 20rpx rgba(65, 117, 238, 0.3); }
}
.doc-stats-bar {
background: #F8FBFF; padding: 20rpx 0; display: flex; border-top: 2rpx solid #f0f0f0;
.stat-item { flex: 1; text-align: center; border-right: 2rpx solid #eee; &:last-child { border: none; } .stat-num { font-size: 32rpx; font-weight: 700; color: #333; display: block; } .stat-label { font-size: 20rpx; color: #999; margin-top: 4rpx; } }
}
}
/* --- 5. 药品卡片 (双列瀑布流) --- */
.product-list-container {
padding: 0 24rpx; /* 左右边距 */
display: flex;
flex-wrap: wrap;
gap: 16rpx; /* 卡片间隙 */
}
.product-card {
background: #fff;
/* 动态计算宽度保留中间16rpx间隙 */
width: calc((100% - 16rpx) / 2);
padding: 0;
border-radius: 16rpx;
display: flex;
flex-direction: column;
box-shadow: $card-shadow;
border: 1rpx solid #f8f8f8;
overflow: hidden;
.prod-img-box {
width: 100%;
height: 280rpx; /* 缩小图片高度原345rpx减小约19% */
background: #f8f8f8;
border-radius: 0;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
.prod-img { width: 100%; height: 100%; border-radius: 0; /* &.prescription-blur { filter: blur(4px); } 暂时禁用模糊 */ }
}
.prod-info {
flex: 1;
display: flex; flex-direction: column; justify-content: space-between;
padding: 12rpx; /* 减小内边距原16rpx */
.prod-title {
font-size: 26rpx; /* 减小字号原28rpx */
font-weight: 600;
color: #333;
line-height: 1.4;
margin-bottom: 8rpx;
height: 72rpx; /* 减小高度原80rpx */
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
}
.tag-row { display: flex; margin-bottom: 8rpx; .otc-tag { font-size: 18rpx; padding: 0rpx 6rpx; border-radius: 4rpx; font-weight: 700; border: 1rpx solid transparent; &.otc-green { color: $secondary-color; background: #E6F7F0; border-color: #A3E6CD; } &.otc-red { color: #FF4D4F; background: #FFF0F0; border-color: #FFCDCD; } } }
.prod-spec { font-size: 18rpx; color: #999; margin-bottom: 12rpx;} /* 减小字号原20rpx */
.prod-bottom {
display: flex; justify-content: space-between; align-items: flex-end;
.price-box { color: #FF4D4F; font-weight: 800; font-size: 30rpx; .price-symbol { font-size: 20rpx; margin-right: 2rpx; } } /* 减小字号原34rpx/22rpx */
.buy-btn { background: linear-gradient(135deg, #4175EE 0%, #298DFF 100%); color: #fff; width: 52rpx; height: 52rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; box-shadow: 0 4rpx 12rpx rgba(65, 117, 238, 0.3); } /* 减小尺寸原56rpx */
}
}
}
.qualifications-section {
padding: 40rpx 32rpx 40rpx;
.qualifications-row {
display: flex;
flex-wrap: wrap;
gap: 20rpx;
.qualification-card {
background: #fff;
padding: 24rpx;
border-radius: 16rpx;
width: calc((100% - 20rpx) / 2); /* 一行两个 */
text-align: center;
box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03);
display: flex;
flex-direction: column;
align-items: center;
.qual-icon {
width: 200rpx;
height: 140rpx;
margin-bottom: 16rpx;
border-radius: 8rpx;
background: #f8f8f8;
}
.qual-name { font-size: 24rpx; color: #333; line-height: 1.4; font-weight: 500; }
}
}
}
.u-line-1 { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
.bg- { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background: rgba(0,0,0,0.5); z-index: 999; display: flex; align-items: center; justify-content: center; }
}
</style>