Files
xk-client-wx/pages/home/home.vue
2026-05-30 16:54:01 +08:00

907 lines
36 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="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(1)">
<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>
<!-- 4. 诊所模式内容医生列表 -->
<view v-if="storeType === '0' || storeType === 0">
<view class="section-header">
<view class="section-title">医生推荐</view>
<view class="section-more" @click="goYard(1)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
</view>
<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>
</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">
<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 || (item.drug && item.drug.drugStoreDrug ? item.drug.drugStoreDrug.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>
</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
} from '../../request/api/api'
import { getPlatformQualificationsApi } from '../../request/api/platform'
import { getStoreDrugListBySalespersonApi, getHomeTopProductsApi, getHomeZonesApi, checkOnlineConsultationConfigApi } from "@/request/api/product";
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: [],
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'
}
},
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: {
// 处理购物车按钮点击
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() {
this.doctorList = [];
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)
}
})
},
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;
}
})
},
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 || [];
}
})
},
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(() => { this.homeZones = []; })
},
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' });
},
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();
},
}
</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: 56rpx; height: 56rpx; }
}
.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; }
}
}
.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; }
}
.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>