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

895 lines
26 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="container">
<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 逻辑 -->
<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>
<view class="spec-item" v-if="manufacturerName">
<text class="label">厂家</text>
<text class="val">{{ manufacturerName }}</text>
</view>
</view>
<!-- 平台资质入口 -->
<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. 功效与用法 (卡片式布局 + 优化标题) -->
<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: [],
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: [], // 商品介绍图数组
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 || '';
},
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);
// 获取委托诊所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
});
},
// 跳转平台资质页面
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; }
}
}
.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>