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 只读医生详情,
  隐藏预约挂号区
This commit is contained in:
李琦
2026-07-21 16:05:09 +08:00
parent 2d2a8abfd4
commit 8b455b1920
11 changed files with 658 additions and 353 deletions

View File

@@ -17,6 +17,7 @@
"style": {
// "navigationBarTitleText": "custom",
"navigationStyle": "custom",
"enablePullDownRefresh": true,
// "navigationStyle": "default",
// "navigationBarBackgroundColor": "#298DFF",
// "navigationBarTextStyle": "white"
@@ -60,6 +61,7 @@
"path": "pages/cate/index",
"style": {
"navigationBarTitleText": "健康资讯",
"enablePullDownRefresh": true,
// 主包异步引用分包 ListSkeleton
"componentPlaceholder": {
"list-skeleton": "view"
@@ -78,6 +80,7 @@
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "消息",
"enablePullDownRefresh": true,
// 主包异步引用分包 ListSkeleton
"componentPlaceholder": {
"list-skeleton": "view"

View File

@@ -5,7 +5,8 @@
<view class="search">
<u-search placeholder="输入文章标题" bg-color="#F5f5f5" color="#DDDDDD" border-color="#F5f5f5"
:show-action="false" maxlength="21" height="66" :clearabled="true" shape="round" v-model="keyword"
@search="toSearch">
@search="toSearch"
@clear="clearSearch">
</u-search>
</view>
@@ -88,9 +89,18 @@
},
methods: {
// 搜索
// 搜索:按当前分类 + 标题关键词拉文章(不再误调分类接口)
toSearch() {
this.getList()
this.infoList = []
this.articleFetchingEmpty = true
this.getArticleList()
},
// 清空搜索词后重新拉当前分类文章
clearSearch() {
this.keyword = ''
this.infoList = []
this.articleFetchingEmpty = true
this.getArticleList()
},
/*切换需要*/
changeStyle(id) {
@@ -102,16 +112,17 @@
url: '/pages/cate/info-detail?id=' + e
})
},
//点击药品一级分类e不清空 infoList有旧数据则保留到新结果返回
// 切分类:立即清空文章出骨架
handleCategory1(e, index) {
this.num = e
this.cid = e
this.infoList = []
this.articleFetchingEmpty = true
this.getArticleList()
},
//获取药品分类:成功后再覆盖,失败保留旧分类
// 获取分类:返回 Promise 供下拉刷新等待
getList() {
categoriesList({
return categoriesList({
method: "post",
data: {
store_id: uni.getStorageSync('store_id') || '11001',
@@ -129,23 +140,28 @@
const firstId = list[0].id
this.num = firstId
this.cid = firstId
this.getArticleList()
return this.getArticleList()
}
}).finally(() => {
this.categoryInitialLoading = false
})
},
// 文章列表:请求前不清空;仅当当前无文章可展示时打骨架标记
// 文章列表:可选 keyword 标题搜索
getArticleList() {
if (!this.infoList || this.infoList.length === 0) {
this.articleFetchingEmpty = true
}
articleList({
const data = {
store_id: uni.getStorageSync('store_id') || '11001',
cid: this.cid
}
const kw = (this.keyword || '').trim()
if (kw) {
data.keyword = kw
}
return articleList({
method: "get",
data: {
store_id: uni.getStorageSync('store_id') || '11001',
cid: this.cid
}
data
}).then((res) => {
if (res.data.code == 0) {
this.infoList = (res.data.result && res.data.result.list) || []
@@ -154,6 +170,19 @@
this.articleInitialLoading = false
this.articleFetchingEmpty = false
})
},
/**
* 下拉刷新:清空分类/文章并重走开骨架
*/
onPullDownRefresh() {
this.categoryLists = []
this.infoList = []
this.categoryInitialLoading = true
this.articleInitialLoading = true
this.articleFetchingEmpty = false
Promise.resolve(this.getList()).finally(() => {
uni.stopPullDownRefresh()
})
}
},
mounted() {

View File

@@ -86,14 +86,20 @@
</view>
<!-- 药店模式单列铺满 -->
<view v-if="storeType === '1' || storeType === 1" class="nav-pharmacy-grid">
<view v-if="storeType === '1' || storeType === 1" class="nav-pharmacy-grid book-medication-appointment-card" @click="golist(3)">
<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>
<!-- <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>-->
<image
src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20260721/91af7f9d-3a99-45bb-aa3b-b0edb39d464c_1784599019.png"
class="nav-icon-img-256"
style="width:320rpx; height:220rpx; right:20rpx; bottom:0rpx;"
></image>
</view>
<!-- <image src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20260721/0d1b63f3-907b-4045-981a-5c298c10cac7_1784599580.jpg" style="width: 100%;" mode="aspectFit">-->
</view>
</view>
@@ -200,7 +206,7 @@
<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">
@@ -697,8 +703,8 @@ export default {
})
},
getList() {
// 不再请求前清空 doctorList避免 onShow 刷新时出现「先空后满」
doctorList({
// 返回 Promise 供下拉刷新 Promise.all 等待
return doctorList({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
@@ -726,7 +732,7 @@ export default {
})
},
getBannered() {
getBanner({
return getBanner({
method: "post",
data: { store_id: uni.getStorageSync('store_id') || '11001' }
}).then((res) => {
@@ -758,7 +764,7 @@ export default {
}
},
getProductList() {
getStoreDrugListBySalespersonApi({
return getStoreDrugListBySalespersonApi({
store_id: uni.getStorageSync('store_id') || '11001',
page: 1,
limit: 3
@@ -772,7 +778,7 @@ export default {
})
},
getHomeTopProducts() {
getHomeTopProductsApi({
return getHomeTopProductsApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data.code == 0 && res.data.result) {
@@ -792,7 +798,7 @@ export default {
}
},
getHomeZones() {
getHomeZonesApi({
return getHomeZonesApi({
store_id: uni.getStorageSync('store_id') || '11001',
}).then((res) => {
if (res.data.code == 0) {
@@ -807,6 +813,32 @@ export default {
this.homeZonesInitialLoading = false
})
},
/**
* 下拉刷新:清空金刚区与卡片,重新走骨架后再拉数
* 为什么:用户期望刷新时看到加载态,而不是短暂保留旧数据
*/
refreshHomeContent() {
this.homeZones = [];
this.homeZonesInitialLoading = true;
this.doctorList = [];
this.doctorListInitialLoading = true;
this.productList = [];
this.topProductsList = [];
this.productListInitialLoading = true;
this._productFetchDone = 0;
return Promise.all([
this.getHomeZones(),
this.getList(),
this.getProductList(),
this.getHomeTopProducts(),
this.getBannered()
]);
},
onPullDownRefresh() {
this.refreshHomeContent().finally(() => {
uni.stopPullDownRefresh();
});
},
getPlatformQualifications() {
getPlatformQualificationsApi({
store_id: uni.getStorageSync('store_id') || '11001',
@@ -1022,6 +1054,7 @@ $card-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.04);
&::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-256 { position: absolute; right: 0; bottom: 0; width: 256rpx; height: 138rpx; z-index: 1; }
.nav-icon-img-64 { position: absolute; right: 30rpx; top: 30rpx; width: 84rpx; height: 84rpx; z-index: 1; }
}
}

View File

@@ -323,7 +323,7 @@ export default {
},
// 拉取 IM 好友会话列表,成功后整份替换,失败不写空
getMyChatFriendsList() {
getChatFriendsListApi().then((res) => {
return getChatFriendsListApi().then((res) => {
const allChatList = res.data.result || []
// 根据type字段区分在线咨询和在线复诊
this.onlineConsultationList = allChatList.filter(item => item.type === 1 || !item.type) // type=1或未设置type的为在线咨询
@@ -338,7 +338,7 @@ export default {
},
// 获取转诊消息列表;失败不重置 transfer避免闪空
getTransferList() {
getTransferPrescriptionListApi().then((res) => {
return getTransferPrescriptionListApi().then((res) => {
if (res.data && res.data.code == 0) {
const transferList = res.data.result || []
// 统计待确认的转诊消息数量
@@ -358,6 +358,33 @@ export default {
}).catch((error) => {
console.error('获取转诊消息列表失败:', error)
})
},
/**
* 下拉刷新:重置骨架并并行重拉消息概览 / IM / 转诊
*/
refreshMessagePage() {
this.noticeInitialLoading = true
if (checkDev('open-im')) {
this.chatInitialLoading = true
this.onlineConsultationList = []
this.onlineReconsultationList = []
this.imChat = []
}
const tasks = [
this.getInfo(),
this.getTransferList()
]
if (checkDev('open-im')) {
tasks.push(this.getMyChatFriendsList())
} else {
this.chatInitialLoading = false
}
return Promise.all(tasks)
},
onPullDownRefresh() {
this.refreshMessagePage().finally(() => {
uni.stopPullDownRefresh()
})
}
},
mounted() {

View File

@@ -80,22 +80,18 @@ const isDev = checkDev('dev');
const ENV_URLS = {
// 生产环境
prod: {
// main: "https://app.xiaokang88.com/member",
// xkApi: "https://api.xiaokang88.com/api/mobile",
// shop: "https://shop.xiaokang88.com/member",
// im: "https://api.ws.g.xiaokang88.com/api",
main: "https://app.xiaokang88.com/member",
xkApi: "https://api.xiaokang88.com/api/mobile",
shop: "https://shop.xiaokang88.com/member",
im: "https://api.ws.g.xiaokang88.com/api",
},
// 测试环境
test: {
main: "https://xk.test.saas.api-yii.nailaoyun.cn/member",
xkApi: "https://xk.test.saas.api.nailaoyun.cn/api/mobile",
shop: "https://test.xk.shop.nailaoyun.cn/member1",
im: "https://xk.ws.nailaoyun.cn/api"
},
// 测试环境
test: {
main: "http://xk.test.saas.api.yii.nailaoyun.cn/member",
xkApi: "http://xk.test.saas.api.nailaoyun.cn/api/mobile",
shop: "https://test.xk.shop.nailaoyun.cn/member1",
im: ""
},
// 本地开发环境
local: {
main: "http://127.0.0.1:18000/member",
@@ -112,7 +108,7 @@ function getEnvUrls() {
return ENV_URLS.local;
}
// 检查是否开启了开发者测试模式
const devMode = uni.getStorageSync('dev_mode');
const devMode = checkDev('test');
return devMode ? ENV_URLS.test : ENV_URLS.prod;
}

View File

@@ -1,59 +1,85 @@
<template>
<!--
列表骨架屏 type 模拟医生卡/消息行/商品卡/资讯卡/分类侧栏
列表骨架屏 type 模拟医生卡/消息行/商品卡/资讯卡/分类侧栏/商品详情
放在分包以控制主包体积主包页通过 componentPlaceholder 异步引用
-->
<view class="list-skeleton" :class="'list-skeleton--' + type">
<view
v-for="i in rowCount"
:key="i"
class="sk-row"
:class="'sk-row--' + type"
>
<!-- 医生推荐卡片 -->
<template v-if="type === 'doctor'">
<view class="sk-avatar sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line sk-line--lg sk-shimmer"></view>
<view class="sk-line sk-line--md sk-shimmer"></view>
<!-- 商品详情整页一块骨架不按 rows 循环 -->
<template v-if="type === 'detail'">
<view class="sk-detail-hero sk-shimmer"></view>
<view class="sk-detail-basic">
<view class="sk-line sk-line--price sk-shimmer"></view>
<view class="sk-line sk-line--lg sk-shimmer"></view>
<view class="sk-line sk-line--md sk-shimmer"></view>
<view class="sk-detail-spec">
<view class="sk-line sk-line--sm sk-shimmer"></view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
</view>
</template>
<!-- 消息列表行 -->
<template v-else-if="type === 'message'">
<view class="sk-avatar sk-avatar--round sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line-row">
</view>
<view class="sk-detail-tip sk-shimmer"></view>
<view
v-for="i in detailCardCount"
:key="'card-' + i"
class="sk-detail-card"
>
<view class="sk-line sk-line--md sk-shimmer"></view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
<view class="sk-line sk-line--lg sk-shimmer"></view>
</view>
</template>
<!-- 列表类骨架按 rows 循环 -->
<template v-else>
<view
v-for="i in rowCount"
:key="i"
class="sk-row"
:class="'sk-row--' + type"
>
<!-- 医生推荐卡片 -->
<template v-if="type === 'doctor'">
<view class="sk-avatar sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line sk-line--lg sk-shimmer"></view>
<view class="sk-line sk-line--md sk-shimmer"></view>
<view class="sk-line sk-line--xs sk-shimmer"></view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
</view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
</view>
</template>
<!-- 商品卡片 -->
<template v-else-if="type === 'product'">
<view class="sk-prod-img sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line sk-line--lg sk-shimmer"></view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
<view class="sk-line sk-line--md sk-shimmer"></view>
</view>
</template>
<!-- 资讯文章卡片 -->
<template v-else-if="type === 'article'">
<view class="sk-cover sk-shimmer"></view>
<view class="sk-cover-title sk-shimmer"></view>
</template>
<!-- 资讯左侧分类 -->
<template v-else-if="type === 'category'">
<view class="sk-cate-item sk-shimmer"></view>
</template>
<!-- 金刚区横滑块 -->
<template v-else-if="type === 'zone'">
<view class="sk-zone-icon sk-shimmer"></view>
<view class="sk-line sk-line--xs sk-shimmer"></view>
</template>
</view>
</template>
<!-- 消息列表行 -->
<template v-else-if="type === 'message'">
<view class="sk-avatar sk-avatar--round sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line-row">
<view class="sk-line sk-line--md sk-shimmer"></view>
<view class="sk-line sk-line--xs sk-shimmer"></view>
</view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
</view>
</template>
<!-- 商品卡片 -->
<template v-else-if="type === 'product'">
<view class="sk-prod-img sk-shimmer"></view>
<view class="sk-body">
<view class="sk-line sk-line--lg sk-shimmer"></view>
<view class="sk-line sk-line--sm sk-shimmer"></view>
<view class="sk-line sk-line--md sk-shimmer"></view>
</view>
</template>
<!-- 资讯文章卡片 -->
<template v-else-if="type === 'article'">
<view class="sk-cover sk-shimmer"></view>
<view class="sk-cover-title sk-shimmer"></view>
</template>
<!-- 资讯左侧分类 -->
<template v-else-if="type === 'category'">
<view class="sk-cate-item sk-shimmer"></view>
</template>
<!-- 金刚区横滑块 -->
<template v-else-if="type === 'zone'">
<view class="sk-zone-icon sk-shimmer"></view>
<view class="sk-line sk-line--xs sk-shimmer"></view>
</template>
</view>
</template>
</view>
</template>
@@ -61,15 +87,15 @@
export default {
name: 'ListSkeleton',
props: {
// 骨架样式类型
// 骨架样式类型detail 为商品详情整页骨架)
type: {
type: String,
default: 'message',
validator(v) {
return ['doctor', 'message', 'product', 'article', 'category', 'zone'].indexOf(v) !== -1
return ['doctor', 'message', 'product', 'article', 'category', 'zone', 'detail'].indexOf(v) !== -1
}
},
// 行数
// 行数detail 时表示信息卡片块数)
rows: {
type: [Number, String],
default: 3
@@ -79,6 +105,10 @@ export default {
rowCount() {
const n = Number(this.rows)
return n > 0 ? n : 3
},
// 详情骨架信息卡数量,默认 3 块
detailCardCount() {
return this.rowCount
}
}
}
@@ -272,4 +302,45 @@ export default {
width: 40%;
margin-bottom: 0;
}
/* 商品详情骨架:大图 + 基础信息 + 物流条 + 信息卡 */
.list-skeleton--detail {
background: #F5F7FA;
min-height: 100vh;
padding-bottom: 40rpx;
}
.sk-detail-hero {
width: 100%;
height: 750rpx;
}
.sk-detail-basic {
background: #fff;
padding: 32rpx 30rpx;
margin-bottom: 20rpx;
.sk-line--price {
width: 200rpx;
height: 48rpx;
margin-bottom: 24rpx;
}
}
.sk-detail-spec {
margin-top: 24rpx;
padding-top: 16rpx;
}
.sk-detail-tip {
height: 80rpx;
margin: 0 0 20rpx;
border-radius: 0;
}
.sk-detail-card {
background: #fff;
padding: 30rpx;
margin-bottom: 20rpx;
}
</style>

View File

@@ -1,8 +1,8 @@
<template>
<view class="container safe-area-inset-bottom">
<MessageNotification />
<!-- 物流提示信息 -->
<view class="order-text-tip" v-if="orderText">
<!-- 物流提示信息有外部配送仓时隐藏顶部文案改在药品行展示 -->
<view class="order-text-tip" v-if="orderText && !hasDeliveryWarehouse">
<text>{{ orderText }}</text>
</view>
@@ -90,6 +90,9 @@
<view class="it_name">{{item.drug_name}}
<text class="it_num" style="margin-left: 8rpx;">x{{item.number}}</text>
</view>
<view class="wh-tip" v-if="hasDeliveryWarehouse && item.delivery_warehouse_name">
该药品由{{ item.delivery_warehouse_name }}配送
</view>
</view>
</view>
@@ -109,6 +112,10 @@
<view class="it_info" v-if="orderInfo.ProductOrder.prescription_type==2 && item.drug && canShowUsage(item.drug, prescriptionStatus)">
{{ item.drug.usage }}
</view>
<!-- 有外部配送仓时按药展示配送来源含本仓药品 -->
<view class="wh-tip" v-if="hasDeliveryWarehouse && item.delivery_warehouse_name">
该药品由{{ item.delivery_warehouse_name }}配送
</view>
<!-- 添加查看说明书按钮 -->
<view class="instruction-btn" v-if="canShowInstruction(item.drug, prescriptionStatus) && item.drug && item.drug.instruction" @click.stop="previewInstruction(item.drug.instruction)">
<text>查看说明书</text>
@@ -300,6 +307,20 @@ export default {
const o = this.orderInfo.ProductOrder
return o.delivery_method == 0 && o.status != 0 && o.address && o.cancel_status == 0
},
// 整单存在任一外部配送仓时:隐藏顶部文案,改在药品行展示
hasDeliveryWarehouse() {
const o = this.orderInfo && this.orderInfo.ProductOrder
if (o && Number(o.has_delivery_warehouse) === 1) {
return true
}
const items = (this.orderInfo && this.orderInfo.ProductOrderItems) || []
for (let i = 0; i < items.length; i++) {
if (Number(items[i].delivery_warehouse_id || 0) > 0) {
return true
}
}
return false
},
},
onLoad(e) {
this.p_id = e.id
@@ -329,18 +350,27 @@ export default {
url: `/subPackages/product/symptoms?${q}`,
});
},
// 获取订单提示文本
// 获取订单提示文本(无外部配送仓时才用于顶部与支付弹窗)
getOrderText() {
getOrderTextApi().then((res) => {
if (res.data && res.data.result) {
this.orderText = res.data.result;
// 设置模态框内容
this.modalContent = `${res.data.result}\n\n请在确认以上信息后继续支付。`;
}
}).catch(err => {
console.error('获取订单提示失败:', err);
})
},
/**
* 有外部配送仓时支付弹窗用中性文案,避免仍写「由萧康医药提供服务」
*/
refreshPayModalContent() {
if (this.hasDeliveryWarehouse) {
this.modalContent = '请确认配送信息后继续支付。';
} else if (this.orderText) {
this.modalContent = `${this.orderText}\n\n请在确认以上信息后继续支付。`;
}
},
// 不需要代煎-代煎费
cancel() {
this.openshow = false;
@@ -538,6 +568,7 @@ export default {
// 查询处方状态
this.getPrescriptionStatus()
this.refreshPayModalContent()
}
})
},
@@ -598,7 +629,8 @@ export default {
return
}
// 先显示物流提示模态框
// 先显示物流提示模态框(有配送仓时弹窗为中性确认文案)
this.refreshPayModalContent();
this.showOrderTextModal = true;
},
@@ -751,6 +783,12 @@ export default {
line-height: 1.4;
border-left: 6rpx solid #1890ff;
}
.wh-tip {
margin-top: 8rpx;
font-size: 24rpx;
color: #1890ff;
line-height: 1.4;
}
.head_cancle {
padding: 20rpx 42rpx;

View File

@@ -1,8 +1,8 @@
<template>
<view class="container safe-area-inset-bottom">
<MessageNotification />
<!-- 物流提示信息 -->
<view class="order-text-tip" v-if="orderText">
<!-- 物流提示信息有外部配送仓时隐藏顶部文案改在药品行展示 -->
<view class="order-text-tip" v-if="orderText && !hasDeliveryWarehouse">
<text>{{ orderText }}</text>
</view>
@@ -70,16 +70,12 @@
<view class="list" v-if="chinese">
<view class="it" v-for="(item,index) in chinese.list" :key="item.id">
<!-- <view class="it_name">{{item.name}}</view> -->
<view class="it_name">{{item.name}}
<text class="it_num" style="margin-left: 8rpx;">x{{item.number}}</text>
</view>
<!-- <view class="it_price">
<text>{{item.price}}</text>
<view class="it_num">
x{{item.number}}
</view>
</view> -->
<view class="wh-tip" v-if="hasDeliveryWarehouse && item.delivery_warehouse_name">
该药品由{{ item.delivery_warehouse_name }}配送
</view>
</view>
</view>
@@ -89,13 +85,9 @@
<view class="it_name">{{item.name}}
<text class="it_num" style="margin-left: 8rpx;">x{{item.number}}</text>
</view>
<!-- <view class="it_name">{{item.name}}</view>
<view class="it_price">
<text>{{item.price}}</text>
<view class="it_num">
x{{item.number}}
</view>
</view> -->
<view class="wh-tip" v-if="hasDeliveryWarehouse && item.delivery_warehouse_name">
该药品由{{ item.delivery_warehouse_name }}配送
</view>
</view>
</view>
@@ -110,6 +102,9 @@
<view class="it_info" v-if="canShowUsage(item.content, prescriptionStatus)">
{{item.content.usage}}
</view>
<view class="wh-tip" v-if="hasDeliveryWarehouse && item.delivery_warehouse_name">
该药品由{{ item.delivery_warehouse_name }}配送
</view>
<view
class="instruction-btn"
v-if="canShowInstruction(item.content, prescriptionStatus) && item.content && item.content.instruction"
@@ -290,6 +285,22 @@
prescriptionStatus: false,
}
},
computed: {
// 整单存在任一外部配送仓时:隐藏顶部文案,改在药品行展示
hasDeliveryWarehouse() {
const o = this.orderInfo && this.orderInfo.ProductOrder
if (o && Number(o.has_delivery_warehouse) === 1) {
return true
}
const items = (this.orderInfo && this.orderInfo.ProductOrderItems) || []
for (let i = 0; i < items.length; i++) {
if (Number(items[i].delivery_warehouse_id || 0) > 0) {
return true
}
}
return false
},
},
onLoad(e) {
this.p_id = e.id
// 获取订单提示文本
@@ -301,18 +312,27 @@
methods: {
canShowUsage,
canShowInstruction,
// 获取订单提示文本
// 获取订单提示文本(无外部配送仓时才用于顶部与支付弹窗)
getOrderText() {
getOrderTextApi().then((res) => {
if (res.data && res.data.result) {
this.orderText = res.data.result;
// 设置模态框内容
this.modalContent = `${res.data.result}\n\n请在确认以上信息后继续支付。`;
}
}).catch(err => {
console.error('获取订单提示失败:', err);
})
},
/**
* 有外部配送仓时支付弹窗用中性文案
*/
refreshPayModalContent() {
if (this.hasDeliveryWarehouse) {
this.modalContent = '请确认配送信息后继续支付。';
} else if (this.orderText) {
this.modalContent = `${this.orderText}\n\n请在确认以上信息后继续支付。`;
}
},
// 物流提示确认 - 用户选择"我知道了"
handleOrderTextConfirm() {
this.showOrderTextModal = false;
@@ -521,6 +541,7 @@
}
}
this.getPrescriptionStatus()
this.refreshPayModalContent()
}
})
},
@@ -609,7 +630,8 @@
// 支付
toPay() {
// 先显示物流提示模态框
// 先显示物流提示模态框(有配送仓时弹窗为中性确认文案)
this.refreshPayModalContent();
this.showOrderTextModal = true;
},
@@ -736,6 +758,12 @@
line-height: 1.4;
border-left: 6rpx solid #1890ff;
}
.wh-tip {
margin-top: 8rpx;
font-size: 24rpx;
color: #1890ff;
line-height: 1.4;
}
.head_cancle {
padding: 20rpx 42rpx;

View File

@@ -21,105 +21,125 @@
</view>
</view>
<!-- 2. 顶部专区Tab -->
<view class="zone-tabs-wrapper">
<scroll-view scroll-x class="zone-tabs-scroll">
<view class="zone-tabs">
<view
v-for="(zone, index) in homeZones"
:key="index"
class="zone-tab-item"
:class="{ active: selectedZoneIndex === index }"
@click="onZoneChange(index)"
>
<text>{{ zone.title }}</text>
<!-- 2. 顶部专区 Tabis-scroll 自动滚到当前选中项与订单列表一致 -->
<view class="zone-tabs-head">
<u-tabs
v-if="zoneTabList.length > 0"
:list="zoneTabList"
:is-scroll="true"
:current="selectedZoneIndex"
@change="onTabChange"
></u-tabs>
</view>
<!-- 专区未就绪时整页骨架避免 swiper item 时白屏 -->
<view v-if="pageInitialLoading && homeZones.length === 0" class="zone-swiper zone-swiper--skeleton">
<view class="main-content">
<view class="category-sidebar">
<ListSkeleton type="category" :rows="8" />
</view>
<view class="product-area">
<ListSkeleton type="product" :rows="4" />
</view>
</view>
</view>
<!-- 3. swiper 丝滑切换专区内容为左分类 + 右商品 -->
<swiper
v-else
class="zone-swiper"
:current="selectedZoneIndex"
@change="onSwiperChange"
>
<swiper-item v-for="(zone, zoneIdx) in homeZones" :key="zone.id || zoneIdx">
<view class="main-content" v-if="Number(selectedZoneIndex) === zoneIdx">
<!-- 左侧分类栏 -->
<view class="category-sidebar">
<scroll-view scroll-y class="category-scroll">
<ListSkeleton v-if="showCategorySkeleton" type="category" :rows="8" />
<block v-else>
<view
class="category-item"
:class="{ active: selectedCategoryId === 0 }"
@click="selectCategory(0)"
>
<text>全部</text>
</view>
<view
v-for="cat in categoryList"
:key="cat.id"
class="category-item"
:class="{ active: selectedCategoryId === cat.id }"
@click="selectCategory(cat.id)"
>
<text class="u-line-1">{{ cat.category_name }}</text>
</view>
</block>
</scroll-view>
</view>
<!-- 右侧商品列表下拉刷新 -->
<view class="product-area">
<scroll-view
scroll-y
class="product-scroll"
@scrolltolower="loadMore"
refresher-enabled="true"
:refresher-triggered="isRefreshing"
@refresherrefresh="onRefresherRefresh"
>
<ListSkeleton v-if="showProductSkeleton" type="product" :rows="4" />
<block v-else>
<!-- 商品列表 - 两列布局 -->
<view class="product-grid" v-if="products.length > 0">
<view
v-for="(item, index) in products"
:key="item.id"
class="product-card"
@click="handClickProduct(item)"
>
<view class="product-img-box">
<image
class="product-img"
:class="{ 'prescription-blur': item.drug && item.drug.is_otc === 0 && item.drug.type == 2 }"
: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"
lazy-load
/>
</view>
<view class="product-info">
<view class="product-title u-line-2">{{ item.drug ? item.drug.drug_name : '' }}</view>
<view class="product-tags" v-if="item.drug && item.drug.type == 2">
<text
class="tag-label"
:class="getProductTag(item.drug).className"
>
{{ getProductTag(item.drug).label }}
</text>
<text class="tag-spec u-line-1">{{ (item.drug && item.drug.specification) ? item.drug.specification : '标准规格' }}</text>
</view>
<view class="product-bottom">
<view class="price-box">
<text class="price-symbol">¥</text>
<text class="price-val">{{ item.price }}</text>
</view>
<view class="cart-btn" @click.stop="showCartPopup(item)">
<u-icon name="plus" color="#ffffff" size="26"></u-icon>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态仅非加载中且无数据时展示 -->
<view v-if="products.length === 0 && !listFetching" class="empty-state">
<u-empty text="暂无相关药品" mode="search" margin-top="100"></u-empty>
</view>
</block>
</scroll-view>
</view>
</view>
</scroll-view>
</view>
<!-- 3. 主体内容区左侧分类 + 右侧商品 -->
<view
class="main-content"
@touchstart="onZoneTouchStart"
@touchend="onZoneTouchEnd"
>
<!-- 左侧分类栏 -->
<view class="category-sidebar">
<scroll-view scroll-y class="category-scroll">
<view
class="category-item"
:class="{ active: selectedCategoryId === 0 }"
@click="selectCategory(0)"
>
<text>全部</text>
</view>
<view
v-for="cat in categoryList"
:key="cat.id"
class="category-item"
:class="{ active: selectedCategoryId === cat.id }"
@click="selectCategory(cat.id)"
>
<text class="u-line-1">{{ cat.category_name }}</text>
</view>
</scroll-view>
</view>
<!-- 右侧商品列表区 -->
<view class="product-area">
<scroll-view scroll-y class="product-scroll" @scrolltolower="loadMore">
<!-- 商品列表 - 两列布局 -->
<view class="product-grid" v-if="products.length > 0">
<view
v-for="(item, index) in products"
:key="item.id"
class="product-card"
@click="handClickProduct(item)"
>
<!-- 商品图片 -->
<view class="product-img-box">
<image
class="product-img"
:class="{ 'prescription-blur': item.drug && item.drug.is_otc === 0 && item.drug.type == 2 }"
: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"
lazy-load
/>
</view>
<!-- 商品信息 -->
<view class="product-info">
<view class="product-title u-line-2">{{ item.drug ? item.drug.drug_name : '' }}</view>
<view class="product-tags" v-if="item.drug.type == 2">
<text
class="tag-label"
:class="getProductTag(item.drug).className"
>
{{ getProductTag(item.drug).label }}
</text>
<text class="tag-spec u-line-1">{{ (item.drug && item.drug.specification) ? item.drug.specification : '标准规格' }}</text>
</view>
<view class="product-bottom">
<view class="price-box">
<text class="price-symbol">¥</text>
<text class="price-val">{{ item.price }}</text>
</view>
<view class="cart-btn" @click.stop="showCartPopup(item)">
<u-icon name="plus" color="#ffffff" size="26"></u-icon>
</view>
</view>
</view>
</view>
</view>
<!-- 空状态 -->
<view v-if="products.length === 0" class="empty-state">
<u-empty text="暂无相关药品" mode="search" margin-top="100"></u-empty>
</view>
</scroll-view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 购物车悬浮球 -->
<view
@@ -184,17 +204,15 @@ export default {
return {
products: [],
searchKeyword: "",
// 专区相关
homeZones: [],
selectedZoneIndex: 0,
selectedZoneType: 'otc',
zoneTouch: { startX: 0, startY: 0 },
// 与订单列表一致:防止 tabs / swiper 互相触发形成回路
tabChanging: false,
// 分类相关
categoryList: [],
selectedCategoryId: 0,
doctorId: 0,
r_type: 0,
delegateStoreId: '',
@@ -203,10 +221,38 @@ export default {
cartMove: { startX: 0, startY: 0, moving: false },
showPopup: false,
currentProduct: {},
quantity: 1
quantity: 1,
// 首次进入骨架:专区+分类+商品首次请求结束前
pageInitialLoading: true,
// 商品列表请求中(切专区有旧数据时不闪空)
listFetching: false,
// 分类请求中
categoryFetching: false,
// scroll-view 下拉刷新状态
isRefreshing: false
};
},
computed: {
// u-tabs 需要 { name } 结构
zoneTabList() {
return (this.homeZones || []).map((z) => ({
name: z.title || ''
}));
},
// 左侧分类骨架:首次加载,或分类请求中且尚无分类可展示
showCategorySkeleton() {
if (this.pageInitialLoading) {
return true;
}
return this.categoryFetching && this.categoryList.length === 0;
},
// 右侧商品骨架:首次加载,或请求中且当前无商品可保留
showProductSkeleton() {
if (this.pageInitialLoading) {
return true;
}
return this.listFetching && this.products.length === 0;
},
getProductImage() {
return (this.currentProduct.drug && this.currentProduct.drug.image) ? this.currentProduct.drug.image : 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png';
},
@@ -221,15 +267,11 @@ export default {
this.doctorId = e.doctor_id;
this.r_type = e.register_type;
this.delegateStoreId = e.delegate_store_id || '';
// 根据首页传入的专区类型初始化
if (e.zone_type) {
this.selectedZoneType = e.zone_type;
} else {
this.selectedZoneType = 'otc';
}
// 获取专区列表
this.getHomeZones();
},
methods: {
@@ -240,19 +282,14 @@ export default {
}).then((res) => {
if (res.data.code == 0) {
this.homeZones = res.data.result || res.data.data || [];
// 根据传入的zone_type找到对应的索引
if (this.homeZones.length > 0) {
const index = this.homeZones.findIndex(z => z.type === this.selectedZoneType);
this.selectedZoneIndex = index >= 0 ? index : 0;
this.selectedZoneType = this.homeZones[this.selectedZoneIndex]?.type || 'otc';
this.selectedZoneType = this.homeZones[this.selectedZoneIndex].type || 'otc';
}
// 获取分类列表
this.getCategoryList();
}
}).catch(() => {
// 使用默认专区
this.homeZones = [
{ id: 1, title: 'OTC', type: 'otc' },
{ id: 2, title: '保健食品', type: 'health_food' }
@@ -260,27 +297,25 @@ export default {
this.getCategoryList();
});
},
// 获取分类列表
getCategoryList() {
// 获取分类列表;不清空旧商品,避免切专区闪空
getCategoryList(callback) {
this.categoryFetching = true;
getDrugCategoriesApi({
zone_type: this.selectedZoneType
}).then((res) => {
if (res.data.code == 0) {
// 将树形结构扁平化为一级分类列表
const categories = res.data.result || [];
this.categoryList = this.flattenCategories(categories);
}
}).catch(() => {
this.categoryList = [];
}).finally(() => {
// 重置分类选择并获取商品列表
this.categoryFetching = false;
this.selectedCategoryId = 0;
this.getList();
this.getList(callback);
});
},
// 扁平化分类树(只取一级分类,或展开所有层级)
// 扁平化分类树
flattenCategories(categories, result = []) {
for (const cat of categories) {
result.push({
@@ -295,37 +330,59 @@ export default {
}
return result;
},
// 专区切换
// Tab 点击切换(与订单列表 onTabChange 一致)
onTabChange(index) {
if (this.tabChanging) return;
const tabIndex = Number(index) || 0;
// 已是当前项时不重复请求;也可挡住 swiper 回写
if (tabIndex === this.selectedZoneIndex) return;
this.tabChanging = true;
this.onZoneChange(tabIndex);
this.$nextTick(() => {
this.tabChanging = false;
});
},
// swiper 滑动切换
onSwiperChange(e) {
if (this.tabChanging) return;
const tabIndex = Number(e.detail.current) || 0;
if (tabIndex === this.selectedZoneIndex) return;
this.tabChanging = true;
this.onZoneChange(tabIndex);
this.$nextTick(() => {
this.tabChanging = false;
});
},
// 专区切换:立即清空旧数据出骨架,再拉分类+商品
onZoneChange(index) {
this.selectedZoneIndex = index;
this.selectedZoneType = this.homeZones[index]?.type || 'otc';
const tabIndex = Number(index) || 0;
this.selectedZoneIndex = tabIndex;
this.selectedZoneType = (this.homeZones[tabIndex] && this.homeZones[tabIndex].type) || 'otc';
this.selectedCategoryId = 0;
this.products = [];
this.categoryList = [];
this.listFetching = true;
this.categoryFetching = true;
this.getCategoryList();
},
onZoneTouchStart(e) {
const touch = e.touches[0] || e.changedTouches[0];
if (!touch) return;
this.zoneTouch.startX = touch.clientX;
this.zoneTouch.startY = touch.clientY;
},
onZoneTouchEnd(e) {
const dx = e.changedTouches[0].clientX - this.zoneTouch.startX;
const dy = e.changedTouches[0].clientY - this.zoneTouch.startY;
if (Math.abs(dx) < 60 || Math.abs(dx) < Math.abs(dy)) return;
const next = dx < 0 ? this.selectedZoneIndex + 1 : this.selectedZoneIndex - 1;
if (next < 0 || next >= this.homeZones.length) return;
this.onZoneChange(next);
},
// 分类选择
// 分类选择:清空商品出骨架后再请求
selectCategory(categoryId) {
this.selectedCategoryId = categoryId;
this.products = [];
this.listFetching = true;
this.getList();
},
// 右侧下拉刷新:清空分类+商品出骨架,再重拉
onRefresherRefresh() {
this.isRefreshing = true;
this.products = [];
this.categoryList = [];
this.listFetching = true;
this.categoryFetching = true;
this.getCategoryList(() => {
this.isRefreshing = false;
});
},
// 核心标签判断逻辑
getProductTag(drug) {
if (!drug) return { label: 'OTC', className: 'tag-green' };
@@ -339,41 +396,43 @@ export default {
return { label: 'Rx', className: 'tag-red' };
}
}
return { label: 'OTC', className: 'tag-green' };
},
getList() {
// 拉取商品列表callback 供下拉刷新收尾
getList(callback) {
this.listFetching = true;
const params = {
store_id: uni.getStorageSync('store_id') || '11001',
page: 1,
limit: 100,
category: this.selectedZoneType
};
if (this.selectedCategoryId > 0) {
params.category_id = this.selectedCategoryId;
}
if (this.searchKeyword) {
params.keyword = this.searchKeyword;
}
getStoreDrugListBySalespersonApi(params).then((res) => {
if (res.data.code == 0) {
this.products = res.data.result.records || [];
this.cartCount = res.data.result.cart_count || 0;
}
}).finally(() => {
this.listFetching = false;
this.pageInitialLoading = false;
if (typeof callback === 'function') {
callback();
}
});
},
handleSearch() {
this.getList();
},
clearSearch() {
this.searchKeyword = "";
this.getList();
},
handClickProduct(item) {
let 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}`;
if (this.delegateStoreId) {
@@ -381,7 +440,6 @@ export default {
}
uni.navigateTo({ url });
},
// 悬浮球逻辑
initCartPosition() {
const systemInfo = uni.getSystemInfoSync();
@@ -433,7 +491,6 @@ export default {
},
quantityChange(value) {},
confirmAddToCart() {
// 检查是否为处方药is_otc === 0 && type == 2
if (this.currentProduct.drug && this.currentProduct.drug.is_otc === 0 && this.currentProduct.drug.type == 2) {
uni.showToast({
title: '处方药不能加入购物车,请通过提交预定购买',
@@ -442,7 +499,6 @@ export default {
});
return;
}
saveCartApi({
store_id: uni.getStorageSync('store_id') || '11001',
drug_id: this.currentProduct.drug.id,
@@ -502,58 +558,33 @@ export default {
background: #fff;
padding: 16rpx 24rpx;
box-shadow: 0 2rpx 8rpx rgba(0,0,0,0.03);
flex-shrink: 0;
.search-box {
width: 100%;
}
}
/* 2. 顶部专区Tab */
.zone-tabs-wrapper {
/* 2. 顶部专区 Tabu-tabs */
.zone-tabs-head {
width: 750rpx;
height: 80rpx;
flex-shrink: 0;
background: #fff;
border-bottom: 2rpx solid #f0f0f0;
.zone-tabs-scroll {
white-space: nowrap;
}
.zone-tabs {
display: inline-flex;
padding: 0 24rpx;
.zone-tab-item {
display: inline-flex;
align-items: center;
justify-content: center;
padding: 24rpx 32rpx;
font-size: 28rpx;
color: #666;
position: relative;
transition: all 0.2s;
&.active {
color: #2B85E4;
font-weight: bold;
&::after {
content: '';
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 48rpx;
height: 6rpx;
background: linear-gradient(90deg, #5CADFF, #2B85E4);
border-radius: 3rpx;
}
}
}
}
z-index: 99;
}
/* 3. 主体内容区 */
.main-content {
/* 3. 专区 swiper占满剩余高度与订单列表 order-swiper 一致 */
.zone-swiper {
flex: 1;
height: 0;
min-height: 0;
}
/* 主体内容区 */
.main-content {
height: 100%;
display: flex;
overflow: hidden;
}
@@ -563,6 +594,7 @@ export default {
width: 180rpx;
background: #fff;
flex-shrink: 0;
height: 100%;
.category-scroll {
height: 100%;
@@ -607,6 +639,7 @@ export default {
.product-area {
flex: 1;
overflow: hidden;
height: 100%;
.product-scroll {
height: 100%;
@@ -643,10 +676,7 @@ export default {
height: 100%;
&.prescription-blur {
/* 暂时禁用模糊
filter: blur(8px);
-webkit-filter: blur(8px);
*/
/* 暂时禁用模糊 */
}
}
}

View File

@@ -1,6 +1,9 @@
<template>
<view class="container">
<MessageNotification />
<!-- 首次详情加载骨架避免半空页可点购买 -->
<ListSkeleton v-if="detailInitialLoading" type="detail" :rows="3" />
<block v-else>
<!-- 1. 顶部轮播图 -->
<view class="swiper-section">
<swiper
@@ -74,10 +77,10 @@
</view>
</view>
<!-- 3. 物流提示 -->
<view class="logistics-tip" v-if="orderText">
<!-- 3. 物流提示有配送仓时展示仓文案否则用 order-text -->
<view class="logistics-tip" v-if="logisticsTipText">
<u-icon name="car-fill" color="#2B85E4" size="34" style="margin-right:12rpx"></u-icon>
<text>{{ orderText }}</text>
<text>{{ logisticsTipText }}</text>
</view>
@@ -169,6 +172,7 @@
</button>
</view>
</view>
</block>
<!-- === 以下为原有弹窗代码 (保留完整逻辑) === -->
@@ -268,7 +272,11 @@ export default {
indications: '',
indications_array: [],
compliance_tip: '', // 合规提示文案,由后端返回
supplier: null
supplier: null,
// 配送仓字段需预声明,否则 Vue2 Object.assign 新 key 不触发视图更新
has_delivery_warehouse: 0,
delivery_warehouse_id: 0,
delivery_warehouse_name: ''
},
formData: {
id: '',
@@ -290,6 +298,12 @@ export default {
showBuyPopup: false,
buyQuantity: 1,
orderText: '本次商品物流配送,由萧康医药提供服务。',
// 物流仓提示单独挂 data保证详情接口返回后一定能驱动 computed 重算
deliveryWarehouseFlag: 0,
deliveryWarehouseId: 0,
deliveryWarehouseName: '',
// 首次详情请求结束前展示骨架
detailInitialLoading: true,
introductionImages: [], // 商品介绍图数组
delegateStoreId: '', // 委托诊所ID
fromOrderId: '',
@@ -316,6 +330,29 @@ export default {
manufacturerName() {
return (this.productDetail.supplier && this.productDetail.supplier.name) || '';
},
// 有仓药绑定时用仓文案,替代萧康 order-text优先读独立字段避免嵌套对象漏更新
hasDeliveryWarehouse() {
if (Number(this.deliveryWarehouseFlag) === 1) {
return true;
}
if (Number(this.deliveryWarehouseId || 0) > 0) {
return true;
}
const d = this.productDetail || {};
if (Number(d.has_delivery_warehouse) === 1) {
return true;
}
return Number(d.delivery_warehouse_id || 0) > 0;
},
logisticsTipText() {
const whName = this.deliveryWarehouseName
|| (this.productDetail && this.productDetail.delivery_warehouse_name)
|| '';
if (this.hasDeliveryWarehouse && whName) {
return '该商品由' + whName + '供应及配送';
}
return this.orderText || '';
},
showFunctionSection() {
if (isHealthFood(this.productDetail)) return false;
return canShowFunction(this.productDetail, this.prescriptionApproved)
@@ -395,6 +432,10 @@ export default {
});
},
getOrderText() {
// 有配送仓时不请求萧康默认文案
if (this.hasDeliveryWarehouse) {
return;
}
getOrderTextApi().then((res) => {
if (res.data && res.data.result) {
this.orderText = res.data.result;
@@ -404,7 +445,6 @@ export default {
})
},
getDetail() {
this.getOrderText();
const params = {
id: this.productDetail.id,
};
@@ -417,8 +457,13 @@ export default {
}
getStoreDrugDetailApi(params).then((res) => {
if (res.data.code === 0) {
const data = res.data.result;
Object.assign(this.productDetail, data);
const data = res.data.result || {};
// 整对象替换:列表进详情时 name/price 常与接口一致,Object.assign 不会触发 Vue2 更新
this.productDetail = Object.assign({}, this.productDetail, data);
// 仓字段再写到顶层 data确保物流提示 computed 一定重算
this.deliveryWarehouseFlag = Number(data.has_delivery_warehouse || 0);
this.deliveryWarehouseId = Number(data.delivery_warehouse_id || 0);
this.deliveryWarehouseName = data.delivery_warehouse_name || '';
if (data.prescription_approved !== undefined) {
this.prescriptionApproved = !!data.prescription_approved;
}
@@ -438,9 +483,14 @@ export default {
} else {
this.introductionImages = [];
}
// 详情返回后再决定是否拉萧康 order-text
this.getOrderText();
}
}).catch(error => {
console.error('加载商品详情失败:', error);
}).finally(() => {
// 无论成功失败都关闭骨架,避免一直卡住
this.detailInitialLoading = false;
});
},
previewImage(index) {

View File

@@ -331,6 +331,6 @@ export class WebSocketManager {
// 全局WebSocket实例
// export const webSocketManager = new WebSocketManager('ws://127.0.0.1:12080/ws');
// const wsUrl = checkDev('dev')? 'ws://127.0.0.1:12080/ws':'wss://api.ws.g.xiaokang88.com/ws';
const wsUrl = checkDev('dev')? 'ws://127.0.0.1:12080/ws':'wss://xk.ws.nailaoyun.cn/ws';
const wsUrl = checkDev('dev')? 'ws://127.0.0.1:12080/ws': checkDev('test')? 'wss://xk.ws.nailaoyun.cn/ws':'wss://api.ws.g.xiaokang88.com/ws';
// const wsUrl = checkDev('dev')? 'ws://127.0.0.1:12080/ws':'wss://xk.ws.nailaoyun.cn/ws';
export const webSocketManager = new WebSocketManager(wsUrl);