1. 特色方
This commit is contained in:
0
components/HealthTeaCard/HealthTeaCard.vue
Normal file
0
components/HealthTeaCard/HealthTeaCard.vue
Normal file
15
pages.json
15
pages.json
@@ -194,6 +194,21 @@
|
||||
"navigationBarTitleText": "用药信息",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "special-prescription/list",
|
||||
"style": {
|
||||
"navigationBarTitleText": "特色药方",
|
||||
"enablePullDownRefresh": false,
|
||||
"disableScroll": true
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "special-prescription/detail",
|
||||
"style": {
|
||||
"navigationBarTitleText": "药方详情",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -43,10 +43,10 @@
|
||||
<!-- 选择坐诊医生 -->
|
||||
<view class="doctor_list">选择坐诊医生</view>
|
||||
<!-- 列表 -->
|
||||
<view class="list" v-for="(item,index) in doctorList" :key="item.id" @click="goLookDoctor(item.id)">
|
||||
<view class="list" v-for="(item,index) in doctorList" :key="item.id">
|
||||
<!-- 图片部分 -->
|
||||
<view class="list_info">
|
||||
<view class="list_left">
|
||||
<view class="list_left" @click="goLookDoctor(item.id)">
|
||||
<image :src="item.avatar || 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png'" mode="医生头像"></image>
|
||||
<!-- 医生 -->
|
||||
<view class="list_title">
|
||||
@@ -61,10 +61,10 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="list_right">{{ typeMap[type] }}</view>
|
||||
<view class="list_right" @click.stop="goRegister(item)">{{ typeMap[type] }}</view>
|
||||
</view>
|
||||
<!-- 文字部分 -->
|
||||
<view class="card">
|
||||
<view class="card" @click="goLookDoctor(item.id)">
|
||||
<!-- 擅长 -->
|
||||
<view class="list_going">
|
||||
擅长:{{item.good_at}}
|
||||
@@ -129,7 +129,7 @@
|
||||
import {
|
||||
dDropdown
|
||||
} from '@/components/d-dropdown/d-dropdown'
|
||||
import { isGuestMode } from '@/common/utils/auth.js'
|
||||
import { isGuestMode, isLoggedIn, saveRedirectInfo, buildUrlWithParams } from '@/common/utils/auth.js'
|
||||
export default {
|
||||
components: {
|
||||
dDropdown
|
||||
@@ -232,6 +232,11 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
resolveRegisterType(pageType) {
|
||||
const t = String(pageType ?? '0');
|
||||
if (['0', '1', '2', '3'].includes(t)) return t;
|
||||
return '0';
|
||||
},
|
||||
closeDropdown() {
|
||||
this.$refs.uDropdown.close();
|
||||
this.getList()
|
||||
@@ -309,6 +314,60 @@
|
||||
})
|
||||
},
|
||||
|
||||
async goRegister(item) {
|
||||
if (!isLoggedIn()) {
|
||||
const storeId = uni.getStorageSync('store_id') || '11001';
|
||||
saveRedirectInfo('/subPackages/doctor/doctor-userinfo', {
|
||||
id: item.id,
|
||||
r_type: this.resolveRegisterType(this.type),
|
||||
store_id: storeId,
|
||||
});
|
||||
uni.navigateTo({
|
||||
url: buildUrlWithParams('/pages/login/login', {
|
||||
redirect: 'doctor-userinfo',
|
||||
doctor_id: item.id,
|
||||
store_id: storeId,
|
||||
})
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const rType = this.resolveRegisterType(this.type);
|
||||
if (rType === '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) {
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/doctor/doctor-userinfo?id=${result.delegate_doctor_id}&r_type=2&delegate_store_id=${result.delegate_store_id || storeId}`
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
title: result?.message || '该门店暂不支持在线问诊功能',
|
||||
icon: 'none',
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.showToast({
|
||||
title: configRes.data?.message || '获取在线问诊信息失败',
|
||||
icon: 'none',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('获取在线问诊信息失败:', error);
|
||||
uni.showToast({ title: '获取在线问诊信息失败', icon: 'none' });
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/doctor/doctor-userinfo?id=${item.id}&r_type=${rType}`,
|
||||
});
|
||||
},
|
||||
|
||||
goProduct() {
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/product/product?id=' + id
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<!-- 全局消息通知组件 -->
|
||||
<MessageNotification />
|
||||
<image-compress-popup ref="imageCompressPopup" />
|
||||
|
||||
|
||||
<!-- 1. 自定义吸顶导航栏 -->
|
||||
<view class="custom-nav"
|
||||
:class="[
|
||||
@@ -66,7 +66,7 @@
|
||||
<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-card" @click="goYard(0)">
|
||||
<view class="nav-content">
|
||||
<view class="nav-title" style="color:#4175EE">到店挂号</view>
|
||||
<view class="nav-desc">预约专家 不排队</view>
|
||||
@@ -94,11 +94,107 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4. 诊所模式内容:医生列表 -->
|
||||
<!-- 待支付挂号提醒 -->
|
||||
<view
|
||||
v-if="unpaidRegisterCount > 0"
|
||||
class="unpaid-banner unpaid-banner--register"
|
||||
@click="onUnpaidRegisterBannerClick"
|
||||
>
|
||||
<text class="unpaid-banner-text">您有 {{ unpaidRegisterCount }} 个待支付挂号订单,点击前往查看</text>
|
||||
<text class="iconfont icon-jiantou1 unpaid-banner-arrow"></text>
|
||||
</view>
|
||||
|
||||
<!-- 待支付商品订单提醒 -->
|
||||
<view
|
||||
v-if="unpaidProductOrderCount > 0"
|
||||
class="unpaid-banner unpaid-banner--product"
|
||||
@click="onUnpaidProductOrderBannerClick"
|
||||
>
|
||||
<text class="unpaid-banner-text">您有 {{ unpaidProductOrderCount }} 个待支付商品订单,点击前往查看</text>
|
||||
<text class="iconfont icon-jiantou1 unpaid-banner-arrow"></text>
|
||||
</view>
|
||||
|
||||
<!-- 4. 诊所模式内容:特色药方 -->
|
||||
<view v-if="(storeType === '0' || storeType === 0) && specialPrescription.enabled" class="tea-section-wrap">
|
||||
<!-- 特色药方异形卡片模块 -->
|
||||
<view class="tea-section">
|
||||
<view class="irregular-card">
|
||||
<!-- 左侧凸起 Tab(右侧带下坡过渡) -->
|
||||
<view class="card-tab">
|
||||
<view class="card-tab-slope"></view>
|
||||
</view>
|
||||
|
||||
<!-- 悬浮艺术字标题:位于凸起与卡片的衔接处 -->
|
||||
<image
|
||||
:src="specialPrescription.title_image"
|
||||
class="main-title"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
|
||||
<!-- 主体区域 -->
|
||||
<view class="card-body">
|
||||
<!-- 悬浮 IP 小熊 -->
|
||||
<image
|
||||
:src="specialPrescription.mascot_image"
|
||||
class="main-mascot"
|
||||
mode="widthFix"
|
||||
></image>
|
||||
|
||||
<view class="tea-subtitle">
|
||||
<view class="subtitle-icon"></view>
|
||||
<text>{{ specialPrescription.subtitle || '精选时令养生茶饮' }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 横向滑动列表 -->
|
||||
<scroll-view scroll-x class="tea-scroll" :show-scrollbar="false" :enable-passive="false">
|
||||
<view class="tea-list">
|
||||
<navigator
|
||||
v-if="detailUrl(tea)"
|
||||
class="tea-item"
|
||||
v-for="(tea, tIndex) in specialPrescription.items"
|
||||
:key="tea.id || tIndex"
|
||||
:url="detailUrl(tea)"
|
||||
hover-class="tea-item--active"
|
||||
>
|
||||
<view class="tea-image-wrap">
|
||||
<image
|
||||
v-if="tea.cover_image"
|
||||
:src="tea.cover_image"
|
||||
class="tea-image"
|
||||
mode="aspectFill"
|
||||
></image>
|
||||
<text v-else class="tea-image-placeholder">特色方</text>
|
||||
</view>
|
||||
<view class="tea-info-wrap">
|
||||
<view class="tea-name u-line-1">{{ tea.name }}</view>
|
||||
<view class="tea-tags">
|
||||
<text class="tea-tag" v-for="(tag, gIndex) in (tea.tags || []).slice(0, 2)" :key="gIndex">{{ tag }}</text>
|
||||
</view>
|
||||
<view class="tea-price" v-if="tea.price_available !== false">
|
||||
<text class="tea-price-symbol">¥</text>{{ tea.price_per_dose }}
|
||||
</view>
|
||||
<view class="tea-price tea-price--unavailable" v-else>暂无报价</view>
|
||||
</view>
|
||||
</navigator>
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作区 -->
|
||||
<view class="tea-bottom-area">
|
||||
<view class="days-left-tag">热卖养生茶</view>
|
||||
<view class="tea-action-button" @click="goTeaList">查看全部药方</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 诊所模式:医生列表 -->
|
||||
<view v-if="storeType === '0' || storeType === 0">
|
||||
<!-- 医生列表 -->
|
||||
<view class="section-header">
|
||||
<view class="section-title">医生推荐</view>
|
||||
<view class="section-more" @click="goYard(1)">更多 <text class="iconfont icon-jiantou1" style="font-size: 20rpx;"></text></view>
|
||||
<view class="section-more" @click="goYard(0)">更多 <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">
|
||||
@@ -118,8 +214,8 @@
|
||||
{{item.good_at}}
|
||||
</view>
|
||||
</view>
|
||||
<!-- <view class="consult-btn">咨询</view>-->
|
||||
<!-- <view class="consult-btn">查看</view>-->
|
||||
<!-- <view class="consult-btn">咨询</view>-->
|
||||
<!-- <view class="consult-btn">查看</view>-->
|
||||
</view>
|
||||
<view class="doc-stats-bar">
|
||||
<view class="stat-item">
|
||||
@@ -209,10 +305,13 @@ import {
|
||||
getToken,
|
||||
storeChange,
|
||||
storeInfo,
|
||||
storeList
|
||||
storeList,
|
||||
fetchUnpaidProductOrderListApi,
|
||||
} from '../../request/api/api'
|
||||
import { getPlatformQualificationsApi } from '../../request/api/platform'
|
||||
import { getStoreDrugListBySalespersonApi, getHomeTopProductsApi, getHomeZonesApi, checkOnlineConsultationConfigApi } from "@/request/api/product";
|
||||
import { getSpecialPrescriptionHomeApi } from '@/request/api/specialPrescription';
|
||||
import { fetchUnpaidRegisterListApi } from '@/request/api/register';
|
||||
import request from "@/request/api/request";
|
||||
import MessageNotification from "@/components/MessageNotification/MessageNotification.vue";
|
||||
import ImageCompressPopup from '@/components/image-compress-popup/image-compress-popup.vue';
|
||||
@@ -251,7 +350,22 @@ export default {
|
||||
isSticky: false,
|
||||
|
||||
// 控制金刚区布局模式: 'scroll' (滑动) 或 'wrap' (换行)
|
||||
zoneLayoutMode: 'scroll'
|
||||
zoneLayoutMode: 'scroll',
|
||||
|
||||
unpaidRegisterCount: 0,
|
||||
unpaidRegisterList: [],
|
||||
unpaidProductOrderCount: 0,
|
||||
unpaidProductOrderList: [],
|
||||
|
||||
// 特色药方(API)
|
||||
specialPrescription: {
|
||||
enabled: false,
|
||||
doctor_id: 0,
|
||||
title_image: '',
|
||||
mascot_image: '',
|
||||
subtitle: '',
|
||||
items: [],
|
||||
},
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -265,7 +379,7 @@ export default {
|
||||
}
|
||||
},
|
||||
onLoad() {
|
||||
|
||||
|
||||
this.isShow_envVersion = uni.getStorageSync('isShow_envVersion')
|
||||
if (this.isShow_envVersion === false) {
|
||||
this.getList();
|
||||
@@ -318,6 +432,55 @@ export default {
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
async fetchUnpaidCounts() {
|
||||
if (!isLoggedIn()) {
|
||||
this.unpaidRegisterCount = 0;
|
||||
this.unpaidRegisterList = [];
|
||||
this.unpaidProductOrderCount = 0;
|
||||
this.unpaidProductOrderList = [];
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const [registerList, productList] = await Promise.all([
|
||||
fetchUnpaidRegisterListApi(),
|
||||
fetchUnpaidProductOrderListApi(),
|
||||
]);
|
||||
this.unpaidRegisterList = registerList;
|
||||
this.unpaidRegisterCount = registerList.length;
|
||||
this.unpaidProductOrderList = productList;
|
||||
this.unpaidProductOrderCount = productList.length;
|
||||
} catch (e) {
|
||||
console.error('fetchUnpaidCounts', e);
|
||||
this.unpaidRegisterCount = 0;
|
||||
this.unpaidRegisterList = [];
|
||||
this.unpaidProductOrderCount = 0;
|
||||
this.unpaidProductOrderList = [];
|
||||
}
|
||||
},
|
||||
onUnpaidRegisterBannerClick() {
|
||||
if (this.unpaidRegisterCount === 1 && this.unpaidRegisterList[0]) {
|
||||
const id = this.unpaidRegisterList[0].id;
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/doctor/doctor-userinfo?step=2®ister_id=${id}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/my/myappiont',
|
||||
});
|
||||
},
|
||||
onUnpaidProductOrderBannerClick() {
|
||||
if (this.unpaidProductOrderCount === 1 && this.unpaidProductOrderList[0]) {
|
||||
const id = this.unpaidProductOrderList[0].id;
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/my/my-drug/drug-info?id=${id}`,
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/my/my-drug/drug-list?index=1',
|
||||
});
|
||||
},
|
||||
// 处理购物车按钮点击
|
||||
handleAddToCart(item) {
|
||||
if(item && item.drug) {
|
||||
@@ -331,10 +494,10 @@ export default {
|
||||
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({
|
||||
@@ -367,21 +530,21 @@ export default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 当 type === 3 (在线复诊) 时,需要先检查门店是否支持在线问诊功能
|
||||
if (type === 3) {
|
||||
try {
|
||||
const storeId = uni.getStorageSync('store_id') || '11001';
|
||||
const configRes = await checkOnlineConsultationConfigApi({
|
||||
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)
|
||||
@@ -411,7 +574,7 @@ export default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 检查通过或非在线复诊类型,正常跳转
|
||||
uni.navigateTo({
|
||||
url: '/pages/Inquiries/Inquiries?type=' + type
|
||||
@@ -476,18 +639,18 @@ export default {
|
||||
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();
|
||||
this.getBannered();
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -529,7 +692,7 @@ export default {
|
||||
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 取值
|
||||
@@ -555,12 +718,12 @@ export default {
|
||||
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 || []
|
||||
@@ -628,8 +791,8 @@ export default {
|
||||
previewQualification(item) {
|
||||
// 获取所有有图片的资质,支持滑动预览
|
||||
const allImages = this.qualifications
|
||||
.filter(q => q.image)
|
||||
.map(q => q.image);
|
||||
.filter(q => q.image)
|
||||
.map(q => q.image);
|
||||
if (allImages.length > 0 && item.image) {
|
||||
uni.previewImage({
|
||||
urls: allImages,
|
||||
@@ -650,6 +813,43 @@ export default {
|
||||
goPlatformInfo() {
|
||||
uni.navigateTo({ url: '/pages/index/platform-info' });
|
||||
},
|
||||
// 跳转到全部药方 / 养生茶饮列表
|
||||
goTeaList() {
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/special-prescription/list',
|
||||
});
|
||||
},
|
||||
goTeaDetail(tea) {
|
||||
const id = Number(tea?.id);
|
||||
if (!id) return;
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/special-prescription/detail?id=${id}`,
|
||||
});
|
||||
},
|
||||
detailUrl(tea) {
|
||||
const id = Number(tea?.id);
|
||||
if (!id) return '';
|
||||
return `/subPackages/special-prescription/detail?id=${id}`;
|
||||
},
|
||||
fetchSpecialPrescriptionHome() {
|
||||
getSpecialPrescriptionHomeApi({
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}).then((res) => {
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
const result = res.data.result || {};
|
||||
this.specialPrescription = {
|
||||
enabled: !!result.enabled,
|
||||
doctor_id: result.doctor_id || 0,
|
||||
title_image: result.title_image || '',
|
||||
mascot_image: result.mascot_image || '',
|
||||
subtitle: result.subtitle || '',
|
||||
items: result.items || [],
|
||||
};
|
||||
}
|
||||
}).catch(() => {
|
||||
this.specialPrescription.enabled = false;
|
||||
});
|
||||
},
|
||||
async getDefaultDoctorId() {
|
||||
try {
|
||||
const storeId = uni.getStorageSync('store_id');
|
||||
@@ -676,6 +876,8 @@ export default {
|
||||
this.getHomeZones();
|
||||
this.getPlatformQualifications(); // 已补回
|
||||
this.getDefaultDoctorId();
|
||||
this.fetchUnpaidCounts();
|
||||
this.fetchSpecialPrescriptionHome();
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -789,12 +991,249 @@ $card-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
}
|
||||
|
||||
.unpaid-banner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin: 16rpx 32rpx 0;
|
||||
padding: 24rpx 28rpx;
|
||||
border-radius: 16rpx;
|
||||
|
||||
&--register {
|
||||
background: linear-gradient(90deg, rgba(65, 117, 238, 0.08) 0%, rgba(65, 117, 238, 0.04) 100%);
|
||||
border: 1rpx solid rgba(65, 117, 238, 0.2);
|
||||
|
||||
.unpaid-banner-text,
|
||||
.unpaid-banner-arrow {
|
||||
color: #4175EE;
|
||||
}
|
||||
}
|
||||
|
||||
&--product {
|
||||
background: linear-gradient(90deg, rgba(0, 181, 120, 0.08) 0%, rgba(0, 181, 120, 0.04) 100%);
|
||||
border: 1rpx solid rgba(0, 181, 120, 0.25);
|
||||
|
||||
.unpaid-banner-text,
|
||||
.unpaid-banner-arrow {
|
||||
color: #00B578;
|
||||
}
|
||||
}
|
||||
|
||||
.unpaid-banner-text {
|
||||
flex: 1;
|
||||
font-size: 26rpx;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.unpaid-banner-arrow {
|
||||
font-size: 24rpx;
|
||||
margin-left: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-header {
|
||||
padding: 0 32rpx; margin: 20rpx 0 32rpx; display: flex; justify-content: space-between; align-items: center;
|
||||
.section-title { font-size: 36rpx; font-weight: 800; color: #333; position: relative; padding-left: 24rpx; &::before { content: ''; position: absolute; left: 0; top: 8rpx; bottom: 8rpx; width: 8rpx; background: $primary-color; border-radius: 4rpx; } }
|
||||
.section-more { font-size: 26rpx; color: #999; display: flex; align-items: center; }
|
||||
}
|
||||
|
||||
/* --- 特色药方 / 养生茶饮异形卡片 --- */
|
||||
.tea-section {
|
||||
padding: 57rpx 32rpx 12rpx;
|
||||
|
||||
.irregular-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
/* 左侧凸起 Tab */
|
||||
.card-tab {
|
||||
width: 220rpx;
|
||||
height: 40rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-bottom: -1rpx;
|
||||
}
|
||||
|
||||
/* 凸起右侧的下坡过渡(凹角) */
|
||||
.card-tab-slope {
|
||||
position: absolute;
|
||||
right: -40rpx;
|
||||
bottom: 0;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
/* 使用径向渐变模拟内凹的下坡曲线 */
|
||||
background-image: radial-gradient(circle at 100% 0%, transparent 40rpx, #FFFFFF 40rpx);
|
||||
}
|
||||
|
||||
/* 悬浮艺术字标题:坐落在凸起与卡片衔接处 */
|
||||
.main-title {
|
||||
position: absolute;
|
||||
left: 8rpx;
|
||||
top: -120rpx;
|
||||
width: 210rpx;
|
||||
height: 128rpx;
|
||||
z-index: 6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 主体区域 */
|
||||
.card-body {
|
||||
background: #FFFFFF;
|
||||
width: 100%;
|
||||
padding: 24rpx 24rpx 24rpx;
|
||||
border-radius: 0 24rpx 24rpx 24rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 12rpx 32rpx rgba(65, 117, 238, 0.16);
|
||||
}
|
||||
|
||||
/* 悬浮 IP 小熊 */
|
||||
.main-mascot {
|
||||
position: absolute;
|
||||
right: 12rpx;
|
||||
top: -140rpx;
|
||||
width: 217rpx;
|
||||
height: 168rpx;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tea-subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20rpx;
|
||||
padding-right: 150rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.subtitle-icon {
|
||||
width: 6rpx;
|
||||
height: 26rpx;
|
||||
background: #4175EE;
|
||||
border-radius: 3rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* 横向滑动列表 */
|
||||
.tea-scroll {
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
margin: 0 -24rpx;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
.tea-list {
|
||||
display: inline-flex;
|
||||
padding: 4rpx 24rpx 12rpx;
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.tea-item {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
width: 176rpx;
|
||||
margin-right: 16rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
border: 1rpx solid #F0F0F0;
|
||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||
position: relative;
|
||||
z-index: 5;
|
||||
&:last-child { margin-right: 0; }
|
||||
&:active,
|
||||
&.tea-item--active { transform: scale(0.97); }
|
||||
|
||||
.tea-image-wrap {
|
||||
width: 100%;
|
||||
height: 132rpx;
|
||||
background: linear-gradient(135deg, #F5F7FA 0%, rgba(65, 117, 238, 0.08) 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
.tea-image { width: 100%; height: 100%; }
|
||||
.tea-image-placeholder { font-size: 22rpx; color: #4175EE; }
|
||||
}
|
||||
|
||||
.tea-info-wrap {
|
||||
padding: 12rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.tea-name {
|
||||
font-size: 24rpx;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
.tea-tags {
|
||||
display: flex;
|
||||
margin-bottom: 8rpx;
|
||||
.tea-tag {
|
||||
font-size: 18rpx;
|
||||
color: #4175EE;
|
||||
background: rgba(65, 117, 238, 0.1);
|
||||
padding: 1rpx 8rpx;
|
||||
border-radius: 4rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
.tea-price {
|
||||
color: #FF5722;
|
||||
font-size: 28rpx;
|
||||
font-weight: 800;
|
||||
.tea-price-symbol { font-size: 20rpx; margin-right: 2rpx; }
|
||||
&--unavailable {
|
||||
color: #999;
|
||||
font-size: 22rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* 底部操作区 */
|
||||
.tea-bottom-area {
|
||||
position: relative;
|
||||
margin-top: 16rpx;
|
||||
.days-left-tag {
|
||||
position: absolute;
|
||||
top: -20rpx;
|
||||
right: 20rpx;
|
||||
background: #FFC107;
|
||||
color: #8C6A00;
|
||||
font-size: 20rpx;
|
||||
font-weight: bold;
|
||||
padding: 6rpx 18rpx;
|
||||
border-radius: 16rpx 16rpx 16rpx 0;
|
||||
z-index: 2;
|
||||
border: 2rpx solid #fff;
|
||||
box-shadow: 0 4rpx 12rpx rgba(255, 193, 7, 0.3);
|
||||
}
|
||||
.tea-action-button {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
padding: 20rpx 0;
|
||||
border-radius: 40rpx;
|
||||
background: linear-gradient(90deg, #4175EE 0%, #298DFF 100%);
|
||||
color: #fff;
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 8rpx 24rpx rgba(65, 117, 238, 0.3);
|
||||
&:active { transform: scale(0.98); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.doctor-card {
|
||||
background: #fff; margin: 0 32rpx 32rpx; border-radius: $card-radius; box-shadow: $card-shadow; overflow: hidden;
|
||||
&:active { transform: scale(0.99); }
|
||||
@@ -876,23 +1315,23 @@ $card-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
.qualifications-section {
|
||||
padding: 40rpx 32rpx 40rpx;
|
||||
.qualifications-row {
|
||||
display: flex;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 20rpx;
|
||||
.qualification-card {
|
||||
background: #fff;
|
||||
padding: 24rpx;
|
||||
border-radius: 16rpx;
|
||||
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;
|
||||
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;
|
||||
.qual-icon {
|
||||
width: 200rpx;
|
||||
height: 140rpx;
|
||||
margin-bottom: 16rpx;
|
||||
border-radius: 8rpx;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
@@ -904,4 +1343,4 @@ $card-shadow: 0 8rpx 20rpx rgba(0, 0, 0, 0.04);
|
||||
.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>
|
||||
</style>
|
||||
|
||||
@@ -375,6 +375,29 @@ export async function getOrdlist(params) {
|
||||
return normalizeXkApiResponse(data)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待支付商品订单列表(首页提醒用)
|
||||
*/
|
||||
export async function fetchUnpaidProductOrderListApi(params = {}) {
|
||||
const storeId = params.store_id || uni.getStorageSync('store_id') || '11001';
|
||||
const res = await getOrdlist({
|
||||
method: 'post',
|
||||
data: {
|
||||
store_id: storeId,
|
||||
type: 'unpaid',
|
||||
page: 1,
|
||||
...params,
|
||||
},
|
||||
});
|
||||
if (res.data?.errcode != 0) {
|
||||
return [];
|
||||
}
|
||||
const list = res.data?.data?.list || [];
|
||||
return list.filter(
|
||||
(item) => item.status === 0 && item.is_pay === 0 && item.cancel_status === 0
|
||||
);
|
||||
}
|
||||
|
||||
// v1/product-order/list
|
||||
export async function getToken(params) {
|
||||
let data = await http('/oldApi/v1/user/login-platform', params)
|
||||
|
||||
@@ -8,3 +8,22 @@ import {post} from './http'
|
||||
export async function getRegisterListApi(params) {
|
||||
return await post('/register/list', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取待支付挂号列表(首页提醒用)
|
||||
* @param params
|
||||
* @returns {Promise<Array>}
|
||||
*/
|
||||
export async function fetchUnpaidRegisterListApi(params = {}) {
|
||||
const storeId = params.store_id || uni.getStorageSync('store_id') || '11001';
|
||||
const res = await post('/register/list?pageSize=100', {
|
||||
store_id: storeId,
|
||||
page: 1,
|
||||
...params,
|
||||
}, 3);
|
||||
if (res.data?.code !== 0 && res.data?.code !== '0') {
|
||||
return [];
|
||||
}
|
||||
const list = res.data?.result?.list || [];
|
||||
return list.filter((item) => item.status === 0 && item.is_cancel !== 1);
|
||||
}
|
||||
|
||||
@@ -147,6 +147,9 @@ function request(url, params, method = 0) {
|
||||
} else if (url.indexOf('/xkApi/store/list') !== -1) {
|
||||
url = url.replace('/xkApi/store/list', '/guest/guest-home/store-list');
|
||||
baseURL = envUrls.xkApi;
|
||||
} else if (url.indexOf('/xkApi/special-prescription/') !== -1) {
|
||||
url = url.replace('/xkApi/special-prescription/', '/guest/guest-special-prescription/');
|
||||
baseURL = envUrls.xkApi;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
92
request/api/specialPrescription.js
Normal file
92
request/api/specialPrescription.js
Normal file
@@ -0,0 +1,92 @@
|
||||
import { get, post } from './http'
|
||||
|
||||
export const SPECIAL_PRESCRIPTION_STORAGE_KEY = 'special_prescription_register_context'
|
||||
|
||||
/**
|
||||
* 首页特色方配置
|
||||
* @param params {{ store_id?: number|string }}
|
||||
*/
|
||||
export async function getSpecialPrescriptionHomeApi(params) {
|
||||
return await get('/special-prescription/home', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 特色方列表页配置
|
||||
* @param params {{ store_id?: number|string }}
|
||||
*/
|
||||
export async function getSpecialPrescriptionListPageApi(params) {
|
||||
return await get('/special-prescription/list-page', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 特色方列表
|
||||
* @param params {{ store_id?: number|string, category_id?: number, name?: string, page?: number, pageSize?: number }}
|
||||
*/
|
||||
export async function getSpecialPrescriptionListApi(params) {
|
||||
return await get('/special-prescription/list', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 特色方详情
|
||||
* @param params {{ id: number|string, store_id?: number|string }}
|
||||
*/
|
||||
export async function getSpecialPrescriptionDetailApi(params) {
|
||||
return await get('/special-prescription/detail', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 特色方分类
|
||||
*/
|
||||
export async function getSpecialPrescriptionCategoriesApi(params = {}) {
|
||||
return await get('/special-prescription/categories', params, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号成功后创建选方记录
|
||||
* @param params {{ register_id, special_prescription_id, dose_count, store_id, doctor_id }}
|
||||
*/
|
||||
export async function createSpecialPrescriptionPatientRecordApi(params) {
|
||||
return await post('/special-prescription/create-patient-record', params, 3)
|
||||
}
|
||||
|
||||
export function saveSpecialPrescriptionRegisterContext(ctx) {
|
||||
if (!ctx || !ctx.special_prescription_id) return
|
||||
uni.setStorageSync(SPECIAL_PRESCRIPTION_STORAGE_KEY, {
|
||||
special_prescription_id: ctx.special_prescription_id,
|
||||
dose_count: ctx.dose_count || 1,
|
||||
doctor_id: ctx.doctor_id || '',
|
||||
})
|
||||
}
|
||||
|
||||
export function getSpecialPrescriptionRegisterContext() {
|
||||
return uni.getStorageSync(SPECIAL_PRESCRIPTION_STORAGE_KEY) || null
|
||||
}
|
||||
|
||||
export function clearSpecialPrescriptionRegisterContext() {
|
||||
uni.removeStorageSync(SPECIAL_PRESCRIPTION_STORAGE_KEY)
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂号成功后绑定特色方选方记录(幂等,失败不阻断流程)
|
||||
* @param registerId
|
||||
*/
|
||||
export async function tryCreateSpecialPrescriptionPatientRecord(registerId) {
|
||||
if (!registerId) return
|
||||
const ctx = getSpecialPrescriptionRegisterContext()
|
||||
if (!ctx || !ctx.special_prescription_id) return
|
||||
|
||||
try {
|
||||
const res = await createSpecialPrescriptionPatientRecordApi({
|
||||
register_id: registerId,
|
||||
special_prescription_id: ctx.special_prescription_id,
|
||||
dose_count: ctx.dose_count || 1,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
doctor_id: ctx.doctor_id,
|
||||
})
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
clearSpecialPrescriptionRegisterContext()
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('tryCreateSpecialPrescriptionPatientRecord', e)
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
949
subPackages/register/components/RegisterConfirmStep.vue
Normal file
949
subPackages/register/components/RegisterConfirmStep.vue
Normal file
@@ -0,0 +1,949 @@
|
||||
<template>
|
||||
<view class="container safe-area-inset-bottom">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container
|
||||
:show="pageBackGuardShow"
|
||||
:overlay="false"
|
||||
@leave="onPageBackGuardLeave"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<!-- 预约信息 -->
|
||||
<view class="title">
|
||||
预约信息
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约诊所
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.store}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约科室
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.depart}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约医生
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.doctor}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约序号
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.order_number}}号
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预计就诊时间
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.created_at}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
挂号金额
|
||||
</view>
|
||||
<view class="detail active">
|
||||
{{registList.price}}元
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
挂号类型
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registerType}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 就诊人 -->
|
||||
<view class="title">
|
||||
就诊人
|
||||
</view>
|
||||
|
||||
<view class="info">
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
{{registList.patient}}
|
||||
</view>
|
||||
<view class="word_num">
|
||||
{{registList.order_number}}号
|
||||
</view>
|
||||
</view>
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
身份证号
|
||||
</view>
|
||||
<view class="word_num" v-if="registList.idcard">
|
||||
{{registList.idcard_tm || registList.idcard}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
手机号
|
||||
</view>
|
||||
<view class="word_num" v-if="registList.mobile">
|
||||
{{registList.mobile_tm || registList.mobile}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<u-popup v-model="show" :safe-area-inset-bottom="true" mode="bottom" height="68%" border-radius="20"
|
||||
:closeable="true" z-index="20000">
|
||||
<view class="agreen">
|
||||
<u-parse :html="content"></u-parse>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-popup v-model="showConfirm" mode="center" border-radius="24" width="600" z-index="10075">
|
||||
<view class="confirm-modal">
|
||||
<view class="confirm-title">预约挂号须知</view>
|
||||
<view class="confirm-desc">
|
||||
请您在使用前仔细阅读并同意
|
||||
<text class="link" @click="show=true">《预约挂号须知》</text>
|
||||
。
|
||||
</view>
|
||||
<view class="confirm-footer">
|
||||
<button class="cancel-btn" @click="showConfirm = false">不同意</button>
|
||||
<button class="agree-btn" @click="agreeAndRegister">同意并挂号</button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<view class="bottom-spacer" aria-hidden="true"></view>
|
||||
|
||||
<view class="footer">
|
||||
<block v-if="registList.is_pay==0">
|
||||
<view class="allknow">
|
||||
<u-icon size="40" :color="form['check']?'#2979FF':'#999999'" name="checkmark-circle-fill"
|
||||
@click="form['check']=!form['check']"></u-icon>
|
||||
<view class="isknow">我已阅读并同意 <text class="notice-link" @click="show=true">《预约挂号须知》</text> </view>
|
||||
</view>
|
||||
<view class="footer-btns">
|
||||
<button class="btn-prev" @click="onPrevStep">上一步</button>
|
||||
<button class="btn" @click="handleRegisterClick">确认挂号</button>
|
||||
</view>
|
||||
</block>
|
||||
<view class="gh_" v-if="registList.is_pay==1" @click="toHome">
|
||||
<button class="btn">您已挂号成功,请等待医生接诊</button>
|
||||
</view>
|
||||
</view>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
registeInfo,
|
||||
registePay,
|
||||
service
|
||||
} from '@/request/api/api';
|
||||
import { createRoomByDoctorIdApi, sendToUserApi } from '@/request/api/im';
|
||||
import { checkDev } from '@/utils/utils';
|
||||
import { genOrderApi } from '@/request/api/order';
|
||||
import request from '@/request/api/request';
|
||||
import {
|
||||
CLINIC_POST_REGISTER_STORAGE_KEY,
|
||||
resolveFollowUpDrugsFromRInfo,
|
||||
} from '@/utils/clinicFollowUpAssistantIm';
|
||||
import { tryCreateSpecialPrescriptionPatientRecord } from '@/request/api/specialPrescription';
|
||||
|
||||
export default {
|
||||
name: 'RegisterConfirmStep',
|
||||
props: {
|
||||
registerId: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
urlDoctorId: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
rInfoProp: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
showConfirm: false,
|
||||
form: { check: false },
|
||||
register_id: '',
|
||||
currentUserId: '',
|
||||
currentDoctorId: '',
|
||||
registList: {},
|
||||
content: '预约挂号须知',
|
||||
flag: true,
|
||||
registerType: '线下就诊',
|
||||
registerMap: {
|
||||
0: '挂号',
|
||||
1: '咨询',
|
||||
2: '购药',
|
||||
3: '复诊',
|
||||
},
|
||||
rInfo: {},
|
||||
_urlDoctorId: '',
|
||||
_getInfoPromise: null,
|
||||
pageBackGuardShow: false,
|
||||
_suppressPageBackGuardLeave: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
shouldEnablePageBackGuard() {
|
||||
return !(this.registList && this.registList.is_pay == 1);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'registList.is_pay'(val) {
|
||||
if (val == 1) {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
this.bindSpecialPrescriptionRecord();
|
||||
} else {
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
registerId: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val) this.bootstrapFromProps();
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.toAbout();
|
||||
const uid = uni.getStorageSync('user_id');
|
||||
this.currentUserId = 'user-' + (uid !== '' && uid !== undefined && uid !== null ? uid : '2512');
|
||||
},
|
||||
methods: {
|
||||
bootstrapFromProps() {
|
||||
this.register_id = this.registerId;
|
||||
this.rInfo = { ...(this.rInfoProp || {}) };
|
||||
this._urlDoctorId = this.urlDoctorId ? this.stripDoctorPrefix(this.urlDoctorId) : '';
|
||||
|
||||
let initialDoc = '';
|
||||
if (this._urlDoctorId) initialDoc = this._urlDoctorId;
|
||||
else if (this.rInfo && (this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '')) {
|
||||
initialDoc = this.stripDoctorPrefix(this.rInfo.doctor_id);
|
||||
} else if (this.rInfo && (this.rInfo.doctorId != null && this.rInfo.doctorId !== '')) {
|
||||
initialDoc = this.stripDoctorPrefix(this.rInfo.doctorId);
|
||||
}
|
||||
this.currentDoctorId = initialDoc ? ('doctor-' + initialDoc) : '';
|
||||
|
||||
uni.setStorageSync('register_id', this.register_id);
|
||||
|
||||
if (this.register_id) {
|
||||
this._getInfoPromise = this.getInfo();
|
||||
this._getInfoPromise.then(() => this.ensureDoctorAfterLoad());
|
||||
} else {
|
||||
this.ensureDoctorAfterLoad();
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
handleBackPress() {
|
||||
if (this.closeTopOverlayIfAny()) {
|
||||
return true;
|
||||
}
|
||||
if (this.registList && this.registList.is_pay == 1) {
|
||||
return false;
|
||||
}
|
||||
this.promptExitBeforeLeave();
|
||||
return true;
|
||||
},
|
||||
onPrevStep() {
|
||||
this.$emit('prev-step');
|
||||
},
|
||||
handleRegisterClick() {
|
||||
if (!this.form.check) {
|
||||
this.showConfirm = true;
|
||||
return;
|
||||
}
|
||||
this.next();
|
||||
},
|
||||
agreeAndRegister() {
|
||||
this.form.check = true;
|
||||
this.showConfirm = false;
|
||||
this.next();
|
||||
},
|
||||
closeTopOverlayIfAny() {
|
||||
if (this.show) {
|
||||
this.show = false;
|
||||
return true;
|
||||
}
|
||||
if (this.showConfirm) {
|
||||
this.showConfirm = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setPageBackGuardShow(show, { suppressLeave = false } = {}) {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (suppressLeave) this._suppressPageBackGuardLeave = true;
|
||||
this.pageBackGuardShow = !!show;
|
||||
// #endif
|
||||
},
|
||||
syncPageBackGuard() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(() => {
|
||||
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, {
|
||||
suppressLeave: !this.shouldEnablePageBackGuard,
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
safeNavigateBack() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 16);
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateBack();
|
||||
// #endif
|
||||
},
|
||||
onPageBackGuardLeave() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this._suppressPageBackGuardLeave) {
|
||||
this._suppressPageBackGuardLeave = false;
|
||||
return;
|
||||
}
|
||||
if (this.closeTopOverlayIfAny()) {
|
||||
this.$nextTick(() => {
|
||||
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, { suppressLeave: true });
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.registList && this.registList.is_pay == 1) {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 16);
|
||||
return;
|
||||
}
|
||||
this.setPageBackGuardShow(false);
|
||||
this.promptExitBeforeLeave({
|
||||
onStay: () => {
|
||||
this.syncPageBackGuard();
|
||||
},
|
||||
onExit: () => {
|
||||
this.safeNavigateBack();
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
disableBackGuardBeforeLeave() {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
},
|
||||
async bindSpecialPrescriptionRecord() {
|
||||
if (!this.register_id) return;
|
||||
await tryCreateSpecialPrescriptionPatientRecord(this.register_id);
|
||||
},
|
||||
promptExitBeforeLeave({ onStay, onExit } = {}) {
|
||||
uni.showActionSheet({
|
||||
itemList: ['返回上一步', '继续支付', '退出'],
|
||||
success: (res) => {
|
||||
if (res.tapIndex === 0) {
|
||||
this.$emit('prev-step');
|
||||
} else if (res.tapIndex === 1) {
|
||||
if (onStay) {
|
||||
onStay();
|
||||
} else {
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
} else if (res.tapIndex === 2) {
|
||||
if (onExit) {
|
||||
onExit();
|
||||
} else {
|
||||
this.safeNavigateBack();
|
||||
}
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
if (onStay) {
|
||||
onStay();
|
||||
} else {
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
stripDoctorPrefix(raw) {
|
||||
if (raw === null || raw === undefined || raw === '') return '';
|
||||
return String(raw).replace(/^doctor-/, '').trim();
|
||||
},
|
||||
applyDoctorNumericId(id) {
|
||||
const n = this.stripDoctorPrefix(id);
|
||||
if (!n) return;
|
||||
this.currentDoctorId = 'doctor-' + n;
|
||||
},
|
||||
getDoctorNumericIdFromLocalSync() {
|
||||
const rl = this.registList;
|
||||
if (rl && typeof rl === 'object' && !Array.isArray(rl) && rl.service_user_id) {
|
||||
const s = this.stripDoctorPrefix(rl.service_user_id);
|
||||
if (s) return s;
|
||||
}
|
||||
if (this.rInfo) {
|
||||
const d = this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '' ? this.rInfo.doctor_id : this.rInfo.doctorId;
|
||||
if (d != null && d !== '') {
|
||||
const s = this.stripDoctorPrefix(d);
|
||||
if (s) return s;
|
||||
}
|
||||
}
|
||||
if (this._urlDoctorId) {
|
||||
const s = this.stripDoctorPrefix(this._urlDoctorId);
|
||||
if (s) return s;
|
||||
}
|
||||
const cur = this.stripDoctorPrefix(this.currentDoctorId);
|
||||
if (cur) return cur;
|
||||
return '';
|
||||
},
|
||||
async fetchDefaultDoctorIdApi() {
|
||||
const storeId = uni.getStorageSync('store_id') || '11001';
|
||||
try {
|
||||
const res = await request('/xkApi/online-consultation/get-default-doctor-id', {
|
||||
method: 'GET',
|
||||
data: { store_id: storeId }
|
||||
}, 0);
|
||||
const id = res.data?.result?.doctor_id ?? res.data?.doctor_id;
|
||||
return id ? this.stripDoctorPrefix(id) : '';
|
||||
} catch (err) {
|
||||
console.error('get-default-doctor-id failed', err);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
async resolveDoctorNumericIdForIm() {
|
||||
let id = this.getDoctorNumericIdFromLocalSync();
|
||||
if (!id) id = await this.fetchDefaultDoctorIdApi();
|
||||
if (id) this.applyDoctorNumericId(id);
|
||||
return id || '';
|
||||
},
|
||||
async ensureDoctorAfterLoad() {
|
||||
const existing = this.getDoctorNumericIdFromLocalSync();
|
||||
if (existing) {
|
||||
this.applyDoctorNumericId(existing);
|
||||
return;
|
||||
}
|
||||
const id = await this.fetchDefaultDoctorIdApi();
|
||||
if (id) this.applyDoctorNumericId(id);
|
||||
},
|
||||
async waitForRegisterDetail() {
|
||||
if (!this.register_id) return;
|
||||
try {
|
||||
if (this._getInfoPromise) await this._getInfoPromise;
|
||||
else await this.getInfo();
|
||||
} catch (err) {
|
||||
console.error('waitForRegisterDetail', err);
|
||||
}
|
||||
},
|
||||
needsClinicPostRegisterDrugPage() {
|
||||
if (!this.registList || this.registList.register_type !== 2) return false;
|
||||
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(this.rInfo);
|
||||
return !!(registerInfoId && drugsPayload.length);
|
||||
},
|
||||
getInfo() {
|
||||
const that = this;
|
||||
if (!this.register_id) return Promise.resolve();
|
||||
const p = registeInfo({
|
||||
method: 'post',
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
register_id: that.register_id
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0 && res.data.data.list && res.data.data.list[0]) {
|
||||
that.registList = res.data.data.list[0];
|
||||
that.registerType = that.registerMap[that.registList.register_type];
|
||||
if (that.registList.service_user_id) {
|
||||
that.applyDoctorNumericId(that.registList.service_user_id);
|
||||
}
|
||||
}
|
||||
that.syncPageBackGuard();
|
||||
});
|
||||
this._getInfoPromise = p;
|
||||
return p;
|
||||
},
|
||||
async handleOnlineConsultationAfterPayment() {
|
||||
const that = this;
|
||||
try {
|
||||
const doctorId = await that.resolveDoctorNumericIdForIm();
|
||||
if (!doctorId) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '无法获取医生信息,请稍后重试',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
const doctorPrefixed = 'doctor-' + doctorId;
|
||||
that.currentDoctorId = doctorPrefixed;
|
||||
|
||||
const patientId = that.currentUserId.replace('user-', '');
|
||||
|
||||
const roomType = (that.registList && that.registList.register_type === 2) ? 2 : 3;
|
||||
const roomRes = await request('/xkApi/online-consultation/get-or-create-chat-room', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
doctor_id: doctorId,
|
||||
patient_id: patientId,
|
||||
type: roomType
|
||||
}
|
||||
}, 1);
|
||||
|
||||
const roomId = roomRes.data?.result?.room_id || roomRes.data?.room_id;
|
||||
if (!roomId) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '创建聊天房间失败',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const storeType = uni.getStorageSync('store_type');
|
||||
if (storeType === '1' || storeType === 1) {
|
||||
const patientExperienceData = {
|
||||
symptom_description: that.rInfo.illness_info || '',
|
||||
has_visited: that.rInfo.has_visited || '0',
|
||||
has_used_drug: that.rInfo.has_used_drug || '0',
|
||||
drug_name: that.rInfo.drug_name || '',
|
||||
illness_info: that.rInfo.illness_info || '',
|
||||
doctor_id: doctorId,
|
||||
user_id: patientId,
|
||||
register_id: that.register_id,
|
||||
created_at: new Date().toLocaleString('zh-CN')
|
||||
};
|
||||
await sendToUserApi({
|
||||
room_id: roomId,
|
||||
sender_user_id: `user-${patientId}`,
|
||||
receiver_user_id: `doctor-${doctorId}`,
|
||||
message_type: 11,
|
||||
message_content: JSON.stringify(patientExperienceData),
|
||||
duration: 0
|
||||
});
|
||||
} else {
|
||||
await sendToUserApi({
|
||||
room_id: roomId,
|
||||
sender_user_id: `user-${patientId}`,
|
||||
receiver_user_id: `doctor-${doctorId}`,
|
||||
message_type: 10,
|
||||
message_content: JSON.stringify({
|
||||
id: that.register_id,
|
||||
}),
|
||||
duration: 0
|
||||
});
|
||||
}
|
||||
|
||||
const chatUrl = `/subPackages/chat/chat?user_id=${encodeURIComponent(doctorPrefixed)}&room_id=${roomId}®ister_id=${that.register_id}`;
|
||||
const goAfterGenOrder = () => {
|
||||
if (that.needsClinicPostRegisterDrugPage()) {
|
||||
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(that.rInfo);
|
||||
uni.setStorageSync(
|
||||
CLINIC_POST_REGISTER_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
register_info_id: registerInfoId,
|
||||
register_id: that.register_id,
|
||||
drugs: drugsPayload,
|
||||
question: '您是否曾使用过此类药品?',
|
||||
room_id: roomId,
|
||||
doctor_id: doctorId,
|
||||
patient_id: patientId,
|
||||
doctor_prefixed: doctorPrefixed,
|
||||
}),
|
||||
);
|
||||
uni.hideLoading();
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.redirectTo({
|
||||
url: '/subPackages/follow-up/medication-info?mode=clinic_post_register',
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.hideLoading();
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({ url: chatUrl });
|
||||
};
|
||||
genOrderApi({
|
||||
register_id: that.register_id,
|
||||
register_info: typeof that.rInfo === 'string' ? that.rInfo : JSON.stringify(that.rInfo),
|
||||
}).then(() => {
|
||||
goAfterGenOrder();
|
||||
}).catch((error) => {
|
||||
console.error('生成订单失败:', error);
|
||||
goAfterGenOrder();
|
||||
});
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('处理在线复诊流程失败:', error);
|
||||
uni.showToast({
|
||||
title: '处理失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
next() {
|
||||
const that = this;
|
||||
if (this.flag) {
|
||||
this.flag = false;
|
||||
registePay({
|
||||
method: 'post',
|
||||
data: {
|
||||
register_id: this.register_id,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then(async (res) => {
|
||||
uni.showLoading({ title: '加载中' });
|
||||
|
||||
if (checkDev('open-im') && that.registList && that.registList.register_type === 1) {
|
||||
createRoomByDoctorIdApi({
|
||||
doctor_id: that.currentDoctorId,
|
||||
type: 1
|
||||
}).then(result => {
|
||||
sendToUserApi({
|
||||
room_id: result.data.result.room_id,
|
||||
sender_user_id: `user-${that.currentUserId}`,
|
||||
receiver_user_id: `doctor-${that.currentDoctorId}`,
|
||||
message_type: 10,
|
||||
message_content: JSON.stringify({
|
||||
id: that.register_id,
|
||||
}),
|
||||
duration: 0
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (res.data.data.is_paid == 1) {
|
||||
await that.waitForRegisterDetail();
|
||||
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
|
||||
await that.handleOnlineConsultationAfterPayment();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/register/register-success?id=${that.register_id}®ister_type=${that.registList.register_type}`,
|
||||
});
|
||||
uni.hideLoading();
|
||||
}, 800);
|
||||
return;
|
||||
}
|
||||
|
||||
uni.requestPayment({
|
||||
provider: res.data.data.appId,
|
||||
timeStamp: res.data.data.timestamp,
|
||||
nonceStr: res.data.data.nonceStr,
|
||||
package: res.data.data.package,
|
||||
signType: res.data.data.signType,
|
||||
paySign: res.data.data.paySign,
|
||||
success: async () => {
|
||||
await that.waitForRegisterDetail();
|
||||
that.flag = false;
|
||||
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
|
||||
uni.showLoading({ title: '加载中' });
|
||||
await that.handleOnlineConsultationAfterPayment();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/register/register-success?id=' + that.register_id
|
||||
});
|
||||
uni.hideLoading();
|
||||
}, 300);
|
||||
},
|
||||
fail: function(err) {
|
||||
that.flag = false;
|
||||
uni.hideLoading();
|
||||
setTimeout(() => {
|
||||
that.$refs.uToast.show({
|
||||
title: (err && err.errMsg) || '支付失败',
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
}, 300);
|
||||
}
|
||||
});
|
||||
}).catch((err) => {
|
||||
console.error('registePay', err);
|
||||
that.flag = true;
|
||||
uni.hideLoading();
|
||||
});
|
||||
}
|
||||
},
|
||||
toHome() {
|
||||
uni.switchTab({
|
||||
url: '/pages/home/home'
|
||||
});
|
||||
},
|
||||
toAbout() {
|
||||
service({
|
||||
method: 'get',
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
type: 3
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0) {
|
||||
this.content = res.data.data.content;
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 750rpx;
|
||||
box-sizing: border-box;
|
||||
padding-top: 32rpx;
|
||||
padding-bottom: calc(320rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(320rpx + env(safe-area-inset-bottom));
|
||||
background: #F8FAFC;
|
||||
|
||||
.bottom-spacer {
|
||||
height: 120rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.title {
|
||||
width: 750rpx;
|
||||
height: 80rpx;
|
||||
margin: 0 auto;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC-Bold, PingFang SC;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
line-height: 80rpx;
|
||||
padding-left: 32rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 750rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 8rpx;
|
||||
padding: 32rpx;
|
||||
margin: 24rpx auto 32rpx;
|
||||
|
||||
.item:first-child {
|
||||
margin: 0 0 48rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
line-height: 33rpx;
|
||||
margin: 48rpx 0 0;
|
||||
|
||||
.aapoint {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #232323;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #F44336;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 750rpx;
|
||||
background: #FFFFFF;
|
||||
padding: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 10rpx auto 48rpx;
|
||||
|
||||
.name_edit:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.name_edit {
|
||||
margin-top: 48rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
line-height: 33rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.word_num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 32rpx;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background-color: #FFFFFF;
|
||||
border-top: 2rpx solid #F1F5F9;
|
||||
z-index: 100;
|
||||
|
||||
.allknow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 710rpx;
|
||||
height: 40rpx;
|
||||
padding: 0 32rpx;
|
||||
margin: 10rpx auto;
|
||||
|
||||
.isknow {
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
margin-left: 8rpx;
|
||||
|
||||
.notice-link {
|
||||
color: #4175EE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer-btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20rpx;
|
||||
margin: 20rpx auto;
|
||||
width: 686rpx;
|
||||
}
|
||||
|
||||
.btn-prev {
|
||||
flex-shrink: 0;
|
||||
width: 180rpx;
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
text-align: center;
|
||||
font-size: 28rpx;
|
||||
color: #4175EE;
|
||||
background: #FFFFFF;
|
||||
border: 2rpx solid #4175EE;
|
||||
border-radius: 64rpx;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
text-align: center;
|
||||
flex: 1;
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC-Medium, PingFang SC;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
background: #4175EE;
|
||||
border-radius: 64rpx;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gh_ {
|
||||
margin-bottom: 30rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.agreen {
|
||||
padding: 60rpx 50rpx 20rpx;
|
||||
}
|
||||
|
||||
.confirm-modal {
|
||||
padding: 40rpx 30rpx;
|
||||
background: #fff;
|
||||
|
||||
.confirm-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.confirm-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
|
||||
.link {
|
||||
color: #2979FF;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20rpx;
|
||||
|
||||
.cancel-btn,
|
||||
.agree-btn {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #F5F7FA;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.agree-btn {
|
||||
background: #4175EE;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
840
subPackages/register/components/RegisterPatientStep.vue
Normal file
840
subPackages/register/components/RegisterPatientStep.vue
Normal file
@@ -0,0 +1,840 @@
|
||||
<template>
|
||||
<view class="container safe-area-inset-bottom">
|
||||
<view class="header-section">
|
||||
<view class="title-row">
|
||||
<text class="main-title">您需要为谁{{ registerType === '3' || registerType === '2' ? '复诊' : '挂号' }}</text>
|
||||
<view class="register-type-tag">
|
||||
<text class="type-label">{{ registerTypeLabel }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<text v-if="showDelegateInfo" class="delegate-info">
|
||||
委托诊所:{{ delegateStoreName }} 委托医生:{{ delegateDoctorName }}
|
||||
</text>
|
||||
<text class="sub-title">请选择患者信息,以便医生给出更准确的诊疗建议,患者信息仅医生可见</text>
|
||||
</view>
|
||||
|
||||
<view class="detail">
|
||||
<view class="one" :class="actived==item.id ? 'active' :''" @click="choose(item.id)"
|
||||
v-for="item in setInfoList" :key="item.id">
|
||||
<view class="name">
|
||||
{{item.name}}
|
||||
<text class="iconfont icon-bianji" v-if="actived==item.id" @click.stop="toEdit"></text>
|
||||
</view>
|
||||
<text class="patient-meta">{{(item.sex)% 2 == 1 ? "男" : "女"}} {{item.age}}岁</text>
|
||||
<image class="choose-badge" src="https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/choose/choosed.png" mode=""></image>
|
||||
</view>
|
||||
<view class="other" @click="goAdd(0)">
|
||||
<text class="add-plus">+</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card follow-up-card modern-card" v-if="showFollowUpForm">
|
||||
<view class="section-header">
|
||||
<text class="title-text">患者主诉</text>
|
||||
<text class="req-tag">* 必填</text>
|
||||
</view>
|
||||
<view class="input-wrapper">
|
||||
<textarea
|
||||
class="chief-textarea"
|
||||
v-model="chiefComplaint"
|
||||
placeholder="请简要描述症状、持续时间等..."
|
||||
placeholder-style="color:#94A3B8; font-size: 28rpx;"
|
||||
maxlength="2000"
|
||||
auto-height
|
||||
/>
|
||||
</view>
|
||||
<view class="section-divider"></view>
|
||||
<view class="section-header">
|
||||
<text class="title-text">选择相关西药</text>
|
||||
<text class="req-tag">* 必选</text>
|
||||
</view>
|
||||
<view class="hint-box">
|
||||
<text class="sub-hint">请至少选择一种药品;仅展示本门店与总后台均已上架的西药;数量仅作展示,提交按药品种类关联。</text>
|
||||
</view>
|
||||
<view class="drug-container">
|
||||
<FollowUpDrugDrawer
|
||||
v-model="selectedFollowUpDrugs"
|
||||
:store-id="resolveRegisterStoreId()"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="card" v-if="showHealthInfo" style="margin-top: 0;">
|
||||
<view class="card_title">健康信息</view>
|
||||
<view class="card_names">
|
||||
<text class="health-label">肝功能</text>
|
||||
<u-radio-group v-model="liver" size="44rpx">
|
||||
<u-radio size="44rpx" active-color="#2979FF" :name="liver">{{liver}}</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="card_names">
|
||||
<text class="health-label">肾功能</text>
|
||||
<u-radio-group v-model="renal" size="44rpx">
|
||||
<u-radio :name="renal">{{renal}}</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="card_history">
|
||||
<view class="name">
|
||||
<text class="health-label">过敏史</text>
|
||||
<u-radio-group v-model="allergic" size="44rpx">
|
||||
<u-radio :name="allergic">{{allergic}}</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="tabs" v-if="allergicList[0]!='' && allergic === '有'">
|
||||
<u-tag bg-color="#2979FF" color="#FFFFFF" v-for="(item,index) in allergicList" :key="index"
|
||||
:text="item" mode="dark" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="card_history">
|
||||
<view class="name">
|
||||
<text class="health-label">个人病史</text>
|
||||
<u-radio-group v-model="person" size="44rpx">
|
||||
<u-radio :name="person">{{person}}</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="tabs" v-if="personList[0]!='' && person === '有'">
|
||||
<u-tag bg-color="#2979FF" color="#FFFFFF" v-for="(item,index) in personList" :key="index"
|
||||
:text="item" mode="dark" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="card_history">
|
||||
<view class="name">
|
||||
<text class="health-label">家族病史</text>
|
||||
<u-radio-group v-model="family" size="44rpx">
|
||||
<u-radio :name="family">{{family}}</u-radio>
|
||||
</u-radio-group>
|
||||
</view>
|
||||
<view class="tabs" v-if="familyList[0]!='' && family === '有'">
|
||||
<u-tag bg-color="#2979FF" color="#FFFFFF" v-for="(item,index) in familyList" :key="index"
|
||||
:text="item" mode="dark" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="footer">
|
||||
<view class="infos" @click="subScriptionMessage">下一步</view>
|
||||
</view>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
userList,
|
||||
userEidtPatient,
|
||||
registe,
|
||||
storeInfo,
|
||||
doctorDetail,
|
||||
} from '@/request/api/api.js';
|
||||
import { getClinicOnlineConsultationInfoApi } from '@/request/api/product';
|
||||
import { createRegisterInfoApi, saveRegisterInfoApi } from '@/request/api/order';
|
||||
import request from '@/request/api/request';
|
||||
|
||||
export default {
|
||||
name: 'RegisterPatientStep',
|
||||
props: {
|
||||
doctorId: {
|
||||
type: [String, Number],
|
||||
default: '',
|
||||
},
|
||||
registerTypeProp: {
|
||||
type: String,
|
||||
default: '0',
|
||||
},
|
||||
initialRInfo: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
},
|
||||
delegateStoreIdProp: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
Did: '',
|
||||
actived: 1,
|
||||
infoList: [],
|
||||
register_id: '',
|
||||
liver: '',
|
||||
renal: '',
|
||||
allergic: '',
|
||||
allergicList: [],
|
||||
person: '',
|
||||
personList: [],
|
||||
family: '',
|
||||
familyList: [],
|
||||
heathList: [],
|
||||
rInfo: {},
|
||||
registerType: '0',
|
||||
delegateStoreId: '',
|
||||
delegateStoreName: '',
|
||||
delegateDoctorName: '',
|
||||
chiefComplaint: '',
|
||||
selectedFollowUpDrugs: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
setInfoList() {
|
||||
return this.infoList.map((item) => ({ ...item }));
|
||||
},
|
||||
registerTypeLabel() {
|
||||
const typeMap = {
|
||||
'0': '线下就诊',
|
||||
'1': '在线问诊',
|
||||
'2': '在线复诊',
|
||||
'3': '在线复诊'
|
||||
};
|
||||
return typeMap[this.registerType] || '未知类型';
|
||||
},
|
||||
hasPrefilledFollowUpInfo() {
|
||||
const r = this.rInfo;
|
||||
return !!(r && typeof r === 'object' && !Array.isArray(r) && r.info_id);
|
||||
},
|
||||
showFollowUpForm() {
|
||||
const t = this.registerType;
|
||||
return (t === '2' || t === '3') && !this.hasPrefilledFollowUpInfo;
|
||||
},
|
||||
showDelegateInfo() {
|
||||
const isFollowUp = this.registerType === '2' || this.registerType === '3';
|
||||
return isFollowUp && !!(this.delegateStoreName || this.delegateDoctorName);
|
||||
},
|
||||
showHealthInfo() {
|
||||
return this.registerType === '0'
|
||||
&& !!this.actived
|
||||
&& this.infoList.some((p) => p.id === this.actived);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
doctorId: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.Did = val;
|
||||
},
|
||||
},
|
||||
registerTypeProp: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
if (val) this.registerType = val;
|
||||
},
|
||||
},
|
||||
initialRInfo: {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(val) {
|
||||
this.rInfo = val ? { ...val } : {};
|
||||
},
|
||||
},
|
||||
delegateStoreIdProp: {
|
||||
immediate: true,
|
||||
handler(val) {
|
||||
this.delegateStoreId = val || '';
|
||||
this.loadDelegateDisplayInfo();
|
||||
},
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getInfolist();
|
||||
},
|
||||
methods: {
|
||||
resolveRegisterStoreId() {
|
||||
return (this.registerType !== '0' && this.delegateStoreId)
|
||||
? this.delegateStoreId
|
||||
: (uni.getStorageSync('store_id') || '11001');
|
||||
},
|
||||
async loadDelegateDisplayInfo() {
|
||||
if (this.registerType !== '2' && this.registerType !== '3') return;
|
||||
const currentStoreId = uni.getStorageSync('store_id') || '11001';
|
||||
try {
|
||||
if (this.registerType === '2') {
|
||||
const res = await getClinicOnlineConsultationInfoApi({ store_id: currentStoreId });
|
||||
const result = res.data?.result;
|
||||
if (res.data?.code === 0 && result) {
|
||||
this.delegateStoreName = result.delegate_store_name || result.store_name || '';
|
||||
this.delegateDoctorName = result.doctor_name || '';
|
||||
}
|
||||
} else {
|
||||
const storeId = this.delegateStoreId || this.resolveRegisterStoreId();
|
||||
const [storeRes, doctorRes] = await Promise.all([
|
||||
storeInfo({ method: 'post', data: { store_id: storeId } }),
|
||||
doctorDetail({ method: 'post', data: { doctor_id: this.Did, store_id: currentStoreId } }),
|
||||
]);
|
||||
if (storeRes.data?.errcode == 0) {
|
||||
this.delegateStoreName = storeRes.data.data?.store?.name || '';
|
||||
}
|
||||
const doctorData = doctorRes.data?.data || doctorRes.data?.result;
|
||||
if (doctorRes.data?.errcode == 0 || doctorRes.data?.code === 0) {
|
||||
this.delegateDoctorName = doctorData?.name || '';
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('loadDelegateDisplayInfo', e);
|
||||
}
|
||||
},
|
||||
followUpDrugIdsForApi() {
|
||||
const ids = [];
|
||||
const seen = new Set();
|
||||
for (const it of this.selectedFollowUpDrugs || []) {
|
||||
const id = Number(it.id);
|
||||
if (id > 0 && !seen.has(id)) {
|
||||
seen.add(id);
|
||||
ids.push(id);
|
||||
}
|
||||
}
|
||||
return ids;
|
||||
},
|
||||
validateFollowUpFields() {
|
||||
const chief = String(this.chiefComplaint || '').trim();
|
||||
if (!chief) return '请填写患者主诉';
|
||||
if (this.followUpDrugIdsForApi().length === 0) return '请至少选择一种药品';
|
||||
return '';
|
||||
},
|
||||
async prepareFollowUpRegisterInfo() {
|
||||
let params = typeof this.rInfo === 'string' ? JSON.parse(this.rInfo) : { ...(this.rInfo || {}) };
|
||||
if (params.info_id) {
|
||||
return params;
|
||||
}
|
||||
const err = this.validateFollowUpFields();
|
||||
if (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
const chief = String(this.chiefComplaint || '').trim();
|
||||
const storeId = this.resolveRegisterStoreId();
|
||||
const westernDrugQty = {};
|
||||
let qtySum = 0;
|
||||
for (const d of this.selectedFollowUpDrugs || []) {
|
||||
const id = Number(d.id);
|
||||
const q = Math.min(99, Math.max(1, Number(d.quantity) || 1));
|
||||
if (id > 0) {
|
||||
westernDrugQty[String(id)] = q;
|
||||
qtySum += q;
|
||||
}
|
||||
}
|
||||
const createRes = await createRegisterInfoApi({
|
||||
doctor_id: Number(this.Did),
|
||||
drug_id: '',
|
||||
has_visited: '',
|
||||
has_used_drug: '',
|
||||
illnessInfo: '',
|
||||
type: 2,
|
||||
number: qtySum >= 1 ? qtySum : 1,
|
||||
store_id: Number(storeId) || storeId,
|
||||
chief_complaint: chief,
|
||||
drug_ids: this.followUpDrugIdsForApi(),
|
||||
western_drug_qty: JSON.stringify(westernDrugQty),
|
||||
});
|
||||
const infoId = createRes.data?.result?.info_id;
|
||||
if (!infoId) {
|
||||
throw new Error('创建就诊信息失败');
|
||||
}
|
||||
const followUpDrugs = (this.selectedFollowUpDrugs || [])
|
||||
.map((d) => ({
|
||||
drug_id: Number(d.id),
|
||||
name: d.drug_name || d.name || '',
|
||||
quantity: Math.min(99, Math.max(1, Number(d.quantity) || 1)),
|
||||
}))
|
||||
.filter((d) => d.drug_id > 0);
|
||||
const followUpNumber = followUpDrugs.reduce((s, r) => s + (Number(r.quantity) || 1), 0) || 1;
|
||||
return { ...params, info_id: infoId, follow_up_drugs: followUpDrugs, follow_up_number: followUpNumber, number: followUpNumber };
|
||||
},
|
||||
goAdd() {
|
||||
uni.navigateTo({ url: '/subPackages/my/myinfo-add?id=' });
|
||||
},
|
||||
toEdit() {
|
||||
uni.navigateTo({ url: '/subPackages/my/myinfo-edit?id=' + this.actived });
|
||||
},
|
||||
choose(id) {
|
||||
this.actived = id;
|
||||
this.getHeath();
|
||||
},
|
||||
getInfolist() {
|
||||
userList({
|
||||
method: 'post',
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0) {
|
||||
this.infoList = res.data.data;
|
||||
this.infoList.filter(item => {
|
||||
if (item.is_default == 1) {
|
||||
this.actived = item.id;
|
||||
}
|
||||
});
|
||||
this.getHeath();
|
||||
}
|
||||
});
|
||||
},
|
||||
normalizeRInfo(followUpRInfo) {
|
||||
if (typeof followUpRInfo === 'string') {
|
||||
try {
|
||||
return JSON.parse(followUpRInfo);
|
||||
} catch (e) {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return followUpRInfo || {};
|
||||
},
|
||||
emitRegisterCreated(followUpRInfo, isHas) {
|
||||
const rInfoObj = this.normalizeRInfo(followUpRInfo);
|
||||
const payload = {
|
||||
register_id: this.register_id,
|
||||
doctor_id: this.Did,
|
||||
rInfo: rInfoObj,
|
||||
};
|
||||
if (isHas) {
|
||||
this.$refs.uToast.show({
|
||||
title: '您已挂号该医生,请继续问诊服务',
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.$emit('register-created', payload);
|
||||
}, 900);
|
||||
} else {
|
||||
this.$emit('register-created', payload);
|
||||
}
|
||||
},
|
||||
async afterRegisterCreated(followUpRInfo) {
|
||||
if (this.registerType === '3' || this.registerType === '2') {
|
||||
const params = this.normalizeRInfo(followUpRInfo);
|
||||
if (params.info_id) {
|
||||
try {
|
||||
await request('/xkApi/order/update-register-info-register-id', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
info_id: params.info_id,
|
||||
register_id: this.register_id,
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('更新就诊信息关联失败:', error);
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
await saveRegisterInfoApi({
|
||||
register_id: this.register_id,
|
||||
doctor_id: params.doctor_id || this.Did,
|
||||
drug_id: params.drug_id || params.id,
|
||||
has_visited: params.has_visited || params.hasVisited,
|
||||
has_used_drug: params.has_used_drug || params.hasUsedDrug,
|
||||
illnessInfo: params.illness_info || params.illnessInfo,
|
||||
type: params.type,
|
||||
number: params.number,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('保存患者就诊信息失败:', error);
|
||||
}
|
||||
}
|
||||
}
|
||||
uni.setStorageSync('register_id', this.register_id);
|
||||
this.emitRegisterCreated(followUpRInfo, false);
|
||||
},
|
||||
async getAsk() {
|
||||
uni.showLoading({ title: '加载中...', mask: true });
|
||||
const storeId = this.resolveRegisterStoreId();
|
||||
let followUpRInfo = this.rInfo;
|
||||
if (this.showFollowUpForm) {
|
||||
try {
|
||||
followUpRInfo = await this.prepareFollowUpRegisterInfo();
|
||||
this.rInfo = followUpRInfo;
|
||||
} catch (e) {
|
||||
uni.hideLoading();
|
||||
this.$refs.uToast.show({
|
||||
title: e.message || '提交失败',
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
const originStoreId = uni.getStorageSync('store_id') || '';
|
||||
const shouldSendOriginStoreId =
|
||||
this.registerType === '2' ||
|
||||
this.registerType === '3' ||
|
||||
(this.registerType !== '0' && !!this.delegateStoreId);
|
||||
registe({
|
||||
method: 'post',
|
||||
data: {
|
||||
user_id: uni.getStorageSync('user_id'),
|
||||
user_patient_id: this.actived,
|
||||
store_id: storeId,
|
||||
origin_store_id: shouldSendOriginStoreId ? originStoreId : 0,
|
||||
service_user_id: this.Did,
|
||||
register_type: this.registerType
|
||||
}
|
||||
}).then(async (res) => {
|
||||
if (res.data.errcode == 0) {
|
||||
this.register_id = res.data.data.register_id;
|
||||
if (this.registerType === '3' || this.registerType === '2') {
|
||||
await this.afterRegisterCreated(followUpRInfo);
|
||||
return;
|
||||
}
|
||||
uni.setStorageSync('register_id', this.register_id);
|
||||
this.emitRegisterCreated(followUpRInfo, res.data.data.isHas == 1);
|
||||
} else {
|
||||
this.$refs.uToast.show({
|
||||
title: res.data.msg,
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
}
|
||||
}).finally(() => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
getHeath() {
|
||||
userEidtPatient({
|
||||
method: 'post',
|
||||
data: {
|
||||
user_patient_id: this.actived,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0) {
|
||||
this.heathList = res.data.data;
|
||||
this.liver = (this.heathList.liver_function) % 2 == 0 ? '正常' : '异常';
|
||||
this.renal = (this.heathList.renal_function) % 2 == 0 ? '正常' : '异常';
|
||||
this.allergic = (this.heathList.allergic_status) % 2 == 0 ? '无' : '有';
|
||||
this.allergicList = (this.heathList.allergic_history) == 'Array' ? [] : (this.heathList.allergic_history);
|
||||
this.person = (this.heathList.person_status) % 2 == 0 ? '无' : '有';
|
||||
this.personList = (this.heathList.person_history) == 'Array' ? [] : (this.heathList.person_history);
|
||||
this.family = (this.heathList.family_status) % 2 == 0 ? '无' : '有';
|
||||
this.familyList = (this.heathList.family_history) == 'Array' ? [] : (this.heathList.family_history);
|
||||
}
|
||||
});
|
||||
},
|
||||
subScriptionMessage() {
|
||||
if (this.showFollowUpForm) {
|
||||
const err = this.validateFollowUpFields();
|
||||
if (err) {
|
||||
this.$refs.uToast.show({
|
||||
title: err,
|
||||
type: 'default',
|
||||
icon: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
uni.requestSubscribeMessage({
|
||||
tmplIds: [
|
||||
'O5P8ZZB6T01V5Z66tkbhmzlobSaSRoEfdoCLATuP2Mw',
|
||||
'ddT3v-m_Sr4kXwGCBLf0O4P89mOS5q10LFMZeEuQm10'
|
||||
],
|
||||
complete: () => {
|
||||
this.getAsk();
|
||||
}
|
||||
});
|
||||
},
|
||||
refreshPatientList() {
|
||||
this.getInfolist();
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 750rpx;
|
||||
max-height: 100%;
|
||||
min-height: 100vh;
|
||||
box-sizing: border-box;
|
||||
background-color: #F8FAFC;
|
||||
padding-bottom: 200rpx;
|
||||
padding-bottom: calc(140rpx + constant(safe-area-inset-bottom));
|
||||
padding-bottom: calc(140rpx + env(safe-area-inset-bottom));
|
||||
|
||||
.header-section {
|
||||
padding: 32rpx 32rpx 0;
|
||||
|
||||
.title-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.main-title {
|
||||
font-size: 38rpx;
|
||||
font-family: PingFang SC-Medium, PingFang SC;
|
||||
font-weight: 600;
|
||||
color: #0F172A;
|
||||
margin-right: 16rpx;
|
||||
}
|
||||
|
||||
.register-type-tag {
|
||||
background: linear-gradient(135deg, #4175EE 0%, #298dff 100%);
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
box-shadow: 0 2rpx 8rpx rgba(65, 117, 238, 0.2);
|
||||
|
||||
.type-label {
|
||||
color: #FFFFFF;
|
||||
font-size: 24rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.sub-title {
|
||||
display: block;
|
||||
font-size: 24rpx;
|
||||
font-weight: 400;
|
||||
color: #64748B;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.delegate-info {
|
||||
display: block;
|
||||
font-size: 26rpx;
|
||||
font-weight: 400;
|
||||
color: #4175EE;
|
||||
line-height: 1.5;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.detail {
|
||||
width: 710rpx;
|
||||
min-height: 124rpx;
|
||||
margin: 32rpx;
|
||||
padding-bottom: 8rpx;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: flex-start;
|
||||
|
||||
.one {
|
||||
width: 218rpx;
|
||||
height: 124rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 0 16rpx 16rpx 0;
|
||||
border: 2rpx solid #CBD5E1;
|
||||
position: relative;
|
||||
background: #FFFFFF;
|
||||
|
||||
.name {
|
||||
margin: 16rpx 0 8rpx 32rpx;
|
||||
height: 44rpx;
|
||||
font-size: 32rpx;
|
||||
font-weight: 400;
|
||||
color: #0F172A;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-right: 18rpx;
|
||||
|
||||
.iconfont {
|
||||
font-size: 32rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.patient-meta {
|
||||
font-size: 28rpx;
|
||||
color: #94A3B8;
|
||||
margin-left: 32rpx;
|
||||
}
|
||||
|
||||
.choose-badge {
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
position: absolute;
|
||||
right: -2rpx;
|
||||
bottom: -1rpx;
|
||||
display: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
border: 2rpx solid #536DFE;
|
||||
background: rgba(83, 109, 254, 0.04);
|
||||
|
||||
.choose-badge {
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.other {
|
||||
width: 218rpx;
|
||||
height: 124rpx;
|
||||
line-height: 104rpx;
|
||||
text-align: center;
|
||||
border-radius: 8rpx;
|
||||
border: 2rpx dashed #CBD5E1;
|
||||
background: #FFFFFF;
|
||||
|
||||
.add-plus {
|
||||
font-size: 68rpx;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 710rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
margin: 20rpx auto 40rpx;
|
||||
padding: 0 32rpx 40rpx;
|
||||
box-shadow: 0 4rpx 24rpx rgba(15, 23, 42, 0.02);
|
||||
|
||||
.card_title {
|
||||
height: 114rpx;
|
||||
line-height: 114rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
border-bottom: 1rpx solid #F1F5F9;
|
||||
}
|
||||
|
||||
.card_names {
|
||||
width: 646rpx;
|
||||
padding: 34rpx 0 32rpx 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 30rpx;
|
||||
border-bottom: 1rpx solid #F1F5F9;
|
||||
|
||||
.health-label {
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
|
||||
.card_history {
|
||||
width: 646rpx;
|
||||
padding: 34rpx 0 32rpx 0;
|
||||
font-size: 30rpx;
|
||||
border-bottom: 1rpx solid #F1F5F9;
|
||||
|
||||
.name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 10rpx;
|
||||
|
||||
.health-label {
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
::v-deep .u-tag {
|
||||
margin: 10rpx 20rpx 10rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.card_history:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.modern-card {
|
||||
padding: 32rpx;
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.title-text {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #0F172A;
|
||||
position: relative;
|
||||
padding-left: 18rpx;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 8rpx;
|
||||
height: 28rpx;
|
||||
background: #4175EE;
|
||||
border-radius: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.req-tag {
|
||||
color: #EF4444;
|
||||
font-size: 24rpx;
|
||||
background: rgba(239, 68, 68, 0.08);
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 6rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.section-divider {
|
||||
height: 1rpx;
|
||||
background-color: #F1F5F9;
|
||||
margin: 40rpx 0 32rpx;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
background: #F8FAFC;
|
||||
border-radius: 12rpx;
|
||||
|
||||
.chief-textarea {
|
||||
width: 100%;
|
||||
min-height: 160rpx;
|
||||
padding: 24rpx;
|
||||
box-sizing: border-box;
|
||||
font-size: 28rpx;
|
||||
color: #334155;
|
||||
line-height: 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
.hint-box {
|
||||
background: #F1F5F9;
|
||||
border-radius: 8rpx;
|
||||
padding: 16rpx 24rpx;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.sub-hint {
|
||||
font-size: 24rpx;
|
||||
color: #64748B;
|
||||
line-height: 1.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.footer {
|
||||
width: 750rpx;
|
||||
min-height: 104rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-top: 2rpx solid #F1F5F9;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
background-color: #FFFFFF;
|
||||
z-index: 100;
|
||||
|
||||
.infos {
|
||||
width: 686rpx;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
margin: 16rpx 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 500;
|
||||
background: linear-gradient(135deg, #4175EE 0%, #298dff 100%);
|
||||
box-shadow: 0 8rpx 20rpx rgba(65, 117, 238, 0.3);
|
||||
border-radius: 198rpx;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
148
subPackages/register/components/RegisterStepBar.vue
Normal file
148
subPackages/register/components/RegisterStepBar.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<view class="register-step-bar">
|
||||
<view
|
||||
class="step-item"
|
||||
:class="{
|
||||
active: currentStep === 1,
|
||||
done: currentStep > 1,
|
||||
clickable: currentStep === 2 && allowBackToStep1,
|
||||
}"
|
||||
@click="onStepClick(1)"
|
||||
>
|
||||
<view class="step-circle">
|
||||
<text v-if="currentStep > 1" class="step-check">✓</text>
|
||||
<text v-else class="step-num">1</text>
|
||||
</view>
|
||||
<text class="step-label">选择就诊人</text>
|
||||
</view>
|
||||
<view class="step-line" :class="{ done: currentStep > 1 }"></view>
|
||||
<view class="step-item" :class="{ active: currentStep === 2 }" @click="onStepClick(2)">
|
||||
<view class="step-circle">
|
||||
<text class="step-num">2</text>
|
||||
</view>
|
||||
<text class="step-label">确认预约信息,支付挂号费</text>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RegisterStepBar',
|
||||
props: {
|
||||
currentStep: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
allowBackToStep1: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
onStepClick(stepNum) {
|
||||
if (stepNum === 1 && this.currentStep === 2 && this.allowBackToStep1) {
|
||||
this.$emit('step-click', 1);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.register-step-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 24rpx 32rpx 16rpx;
|
||||
background: #F8FAFC;
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
min-width: 140rpx;
|
||||
|
||||
&.clickable {
|
||||
.step-circle,
|
||||
.step-label {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
&:active {
|
||||
opacity: 0.75;
|
||||
}
|
||||
}
|
||||
|
||||
.step-circle {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
border-radius: 50%;
|
||||
background: #E2E8F0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-bottom: 8rpx;
|
||||
|
||||
.step-num,
|
||||
.step-check {
|
||||
font-size: 24rpx;
|
||||
font-weight: 600;
|
||||
color: #94A3B8;
|
||||
}
|
||||
}
|
||||
|
||||
.step-label {
|
||||
font-size: 24rpx;
|
||||
color: #94A3B8;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&.active {
|
||||
.step-circle {
|
||||
background: #4175EE;
|
||||
|
||||
.step-num {
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
.step-label {
|
||||
color: #4175EE;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
&.done {
|
||||
.step-circle {
|
||||
background: rgba(65, 117, 238, 0.15);
|
||||
|
||||
.step-check {
|
||||
color: #4175EE;
|
||||
}
|
||||
}
|
||||
|
||||
.step-label {
|
||||
color: #64748B;
|
||||
}
|
||||
|
||||
&.clickable .step-label {
|
||||
color: #4175EE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step-line {
|
||||
flex: 1;
|
||||
max-width: 120rpx;
|
||||
height: 4rpx;
|
||||
background: #E2E8F0;
|
||||
margin: 0 16rpx;
|
||||
margin-bottom: 32rpx;
|
||||
border-radius: 2rpx;
|
||||
|
||||
&.done {
|
||||
background: #4175EE;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -18,6 +18,11 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
saveSpecialPrescriptionRegisterContext,
|
||||
tryCreateSpecialPrescriptionPatientRecord,
|
||||
} from '@/request/api/specialPrescription';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
@@ -26,8 +31,19 @@
|
||||
},
|
||||
onLoad(e) {
|
||||
this.register_id = e.id
|
||||
if (e.special_prescription_id) {
|
||||
saveSpecialPrescriptionRegisterContext({
|
||||
special_prescription_id: e.special_prescription_id,
|
||||
dose_count: e.special_prescription_dose_count || 1,
|
||||
doctor_id: e.doctor_id || '',
|
||||
})
|
||||
}
|
||||
this.bindSpecialPrescriptionRecord()
|
||||
},
|
||||
methods: {
|
||||
async bindSpecialPrescriptionRecord() {
|
||||
await tryCreateSpecialPrescriptionPatientRecord(this.register_id)
|
||||
},
|
||||
// 确认
|
||||
goOrder() {
|
||||
uni.navigateTo({
|
||||
|
||||
@@ -1,922 +1,39 @@
|
||||
<template>
|
||||
<view class="container safe-area-inset-bottom">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container
|
||||
:show="pageBackGuardShow"
|
||||
:overlay="false"
|
||||
@leave="onPageBackGuardLeave"
|
||||
/>
|
||||
<!-- #endif -->
|
||||
<MessageNotification />
|
||||
<!-- 预约信息 -->
|
||||
<view class="title">
|
||||
预约信息
|
||||
</view>
|
||||
|
||||
<view class="card">
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约诊所
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.store}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约科室
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.depart}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约医生
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.doctor}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预约序号
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.order_number}}号
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
预计就诊时间
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registList.created_at}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
挂号金额
|
||||
</view>
|
||||
<view class="detail active">
|
||||
{{registList.price}}元
|
||||
</view>
|
||||
</view>
|
||||
<view class="item">
|
||||
<view class="aapoint">
|
||||
挂号类型
|
||||
</view>
|
||||
<view class="detail">
|
||||
{{registerType}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 就诊人 -->
|
||||
<view class="title">
|
||||
就诊人
|
||||
</view>
|
||||
|
||||
<view class="info">
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
{{registList.patient}}
|
||||
</view>
|
||||
<view class="word_num">
|
||||
{{registList.order_number}}号
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- replace(/(\w{6})\w*(\w{4})/,'$1********$2') -->
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
身份证号
|
||||
</view>
|
||||
<view class="word_num" v-if="registList.idcard">
|
||||
{{registList.idcard_tm || registList.idcard}}
|
||||
</view>
|
||||
</view>
|
||||
<view class="name_edit">
|
||||
<view class="name">
|
||||
手机号
|
||||
</view>
|
||||
<view class="word_num" v-if="registList.mobile">
|
||||
{{registList.mobile_tm || registList.mobile}}
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 须知 -->
|
||||
<u-popup v-model="show" :safe-area-inset-bottom="true" mode="bottom" height="68%" border-radius="20"
|
||||
:closeable="true" z-index="20000">
|
||||
<view class="agreen">
|
||||
<u-parse :html="content"></u-parse>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 未勾选须知时的确认弹窗 -->
|
||||
<u-popup v-model="showConfirm" mode="center" border-radius="24" width="600" z-index="10075">
|
||||
<view class="confirm-modal">
|
||||
<view class="confirm-title">预约挂号须知</view>
|
||||
<view class="confirm-desc">
|
||||
请您在使用前仔细阅读并同意
|
||||
<text class="link" @click="show=true">《预约挂号须知》</text>
|
||||
。
|
||||
</view>
|
||||
<view class="confirm-footer">
|
||||
<button class="cancel-btn" @click="showConfirm = false">不同意</button>
|
||||
<button class="agree-btn" @click="agreeAndRegister">同意并挂号</button>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 底部 -->
|
||||
<view class="footer">
|
||||
<block v-if="registList.is_pay==0">
|
||||
<!-- 须知 -->
|
||||
<view class="allknow">
|
||||
<u-icon size="40" :color="form['check']?'#2979FF':'#999999'" name="checkmark-circle-fill"
|
||||
@click="form['check']=!form['check']"></u-icon>
|
||||
<view class="isknow">我已阅读并同意 <text @click="show=true">《预约挂号须知》</text> </view>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<button class="btn" @click="handleRegisterClick">确认挂号
|
||||
</button>
|
||||
</block>
|
||||
<view class="gh_" v-if="registList.is_pay==1" @click="toHome">
|
||||
<button class="btn">您已挂号成功,请等待医生接诊
|
||||
</button>
|
||||
</view>
|
||||
</view>
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
<view class="register-redirect">
|
||||
<u-loading mode="circle" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
registeInfo,
|
||||
registePay,
|
||||
registe,
|
||||
service
|
||||
} from '../../request/api/api';
|
||||
import {createRoomByDoctorIdApi, sendToUserApi} from "@/request/api/im";
|
||||
import {checkDev} from "@/utils/utils";
|
||||
import {genOrderApi, saveRegisterInfoApi} from "@/request/api/order";
|
||||
import request from "@/request/api/request";
|
||||
import {
|
||||
CLINIC_POST_REGISTER_STORAGE_KEY,
|
||||
resolveFollowUpDrugsFromRInfo,
|
||||
} from '@/utils/clinicFollowUpAssistantIm';
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
show: false,
|
||||
showConfirm: false,
|
||||
know: "",
|
||||
form: {
|
||||
check: false
|
||||
},
|
||||
register_id: "",
|
||||
currentUserId: "",
|
||||
currentDoctorId: "",
|
||||
registList: [],
|
||||
content: `预约挂号须知`,
|
||||
flag: true,
|
||||
registerType: '线下就诊',
|
||||
registerMap: {
|
||||
0: '挂号',
|
||||
1: '咨询',
|
||||
2: '购药',
|
||||
3: '复诊',
|
||||
},
|
||||
rInfo: {},
|
||||
_urlDoctorId: '',
|
||||
_getInfoPromise: null,
|
||||
pageBackGuardShow: false,
|
||||
_suppressPageBackGuardLeave: false,
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
shouldEnablePageBackGuard() {
|
||||
return !(this.registList && this.registList.is_pay == 1);
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'registList.is_pay'(val) {
|
||||
if (val == 1) {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
} else {
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
},
|
||||
onLoad(e) {
|
||||
const uid = uni.getStorageSync('user_id');
|
||||
this.currentUserId = 'user-' + (uid !== '' && uid !== undefined && uid !== null ? uid : '2512');
|
||||
|
||||
this.register_id = e.id;
|
||||
try {
|
||||
this.rInfo = e.r_info ? (typeof e.r_info === 'string' ? JSON.parse(decodeURIComponent(e.r_info)) : e.r_info) : {};
|
||||
} catch (err) {
|
||||
console.error('register onLoad r_info parse', err);
|
||||
this.rInfo = {};
|
||||
}
|
||||
|
||||
const urlRaw = e.doctor_id && String(e.doctor_id).trim() !== '' ? String(e.doctor_id) : '';
|
||||
this._urlDoctorId = urlRaw ? this.stripDoctorPrefix(urlRaw) : '';
|
||||
|
||||
let initialDoc = '';
|
||||
if (this._urlDoctorId) initialDoc = this._urlDoctorId;
|
||||
else if (this.rInfo && (this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '')) {
|
||||
initialDoc = this.stripDoctorPrefix(this.rInfo.doctor_id);
|
||||
} else if (this.rInfo && (this.rInfo.doctorId != null && this.rInfo.doctorId !== '')) {
|
||||
initialDoc = this.stripDoctorPrefix(this.rInfo.doctorId);
|
||||
}
|
||||
this.currentDoctorId = initialDoc ? ('doctor-' + initialDoc) : '';
|
||||
|
||||
uni.setStorageSync('register_id', this.register_id);
|
||||
|
||||
if (this.register_id) {
|
||||
this._getInfoPromise = this.getInfo();
|
||||
this._getInfoPromise.then(() => this.ensureDoctorAfterLoad());
|
||||
} else {
|
||||
this.ensureDoctorAfterLoad();
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
onBackPress() {
|
||||
if (this.closeTopOverlayIfAny()) {
|
||||
return true;
|
||||
}
|
||||
if (this.registList && this.registList.is_pay == 1) {
|
||||
return false;
|
||||
}
|
||||
this.promptExitBeforeLeave();
|
||||
return true;
|
||||
},
|
||||
methods: {
|
||||
handleRegisterClick() {
|
||||
if (!this.form.check) {
|
||||
this.showConfirm = true;
|
||||
return;
|
||||
}
|
||||
this.next();
|
||||
},
|
||||
agreeAndRegister() {
|
||||
this.form.check = true;
|
||||
this.showConfirm = false;
|
||||
this.next();
|
||||
},
|
||||
closeTopOverlayIfAny() {
|
||||
if (this.show) {
|
||||
this.show = false;
|
||||
return true;
|
||||
}
|
||||
if (this.showConfirm) {
|
||||
this.showConfirm = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
setPageBackGuardShow(show, { suppressLeave = false } = {}) {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (suppressLeave) this._suppressPageBackGuardLeave = true;
|
||||
this.pageBackGuardShow = !!show;
|
||||
// #endif
|
||||
},
|
||||
syncPageBackGuard() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.$nextTick(() => {
|
||||
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, {
|
||||
suppressLeave: !this.shouldEnablePageBackGuard,
|
||||
});
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
safeNavigateBack() {
|
||||
// #ifdef MP-WEIXIN
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 16);
|
||||
// #endif
|
||||
// #ifndef MP-WEIXIN
|
||||
uni.navigateBack();
|
||||
// #endif
|
||||
},
|
||||
onPageBackGuardLeave() {
|
||||
// #ifdef MP-WEIXIN
|
||||
if (this._suppressPageBackGuardLeave) {
|
||||
this._suppressPageBackGuardLeave = false;
|
||||
return;
|
||||
}
|
||||
if (this.closeTopOverlayIfAny()) {
|
||||
this.$nextTick(() => {
|
||||
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, { suppressLeave: true });
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.registList && this.registList.is_pay == 1) {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
}, 16);
|
||||
return;
|
||||
}
|
||||
this.setPageBackGuardShow(false);
|
||||
this.promptExitBeforeLeave({
|
||||
onStay: () => {
|
||||
this.syncPageBackGuard();
|
||||
},
|
||||
onExit: () => {
|
||||
this.safeNavigateBack();
|
||||
},
|
||||
});
|
||||
// #endif
|
||||
},
|
||||
disableBackGuardBeforeLeave() {
|
||||
this.setPageBackGuardShow(false, { suppressLeave: true });
|
||||
},
|
||||
promptExitBeforeLeave({ onStay, onExit } = {}) {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '当前未挂号成功,是否继续还是退出?',
|
||||
cancelText: '继续',
|
||||
confirmText: '退出',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
if (onExit) {
|
||||
onExit();
|
||||
} else {
|
||||
this.safeNavigateBack();
|
||||
}
|
||||
} else if (onStay) {
|
||||
onStay();
|
||||
} else {
|
||||
this.syncPageBackGuard();
|
||||
}
|
||||
},
|
||||
});
|
||||
},
|
||||
stripDoctorPrefix(raw) {
|
||||
if (raw === null || raw === undefined || raw === '') return '';
|
||||
return String(raw).replace(/^doctor-/, '').trim();
|
||||
},
|
||||
applyDoctorNumericId(id) {
|
||||
const n = this.stripDoctorPrefix(id);
|
||||
if (!n) return;
|
||||
this.currentDoctorId = 'doctor-' + n;
|
||||
},
|
||||
/** 同步可用的医生数字 id:详情 > rInfo > URL > 当前 currentDoctorId */
|
||||
getDoctorNumericIdFromLocalSync() {
|
||||
const rl = this.registList;
|
||||
if (rl && typeof rl === 'object' && !Array.isArray(rl) && rl.service_user_id) {
|
||||
const s = this.stripDoctorPrefix(rl.service_user_id);
|
||||
if (s) return s;
|
||||
}
|
||||
if (this.rInfo) {
|
||||
const d = this.rInfo.doctor_id != null && this.rInfo.doctor_id !== '' ? this.rInfo.doctor_id : this.rInfo.doctorId;
|
||||
if (d != null && d !== '') {
|
||||
const s = this.stripDoctorPrefix(d);
|
||||
if (s) return s;
|
||||
}
|
||||
}
|
||||
if (this._urlDoctorId) {
|
||||
const s = this.stripDoctorPrefix(this._urlDoctorId);
|
||||
if (s) return s;
|
||||
}
|
||||
const cur = this.stripDoctorPrefix(this.currentDoctorId);
|
||||
if (cur) return cur;
|
||||
return '';
|
||||
},
|
||||
async fetchDefaultDoctorIdApi() {
|
||||
const storeId = uni.getStorageSync('store_id') || '11001';
|
||||
try {
|
||||
const res = await request('/xkApi/online-consultation/get-default-doctor-id', {
|
||||
method: 'GET',
|
||||
data: { store_id: storeId }
|
||||
}, 0);
|
||||
const id = res.data?.result?.doctor_id ?? res.data?.doctor_id;
|
||||
return id ? this.stripDoctorPrefix(id) : '';
|
||||
} catch (err) {
|
||||
console.error('get-default-doctor-id failed', err);
|
||||
return '';
|
||||
}
|
||||
},
|
||||
async resolveDoctorNumericIdForIm() {
|
||||
let id = this.getDoctorNumericIdFromLocalSync();
|
||||
if (!id) id = await this.fetchDefaultDoctorIdApi();
|
||||
if (id) this.applyDoctorNumericId(id);
|
||||
return id || '';
|
||||
},
|
||||
async ensureDoctorAfterLoad() {
|
||||
const existing = this.getDoctorNumericIdFromLocalSync();
|
||||
if (existing) {
|
||||
this.applyDoctorNumericId(existing);
|
||||
return;
|
||||
}
|
||||
const id = await this.fetchDefaultDoctorIdApi();
|
||||
if (id) this.applyDoctorNumericId(id);
|
||||
},
|
||||
async waitForRegisterDetail() {
|
||||
if (!this.register_id) return;
|
||||
try {
|
||||
if (this._getInfoPromise) await this._getInfoPromise;
|
||||
else await this.getInfo();
|
||||
} catch (err) {
|
||||
console.error('waitForRegisterDetail', err);
|
||||
}
|
||||
},
|
||||
/** 诊所复诊(2)且存在复诊药:支付后先走独立用药确认页,再进聊天 */
|
||||
needsClinicPostRegisterDrugPage() {
|
||||
if (!this.registList || this.registList.register_type !== 2) return false;
|
||||
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(this.rInfo);
|
||||
return !!(registerInfoId && drugsPayload.length);
|
||||
},
|
||||
// 挂号信息
|
||||
getInfo() {
|
||||
const that = this;
|
||||
if (!this.register_id) return Promise.resolve();
|
||||
const p = registeInfo({
|
||||
method: "post",
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
register_id: that.register_id
|
||||
}
|
||||
}).then((res) => {
|
||||
if (res.data.errcode == 0 && res.data.data.list && res.data.data.list[0]) {
|
||||
that.registList = res.data.data.list[0];
|
||||
that.registerType = that.registerMap[that.registList.register_type];
|
||||
if (that.registList.service_user_id) {
|
||||
that.applyDoctorNumericId(that.registList.service_user_id);
|
||||
}
|
||||
}
|
||||
that.syncPageBackGuard();
|
||||
});
|
||||
this._getInfoPromise = p;
|
||||
return p;
|
||||
},
|
||||
// 处理在线复诊支付后的流程
|
||||
async handleOnlineConsultationAfterPayment() {
|
||||
const that = this;
|
||||
try {
|
||||
const doctorId = await that.resolveDoctorNumericIdForIm();
|
||||
if (!doctorId) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '无法获取医生信息,请稍后重试',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
const doctorPrefixed = 'doctor-' + doctorId;
|
||||
that.currentDoctorId = doctorPrefixed;
|
||||
|
||||
const patientId = that.currentUserId.replace('user-', '');
|
||||
|
||||
// 3. 创建或获取聊天房间
|
||||
// type=2 表示诊所在线复诊,type=3 表示药店在线复诊/购药
|
||||
const roomType = (that.registList && that.registList.register_type === 2) ? 2 : 3;
|
||||
const roomRes = await request('/xkApi/online-consultation/get-or-create-chat-room', {
|
||||
method: 'POST',
|
||||
data: {
|
||||
doctor_id: doctorId,
|
||||
patient_id: patientId,
|
||||
type: roomType
|
||||
}
|
||||
}, 1);
|
||||
|
||||
const roomId = roomRes.data?.result?.room_id || roomRes.data?.room_id;
|
||||
if (!roomId) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({
|
||||
title: '创建聊天房间失败',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 4. 仅药店模式(store_type='1')发送患者就诊经历卡片
|
||||
const storeType = uni.getStorageSync('store_type')
|
||||
if (storeType === '1' || storeType === 1) {
|
||||
const patientExperienceData = {
|
||||
symptom_description: that.rInfo.illness_info || '',
|
||||
has_visited: that.rInfo.has_visited || '0',
|
||||
has_used_drug: that.rInfo.has_used_drug || '0',
|
||||
drug_name: that.rInfo.drug_name || '',
|
||||
illness_info: that.rInfo.illness_info || '',
|
||||
doctor_id: doctorId,
|
||||
user_id: patientId,
|
||||
register_id: that.register_id,
|
||||
created_at: new Date().toLocaleString('zh-CN')
|
||||
};
|
||||
|
||||
// 5. 发送患者就诊经历消息(消息类型11)
|
||||
await sendToUserApi({
|
||||
room_id: roomId,
|
||||
sender_user_id: `user-${patientId}`,
|
||||
receiver_user_id: `doctor-${doctorId}`,
|
||||
message_type: 11, // 患者就诊经历卡片
|
||||
message_content: JSON.stringify(patientExperienceData),
|
||||
duration: 0
|
||||
});
|
||||
} else {
|
||||
// 诊所模式(store_type='0')发送挂号信息卡片(message_type=10)
|
||||
await sendToUserApi({
|
||||
room_id: roomId,
|
||||
sender_user_id: `user-${patientId}`,
|
||||
receiver_user_id: `doctor-${doctorId}`,
|
||||
message_type: 10, // 挂号信息卡片
|
||||
message_content: JSON.stringify({
|
||||
id: that.register_id,
|
||||
}),
|
||||
duration: 0
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 生成订单(如果需要);诊所复诊有药时先 redirect 用药确认页,助理卡改在页内提交后发
|
||||
const chatUrl = `/subPackages/chat/chat?user_id=${encodeURIComponent(doctorPrefixed)}&room_id=${roomId}®ister_id=${that.register_id}`;
|
||||
const goAfterGenOrder = () => {
|
||||
if (that.needsClinicPostRegisterDrugPage()) {
|
||||
const { registerInfoId, drugsPayload } = resolveFollowUpDrugsFromRInfo(that.rInfo);
|
||||
uni.setStorageSync(
|
||||
CLINIC_POST_REGISTER_STORAGE_KEY,
|
||||
JSON.stringify({
|
||||
register_info_id: registerInfoId,
|
||||
register_id: that.register_id,
|
||||
drugs: drugsPayload,
|
||||
question: '您是否曾使用过此类药品?',
|
||||
room_id: roomId,
|
||||
doctor_id: doctorId,
|
||||
patient_id: patientId,
|
||||
doctor_prefixed: doctorPrefixed,
|
||||
}),
|
||||
);
|
||||
uni.hideLoading();
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.redirectTo({
|
||||
url: '/subPackages/follow-up/medication-info?mode=clinic_post_register',
|
||||
});
|
||||
return;
|
||||
}
|
||||
uni.hideLoading();
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({ url: chatUrl });
|
||||
};
|
||||
genOrderApi({
|
||||
register_id: that.register_id,
|
||||
register_info: typeof that.rInfo === 'string' ? that.rInfo : JSON.stringify(that.rInfo),
|
||||
}).then(() => {
|
||||
goAfterGenOrder();
|
||||
}).catch((error) => {
|
||||
console.error('生成订单失败:', error);
|
||||
goAfterGenOrder();
|
||||
});
|
||||
} catch (error) {
|
||||
uni.hideLoading();
|
||||
console.error('处理在线复诊流程失败:', error);
|
||||
uni.showToast({
|
||||
title: '处理失败,请重试',
|
||||
icon: 'none'
|
||||
});
|
||||
}
|
||||
},
|
||||
// 确认挂号
|
||||
next() {
|
||||
|
||||
let that = this
|
||||
if (this.flag) {
|
||||
this.flag = false
|
||||
// 获取支付配置
|
||||
registePay({
|
||||
method: "post",
|
||||
data: {
|
||||
register_id: this.register_id,
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
}
|
||||
}).then(async (res) => {
|
||||
uni.showLoading({
|
||||
title: "加载中"
|
||||
});
|
||||
|
||||
if (checkDev('open-im') && that.registList && that.registList.register_type === 1) {
|
||||
createRoomByDoctorIdApi({
|
||||
doctor_id: that.currentDoctorId,
|
||||
type: 1
|
||||
}).then(result => {
|
||||
sendToUserApi({
|
||||
room_id: result.data.result.room_id,
|
||||
sender_user_id: `user-${that.currentUserId}`,
|
||||
receiver_user_id: `doctor-${that.currentDoctorId}`,
|
||||
message_type: 10,
|
||||
message_content: JSON.stringify({
|
||||
id: that.register_id,
|
||||
}),
|
||||
duration: 0
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
if (res.data.data.is_paid == 1) {
|
||||
await that.waitForRegisterDetail();
|
||||
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
|
||||
await that.handleOnlineConsultationAfterPayment();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/register/register-success?id=${that.register_id}®ister_type=${that.registList.register_type}`,
|
||||
})
|
||||
uni.hideLoading();
|
||||
}, 800)
|
||||
return
|
||||
}
|
||||
|
||||
uni.requestPayment({
|
||||
provider: res.data.data.appId,
|
||||
timeStamp: res.data.data.timestamp,
|
||||
nonceStr: res.data.data.nonceStr,
|
||||
package: res.data.data.package,
|
||||
signType: res.data.data.signType,
|
||||
paySign: res.data.data.paySign,
|
||||
success: async () => {
|
||||
await that.waitForRegisterDetail();
|
||||
that.flag = false;
|
||||
if (that.registList && (that.registList.register_type === 2 || that.registList.register_type === 3)) {
|
||||
uni.showLoading({ title: '加载中' });
|
||||
await that.handleOnlineConsultationAfterPayment();
|
||||
return;
|
||||
}
|
||||
setTimeout(() => {
|
||||
that.disableBackGuardBeforeLeave();
|
||||
uni.navigateTo({
|
||||
url: '/subPackages/register/register-success?id=' + that.register_id
|
||||
})
|
||||
uni.hideLoading();
|
||||
}, 300)
|
||||
},
|
||||
fail: function(err) {
|
||||
that.flag = false
|
||||
uni.hideLoading();
|
||||
setTimeout(() => {
|
||||
that.$refs.uToast.show({
|
||||
title: (err && err.errMsg) || '支付失败',
|
||||
type: 'default',
|
||||
icon: false
|
||||
})
|
||||
}, 300)
|
||||
}
|
||||
})
|
||||
}).catch((err) => {
|
||||
console.error('registePay', err);
|
||||
that.flag = true;
|
||||
uni.hideLoading();
|
||||
})
|
||||
}
|
||||
},
|
||||
toHome() {
|
||||
uni.switchTab({
|
||||
url: '/pages/home/home'
|
||||
})
|
||||
},
|
||||
// 预约挂号须知
|
||||
toAbout() {
|
||||
service({
|
||||
method: "get",
|
||||
data: {
|
||||
store_id: uni.getStorageSync('store_id') || '11001',
|
||||
type: 3
|
||||
}
|
||||
}).then((res) => {
|
||||
// console.log(res, 'gh');
|
||||
if (res.data.errcode == 0) {
|
||||
this.content = res.data.data.content
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.toAbout()
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 兼容壳:旧链接 /subPackages/register/register?id=register_id 重定向到合并后的挂号流程 Step 2
|
||||
*/
|
||||
export default {
|
||||
onLoad(e) {
|
||||
const registerId = e.id || e.register_id || '';
|
||||
const doctorId = e.doctor_id || '';
|
||||
let rInfoQuery = '';
|
||||
if (e.r_info) {
|
||||
const str = typeof e.r_info === 'string' ? e.r_info : JSON.stringify(e.r_info);
|
||||
rInfoQuery = `&r_info=${encodeURIComponent(str)}`;
|
||||
}
|
||||
const doctorQuery = doctorId ? `&doctor_id=${doctorId}` : '';
|
||||
uni.redirectTo({
|
||||
url: `/subPackages/doctor/doctor-userinfo?step=2®ister_id=${registerId}${doctorQuery}${rInfoQuery}`,
|
||||
fail: () => {
|
||||
uni.showToast({ title: '页面跳转失败', icon: 'none' });
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.container {
|
||||
width: 750rpx;
|
||||
max-height: 100%;
|
||||
min-height: 100vh;
|
||||
padding-top: 32rpx;
|
||||
background: #F8FAFC;
|
||||
|
||||
.title {
|
||||
width: 750rpx;
|
||||
height: 80rpx;
|
||||
margin: 0 auto;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC-Bold, PingFang SC;
|
||||
font-weight: bold;
|
||||
color: #666666;
|
||||
line-height: 80rpx;
|
||||
padding-left: 32rpx;
|
||||
}
|
||||
|
||||
.card {
|
||||
width: 750rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 8rpx;
|
||||
padding: 32rpx;
|
||||
margin: 24rpx auto 32rpx;
|
||||
|
||||
.item:first-child {
|
||||
margin: 0 0 48rpx;
|
||||
}
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
height: 40rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
line-height: 33rpx;
|
||||
margin: 48rpx 0 0;
|
||||
|
||||
.aapoint {
|
||||
font-size: 28rpx;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.detail {
|
||||
font-size: 28rpx;
|
||||
font-weight: bold;
|
||||
color: #232323;
|
||||
}
|
||||
|
||||
.active {
|
||||
color: #F44336;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info {
|
||||
width: 750rpx;
|
||||
background: #FFFFFF;
|
||||
padding: 32rpx;
|
||||
border-radius: 8rpx;
|
||||
margin: 10rpx auto 40rpx;
|
||||
|
||||
.name_edit:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.name_edit {
|
||||
margin-top: 48rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
line-height: 33rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
.name {
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.word_num {
|
||||
font-size: 30rpx;
|
||||
font-weight: 600;
|
||||
color: #1E293B;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.footer {
|
||||
width: 750rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 0 32rpx;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
bottom: env(safe-area-inset-bottom);
|
||||
z-index: 10;
|
||||
|
||||
.allknow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 710rpx;
|
||||
height: 40rpx;
|
||||
padding: 0 32rpx;
|
||||
margin: 10rpx auto;
|
||||
|
||||
.isknow {
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #666666;
|
||||
margin-left: 8rpx;
|
||||
|
||||
text {
|
||||
color: #4175EE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
text-align: center;
|
||||
width: 686rpx;
|
||||
height: 86rpx;
|
||||
line-height: 86rpx;
|
||||
font-size: 32rpx;
|
||||
font-family: PingFang SC-Medium, PingFang SC;
|
||||
font-weight: 500;
|
||||
color: #FFFFFF;
|
||||
background: #4175EE;
|
||||
border-radius: 64rpx;
|
||||
margin: 20rpx auto;
|
||||
}
|
||||
|
||||
.bg {
|
||||
width: 688rpx;
|
||||
height: 88rpx;
|
||||
background: #8ABAFF;
|
||||
line-height: 88rpx;
|
||||
font-size: 32rpx;
|
||||
text-align: center;
|
||||
font-family: PingFang SC-Medium, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #FFFFFF;
|
||||
border-radius: 64rpx;
|
||||
}
|
||||
|
||||
.gh_ {
|
||||
margin-bottom: 30rpx;
|
||||
font-size: 28rpx;
|
||||
font-family: PingFang SC-Regular, PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
|
||||
.agreen {
|
||||
padding: 60rpx 50rpx 20rpx;
|
||||
}
|
||||
|
||||
.confirm-modal {
|
||||
padding: 40rpx 30rpx;
|
||||
background: #fff;
|
||||
|
||||
.confirm-title {
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin-bottom: 30rpx;
|
||||
}
|
||||
|
||||
.confirm-desc {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 40rpx;
|
||||
text-align: center;
|
||||
|
||||
.link {
|
||||
color: #2979FF;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20rpx;
|
||||
|
||||
button {
|
||||
flex: 1;
|
||||
height: 80rpx;
|
||||
line-height: 80rpx;
|
||||
border-radius: 40rpx;
|
||||
font-size: 30rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background: #F5F7FA;
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
.agree-btn {
|
||||
background: #4175EE;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
.register-redirect {
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #F8FAFC;
|
||||
}
|
||||
</style>
|
||||
|
||||
540
subPackages/special-prescription/detail.vue
Normal file
540
subPackages/special-prescription/detail.vue
Normal file
@@ -0,0 +1,540 @@
|
||||
<template>
|
||||
<view class="container safe-area-inset-bottom">
|
||||
<MessageNotification />
|
||||
|
||||
<view v-if="loading" class="loading-wrap">
|
||||
<u-loading mode="circle" size="48"></u-loading>
|
||||
</view>
|
||||
|
||||
<block v-else-if="detail.id">
|
||||
<!-- 顶部轮播 -->
|
||||
<view class="swiper-section">
|
||||
<swiper
|
||||
class="swiper"
|
||||
indicator-dots
|
||||
autoplay
|
||||
circular
|
||||
indicator-color="rgba(0,0,0,0.1)"
|
||||
indicator-active-color="#4175EE"
|
||||
>
|
||||
<swiper-item v-for="(img, index) in displayImages" :key="index">
|
||||
<image
|
||||
class="swiper-image"
|
||||
:src="img"
|
||||
mode="aspectFill"
|
||||
@click="previewImage(index)"
|
||||
/>
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
</view>
|
||||
|
||||
<!-- 基本信息 -->
|
||||
<view class="basic-info">
|
||||
<view class="price-row">
|
||||
<view class="price-left" v-if="detail.price_available !== false">
|
||||
<text class="symbol">¥</text>
|
||||
<text class="amount">{{ detail.price_per_dose }}</text>
|
||||
<text class="unit">/贴</text>
|
||||
</view>
|
||||
<view class="price-left price-left--unavailable" v-else>
|
||||
<text class="amount">暂无报价</text>
|
||||
</view>
|
||||
<text class="sales" v-if="detail.sales_count">已售 {{ detail.sales_count }}</text>
|
||||
</view>
|
||||
|
||||
<view class="title">{{ detail.name }}</view>
|
||||
|
||||
<view class="tag-row" v-if="detail.tags && detail.tags.length">
|
||||
<text class="tag" v-for="(tag, idx) in detail.tags" :key="idx">{{ tag }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 图文介绍 -->
|
||||
<view class="intro-section" v-if="detail.intro_text || (detail.introduction_images && detail.introduction_images.length)">
|
||||
<view class="section-title">详情介绍</view>
|
||||
<view class="intro-text" v-if="detail.intro_text">
|
||||
<u-parse :html="detail.intro_text"></u-parse>
|
||||
</view>
|
||||
<view class="intro-images" v-if="detail.introduction_images && detail.introduction_images.length">
|
||||
<image
|
||||
v-for="(img, idx) in detail.introduction_images"
|
||||
:key="idx"
|
||||
:src="img"
|
||||
mode="widthFix"
|
||||
class="intro-img"
|
||||
@click="previewIntroImage(idx)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部操作 -->
|
||||
<view class="footer-spacer"></view>
|
||||
<view class="footer safe-area-inset-bottom">
|
||||
<button
|
||||
class="register-btn"
|
||||
:class="{ 'register-btn--disabled': detail.price_available === false }"
|
||||
:disabled="detail.price_available === false"
|
||||
@click="goRegister"
|
||||
>去挂号选方</button>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
<view v-else class="empty-wrap">
|
||||
<u-empty text="特色方不存在或已下架" mode="page" margin-top="120"></u-empty>
|
||||
</view>
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<page-container :show="registerDrawerShow" :overlay="false" @leave="onPageBackGuardLeave" />
|
||||
<!-- #endif -->
|
||||
|
||||
<u-popup
|
||||
v-model="registerDrawerShow"
|
||||
mode="bottom"
|
||||
border-radius="24"
|
||||
safe-area-inset-bottom
|
||||
@close="onRegisterDrawerClose"
|
||||
>
|
||||
<view class="register-drawer">
|
||||
<view class="drawer-header">
|
||||
<text class="drawer-title">确认贴数</text>
|
||||
<view class="drawer-close" @tap="closeRegisterDrawer">
|
||||
<u-icon name="close" size="32" color="#999"></u-icon>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="drawer-dose-row">
|
||||
<text class="dose-label">贴数</text>
|
||||
<u-number-box
|
||||
v-model="doseCount"
|
||||
:min="1"
|
||||
:max="99"
|
||||
integer
|
||||
></u-number-box>
|
||||
</view>
|
||||
|
||||
<view class="drawer-price-row">
|
||||
<text class="price-label">单价</text>
|
||||
<text class="price-value">¥{{ detail.price_per_dose }}/贴</text>
|
||||
</view>
|
||||
|
||||
<view class="drawer-total-row">
|
||||
<text class="total-label">预估金额</text>
|
||||
<text class="total-price">¥{{ totalPrice }}</text>
|
||||
</view>
|
||||
|
||||
<button class="confirm-btn" @click="confirmRegister">确认并挂号</button>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<u-toast ref="uToast" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getSpecialPrescriptionDetailApi,
|
||||
saveSpecialPrescriptionRegisterContext,
|
||||
} from '@/request/api/specialPrescription'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
id: '',
|
||||
detail: {},
|
||||
doseCount: 1,
|
||||
loading: true,
|
||||
registerDrawerShow: false,
|
||||
defaultCover: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
displayImages() {
|
||||
const images = []
|
||||
if (this.detail.cover_image) {
|
||||
images.push(this.detail.cover_image)
|
||||
}
|
||||
if (this.detail.introduction_images && this.detail.introduction_images.length) {
|
||||
this.detail.introduction_images.forEach((img) => {
|
||||
if (img && images.indexOf(img) === -1) {
|
||||
images.push(img)
|
||||
}
|
||||
})
|
||||
}
|
||||
return images.length ? images : [this.defaultCover]
|
||||
},
|
||||
totalPrice() {
|
||||
if (this.detail.price_available === false) {
|
||||
return '0.00'
|
||||
}
|
||||
const price = parseFloat(this.detail.price_per_dose) || 0
|
||||
return (price * this.doseCount).toFixed(2)
|
||||
},
|
||||
},
|
||||
onLoad(e) {
|
||||
this.id = e.id || ''
|
||||
if (this.id) {
|
||||
this.fetchDetail()
|
||||
} else {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getStoreId() {
|
||||
return uni.getStorageSync('store_id') || '11001'
|
||||
},
|
||||
async fetchDetail() {
|
||||
this.loading = true
|
||||
try {
|
||||
const res = await getSpecialPrescriptionDetailApi({
|
||||
id: this.id,
|
||||
store_id: this.getStoreId(),
|
||||
})
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
this.detail = res.data.result || {}
|
||||
} else {
|
||||
this.$refs.uToast && this.$refs.uToast.show({
|
||||
title: res.data?.message || '加载失败',
|
||||
type: 'default',
|
||||
icon: false,
|
||||
})
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchDetail', e)
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
previewImage(index) {
|
||||
uni.previewImage({
|
||||
urls: this.displayImages,
|
||||
current: this.displayImages[index],
|
||||
})
|
||||
},
|
||||
previewIntroImage(index) {
|
||||
const urls = this.detail.introduction_images || []
|
||||
if (!urls.length) return
|
||||
uni.previewImage({
|
||||
urls,
|
||||
current: urls[index],
|
||||
})
|
||||
},
|
||||
goRegister() {
|
||||
if (this.detail.price_available === false) {
|
||||
this.$refs.uToast.show({
|
||||
title: '当前诊所暂无法报价,请联系诊所',
|
||||
type: 'default',
|
||||
icon: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
const doctorId = this.detail.doctor_id
|
||||
if (!doctorId) {
|
||||
this.$refs.uToast.show({
|
||||
title: '诊所未配置特色方挂号医生',
|
||||
type: 'default',
|
||||
icon: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
this.registerDrawerShow = true
|
||||
},
|
||||
closeRegisterDrawer() {
|
||||
this.registerDrawerShow = false
|
||||
},
|
||||
onRegisterDrawerClose() {
|
||||
this.registerDrawerShow = false
|
||||
},
|
||||
onPageBackGuardLeave() {
|
||||
if (this._suppressPageBackGuardLeave) {
|
||||
this._suppressPageBackGuardLeave = false
|
||||
return
|
||||
}
|
||||
if (this.registerDrawerShow) {
|
||||
this._suppressPageBackGuardLeave = true
|
||||
this.registerDrawerShow = false
|
||||
}
|
||||
},
|
||||
confirmRegister() {
|
||||
const doctorId = this.detail.doctor_id
|
||||
if (!doctorId) {
|
||||
this.$refs.uToast.show({
|
||||
title: '诊所未配置特色方挂号医生',
|
||||
type: 'default',
|
||||
icon: false,
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
const ctx = {
|
||||
special_prescription_id: this.detail.id,
|
||||
dose_count: this.doseCount,
|
||||
doctor_id: doctorId,
|
||||
}
|
||||
saveSpecialPrescriptionRegisterContext(ctx)
|
||||
|
||||
this._suppressPageBackGuardLeave = true
|
||||
this.registerDrawerShow = false
|
||||
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/doctor/doctor-userinfo?id=${doctorId}&r_type=0&special_prescription_id=${ctx.special_prescription_id}&special_prescription_dose_count=${ctx.dose_count}`,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$sp-primary: #4175EE;
|
||||
$sp-primary-light: rgba(65, 117, 238, 0.1);
|
||||
$sp-gradient: linear-gradient(90deg, #4175EE 0%, #298DFF 100%);
|
||||
|
||||
.container {
|
||||
min-height: 100vh;
|
||||
background: #F5F7FA;
|
||||
padding-bottom: 140rpx;
|
||||
}
|
||||
|
||||
.loading-wrap,
|
||||
.empty-wrap {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 60vh;
|
||||
}
|
||||
|
||||
.swiper-section {
|
||||
.swiper {
|
||||
height: 750rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.swiper-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.basic-info {
|
||||
background: #fff;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.price-row {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.price-left {
|
||||
color: #FF5722;
|
||||
|
||||
.symbol {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 48rpx;
|
||||
font-weight: 800;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
|
||||
&--unavailable .amount {
|
||||
font-size: 32rpx;
|
||||
color: #999;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.sales {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.tag-row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.tag {
|
||||
font-size: 22rpx;
|
||||
color: $sp-primary;
|
||||
background: $sp-primary-light;
|
||||
padding: 4rpx 16rpx;
|
||||
border-radius: 8rpx;
|
||||
margin-right: 12rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.intro-section {
|
||||
background: #fff;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 30rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
margin-bottom: 24rpx;
|
||||
padding-left: 16rpx;
|
||||
border-left: 6rpx solid $sp-primary;
|
||||
}
|
||||
|
||||
.intro-images {
|
||||
font-size: 0;
|
||||
line-height: 0;
|
||||
|
||||
.intro-img {
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-bottom: 0;
|
||||
vertical-align: top;
|
||||
}
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
margin-bottom: 24rpx;
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.footer-spacer {
|
||||
height: 120rpx;
|
||||
}
|
||||
|
||||
.footer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: #fff;
|
||||
padding: 16rpx 32rpx;
|
||||
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.06);
|
||||
z-index: 100;
|
||||
|
||||
.register-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: $sp-gradient;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&--disabled {
|
||||
background: #e0e0e0;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.register-drawer {
|
||||
padding: 32rpx;
|
||||
background: #fff;
|
||||
|
||||
.drawer-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 32rpx;
|
||||
}
|
||||
|
||||
.drawer-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 700;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.drawer-close {
|
||||
padding: 8rpx;
|
||||
}
|
||||
|
||||
.drawer-dose-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 24rpx;
|
||||
|
||||
.dose-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-price-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.price-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
.drawer-total-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 24rpx 0 32rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.total-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-size: 40rpx;
|
||||
font-weight: 800;
|
||||
color: #FF5722;
|
||||
}
|
||||
}
|
||||
|
||||
.confirm-btn {
|
||||
width: 100%;
|
||||
height: 88rpx;
|
||||
line-height: 88rpx;
|
||||
background: $sp-gradient;
|
||||
color: #fff;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
border-radius: 44rpx;
|
||||
border: none;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
759
subPackages/special-prescription/list.vue
Normal file
759
subPackages/special-prescription/list.vue
Normal file
@@ -0,0 +1,759 @@
|
||||
<template>
|
||||
<view class="container safe-area-inset-bottom">
|
||||
<MessageNotification />
|
||||
|
||||
<view class="list-fixed-header">
|
||||
<!-- simple:简洁横幅 -->
|
||||
<view v-if="hasBanner && headerStyle === 'simple'" class="list-hero">
|
||||
<view class="list-hero__inner">
|
||||
<view class="list-hero__title-row">
|
||||
<image
|
||||
v-if="pageConfig.title_image"
|
||||
:src="pageConfig.title_image"
|
||||
class="list-hero__title"
|
||||
mode="heightFix"
|
||||
/>
|
||||
<image
|
||||
v-if="pageConfig.mascot_image"
|
||||
:src="pageConfig.mascot_image"
|
||||
class="list-hero__mascot"
|
||||
mode="heightFix"
|
||||
/>
|
||||
</view>
|
||||
<view v-if="pageConfig.subtitle" class="list-hero__subtitle">{{ pageConfig.subtitle }}</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- card:首页异形卡片(仅头部,无商品横滑) -->
|
||||
<view v-else-if="hasBanner && headerStyle === 'card'" class="tea-section">
|
||||
<view class="irregular-card">
|
||||
<view class="card-tab">
|
||||
<view class="card-tab-slope"></view>
|
||||
</view>
|
||||
<image
|
||||
v-if="pageConfig.title_image"
|
||||
:src="pageConfig.title_image"
|
||||
class="main-title"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view class="card-body">
|
||||
<image
|
||||
v-if="pageConfig.mascot_image"
|
||||
:src="pageConfig.mascot_image"
|
||||
class="main-mascot"
|
||||
mode="widthFix"
|
||||
/>
|
||||
<view v-if="pageConfig.subtitle" class="tea-subtitle">
|
||||
<view class="subtitle-icon"></view>
|
||||
<text>{{ pageConfig.subtitle }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 顶部搜索 -->
|
||||
<view class="header-section">
|
||||
<view class="search-box">
|
||||
<u-search
|
||||
v-model="searchKeyword"
|
||||
placeholder="搜索药方名称"
|
||||
:show-action="true"
|
||||
action-text="搜索"
|
||||
bg-color="#F5F7FA"
|
||||
placeholder-color="#9CA3AF"
|
||||
shape="round"
|
||||
height="64"
|
||||
:input-style="{ fontSize: '26rpx' }"
|
||||
@custom="handleSearch"
|
||||
@search="handleSearch"
|
||||
@clear="clearSearch"
|
||||
></u-search>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- tabs 布局:顶部横向分类 -->
|
||||
<view v-if="layout === 'tabs'" class="category-tabs-wrap">
|
||||
<scroll-view scroll-x class="category-tabs-scroll" :show-scrollbar="false">
|
||||
<view class="category-tabs">
|
||||
<view
|
||||
class="category-tab"
|
||||
:class="{ active: selectedCategoryId === 0 }"
|
||||
@tap="selectCategory(0)"
|
||||
>
|
||||
<text>全部</text>
|
||||
</view>
|
||||
<view
|
||||
v-for="cat in categories"
|
||||
:key="cat.id"
|
||||
class="category-tab"
|
||||
:class="{ active: selectedCategoryId === cat.id }"
|
||||
@tap="selectCategory(cat.id)"
|
||||
>
|
||||
<text class="u-line-1">{{ cat.name }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 主体:左右独立滚动 -->
|
||||
<view class="list-body">
|
||||
<view v-if="layout === 'sidebar'" class="category-sidebar">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="category-scroll"
|
||||
:show-scrollbar="false"
|
||||
enable-flex
|
||||
>
|
||||
<view
|
||||
class="category-item"
|
||||
:class="{ active: selectedCategoryId === 0 }"
|
||||
@tap="selectCategory(0)"
|
||||
>
|
||||
<text>全部</text>
|
||||
</view>
|
||||
<view
|
||||
v-for="cat in categories"
|
||||
:key="cat.id"
|
||||
class="category-item"
|
||||
:class="{ active: selectedCategoryId === cat.id }"
|
||||
@tap="selectCategory(cat.id)"
|
||||
>
|
||||
<text class="u-line-1">{{ cat.name }}</text>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
|
||||
<view class="list-area" :class="{ 'list-area--full': layout === 'tabs' }">
|
||||
<scroll-view
|
||||
scroll-y
|
||||
class="list-scroll"
|
||||
:show-scrollbar="false"
|
||||
enable-flex
|
||||
refresher-enabled
|
||||
:refresher-triggered="isRefreshing"
|
||||
@refresherrefresh="onRefresherRefresh"
|
||||
@scrolltolower="loadMore"
|
||||
:lower-threshold="80"
|
||||
>
|
||||
<view class="item-grid" v-if="items.length > 0">
|
||||
<view
|
||||
class="item-card"
|
||||
v-for="item in items"
|
||||
:key="item.id"
|
||||
@tap="goDetail(item)"
|
||||
>
|
||||
<view class="item-img-box">
|
||||
<image
|
||||
:src="item.cover_image || defaultCover"
|
||||
class="item-img"
|
||||
mode="aspectFill"
|
||||
lazy-load
|
||||
/>
|
||||
</view>
|
||||
<view class="item-info">
|
||||
<view class="item-name u-line-2">{{ item.name }}</view>
|
||||
<view class="item-tags" v-if="item.tags && item.tags.length">
|
||||
<text class="item-tag" v-for="(tag, idx) in item.tags.slice(0, 2)" :key="idx">{{ tag }}</text>
|
||||
</view>
|
||||
<view class="item-bottom">
|
||||
<view class="item-price" v-if="item.price_available !== false">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text>{{ item.price_per_dose }}</text>
|
||||
<text class="price-unit">/贴</text>
|
||||
</view>
|
||||
<text class="price-unavailable" v-else>暂无报价</text>
|
||||
<text class="sales-text" v-if="item.sales_count">已售{{ item.sales_count }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view v-if="items.length === 0 && !loading" class="empty-state">
|
||||
<image :src="emptyImage" mode="aspectFit" class="empty-image"></image>
|
||||
<text class="empty-text">暂无特色方</text>
|
||||
</view>
|
||||
|
||||
<view v-if="loading" class="loading-tip">
|
||||
<u-loading mode="circle" size="36"></u-loading>
|
||||
<text>加载中...</text>
|
||||
</view>
|
||||
<view v-else-if="finished && items.length > 0" class="finished-tip">没有更多了</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {
|
||||
getSpecialPrescriptionCategoriesApi,
|
||||
getSpecialPrescriptionListApi,
|
||||
getSpecialPrescriptionListPageApi,
|
||||
} from '@/request/api/specialPrescription'
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
layout: 'sidebar',
|
||||
headerStyle: 'simple',
|
||||
pageConfig: {
|
||||
title_image: '',
|
||||
mascot_image: '',
|
||||
subtitle: '',
|
||||
empty_image: '',
|
||||
},
|
||||
defaultEmptyImage: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk_upload_20260701/eca70782-cdb5-4def-8eb0-542b147240a6_1782890269.png',
|
||||
categories: [],
|
||||
selectedCategoryId: 0,
|
||||
searchKeyword: '',
|
||||
items: [],
|
||||
page: 1,
|
||||
pageSize: 20,
|
||||
pageCount: 1,
|
||||
loading: false,
|
||||
finished: false,
|
||||
isRefreshing: false,
|
||||
defaultCover: 'https://xiaokang88.oss-cn-hangzhou.aliyuncs.com/xk-client-wx/static/mine/avatar_1.png',
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
hasBanner() {
|
||||
const cfg = this.pageConfig || {}
|
||||
return !!(cfg.title_image || cfg.mascot_image || cfg.subtitle)
|
||||
},
|
||||
emptyImage() {
|
||||
return this.pageConfig.empty_image || this.defaultEmptyImage
|
||||
},
|
||||
},
|
||||
onLoad() {
|
||||
this.fetchPageConfig()
|
||||
this.fetchCategories()
|
||||
this.fetchList(true)
|
||||
},
|
||||
methods: {
|
||||
getStoreId() {
|
||||
return uni.getStorageSync('store_id') || '11001'
|
||||
},
|
||||
async fetchPageConfig() {
|
||||
try {
|
||||
const res = await getSpecialPrescriptionListPageApi({ store_id: this.getStoreId() })
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
const cfg = res.data.result || {}
|
||||
this.layout = cfg.layout === 'tabs' ? 'tabs' : 'sidebar'
|
||||
this.headerStyle = cfg.header_style === 'card' ? 'card' : 'simple'
|
||||
this.pageConfig = {
|
||||
title_image: cfg.title_image || '',
|
||||
mascot_image: cfg.mascot_image || '',
|
||||
subtitle: cfg.subtitle || '',
|
||||
empty_image: cfg.empty_image || '',
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchPageConfig', e)
|
||||
}
|
||||
},
|
||||
async fetchCategories() {
|
||||
try {
|
||||
const res = await getSpecialPrescriptionCategoriesApi({ store_id: this.getStoreId() })
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
this.categories = res.data.result || []
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchCategories', e)
|
||||
}
|
||||
},
|
||||
selectCategory(categoryId) {
|
||||
if (this.selectedCategoryId === categoryId) return
|
||||
this.selectedCategoryId = categoryId
|
||||
this.fetchList(true)
|
||||
},
|
||||
handleSearch() {
|
||||
this.fetchList(true)
|
||||
},
|
||||
clearSearch() {
|
||||
this.searchKeyword = ''
|
||||
this.fetchList(true)
|
||||
},
|
||||
async fetchList(reset = false, options = {}) {
|
||||
const { skipLoadingCheck = false } = options
|
||||
if (!skipLoadingCheck && this.loading) return
|
||||
if (reset) {
|
||||
this.page = 1
|
||||
this.finished = false
|
||||
this.items = []
|
||||
}
|
||||
if (this.finished && !reset) return
|
||||
|
||||
this.loading = true
|
||||
try {
|
||||
const params = {
|
||||
store_id: this.getStoreId(),
|
||||
page: this.page,
|
||||
pageSize: this.pageSize,
|
||||
}
|
||||
if (this.selectedCategoryId > 0) {
|
||||
params.category_id = this.selectedCategoryId
|
||||
}
|
||||
const keyword = (this.searchKeyword || '').trim()
|
||||
if (keyword) {
|
||||
params.name = keyword
|
||||
}
|
||||
const res = await getSpecialPrescriptionListApi(params)
|
||||
if (res.data?.code === 0 || res.data?.code === '0') {
|
||||
const result = res.data.result || {}
|
||||
const list = result.items || []
|
||||
if (result.layout === 'tabs' || result.layout === 'sidebar') {
|
||||
this.layout = result.layout
|
||||
}
|
||||
if (result.header_style === 'card' || result.header_style === 'simple') {
|
||||
this.headerStyle = result.header_style
|
||||
}
|
||||
this.items = reset ? list : this.items.concat(list)
|
||||
this.pageCount = result.page_count || 1
|
||||
this.finished = this.page >= this.pageCount || list.length === 0
|
||||
if (!this.finished) {
|
||||
this.page += 1
|
||||
}
|
||||
} else {
|
||||
this.finished = true
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('fetchList', e)
|
||||
this.finished = true
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
loadMore() {
|
||||
this.fetchList(false)
|
||||
},
|
||||
async onRefresherRefresh() {
|
||||
if (this.isRefreshing) return
|
||||
this.isRefreshing = true
|
||||
try {
|
||||
await Promise.all([
|
||||
this.fetchPageConfig(),
|
||||
this.fetchCategories(),
|
||||
this.fetchList(true, { skipLoadingCheck: true }),
|
||||
])
|
||||
} finally {
|
||||
this.isRefreshing = false
|
||||
}
|
||||
},
|
||||
goDetail(item) {
|
||||
const id = Number(item?.id)
|
||||
if (!id) return
|
||||
uni.navigateTo({
|
||||
url: `/subPackages/special-prescription/detail?id=${id}`,
|
||||
})
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
$sp-primary: #4175EE;
|
||||
$sp-primary-light: rgba(65, 117, 238, 0.1);
|
||||
$sp-primary-shadow: rgba(65, 117, 238, 0.16);
|
||||
|
||||
.container {
|
||||
height: 100vh;
|
||||
background: #F5F7FA;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.list-fixed-header {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.list-body {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
display: flex;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* --- simple 简洁横幅 --- */
|
||||
.list-hero {
|
||||
background: #fff;
|
||||
padding: 20rpx 24rpx 8rpx;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.list-hero__inner {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.list-hero__title-row {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-height: 72rpx;
|
||||
padding-right: 100rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.list-hero__title {
|
||||
height: 56rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.list-hero__mascot {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 72rpx;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.list-hero__subtitle {
|
||||
margin-top: 8rpx;
|
||||
text-align: center;
|
||||
font-size: 24rpx;
|
||||
color: #666;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* --- card 异形卡片(复用首页结构) --- */
|
||||
.tea-section {
|
||||
padding: 87rpx 24rpx 0;
|
||||
|
||||
.irregular-card {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.card-tab {
|
||||
width: 220rpx;
|
||||
height: 40rpx;
|
||||
background: #FFFFFF;
|
||||
border-radius: 24rpx 24rpx 0 0;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
margin-bottom: -1rpx;
|
||||
}
|
||||
|
||||
.card-tab-slope {
|
||||
position: absolute;
|
||||
right: -40rpx;
|
||||
bottom: 0;
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
background-image: radial-gradient(circle at 100% 0%, transparent 40rpx, #FFFFFF 40rpx);
|
||||
}
|
||||
|
||||
.main-title {
|
||||
position: absolute;
|
||||
left: 8rpx;
|
||||
top: -120rpx;
|
||||
width: 210rpx;
|
||||
height: 128rpx;
|
||||
z-index: 6;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.card-body {
|
||||
background: #FFFFFF;
|
||||
width: 100%;
|
||||
padding: 24rpx;
|
||||
border-radius: 0 24rpx 24rpx 24rpx;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 12rpx 32rpx $sp-primary-shadow;
|
||||
}
|
||||
|
||||
.main-mascot {
|
||||
position: absolute;
|
||||
right: 12rpx;
|
||||
top: -140rpx;
|
||||
width: 217rpx;
|
||||
height: 168rpx;
|
||||
z-index: 10;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.tea-subtitle {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
padding-right: 150rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.subtitle-icon {
|
||||
width: 6rpx;
|
||||
height: 26rpx;
|
||||
background: $sp-primary;
|
||||
border-radius: 3rpx;
|
||||
margin-right: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.header-section {
|
||||
background: #fff;
|
||||
padding: 16rpx 24rpx;
|
||||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
.search-box {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.category-tabs-wrap {
|
||||
background: #fff;
|
||||
border-bottom: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.category-tabs-scroll {
|
||||
white-space: nowrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.category-tabs {
|
||||
display: inline-flex;
|
||||
padding: 0 16rpx;
|
||||
}
|
||||
|
||||
.category-tab {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20rpx 28rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
|
||||
text {
|
||||
max-width: 200rpx;
|
||||
}
|
||||
|
||||
&.active {
|
||||
color: $sp-primary;
|
||||
font-weight: bold;
|
||||
|
||||
&::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 8rpx;
|
||||
transform: translateX(-50%);
|
||||
width: 40rpx;
|
||||
height: 4rpx;
|
||||
background: $sp-primary;
|
||||
border-radius: 2rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.category-sidebar {
|
||||
width: 180rpx;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
|
||||
.category-scroll {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.category-item {
|
||||
height: 100rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 26rpx;
|
||||
color: #666;
|
||||
position: relative;
|
||||
padding: 0 12rpx;
|
||||
|
||||
text {
|
||||
text-align: center;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background: #F5F7FA;
|
||||
color: $sp-primary;
|
||||
font-weight: bold;
|
||||
|
||||
&::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
width: 6rpx;
|
||||
height: 48rpx;
|
||||
background: $sp-primary;
|
||||
border-radius: 0 3rpx 3rpx 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.list-area {
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
&--full {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-scroll {
|
||||
height: 100%;
|
||||
padding: 16rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
|
||||
.item-grid {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
width: calc((100% - 16rpx) / 2);
|
||||
background: #fff;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||
|
||||
&:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
}
|
||||
|
||||
.item-img-box {
|
||||
width: 100%;
|
||||
height: 240rpx;
|
||||
background: #f8f8f8;
|
||||
|
||||
.item-img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-info {
|
||||
padding: 16rpx;
|
||||
|
||||
.item-name {
|
||||
font-size: 28rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
line-height: 1.4;
|
||||
min-height: 78rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
.item-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
.item-tag {
|
||||
font-size: 20rpx;
|
||||
color: $sp-primary;
|
||||
background: $sp-primary-light;
|
||||
padding: 2rpx 10rpx;
|
||||
border-radius: 6rpx;
|
||||
margin-right: 8rpx;
|
||||
margin-bottom: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.item-price {
|
||||
color: #FF5722;
|
||||
font-size: 32rpx;
|
||||
font-weight: 800;
|
||||
|
||||
.price-symbol {
|
||||
font-size: 22rpx;
|
||||
}
|
||||
|
||||
.price-unit {
|
||||
font-size: 22rpx;
|
||||
color: #999;
|
||||
font-weight: 400;
|
||||
}
|
||||
}
|
||||
|
||||
.price-unavailable {
|
||||
font-size: 24rpx;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.sales-text {
|
||||
font-size: 20rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 120rpx 0 40rpx;
|
||||
|
||||
.empty-image {
|
||||
width: 440rpx;
|
||||
height: 393rpx;
|
||||
}
|
||||
|
||||
.empty-text {
|
||||
margin-top: 24rpx;
|
||||
font-size: 26rpx;
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
|
||||
.loading-tip,
|
||||
.finished-tip {
|
||||
text-align: center;
|
||||
padding: 40rpx 0;
|
||||
color: #999;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.loading-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 12rpx;
|
||||
}
|
||||
|
||||
.u-line-1 {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.u-line-2 {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user