Files
xk-client-wx/subPackages/product/symptoms.vue

895 lines
26 KiB
Vue
Raw Normal View History

<template>
<view class="container">
2026-01-07 14:21:29 +08:00
<MessageNotification />
<!-- 1. 顶部轮播图 -->
<view class="swiper-section">
<swiper
class="swiper"
indicator-dots
autoplay
circular
indicator-color="rgba(0,0,0,0.1)"
indicator-active-color="#2B85E4"
>
<swiper-item v-for="(img, index) in productImages" :key="index">
<image
class="swiper-image"
:src="img"
mode="aspectFill"
@click="previewImage(index)"
/>
</swiper-item>
</swiper>
</view>
<!-- 2. 核心信息区 -->
<view class="basic-info">
<!-- 价格与标签行 -->
<view class="price-row">
<view class="price-left">
<text class="symbol">¥</text>
<text class="amount">{{ productPrice }}</text>
<text class="old-price" v-if="productDetail.daijian_price">¥{{ productDetail.daijian_price }}</text>
</view>
<!-- 优化点根据 type 判断标签显示逻辑 -->
<!-- 情况1type === 3 为保健食品显示特定标签 -->
<view class="tag-capsule type-health" v-if="productDetail.type == 3">
<text class="tag-text">保健食品</text>
</view>
<!-- 情况2其他 type === 2 西药 OTC/Rx 逻辑 -->
2026-01-09 13:58:09 +08:00
<view class="tag-capsule" :class="productDetail.is_otc === 0 ? 'type-rx' : 'type-otc'" v-else-if="productDetail.type==2">
<text class="tag-text">{{ productDetail.is_otc === 0 ? 'Rx 处方药' : 'OTC 非处方药' }}</text>
</view>
</view>
<!-- 合规提示 - 移动到商品名称上方 -->
<view class="compliance-tip-card" v-if="complianceTip">
<u-icon name="info-circle" color="#FF4D4F" size="32" style="margin-right: 12rpx;"></u-icon>
<text class="tip-text">{{ complianceTip }}</text>
</view>
<view class="title">{{ productDetail.drug_name }}</view>
<view class="sub-title" v-if="productDetail.drug_alias">{{ productDetail.drug_alias }}</view>
<!-- 规格条 -->
<view class="spec-bar">
<view class="spec-item">
<text class="label">规格</text>
<text class="val">{{ productDetail.specification || '标准规格' }}</text>
</view>
<view class="spec-item" v-if="productDetail.guozi_no">
<text class="label">批准文号</text>
<text class="val">{{ productDetail.guozi_no }}</text>
</view>
2026-05-30 16:54:01 +08:00
<view class="spec-item" v-if="manufacturerName">
<text class="label">厂家</text>
<text class="val">{{ manufacturerName }}</text>
</view>
</view>
2025-12-31 10:18:07 +08:00
<!-- 平台资质入口 -->
<view class="platform-qualification-entry" @click="goPlatformQualification">
<text>查看平台资质</text>
<u-icon name="arrow-right" size="28" color="#909399"></u-icon>
</view>
</view>
<!-- 3. 物流提示 -->
<view class="logistics-tip" v-if="orderText">
<u-icon name="car-fill" color="#2B85E4" size="34" style="margin-right:12rpx"></u-icon>
<text>{{ orderText }}</text>
</view>
<!-- 4. 功效与用法 (卡片式布局 + 优化标题) -->
2026-05-30 16:54:01 +08:00
<view class="info-card" v-if="showFunctionSection">
<!-- 优化点医疗风格标题 -->
<view class="section-title-modern">
<view class="indicator"></view>
<text>功能主治</text>
</view>
<view class="card-body">
{{ productDetail.function || productDetail.indications || '暂无详细信息,请以说明书为准' }}
</view>
</view>
<!-- 保健食品不展示用法用量 -->
<view class="info-card" v-if="showUsageSection">
<view class="section-title-modern">
<view class="indicator"></view>
<text>用法用量</text>
</view>
<view class="card-body">
{{ productDetail.usage || '请遵医嘱或参照说明书' }}
</view>
</view>
<!-- 说明书入口 - 处方药未审核通过不展示 -->
<view class="info-card" v-if="showInstructionSection">
<view class="section-title-modern">
<view class="indicator"></view>
<text>说明书</text>
</view>
<view class="instruction-content">
<image
v-if="productDetail.instruction"
class="instruction-image"
:src="productDetail.instruction"
mode="widthFix"
@click="previewInstruction"
/>
<view v-else class="no-instruction">
<u-icon name="file-text" size="60" color="#999"></u-icon>
<text>详见产品包装内部说明书</text>
</view>
</view>
</view>
<!-- 5. 商品详情图 (淘宝式无缝加载 + 优化标题) -->
<view class="detail-images-wrapper" v-if="introductionImages.length > 0">
<view class="section-divider-modern">
<text class="text">商品详情</text>
</view>
<view class="image-list">
<image
v-for="(img, idx) in introductionImages"
:key="idx"
class="seamless-img"
:src="img"
mode="widthFix"
lazy-load
/>
</view>
</view>
<!-- 占位符防止底部操作栏遮挡内容 -->
<view class="footer-placeholder"></view>
<!-- 底部操作栏 -->
<view class="footer-bar safe-area-inset-bottom" v-if="!hidePurchase">
<view class="icons-area">
<view class="icon-item" @click="goBack">
<u-icon name="home" size="40" color="#606266"></u-icon>
<text>首页</text>
</view>
<view class="icon-item" @click="goToCart">
<view class="icon-rel">
<u-icon name="shopping-cart" size="40" color="#606266"></u-icon>
<!-- 徽标 -->
<view class="badge-custom" v-if="cartCount > 0">{{ cartCount > 99 ? '99+' : cartCount }}</view>
</view>
<text>购物车</text>
</view>
</view>
<view class="btns-area">
<button class="btn btn-cart" @click="showCartPopup = true">加入购物车</button>
<button class="btn btn-buy" @click="handleBuyNow">
{{ productDetail.is_otc === 0 && productDetail.type == 2 ? '提交预定' : '立即购买' }}
</button>
</view>
</view>
<!-- === 以下为原有弹窗代码 (保留完整逻辑) === -->
<!-- 购物车弹窗 -->
<u-popup
v-model="showCartPopup"
mode="bottom"
border-radius="24"
:closeable="true"
@close="closeCartPopup"
>
<view class="popup-wrapper safe-area-bottom">
<view class="popup-header-title">选择购买数量</view>
<view class="product-info-row">
<image class="p-img" :src="productDetail.image" mode="aspectFill"></image>
<view class="p-info">
<view class="p-name">{{ productDetail.drug_name }}</view>
<view class="p-spec">{{ productDetail.specification || '暂无规格' }}</view>
<view class="p-price">¥{{ productPrice }}</view>
</view>
</view>
<view class="quantity-row">
<text class="label">购买数量</text>
<u-number-box v-model="cartQuantity" :min="1" :max="999" integer :step="1"></u-number-box>
</view>
<view class="popup-actions">
<button class="btn-cancel" @click="closeCartPopup">取消</button>
<button class="btn-confirm" @click="addToCart">确定</button>
</view>
</view>
</u-popup>
<!-- 立即购买弹窗非处方药 -->
<u-popup
v-model="showBuyPopup"
mode="bottom"
border-radius="24"
:closeable="true"
@close="closeBuyPopup"
>
<view class="popup-wrapper safe-area-bottom">
<view class="popup-header-title">选择购买数量</view>
<view class="product-info-row">
<image class="p-img" :src="productDetail.image" mode="aspectFill"></image>
<view class="p-info">
<view class="p-name">{{ productDetail.drug_name }}</view>
<view class="p-spec">{{ productDetail.specification || '暂无规格' }}</view>
<view class="p-price">¥{{ productPrice }}</view>
</view>
</view>
<view class="quantity-row">
<text class="label">购买数量</text>
<u-number-box v-model="buyQuantity" :min="1" :max="999" integer :step="1"></u-number-box>
</view>
<view class="popup-actions">
<button class="btn-cancel" @click="closeBuyPopup">取消</button>
<button class="btn-confirm" @click="buyNowConfirm">立即下单</button>
</view>
</view>
</u-popup>
<u-toast ref="uToast" />
</view>
</template>
<script>
// 完整引入原有API
import { getOrderTextApi, getStoreDrugDetailApi } from "@/request/api/product";
import { genOrderApi, getPrescriptionStatusApi } from "@/request/api/order";
import { getCartCountsApi, saveCartApi } from "@/request/api/cart";
import { canShowFunction, canShowUsage, canShowInstruction, isPrescriptionApprovedStatus } from '@/common/utils/drug';
export default {
data() {
return {
productDetail: {
id: '',
type: 0, // 新增:药品类型 (2:西药, 3:保健食品)
drug_name: '',
drug_alias: '',
image: '',
price: 0,
drug_store_drug: { price: 0 },
daijian_price: null,
specification: '',
function: '',
usage: '',
instruction: '',
is_otc: 1,
guozi_no: '',
bar_code: '',
indications: '',
indications_array: [],
2026-05-30 16:54:01 +08:00
compliance_tip: '', // 合规提示文案,由后端返回
supplier: null
},
formData: {
id: '',
hasVisited: '1',
hasUsedDrug: '1',
number: 1,
illnessInfo: '',
doctor_id: '',
type: '',
drug_name: '',
image: '',
price: 0
},
productImages: [],
isFavorite: false,
cartCount: 0,
showCartPopup: false,
cartQuantity: 1,
showBuyPopup: false,
buyQuantity: 1,
orderText: '本次商品物流配送,由萧康医药提供服务。',
introductionImages: [], // 商品介绍图数组
2025-12-25 14:28:25 +08:00
delegateStoreId: '', // 委托诊所ID
fromOrderId: '',
orderStoreId: '',
prescriptionApproved: false,
hidePurchase: false,
};
},
computed: {
productPrice() {
const rel = this.productDetail.drug_store_relations;
if (rel && rel.price) {
return parseFloat(rel.price);
}
if (this.productDetail.drug_store_drug && this.productDetail.drug_store_drug.price) {
return parseFloat(this.productDetail.drug_store_drug.price);
}
return parseFloat(this.productDetail.price || 0);
},
// 合规提示文案 - 从后端返回的 compliance_tip 字段获取
complianceTip() {
return this.productDetail.compliance_tip || '';
2026-05-30 16:54:01 +08:00
},
manufacturerName() {
return (this.productDetail.supplier && this.productDetail.supplier.name) || '';
},
showFunctionSection() {
return canShowFunction(this.productDetail, this.prescriptionApproved);
},
showUsageSection() {
return canShowUsage(this.productDetail, this.prescriptionApproved);
},
showInstructionSection() {
return canShowInstruction(this.productDetail, this.prescriptionApproved);
}
},
onLoad(options) {
if (options.store_id) {
this.orderStoreId = options.store_id;
}
if (options.order_id) {
this.fromOrderId = options.order_id;
this.hidePurchase = true;
}
if (options.id) {
this.productDetail.id = options.id;
this.formData.id = options.id;
this.formData.type = options.type || '';
this.formData.image = options.image || '';
this.formData.price = parseFloat(options.price || 0);
this.formData.doctor_id = options.doctor_id || '';
this.formData.drug_name = options.drug_name || '';
this.productDetail.drug_name = options.drug_name || '';
this.productDetail.image = options.image || '';
this.productDetail.price = parseFloat(options.price || 0);
2025-12-25 14:28:25 +08:00
// 获取委托诊所ID如果有
this.delegateStoreId = options.delegate_store_id || '';
this.initPageData();
} else {
const savedId = uni.getStorageSync('symptoms_id');
if (savedId) {
this.productDetail.id = savedId;
this.formData.id = savedId;
this.initPageData();
}
}
if (!this.hidePurchase) {
this.updateCartCount();
}
},
onShow() {
if (!this.hidePurchase) {
this.updateCartCount();
}
},
methods: {
initPageData() {
if (this.fromOrderId) {
this.fetchPrescriptionStatus().then(() => {
this.getDetail();
});
} else {
this.getDetail();
}
},
fetchPrescriptionStatus() {
return getPrescriptionStatusApi({
order_id: this.fromOrderId,
}).then((res) => {
if (res.data && (res.data.code == 0 || res.data.errcode == 0)) {
const data = res.data.data || res.data.result || {};
this.prescriptionApproved = isPrescriptionApprovedStatus(data);
} else {
this.prescriptionApproved = false;
}
}).catch(() => {
this.prescriptionApproved = false;
});
},
getOrderText() {
getOrderTextApi().then((res) => {
if (res.data && res.data.result) {
this.orderText = res.data.result;
}
}).catch(err => {
console.error('获取订单提示失败:', err);
})
},
getDetail() {
this.getOrderText();
const params = {
id: this.productDetail.id,
store_id: this.orderStoreId || uni.getStorageSync('store_id') || '11001',
};
if (this.fromOrderId) {
params.order_id = this.fromOrderId;
}
getStoreDrugDetailApi(params).then((res) => {
if (res.data.code === 0) {
const data = res.data.result;
Object.assign(this.productDetail, data);
if (data.prescription_approved !== undefined) {
this.prescriptionApproved = !!data.prescription_approved;
}
if (data.hide_purchase !== undefined) {
this.hidePurchase = !!data.hide_purchase;
}
if (data.order_store_id && !this.orderStoreId) {
this.orderStoreId = data.order_store_id;
}
this.productImages = data.image ? [data.image] : [];
if (!this.productDetail.indications && data.function) {
this.productDetail.indications = data.function;
}
// 设置商品介绍图
if (data.introduction_images && Array.isArray(data.introduction_images)) {
this.introductionImages = data.introduction_images;
} else {
this.introductionImages = [];
}
}
}).catch(error => {
console.error('加载商品详情失败:', error);
});
},
previewImage(index) {
if (this.productImages.length > 0) {
uni.previewImage({
current: index,
urls: this.productImages
});
}
},
previewInstruction() {
if (this.productDetail.instruction) {
uni.previewImage({
urls: [this.productDetail.instruction]
});
}
},
// 预览商品介绍图
previewIntroductionImage(index) {
if (this.introductionImages.length > 0) {
uni.previewImage({
current: index,
urls: this.introductionImages
});
}
},
updateCartCount() {
getCartCountsApi({
store_id: uni.getStorageSync('store_id') || '11001',
page: 1,
limit: 100
}).then((res) => {
if (res.data.code == 0) {
this.cartCount = res.data.result.cart_count;
}
})
},
addToCart() {
// 检查是否为处方药is_otc === 0 && type == 2
if (this.productDetail.is_otc === 0 && this.productDetail.type == 2) {
uni.showToast({
title: '处方药不能加入购物车,请通过提交预定购买',
icon: 'none',
duration: 2000
});
return;
}
saveCartApi({
store_id: uni.getStorageSync('store_id') || '11001',
drug_id: this.productDetail.id,
quantity: this.cartQuantity
}).then(() => {
this.updateCartCount();
this.closeCartPopup();
uni.showToast({
title: `已加入${this.cartQuantity}件商品`,
icon: 'success',
duration: 1500
});
})
},
handleBuyNow() {
if (this.productDetail.is_otc === 0 && this.productDetail.type == 2) {
const q = [
`mode=pharmacy`,
`id=${encodeURIComponent(this.productDetail.id)}`,
`doctor_id=${encodeURIComponent(this.formData.doctor_id || '')}`,
`delegate_store_id=${encodeURIComponent(this.delegateStoreId || '')}`,
`type=${encodeURIComponent(String(this.productDetail.type || 2))}`,
`image=${encodeURIComponent(this.productDetail.image || '')}`,
`price=${encodeURIComponent(String(this.productPrice))}`,
`drug_name=${encodeURIComponent(this.productDetail.drug_name || '')}`,
].join('&');
uni.navigateTo({
url: `/subPackages/follow-up/medication-info?${q}`,
});
} else {
this.showBuyPopup = true;
}
},
buyNowConfirm() {
genOrderApi({
drug_id: this.productDetail.id,
number: this.buyQuantity,
store_id: uni.getStorageSync('store_id') || '11001',
}).then((resOrder) => {
this.closeBuyPopup();
uni.navigateTo({
url: `/subPackages/my/my-drug/drug-info?id=${resOrder.data.result.id}&order_type=${resOrder.data.result.order_type}`
});
})
},
goToCart() {
uni.navigateTo({
url: '/subPackages/shop/shop-cate'
});
},
goBack() {
uni.navigateBack({
delta: 1
});
},
2025-12-31 10:18:07 +08:00
// 跳转平台资质页面
goPlatformQualification() {
uni.navigateTo({
url: '/pages/index/platform-info?store_id=0'
});
},
closeCartPopup() {
this.showCartPopup = false;
this.cartQuantity = 1;
},
closeBuyPopup() {
this.showBuyPopup = false;
this.buyQuantity = 1;
},
}
}
</script>
<style lang="scss" scoped>
.container {
background-color: #F5F7FA;
min-height: 100vh;
}
/* 1. 轮播图 */
.swiper-section {
height: 750rpx;
background: #fff;
.swiper, .swiper-image { width: 100%; height: 100%; }
}
/* 2. 基础信息区 */
.basic-info {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
border-radius: 0 0 24rpx 24rpx;
.price-row {
display: flex;
justify-content: space-between;
align-items: center; // 垂直居中对齐
margin-bottom: 24rpx;
.price-left {
color: #FF4D4F;
display: flex;
align-items: baseline;
.symbol { font-size: 28rpx; font-weight: bold; margin-right: 4rpx; }
.amount { font-size: 56rpx; font-weight: 800; font-family: 'DIN Alternate', sans-serif; line-height: 1; }
.old-price { font-size: 24rpx; color: #999; text-decoration: line-through; margin-left: 16rpx; }
}
// 优化:胶囊式标签
.tag-capsule {
display: flex;
align-items: center;
padding: 0 20rpx;
height: 48rpx;
border-radius: 24rpx;
&.type-rx {
background: #FEF0F0;
border: 2rpx solid #FAB6B6;
.tag-text { color: #F56C6C; font-size: 24rpx; font-weight: bold; }
}
&.type-otc {
background: #F0F9EB;
border: 2rpx solid #B3E19D;
.tag-text { color: #67C23A; font-size: 24rpx; font-weight: bold; }
}
&.type-health {
background: #FDF6EC;
border: 2rpx solid #FAECD8;
.tag-text { color: #E6A23C; font-size: 24rpx; font-weight: bold; }
}
}
}
.title { font-size: 36rpx; font-weight: 800; color: #303133; line-height: 1.4; margin-bottom: 12rpx; }
.sub-title { font-size: 26rpx; color: #909399; margin-bottom: 24rpx; }
.spec-bar {
background: #F7F8FA;
padding: 24rpx;
border-radius: 12rpx;
.spec-item {
display: flex;
font-size: 26rpx;
margin-bottom: 12rpx;
&:last-child { margin-bottom: 0; }
.label { color: #909399; width: 140rpx; }
.val { color: #303133; font-weight: 500; }
}
}
2025-12-31 10:18:07 +08:00
.platform-qualification-entry {
display: flex;
align-items: center;
justify-content: space-between;
background: #F7F8FA;
padding: 24rpx;
border-radius: 12rpx;
margin-top: 20rpx;
font-size: 28rpx;
color: #606266;
}
}
/* 3. 物流提示 */
.logistics-tip {
background: #ECF5FF;
padding: 24rpx 30rpx;
margin-bottom: 20rpx;
display: flex;
align-items: center;
font-size: 26rpx;
color: #2B85E4;
}
/* 4. 信息卡片 & 优化标题 */
.info-card {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
// 优化:现代风格标题
.section-title-modern {
display: flex;
align-items: center;
font-size: 32rpx;
font-weight: 800;
color: #333;
margin-bottom: 24rpx;
.indicator {
width: 8rpx;
height: 32rpx;
background: linear-gradient(180deg, #2B85E4 0%, #5CADFF 100%);
border-radius: 4rpx;
margin-right: 16rpx;
}
}
.card-body {
font-size: 28rpx;
color: #606266;
line-height: 1.8;
text-align: justify;
}
.instruction-content {
.instruction-image { width: 100%; border-radius: 8rpx; }
.no-instruction {
padding: 40rpx 0;
display: flex;
flex-direction: column;
align-items: center;
text { font-size: 26rpx; color: #999; margin-top: 10rpx; }
}
}
}
/* 合规提示卡片 - 与物流提示UI一致 */
.compliance-tip-card {
background: #FFF1F0;
padding: 24rpx 30rpx;
margin: 0 -30rpx 20rpx -30rpx;
display: flex;
align-items: center;
font-size: 26rpx;
color: #FF4D4F;
.tip-text {
font-weight: 600;
line-height: 1.6;
flex: 1;
}
}
/* 5. 详情图区域 */
.detail-images-wrapper {
background: #fff;
padding-bottom: 20rpx;
.section-divider-modern {
text-align: center;
padding: 40rpx 0;
.text {
font-size: 28rpx;
color: #909399;
position: relative;
&::before, &::after {
content: '';
display: inline-block;
width: 60rpx;
height: 1px;
background: #E4E7ED;
vertical-align: middle;
margin: 0 20rpx;
}
}
}
.image-list {
width: 100%;
font-size: 0; // 消除间隙
.seamless-img { width: 100%; display: block; }
}
}
/* 6. 底部操作栏 */
.footer-placeholder {
height: calc(110rpx + env(safe-area-inset-bottom));
min-height: 140rpx; /* 确保最小高度,避免在小屏设备上被遮挡 */
}
.footer-bar {
position: fixed;
bottom: 0;
left: 0;
width: 100%;
background: #fff;
height: 110rpx;
box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
display: flex;
align-items: center;
padding: 0 20rpx;
box-sizing: border-box;
z-index: 999;
padding-bottom: env(safe-area-inset-bottom);
height: calc(110rpx + env(safe-area-inset-bottom));
.icons-area {
display: flex;
margin-right: 30rpx;
.icon-item {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-right: 30rpx;
position: relative;
min-width: 80rpx;
text { font-size: 20rpx; color: #606266; margin-top: 4rpx; }
.icon-rel {
position: relative;
.badge-custom {
position: absolute;
top: -6rpx;
right: -10rpx;
background: #FA3534;
color: #fff;
font-size: 20rpx;
padding: 2rpx 8rpx;
border-radius: 20rpx;
border: 2rpx solid #fff;
line-height: 1;
}
}
}
}
.btns-area {
flex: 1;
display: flex;
gap: 20rpx;
.btn {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 28rpx;
font-weight: bold;
border: none;
display: flex;
align-items: center;
justify-content: center;
&.btn-cart {
background: #EBF4FF;
color: #2B85E4;
}
&.btn-buy {
background: linear-gradient(90deg, #5CADFF 0%, #2B85E4 100%);
color: #fff;
box-shadow: 0 6rpx 12rpx rgba(43, 133, 228, 0.3);
}
}
}
}
/* 7. 弹窗样式补充 */
.popup-wrapper {
padding: 30rpx;
background: #fff;
&.safe-area-bottom {
padding-bottom: calc(30rpx + env(safe-area-inset-bottom));
}
.popup-header-title {
text-align: center;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 30rpx;
}
.product-info-row {
display: flex;
margin-bottom: 30rpx;
.p-img { width: 160rpx; height: 160rpx; border-radius: 12rpx; background: #f8f8f8; margin-right: 20rpx; }
.p-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.p-name { font-size: 30rpx; font-weight: bold; color: #333; }
.p-spec { font-size: 24rpx; color: #999; }
.p-price { font-size: 36rpx; color: #FF4D4F; font-weight: bold; }
}
}
.quantity-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-top: 1rpx solid #f5f5f5;
border-bottom: 1rpx solid #f5f5f5;
margin-bottom: 40rpx;
.label { font-size: 28rpx; color: #333; }
}
.popup-actions {
display: flex;
gap: 30rpx;
button {
flex: 1;
height: 80rpx;
border-radius: 40rpx;
font-size: 30rpx;
border: none;
&.btn-cancel { background: #F5F7FA; color: #606266; }
&.btn-confirm { background: #2B85E4; color: #fff; }
}
}
}
</style>