feat: 会员管理、在线复诊优化、新增配送仓库
This commit is contained in:
@@ -88,6 +88,10 @@ export function createBusinessApi(prefix, headerFlag) {
|
|||||||
getOrderStatusOption() {
|
getOrderStatusOption() {
|
||||||
return bizRequest({ url: `${prefix}-order/status-option`, method: 'GET' });
|
return bizRequest({ url: `${prefix}-order/status-option`, method: 'GET' });
|
||||||
},
|
},
|
||||||
|
/** 各状态订单数量(筛选不含 status,供 Tab 徽标) */
|
||||||
|
getOrderStatusCount(params) {
|
||||||
|
return bizRequest({ url: `${prefix}-order/status-count`, method: 'GET', data: params });
|
||||||
|
},
|
||||||
getOrderDetail(id) {
|
getOrderDetail(id) {
|
||||||
return bizRequest({ url: `${prefix}-order/detail`, method: 'GET', data: { id } });
|
return bizRequest({ url: `${prefix}-order/detail`, method: 'GET', data: { id } });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -127,6 +127,11 @@ export function getOrderStatusOption() {
|
|||||||
return clinicRequest({ url: `${PREFIX}-order/status-option`, method: 'GET' });
|
return clinicRequest({ url: `${PREFIX}-order/status-option`, method: 'GET' });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 各状态订单数量(筛选不含 status,供 Tab 徽标) */
|
||||||
|
export function getOrderStatusCount(params) {
|
||||||
|
return clinicRequest({ url: `${PREFIX}-order/status-count`, method: 'GET', data: params });
|
||||||
|
}
|
||||||
|
|
||||||
export function getOrderDetail(id) {
|
export function getOrderDetail(id) {
|
||||||
return clinicRequest({ url: `${PREFIX}-order/detail`, method: 'GET', data: { id } });
|
return clinicRequest({ url: `${PREFIX}-order/detail`, method: 'GET', data: { id } });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -344,6 +344,18 @@ export function getCurrentStoreTypeApi(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开方处方类型选项(含默认选中、可选 icon / icon_text)
|
||||||
|
* 后端按 clinic_type 判断 default;未传 icon 时前端不展示
|
||||||
|
*/
|
||||||
|
export function getPrescriptionTypeOptionsApi(data) {
|
||||||
|
return req.request({
|
||||||
|
url: '/newApi/doctor-reception-wx/prescription-type-options',
|
||||||
|
method: 'GET',
|
||||||
|
data: data || {}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 订单百分比调价配置(开方前)
|
// 订单百分比调价配置(开方前)
|
||||||
export function getPriceAdjustConfigApi(storeId) {
|
export function getPriceAdjustConfigApi(storeId) {
|
||||||
return req.request({
|
return req.request({
|
||||||
|
|||||||
16
pages.json
16
pages.json
@@ -172,7 +172,13 @@
|
|||||||
"path": "prescription_v2/index",
|
"path": "prescription_v2/index",
|
||||||
"style": {
|
"style": {
|
||||||
"navigationBarTitleText": "开方",
|
"navigationBarTitleText": "开方",
|
||||||
"navigationStyle": "custom"
|
"navigationStyle": "custom",
|
||||||
|
"usingComponents": {
|
||||||
|
"order-price-percent-adjust-popup": "/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup"
|
||||||
|
},
|
||||||
|
"componentPlaceholder": {
|
||||||
|
"order-price-percent-adjust-popup": "view"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -437,7 +443,11 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "order/index",
|
"path": "order/index",
|
||||||
"style": { "navigationBarTitleText": "商品订单", "navigationStyle": "custom" }
|
"style": {
|
||||||
|
"navigationBarTitleText": "商品订单",
|
||||||
|
"navigationStyle": "custom",
|
||||||
|
"disableScroll": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"path": "order/detail",
|
"path": "order/detail",
|
||||||
@@ -468,7 +478,7 @@
|
|||||||
}, {
|
}, {
|
||||||
"root": "subPackages/sub_business_shared",
|
"root": "subPackages/sub_business_shared",
|
||||||
"pages": [
|
"pages": [
|
||||||
{ "path": "order/index", "style": { "navigationBarTitleText": "商品订单", "navigationStyle": "custom" } },
|
{ "path": "order/index", "style": { "navigationBarTitleText": "商品订单", "navigationStyle": "custom", "disableScroll": true } },
|
||||||
{ "path": "order/detail", "style": { "navigationBarTitleText": "订单详情", "navigationStyle": "custom" } },
|
{ "path": "order/detail", "style": { "navigationBarTitleText": "订单详情", "navigationStyle": "custom" } },
|
||||||
{ "path": "order/trace/index", "style": { "navigationBarTitleText": "订单追溯", "navigationStyle": "custom" } },
|
{ "path": "order/trace/index", "style": { "navigationBarTitleText": "订单追溯", "navigationStyle": "custom" } },
|
||||||
{ "path": "reconciliation/index", "style": { "navigationBarTitleText": "对账单", "navigationStyle": "custom" } },
|
{ "path": "reconciliation/index", "style": { "navigationBarTitleText": "对账单", "navigationStyle": "custom" } },
|
||||||
|
|||||||
@@ -226,6 +226,46 @@ export function mapOrderListRow(item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表卡片瘦字段:只保留 UI 需要的标量,丢弃处方 content / 明细整包,
|
||||||
|
* 避免微信小程序 setData 体积过大(曾出现 ~1.5MB 告警)
|
||||||
|
* @param {Object} raw 接口订单行
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function toOrderCardRow(raw) {
|
||||||
|
if (!raw) return { _wxKey: '0', id: 0 }
|
||||||
|
const d = mapOrderListRow(raw)
|
||||||
|
const id = raw.id
|
||||||
|
const status = raw.status
|
||||||
|
// 样式用:已完成/取消给语义 class,其余用状态码
|
||||||
|
let statusKey = status != null ? String(status) : 'default'
|
||||||
|
if (status === 6 || status === 7) statusKey = 'done'
|
||||||
|
else if (status === 9) statusKey = 'cancel'
|
||||||
|
const payText = (d.totalPayPrice || '').replace('¥', '').trim()
|
||||||
|
return {
|
||||||
|
_wxKey: id != null && id !== '' ? String(id) : 'ord0',
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
statusKey,
|
||||||
|
orderNo: d.orderNo,
|
||||||
|
storeName: d.storeName,
|
||||||
|
statusText: d.statusText,
|
||||||
|
productSummary: d.productSummary || '药品订单',
|
||||||
|
prescriptionTypeText: d.prescriptionTypeText,
|
||||||
|
deliveryText: d.deliveryText,
|
||||||
|
patientName: d.patientName,
|
||||||
|
userNickname: d.userNickname,
|
||||||
|
receiverName: d.receiverName,
|
||||||
|
receiverMobile: d.receiverMobile,
|
||||||
|
createdAt: d.createdAt,
|
||||||
|
totalPayPrice: d.totalPayPrice,
|
||||||
|
payAmountText: payText,
|
||||||
|
pId: d.pId,
|
||||||
|
canViewPrescription: d.canViewPrescription,
|
||||||
|
upId: d.upId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 物流详情(对齐 PC express-detail/detail-by-order) */
|
/** 物流详情(对齐 PC express-detail/detail-by-order) */
|
||||||
export function mapExpressDetail(data) {
|
export function mapExpressDetail(data) {
|
||||||
if (!data || typeof data !== 'object') {
|
if (!data || typeof data !== 'object') {
|
||||||
|
|||||||
@@ -30,3 +30,15 @@ export function buildOrderSaleAmountParams(opts) {
|
|||||||
const { page, pageSize, ...rest } = opts || {}
|
const { page, pageSize, ...rest } = opts || {}
|
||||||
return buildOrderListParams(rest)
|
return buildOrderListParams(rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态数量接口参数:与列表筛选一致,但不传 status
|
||||||
|
* (徽标要展示每个状态的数量,不能被当前 Tab 的 status 过滤)
|
||||||
|
*/
|
||||||
|
export function buildOrderStatusCountParams(opts) {
|
||||||
|
const params = buildOrderSaleAmountParams(opts)
|
||||||
|
if (params && Object.prototype.hasOwnProperty.call(params, 'status')) {
|
||||||
|
delete params.status
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|||||||
156
subPackages/sub_business_shared/components/ListSkeleton.vue
Normal file
156
subPackages/sub_business_shared/components/ListSkeleton.vue
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
<template>
|
||||||
|
<!--
|
||||||
|
列表骨架屏:按 type 模拟订单卡等,放在业务分包以控制主包体积。
|
||||||
|
order 类型对齐商品订单卡片结构(顶栏/正文/底栏)。
|
||||||
|
-->
|
||||||
|
<view class="list-skeleton" :class="'list-skeleton--' + type">
|
||||||
|
<view
|
||||||
|
v-for="i in rowCount"
|
||||||
|
:key="i"
|
||||||
|
class="sk-row"
|
||||||
|
:class="'sk-row--' + type"
|
||||||
|
>
|
||||||
|
<template v-if="type === 'order'">
|
||||||
|
<view class="sk-order-head">
|
||||||
|
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--xs sk-shimmer"></view>
|
||||||
|
</view>
|
||||||
|
<view class="sk-order-body">
|
||||||
|
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||||||
|
</view>
|
||||||
|
<view class="sk-order-foot">
|
||||||
|
<view class="sk-line sk-line--xs sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--price sk-shimmer"></view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<view class="sk-line sk-line--lg sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--md sk-shimmer"></view>
|
||||||
|
<view class="sk-line sk-line--sm sk-shimmer"></view>
|
||||||
|
</template>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ListSkeleton',
|
||||||
|
props: {
|
||||||
|
/** 骨架样式:order=订单卡;default=通用三行 */
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
default: 'order',
|
||||||
|
validator(v) {
|
||||||
|
return ['order', 'default'].indexOf(v) !== -1
|
||||||
|
},
|
||||||
|
},
|
||||||
|
/** 行数 */
|
||||||
|
rows: {
|
||||||
|
type: [Number, String],
|
||||||
|
default: 4,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
rowCount() {
|
||||||
|
const n = Number(this.rows)
|
||||||
|
return n > 0 ? n : 4
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list-skeleton {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0 24rpx 24rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-shimmer {
|
||||||
|
background: linear-gradient(90deg, #ebebeb 25%, #f5f5f5 50%, #ebebeb 75%);
|
||||||
|
background-size: 400% 100%;
|
||||||
|
animation: sk-shimmer 1.4s ease infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes sk-shimmer {
|
||||||
|
0% {
|
||||||
|
background-position: 100% 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-row--order {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx;
|
||||||
|
padding: 28rpx 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.03);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-order-head {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-order-body {
|
||||||
|
margin-bottom: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-order-foot {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-row--default {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
padding: 28rpx 24rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line {
|
||||||
|
height: 24rpx;
|
||||||
|
border-radius: 6rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line--lg {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line--md {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line--sm {
|
||||||
|
width: 90%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line--xs {
|
||||||
|
width: 120rpx;
|
||||||
|
height: 22rpx;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-line--price {
|
||||||
|
width: 140rpx;
|
||||||
|
height: 32rpx;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sk-order-head .sk-line--md {
|
||||||
|
width: 45%;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,12 +1,26 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<business-page-layout title="商品订单" :show-back="true">
|
<business-page-layout title="商品订单" :show-back="true">
|
||||||
<view class="order-list-container">
|
<!-- page-flex 高度=视口减导航与 layout 底部 32rpx;header 不可再设 relative top(会空白+遮挡列表) -->
|
||||||
|
<view
|
||||||
<!-- 终极修复:弃用会导致冲突的 page-sticky-header -->
|
class="order-list-container page-flex"
|
||||||
<!-- 改用原生 native-sticky-header,绑定动态计算的导航栏高度,精准吸附在 u-navbar 下方 -->
|
:style="{ height: 'calc(100vh - ' + navTopOffset + 'px - 32rpx)' }"
|
||||||
<view class="native-sticky-header" :style="{ top: navTopOffset + 'px' }">
|
>
|
||||||
|
<view class="native-sticky-header rx-flex-fixed">
|
||||||
<view class="header-solid-bg">
|
<view class="header-solid-bg">
|
||||||
|
<!-- 状态 Tab:count>0 显示徽标,0 由 u-badge 自动隐藏 -->
|
||||||
|
<view class="tabs-wrap">
|
||||||
|
<u-tabs
|
||||||
|
:list="statusTabsList"
|
||||||
|
:is-scroll="true"
|
||||||
|
:current="statusIndex"
|
||||||
|
active-color="#6ACDBB"
|
||||||
|
inactive-color="#86909C"
|
||||||
|
bg-color="#ffffff"
|
||||||
|
:bold="true"
|
||||||
|
@change="onStatusTabChange"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
<view class="quick-filter">
|
<view class="quick-filter">
|
||||||
<view class="filter-row date-row">
|
<view class="filter-row date-row">
|
||||||
<picker mode="date" :value="dateStart" @change="onStart">
|
<picker mode="date" :value="dateStart" @change="onStart">
|
||||||
@@ -25,12 +39,6 @@
|
|||||||
<text class="filter-label">订单号</text>
|
<text class="filter-label">订单号</text>
|
||||||
<input class="filter-input" v-model="orderNo" placeholder="输入订单号" placeholder-style="color:#BDBDBD" />
|
<input class="filter-input" v-model="orderNo" placeholder="输入订单号" placeholder-style="color:#BDBDBD" />
|
||||||
</view>
|
</view>
|
||||||
<view class="filter-row">
|
|
||||||
<text class="filter-label">订单状态</text>
|
|
||||||
<picker :range="statusLabels" @change="onStatus">
|
|
||||||
<view class="filter-picker">{{ statusLabels[statusIndex] || '全部' }}</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
<view class="filter-row">
|
<view class="filter-row">
|
||||||
<text class="filter-label">发货方式</text>
|
<text class="filter-label">发货方式</text>
|
||||||
<picker :range="deliveryLabels" @change="onDelivery">
|
<picker :range="deliveryLabels" @change="onDelivery">
|
||||||
@@ -47,8 +55,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 统计卡片区 -->
|
<scroll-view v-if="statItems.length" class="stats-scroll rx-flex-fixed" scroll-x>
|
||||||
<scroll-view v-if="statItems.length" class="stats-scroll" scroll-x>
|
|
||||||
<view class="stats">
|
<view class="stats">
|
||||||
<view v-for="s in statItems" :key="s.key" class="stat-card">
|
<view v-for="s in statItems" :key="s.key" class="stat-card">
|
||||||
<text class="stat-label">{{ s.label }}</text>
|
<text class="stat-label">{{ s.label }}</text>
|
||||||
@@ -57,8 +64,29 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 顶级悬浮卡片列表 -->
|
<!-- 左右滑切换状态;内层 scroll-view 负责下拉刷新与触底分页 -->
|
||||||
<view class="card-list-wrap">
|
<swiper
|
||||||
|
class="order-swiper"
|
||||||
|
:current="statusIndex"
|
||||||
|
:disable-touch="statusOptions.length < 2"
|
||||||
|
@change="onStatusSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item
|
||||||
|
v-for="(cat, idx) in statusOptions"
|
||||||
|
:key="idx"
|
||||||
|
>
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="order-scroll"
|
||||||
|
refresher-enabled="true"
|
||||||
|
:refresher-triggered="isRefreshing"
|
||||||
|
@refresherrefresh="onRefresherRefresh"
|
||||||
|
@scrolltolower="onScrollToLower"
|
||||||
|
>
|
||||||
|
<block v-if="statusIndex === idx">
|
||||||
|
<!-- 仅首屏 loading 且无数据时展示骨架,上拉加载不盖骨架 -->
|
||||||
|
<list-skeleton v-if="loading && !list.length" type="order" :rows="4" />
|
||||||
|
<view v-else class="card-list-wrap">
|
||||||
<view
|
<view
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
:key="item._wxKey"
|
:key="item._wxKey"
|
||||||
@@ -67,67 +95,58 @@
|
|||||||
:hover-stay-time="150"
|
:hover-stay-time="150"
|
||||||
@click="goDetail(item)"
|
@click="goDetail(item)"
|
||||||
>
|
>
|
||||||
<!-- 顶部:业务归属与状态 -->
|
|
||||||
<view class="card-header">
|
<view class="card-header">
|
||||||
<view class="header-left">
|
<view class="header-left">
|
||||||
<!-- 诊所信息提权,放在最醒目的左上角 -->
|
<view class="clinic-tag" v-if="item.storeName !== '-'">
|
||||||
<view class="clinic-tag" v-if="rowDisplay(item).storeName !== '-'">
|
<text class="clinic-name">{{ item.storeName }}</text>
|
||||||
<text class="clinic-name">{{ rowDisplay(item).storeName }}</text>
|
|
||||||
</view>
|
</view>
|
||||||
<view class="order-no-wrap">
|
<view class="order-no-wrap">
|
||||||
<text class="lbl">单号</text>
|
<text class="lbl">单号</text>
|
||||||
<text class="val">{{ rowDisplay(item).orderNo }}</text>
|
<text class="val">{{ item.orderNo }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="status-text" :class="['status-' + (rowDisplay(item).status || 'default')]">
|
<view class="status-text" :class="['status-' + (item.statusKey || 'default')]">
|
||||||
{{ rowDisplay(item).statusText }}
|
{{ item.statusText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 核心:商品与收件信息(无边框内联排版,节省大量高度) -->
|
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<!-- 药品名称与标签同行排版 -->
|
|
||||||
<view class="product-row">
|
<view class="product-row">
|
||||||
<text class="product-name">{{ rowDisplay(item).productSummary || '药品订单' }}</text>
|
<text class="product-name">{{ item.productSummary || '药品订单' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="tags-row">
|
<view class="tags-row">
|
||||||
<text class="modern-tag tag-type">{{ rowDisplay(item).prescriptionTypeText }}</text>
|
<text class="modern-tag tag-type">{{ item.prescriptionTypeText }}</text>
|
||||||
<text class="modern-tag tag-delivery">{{ rowDisplay(item).deliveryText }}</text>
|
<text class="modern-tag tag-delivery">{{ item.deliveryText }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 去掉臃肿的灰色岛,采用精致的文本网格 -->
|
|
||||||
<view class="info-grid">
|
<view class="info-grid">
|
||||||
<view
|
<view
|
||||||
class="info-item"
|
class="info-item"
|
||||||
v-if="rowDisplay(item).patientName !== '-' || rowDisplay(item).userNickname !== '-'"
|
v-if="item.patientName !== '-' || item.userNickname !== '-'"
|
||||||
@click.stop="openPatient(item)"
|
@click.stop="openPatient(item)"
|
||||||
>
|
>
|
||||||
<text class="info-lbl">就诊人</text>
|
<text class="info-lbl">就诊人</text>
|
||||||
<text class="info-val link">{{ rowDisplay(item).patientName !== '-' ? rowDisplay(item).patientName : rowDisplay(item).userNickname }}</text>
|
<text class="info-val link">{{ item.patientName !== '-' ? item.patientName : item.userNickname }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="info-item">
|
<view class="info-item">
|
||||||
<text class="info-lbl">收件人</text>
|
<text class="info-lbl">收件人</text>
|
||||||
<text class="info-val">
|
<text class="info-val">
|
||||||
{{ rowDisplay(item).receiverName }}
|
{{ item.receiverName }}
|
||||||
<text class="info-sub" v-if="rowDisplay(item).receiverMobile">{{ rowDisplay(item).receiverMobile }}</text>
|
<text class="info-sub" v-if="item.receiverMobile">{{ item.receiverMobile }}</text>
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部:时间、价格与操作(水平对齐) -->
|
|
||||||
<view class="card-footer">
|
<view class="card-footer">
|
||||||
<text class="time">{{ rowDisplay(item).createdAt }}</text>
|
<text class="time">{{ item.createdAt }}</text>
|
||||||
<view class="footer-actions">
|
<view class="footer-actions">
|
||||||
<view class="price-block">
|
<view class="price-block">
|
||||||
<text class="price-lbl">实付</text>
|
<text class="price-lbl">实付</text>
|
||||||
<text class="currency">¥</text>
|
<text class="currency">¥</text>
|
||||||
<text class="amount">{{ rowDisplay(item).totalPayPrice.replace('¥', '').trim() }}</text>
|
<text class="amount">{{ item.payAmountText }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
v-if="rowDisplay(item).canViewPrescription"
|
v-if="item.canViewPrescription"
|
||||||
class="btn-outline"
|
class="btn-outline"
|
||||||
@click.stop="openPrescription(rowDisplay(item).pId)"
|
@click.stop="openPrescription(item.pId)"
|
||||||
>
|
>
|
||||||
查看处方
|
查看处方
|
||||||
</view>
|
</view>
|
||||||
@@ -135,12 +154,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!loading && !list.length" class="empty-state">
|
<view v-if="!loading && !list.length" class="empty-state">
|
||||||
<text>暂无订单记录</text>
|
<text>暂无订单记录</text>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
<view v-else class="rx-swiper-placeholder"></view>
|
||||||
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</business-page-layout>
|
</business-page-layout>
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
<user-patient-detail
|
<user-patient-detail
|
||||||
@@ -157,14 +179,15 @@
|
|||||||
// 这里已经移除了引入产生冲突的 PageStickyHeader,以保证代码的纯净性
|
// 这里已经移除了引入产生冲突的 PageStickyHeader,以保证代码的纯净性
|
||||||
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue'
|
import BusinessPageLayout from '@/subPackages/sub_business_shared/components/business-page-layout.vue'
|
||||||
import CollapsibleFilterPanel from '@/subPackages/sub_business_shared/components/CollapsibleFilterPanel.vue'
|
import CollapsibleFilterPanel from '@/subPackages/sub_business_shared/components/CollapsibleFilterPanel.vue'
|
||||||
|
import ListSkeleton from '@/subPackages/sub_business_shared/components/ListSkeleton.vue'
|
||||||
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
||||||
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
import { withWxKey } from '@/utils/wxListKey.js'
|
import { toOrderCardRow } from '@/subPackages/sub_business_shared/common/display.js'
|
||||||
import { mapOrderListRow } from '@/subPackages/sub_business_shared/common/display.js'
|
|
||||||
import { formatMoney } from '@/subPackages/sub_business_shared/common/format.js'
|
import { formatMoney } from '@/subPackages/sub_business_shared/common/format.js'
|
||||||
import {
|
import {
|
||||||
buildOrderListParams,
|
buildOrderListParams,
|
||||||
buildOrderSaleAmountParams,
|
buildOrderSaleAmountParams,
|
||||||
|
buildOrderStatusCountParams,
|
||||||
} from '@/subPackages/sub_business_shared/common/orderParams.js'
|
} from '@/subPackages/sub_business_shared/common/orderParams.js'
|
||||||
import {
|
import {
|
||||||
loadOrderFilter,
|
loadOrderFilter,
|
||||||
@@ -190,14 +213,24 @@ const PRESCRIPTION_TYPE_OPTIONS = [
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [businessMixin],
|
mixins: [businessMixin],
|
||||||
components: { BusinessPageLayout, CollapsibleFilterPanel, PrescriptionDetailPopup, UserPatientDetail },
|
components: {
|
||||||
|
BusinessPageLayout,
|
||||||
|
CollapsibleFilterPanel,
|
||||||
|
ListSkeleton,
|
||||||
|
PrescriptionDetailPopup,
|
||||||
|
UserPatientDetail,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: [],
|
list: [],
|
||||||
page: 1,
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
hasMore: true,
|
||||||
|
isRefreshing: false,
|
||||||
|
tabChanging: false,
|
||||||
orderNo: '',
|
orderNo: '',
|
||||||
statusOptions: [{ label: '全部', value: '' }],
|
statusOptions: [{ label: '全部', value: '', count: 0 }],
|
||||||
statusIndex: 0,
|
statusIndex: 0,
|
||||||
deliveryIndex: 0,
|
deliveryIndex: 0,
|
||||||
prescriptionTypeIndex: 0,
|
prescriptionTypeIndex: 0,
|
||||||
@@ -211,8 +244,15 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
statusLabels() {
|
/** u-tabs:name + count(0 不显示徽标) */
|
||||||
return this.statusOptions.map(s => s.label || s.name || '全部')
|
statusTabsList() {
|
||||||
|
return (this.statusOptions || []).map((s) => {
|
||||||
|
const count = Number(s.count) > 0 ? Number(s.count) : 0
|
||||||
|
return {
|
||||||
|
name: s.label || s.name || '全部',
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
},
|
},
|
||||||
deliveryLabels() {
|
deliveryLabels() {
|
||||||
return DELIVERY_OPTIONS.map(o => o.label)
|
return DELIVERY_OPTIONS.map(o => o.label)
|
||||||
@@ -222,8 +262,7 @@ export default {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
created() {
|
created() {
|
||||||
// 动态计算顶部 u-navbar 的总高度 (状态栏 + navbar 固定 44px 高度)
|
// 仅用于 page-flex 高度 calc;layout 已 paddingTop,勿再绑到 header 的 relative top
|
||||||
// 这一步与 business-page-layout 内的计算逻辑保持完全一致,保证完美对接
|
|
||||||
const sys = uni.getSystemInfoSync()
|
const sys = uni.getSystemInfoSync()
|
||||||
const statusBarHeight = this.$statusBarHeight || sys.statusBarHeight || 0
|
const statusBarHeight = this.$statusBarHeight || sys.statusBarHeight || 0
|
||||||
const navbarHeight = this.$navbarHeight || 44
|
const navbarHeight = this.$navbarHeight || 44
|
||||||
@@ -234,26 +273,24 @@ export default {
|
|||||||
this.bizApi.getOrderStatusOption().then(w => {
|
this.bizApi.getOrderStatusOption().then(w => {
|
||||||
const opts = (w.data && (w.data.items || w.data)) || []
|
const opts = (w.data && (w.data.items || w.data)) || []
|
||||||
if (Array.isArray(opts) && opts.length) {
|
if (Array.isArray(opts) && opts.length) {
|
||||||
this.statusOptions = [{ label: '全部', value: '' }].concat(opts.map(o => ({
|
this.statusOptions = [{ label: '全部', value: '', count: 0 }].concat(opts.map(o => ({
|
||||||
label: o.label || o.name,
|
label: o.label || o.name,
|
||||||
value: o.value != null ? o.value : o.id,
|
value: o.value != null ? o.value : o.id,
|
||||||
|
count: 0,
|
||||||
})))
|
})))
|
||||||
|
if (this.statusIndex >= this.statusOptions.length) this.statusIndex = 0
|
||||||
}
|
}
|
||||||
|
this.loadStatusCount()
|
||||||
})
|
})
|
||||||
this.checkDateEndAndReload()
|
this.checkDateEndAndReload()
|
||||||
},
|
},
|
||||||
/** 离开/隐藏时关掉处方抽屉,避免 page-container 残留白屏 */
|
|
||||||
onHide() {
|
onHide() {
|
||||||
this.closePrescriptionPopup()
|
this.closePrescriptionPopup()
|
||||||
},
|
},
|
||||||
onUnload() {
|
onUnload() {
|
||||||
this.closePrescriptionPopup()
|
this.closePrescriptionPopup()
|
||||||
},
|
},
|
||||||
onReachBottom() {
|
|
||||||
this.load(this.page + 1, true)
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
/** 关闭处方抽屉 */
|
|
||||||
closePrescriptionPopup() {
|
closePrescriptionPopup() {
|
||||||
const pop = this.$refs.prescriptionPopup
|
const pop = this.$refs.prescriptionPopup
|
||||||
if (pop && typeof pop.onClose === 'function') {
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
@@ -314,14 +351,14 @@ export default {
|
|||||||
dateEnd: this.dateEnd,
|
dateEnd: this.dateEnd,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowDisplay(item) {
|
/** 重新加载列表、销售金额与状态徽标 */
|
||||||
return mapOrderListRow(item)
|
|
||||||
},
|
|
||||||
reload() {
|
reload() {
|
||||||
this.persistOrderFilter()
|
this.persistOrderFilter()
|
||||||
this.page = 1
|
this.page = 1
|
||||||
|
this.hasMore = true
|
||||||
this.load(1, false)
|
this.load(1, false)
|
||||||
this.loadSaleAmount()
|
this.loadSaleAmount()
|
||||||
|
this.loadStatusCount()
|
||||||
},
|
},
|
||||||
loadSaleAmount() {
|
loadSaleAmount() {
|
||||||
const params = buildOrderSaleAmountParams(this.filterValues())
|
const params = buildOrderSaleAmountParams(this.filterValues())
|
||||||
@@ -337,21 +374,83 @@ export default {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 拉各状态数量写回 statusOptions.count(不含当前 status 筛选)
|
||||||
|
*/
|
||||||
|
loadStatusCount() {
|
||||||
|
const params = buildOrderStatusCountParams(this.filterValues())
|
||||||
|
this.bizApi.getOrderStatusCount(params).then((w) => {
|
||||||
|
if (!w.ok || !w.data) return
|
||||||
|
const total = Number(w.data.total || 0)
|
||||||
|
const map = {}
|
||||||
|
const items = w.data.items || []
|
||||||
|
items.forEach((it) => {
|
||||||
|
map[Number(it.id)] = Number(it.count || 0)
|
||||||
|
})
|
||||||
|
this.statusOptions = (this.statusOptions || []).map((s, i) => {
|
||||||
|
const isAll = i === 0 || s.value === '' || s.value == null
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
count: isAll ? total : (map[Number(s.value)] || 0),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
load(page, append) {
|
load(page, append) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
const pageSize = this.pageSize || 15
|
||||||
const params = buildOrderListParams({
|
const params = buildOrderListParams({
|
||||||
page,
|
page,
|
||||||
pageSize: 15,
|
pageSize,
|
||||||
...this.filterValues(),
|
...this.filterValues(),
|
||||||
})
|
})
|
||||||
this.bizApi.getOrderList(params).then(w => {
|
this.bizApi.getOrderList(params).then(w => {
|
||||||
const items = withWxKey((w.data && w.data.items) || [], 'id', 'ord')
|
// 只写入卡片瘦字段,避免把处方 content / 明细整包 setData
|
||||||
|
const rawItems = (w.data && w.data.items) || []
|
||||||
|
const items = rawItems.map((raw) => toOrderCardRow(raw))
|
||||||
this.list = append ? this.list.concat(items) : items
|
this.list = append ? this.list.concat(items) : items
|
||||||
this.page = page
|
this.page = page
|
||||||
}).finally(() => { this.loading = false })
|
this.hasMore = rawItems.length >= pageSize
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
if (this.isRefreshing) this.isRefreshing = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onStatus(e) {
|
/** scroll-view 下拉刷新 */
|
||||||
this.statusIndex = Number(e.detail.value)
|
onRefresherRefresh() {
|
||||||
|
this.isRefreshing = true
|
||||||
|
this.reload()
|
||||||
|
},
|
||||||
|
/** scroll-view 触底分页 */
|
||||||
|
onScrollToLower() {
|
||||||
|
if (this.loading || !this.hasMore) return
|
||||||
|
this.load(this.page + 1, true)
|
||||||
|
},
|
||||||
|
/** 状态 Tab 点击:与 swiper 双向同步 */
|
||||||
|
onStatusTabChange(index) {
|
||||||
|
if (this.tabChanging) return
|
||||||
|
this.tabChanging = true
|
||||||
|
const idx = typeof index === 'object' ? Number(index.index != null ? index.index : index) : Number(index)
|
||||||
|
if (!Number.isNaN(idx) && idx !== this.statusIndex) {
|
||||||
|
this.statusIndex = idx
|
||||||
|
this.reload()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 左右滑切换状态 */
|
||||||
|
onStatusSwiperChange(e) {
|
||||||
|
if (this.tabChanging) return
|
||||||
|
this.tabChanging = true
|
||||||
|
const idx = Number((e.detail && e.detail.current) || 0)
|
||||||
|
if (idx !== this.statusIndex) {
|
||||||
|
this.statusIndex = idx
|
||||||
|
this.reload()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onDelivery(e) {
|
onDelivery(e) {
|
||||||
this.deliveryIndex = Number(e.detail.value)
|
this.deliveryIndex = Number(e.detail.value)
|
||||||
@@ -368,16 +467,14 @@ export default {
|
|||||||
if (!pId) return
|
if (!pId) return
|
||||||
this.$refs.prescriptionPopup.open(pId)
|
this.$refs.prescriptionPopup.open(pId)
|
||||||
},
|
},
|
||||||
/** 打开患者视角详情抽屉 */
|
|
||||||
openPatient(item) {
|
openPatient(item) {
|
||||||
const d = this.rowDisplay(item)
|
const upId = Number(item.upId || 0)
|
||||||
const upId = Number(d.upId || 0)
|
|
||||||
if (!upId) {
|
if (!upId) {
|
||||||
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.patientDetailUpId = upId
|
this.patientDetailUpId = upId
|
||||||
this.patientDetailName = d.patientName !== '-' ? d.patientName : ''
|
this.patientDetailName = item.patientName !== '-' ? item.patientName : ''
|
||||||
this.patientDetailVisible = true
|
this.patientDetailVisible = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -394,30 +491,58 @@ $color-border: #E5E6EB;
|
|||||||
|
|
||||||
.order-list-container {
|
.order-list-container {
|
||||||
background-color: $color-bg-base;
|
background-color: $color-bg-base;
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
padding-bottom: 60rpx;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.page-flex {
|
||||||
|
/* height 由行内 calc(100vh - navTopOffset) 控制,避免再叠 layout 的 paddingTop */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.rx-flex-fixed {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.order-swiper {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.order-scroll {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.rx-swiper-placeholder {
|
||||||
|
min-height: 200rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ================= 核心:完美吸顶方案 ================= */
|
/* ================= 核心:完美吸顶方案 ================= */
|
||||||
/* 原生 sticky 结合 Vue 计算出的精确导航栏高度(top),严丝合缝拦截在导航栏正下方 */
|
|
||||||
.native-sticky-header {
|
.native-sticky-header {
|
||||||
position: -webkit-sticky;
|
position: relative;
|
||||||
position: sticky;
|
z-index: 100;
|
||||||
z-index: 100; /* 高层级,保证滑动时覆盖在下方的卡片上 */
|
background-color: $color-bg-base;
|
||||||
background-color: $color-bg-base; /* 必须有底色用于防穿透遮挡 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-solid-bg {
|
.header-solid-bg {
|
||||||
/* 使用 padding 将内容向内挤,防止滑动过程的透明间隙穿透问题 */
|
/* 使用 padding 将内容向内挤,防止滑动过程的透明间隙穿透问题 */
|
||||||
padding: 16rpx 24rpx 16rpx;
|
padding: 16rpx 24rpx 16rpx;
|
||||||
}
|
}
|
||||||
|
.tabs-wrap {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 20rpx 20rpx 0 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
/* 筛选项 UI 美化 */
|
/* 筛选项 UI 美化 */
|
||||||
.quick-filter {
|
.quick-filter {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 16rpx 20rpx;
|
padding: 16rpx 20rpx;
|
||||||
border-radius: 20rpx;
|
border-radius: 0 0 20rpx 20rpx;
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
||||||
|
margin-bottom: 16rpx;
|
||||||
}
|
}
|
||||||
.filter-row {
|
.filter-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -226,6 +226,45 @@ export function mapOrderListRow(item) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表卡片瘦字段:只保留 UI 需要的标量,丢弃处方 content / 明细整包,
|
||||||
|
* 避免微信小程序 setData 体积过大
|
||||||
|
* @param {Object} raw 接口订单行
|
||||||
|
* @returns {Object}
|
||||||
|
*/
|
||||||
|
export function toOrderCardRow(raw) {
|
||||||
|
if (!raw) return { _wxKey: '0', id: 0 }
|
||||||
|
const d = mapOrderListRow(raw)
|
||||||
|
const id = raw.id
|
||||||
|
const status = raw.status
|
||||||
|
let statusKey = status != null ? String(status) : 'default'
|
||||||
|
if (status === 6 || status === 7) statusKey = 'done'
|
||||||
|
else if (status === 9) statusKey = 'cancel'
|
||||||
|
const payText = (d.totalPayPrice || '').replace('¥', '').trim()
|
||||||
|
return {
|
||||||
|
_wxKey: id != null && id !== '' ? String(id) : 'ord0',
|
||||||
|
id,
|
||||||
|
status,
|
||||||
|
statusKey,
|
||||||
|
orderNo: d.orderNo,
|
||||||
|
storeName: d.storeName,
|
||||||
|
statusText: d.statusText,
|
||||||
|
productSummary: d.productSummary || '药品订单',
|
||||||
|
prescriptionTypeText: d.prescriptionTypeText,
|
||||||
|
deliveryText: d.deliveryText,
|
||||||
|
patientName: d.patientName,
|
||||||
|
userNickname: d.userNickname,
|
||||||
|
receiverName: d.receiverName,
|
||||||
|
receiverMobile: d.receiverMobile,
|
||||||
|
createdAt: d.createdAt,
|
||||||
|
totalPayPrice: d.totalPayPrice,
|
||||||
|
payAmountText: payText,
|
||||||
|
pId: d.pId,
|
||||||
|
canViewPrescription: d.canViewPrescription,
|
||||||
|
upId: d.upId,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 物流详情(对齐 PC express-detail/detail-by-order) */
|
/** 物流详情(对齐 PC express-detail/detail-by-order) */
|
||||||
export function mapExpressDetail(data) {
|
export function mapExpressDetail(data) {
|
||||||
if (!data || typeof data !== 'object') {
|
if (!data || typeof data !== 'object') {
|
||||||
|
|||||||
@@ -30,3 +30,14 @@ export function buildOrderSaleAmountParams(opts) {
|
|||||||
const { page, pageSize, ...rest } = opts || {}
|
const { page, pageSize, ...rest } = opts || {}
|
||||||
return buildOrderListParams(rest)
|
return buildOrderListParams(rest)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 状态数量接口参数:与列表筛选一致,但不传 status
|
||||||
|
*/
|
||||||
|
export function buildOrderStatusCountParams(opts) {
|
||||||
|
const params = buildOrderSaleAmountParams(opts)
|
||||||
|
if (params && Object.prototype.hasOwnProperty.call(params, 'status')) {
|
||||||
|
delete params.status
|
||||||
|
}
|
||||||
|
return params
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,10 +1,25 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<clinic-page-layout title="商品订单" :show-back="true">
|
<clinic-page-layout title="商品订单" :show-back="true">
|
||||||
<view class="order-list-container">
|
<!-- page-flex 高度=视口减导航与 layout 底部 32rpx;header 不可再设 relative top -->
|
||||||
<!-- 顶部筛选 -->
|
<view
|
||||||
<view class="native-sticky-header" :style="{ top: navTopOffset + 'px' }">
|
class="order-list-container page-flex"
|
||||||
|
:style="{ height: 'calc(100vh - ' + navTopOffset + 'px - 32rpx)' }"
|
||||||
|
>
|
||||||
|
<view class="native-sticky-header rx-flex-fixed">
|
||||||
<view class="header-solid-bg">
|
<view class="header-solid-bg">
|
||||||
|
<view class="tabs-wrap">
|
||||||
|
<u-tabs
|
||||||
|
:list="statusTabsList"
|
||||||
|
:is-scroll="true"
|
||||||
|
:current="statusIndex"
|
||||||
|
active-color="#6ACDBB"
|
||||||
|
inactive-color="#86909C"
|
||||||
|
bg-color="#ffffff"
|
||||||
|
:bold="true"
|
||||||
|
@change="onStatusTabChange"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
<view class="quick-filter">
|
<view class="quick-filter">
|
||||||
<view class="filter-row date-row">
|
<view class="filter-row date-row">
|
||||||
<picker mode="date" :value="dateStart" @change="onStart">
|
<picker mode="date" :value="dateStart" @change="onStart">
|
||||||
@@ -23,12 +38,6 @@
|
|||||||
<text class="filter-label">订单号</text>
|
<text class="filter-label">订单号</text>
|
||||||
<input class="filter-input" v-model="orderNo" placeholder="输入订单号" placeholder-style="color:#BDBDBD" />
|
<input class="filter-input" v-model="orderNo" placeholder="输入订单号" placeholder-style="color:#BDBDBD" />
|
||||||
</view>
|
</view>
|
||||||
<view class="filter-row">
|
|
||||||
<text class="filter-label">订单状态</text>
|
|
||||||
<picker :range="statusLabels" @change="onStatus">
|
|
||||||
<view class="filter-picker">{{ statusLabels[statusIndex] || '全部' }}</view>
|
|
||||||
</picker>
|
|
||||||
</view>
|
|
||||||
<view class="filter-row">
|
<view class="filter-row">
|
||||||
<text class="filter-label">发货方式</text>
|
<text class="filter-label">发货方式</text>
|
||||||
<picker :range="deliveryLabels" @change="onDelivery">
|
<picker :range="deliveryLabels" @change="onDelivery">
|
||||||
@@ -45,8 +54,7 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 统计卡片区 -->
|
<scroll-view v-if="statItems.length" class="stats-scroll rx-flex-fixed" scroll-x>
|
||||||
<scroll-view v-if="statItems.length" class="stats-scroll" scroll-x>
|
|
||||||
<view class="stats">
|
<view class="stats">
|
||||||
<view v-for="s in statItems" :key="s.key" class="stat-card">
|
<view v-for="s in statItems" :key="s.key" class="stat-card">
|
||||||
<text class="stat-label">{{ s.label }}</text>
|
<text class="stat-label">{{ s.label }}</text>
|
||||||
@@ -55,8 +63,27 @@
|
|||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
|
||||||
<!-- 顶级悬浮卡片列表 -->
|
<swiper
|
||||||
<view class="card-list-wrap">
|
class="order-swiper"
|
||||||
|
:current="statusIndex"
|
||||||
|
:disable-touch="statusOptions.length < 2"
|
||||||
|
@change="onStatusSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item
|
||||||
|
v-for="(cat, idx) in statusOptions"
|
||||||
|
:key="idx"
|
||||||
|
>
|
||||||
|
<scroll-view
|
||||||
|
scroll-y
|
||||||
|
class="order-scroll"
|
||||||
|
refresher-enabled="true"
|
||||||
|
:refresher-triggered="isRefreshing"
|
||||||
|
@refresherrefresh="onRefresherRefresh"
|
||||||
|
@scrolltolower="onScrollToLower"
|
||||||
|
>
|
||||||
|
<block v-if="statusIndex === idx">
|
||||||
|
<list-skeleton v-if="loading && !list.length" type="order" :rows="4" />
|
||||||
|
<view v-else class="card-list-wrap">
|
||||||
<view
|
<view
|
||||||
v-for="item in list"
|
v-for="item in list"
|
||||||
:key="item._wxKey"
|
:key="item._wxKey"
|
||||||
@@ -65,68 +92,61 @@
|
|||||||
:hover-stay-time="150"
|
:hover-stay-time="150"
|
||||||
@click="goDetail(item)"
|
@click="goDetail(item)"
|
||||||
>
|
>
|
||||||
<!-- 顶部:单号与状态 -->
|
|
||||||
<view class="card-head">
|
<view class="card-head">
|
||||||
<view class="order-id">
|
<view class="order-id">
|
||||||
<text class="id-icon">单</text>
|
<text class="id-icon">单</text>
|
||||||
<text class="id-text">{{ rowDisplay(item).orderNo }}</text>
|
<text class="id-text">{{ item.orderNo }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view
|
<view
|
||||||
class="status-badge"
|
class="status-badge"
|
||||||
:class="['status-' + (rowDisplay(item).status || 'default')]"
|
:class="['status-' + (item.statusKey || 'default')]"
|
||||||
>
|
>
|
||||||
{{ rowDisplay(item).statusText }}
|
{{ item.statusText }}
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="card-body">
|
<view class="card-body">
|
||||||
<!-- 核心视觉区:商品与金额 -->
|
|
||||||
<view class="main-info">
|
<view class="main-info">
|
||||||
<view class="title-wrap">
|
<view class="title-wrap">
|
||||||
<text class="goods-title">{{ rowDisplay(item).productSummary || '药品订单' }}</text>
|
<text class="goods-title">{{ item.productSummary || '药品订单' }}</text>
|
||||||
<view class="goods-tags">
|
<view class="goods-tags">
|
||||||
<text class="modern-tag tag-type">{{ rowDisplay(item).prescriptionTypeText }}</text>
|
<text class="modern-tag tag-type">{{ item.prescriptionTypeText }}</text>
|
||||||
<text class="modern-tag tag-delivery">{{ rowDisplay(item).deliveryText }}</text>
|
<text class="modern-tag tag-delivery">{{ item.deliveryText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="price-wrap">
|
<view class="price-wrap">
|
||||||
<text class="currency">¥</text>
|
<text class="currency">¥</text>
|
||||||
<text class="amount">{{ rowDisplay(item).totalPayPrice.replace('¥', '').trim() }}</text>
|
<text class="amount">{{ item.payAmountText }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 信息岛 (Info Island):聚合次要信息的高级灰底区块 -->
|
|
||||||
<view class="info-island">
|
<view class="info-island">
|
||||||
<view
|
<view
|
||||||
class="island-row"
|
class="island-row"
|
||||||
v-if="rowDisplay(item).patientName !== '-' || rowDisplay(item).userNickname !== '-'"
|
v-if="item.patientName !== '-' || item.userNickname !== '-'"
|
||||||
@click.stop="openPatient(item)"
|
@click.stop="openPatient(item)"
|
||||||
>
|
>
|
||||||
<text class="island-label">就诊人</text>
|
<text class="island-label">就诊人</text>
|
||||||
<text class="island-value link">{{ rowDisplay(item).patientName !== '-' ? rowDisplay(item).patientName : rowDisplay(item).userNickname }}</text>
|
<text class="island-value link">{{ item.patientName !== '-' ? item.patientName : item.userNickname }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="island-row">
|
<view class="island-row">
|
||||||
<text class="island-label">收件人</text>
|
<text class="island-label">收件人</text>
|
||||||
<text class="island-value">
|
<text class="island-value">
|
||||||
{{ rowDisplay(item).receiverName }}
|
{{ item.receiverName }}
|
||||||
<text class="muted" v-if="rowDisplay(item).receiverMobile"> · {{ rowDisplay(item).receiverMobile }}</text>
|
<text class="muted" v-if="item.receiverMobile"> · {{ item.receiverMobile }}</text>
|
||||||
</text>
|
</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="island-row" v-if="rowDisplay(item).storeName !== '-'">
|
<view class="island-row" v-if="item.storeName !== '-'">
|
||||||
<text class="island-label">开方诊所</text>
|
<text class="island-label">开方诊所</text>
|
||||||
<text class="island-value">{{ rowDisplay(item).storeName }}</text>
|
<text class="island-value">{{ item.storeName }}</text>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 底部:时间与操作按键 -->
|
|
||||||
<view class="card-foot">
|
<view class="card-foot">
|
||||||
<text class="time">{{ rowDisplay(item).createdAt }}</text>
|
<text class="time">{{ item.createdAt }}</text>
|
||||||
<view class="actions">
|
<view class="actions">
|
||||||
<view
|
<view
|
||||||
v-if="rowDisplay(item).canViewPrescription"
|
v-if="item.canViewPrescription"
|
||||||
class="btn-ghost"
|
class="btn-ghost"
|
||||||
@click.stop="openPrescription(rowDisplay(item).pId)"
|
@click.stop="openPrescription(item.pId)"
|
||||||
>
|
>
|
||||||
查看处方
|
查看处方
|
||||||
</view>
|
</view>
|
||||||
@@ -134,12 +154,15 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!loading && !list.length" class="empty-state">
|
<view v-if="!loading && !list.length" class="empty-state">
|
||||||
<text>暂无订单记录</text>
|
<text>暂无订单记录</text>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
<view v-else class="rx-swiper-placeholder"></view>
|
||||||
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
</clinic-page-layout>
|
</clinic-page-layout>
|
||||||
<prescription-detail-popup ref="prescriptionPopup" />
|
<prescription-detail-popup ref="prescriptionPopup" />
|
||||||
<user-patient-detail
|
<user-patient-detail
|
||||||
@@ -154,17 +177,22 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
import ClinicPageLayout from '@/subPackages/sub_clinic_admin/components/clinic-page-layout.vue'
|
||||||
import PageStickyHeader from '@/subPackages/sub_clinic_admin/components/PageStickyHeader.vue'
|
|
||||||
import CollapsibleFilterPanel from '@/subPackages/sub_clinic_admin/components/CollapsibleFilterPanel.vue'
|
import CollapsibleFilterPanel from '@/subPackages/sub_clinic_admin/components/CollapsibleFilterPanel.vue'
|
||||||
|
import ListSkeleton from '@/subPackages/sub_business_shared/components/ListSkeleton.vue'
|
||||||
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
import PrescriptionDetailPopup from './components/PrescriptionDetailPopup.vue'
|
||||||
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
import UserPatientDetail from '@/subPackages/sub_business_shared/components/UserPatientDetail.vue'
|
||||||
import { getOrderList, getOrderStatusOption, getOrderSaleAmount } from '@/api/clinicAdmin.js'
|
import {
|
||||||
import { withWxKey } from '@/utils/wxListKey.js'
|
getOrderList,
|
||||||
import { mapOrderListRow } from '@/subPackages/sub_clinic_admin/common/display.js'
|
getOrderStatusOption,
|
||||||
|
getOrderSaleAmount,
|
||||||
|
getOrderStatusCount,
|
||||||
|
} from '@/api/clinicAdmin.js'
|
||||||
|
import { toOrderCardRow } from '@/subPackages/sub_clinic_admin/common/display.js'
|
||||||
import { formatMoney } from '@/subPackages/sub_clinic_admin/common/format.js'
|
import { formatMoney } from '@/subPackages/sub_clinic_admin/common/format.js'
|
||||||
import {
|
import {
|
||||||
buildOrderListParams,
|
buildOrderListParams,
|
||||||
buildOrderSaleAmountParams,
|
buildOrderSaleAmountParams,
|
||||||
|
buildOrderStatusCountParams,
|
||||||
} from '@/subPackages/sub_clinic_admin/common/orderParams.js'
|
} from '@/subPackages/sub_clinic_admin/common/orderParams.js'
|
||||||
import {
|
import {
|
||||||
loadOrderFilter,
|
loadOrderFilter,
|
||||||
@@ -188,28 +216,45 @@ const PRESCRIPTION_TYPE_OPTIONS = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { ClinicPageLayout, PageStickyHeader, CollapsibleFilterPanel, PrescriptionDetailPopup, UserPatientDetail },
|
components: {
|
||||||
|
ClinicPageLayout,
|
||||||
|
CollapsibleFilterPanel,
|
||||||
|
ListSkeleton,
|
||||||
|
PrescriptionDetailPopup,
|
||||||
|
UserPatientDetail,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
list: [],
|
list: [],
|
||||||
page: 1,
|
page: 1,
|
||||||
|
pageSize: 15,
|
||||||
loading: false,
|
loading: false,
|
||||||
|
hasMore: true,
|
||||||
|
isRefreshing: false,
|
||||||
|
tabChanging: false,
|
||||||
orderNo: '',
|
orderNo: '',
|
||||||
statusOptions: [{ label: '全部', value: '' }],
|
statusOptions: [{ label: '全部', value: '', count: 0 }],
|
||||||
statusIndex: 0,
|
statusIndex: 0,
|
||||||
deliveryIndex: 0,
|
deliveryIndex: 0,
|
||||||
prescriptionTypeIndex: 0,
|
prescriptionTypeIndex: 0,
|
||||||
dateStart: '',
|
dateStart: '',
|
||||||
dateEnd: '',
|
dateEnd: '',
|
||||||
statItems: [],
|
statItems: [],
|
||||||
|
navTopOffset: 0,
|
||||||
patientDetailVisible: false,
|
patientDetailVisible: false,
|
||||||
patientDetailUpId: 0,
|
patientDetailUpId: 0,
|
||||||
patientDetailName: '',
|
patientDetailName: '',
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
statusLabels() {
|
statusTabsList() {
|
||||||
return this.statusOptions.map(s => s.label || s.name || '全部')
|
return (this.statusOptions || []).map((s) => {
|
||||||
|
const count = Number(s.count) > 0 ? Number(s.count) : 0
|
||||||
|
return {
|
||||||
|
name: s.label || s.name || '全部',
|
||||||
|
count,
|
||||||
|
}
|
||||||
|
}) || []
|
||||||
},
|
},
|
||||||
deliveryLabels() {
|
deliveryLabels() {
|
||||||
return DELIVERY_OPTIONS.map(o => o.label)
|
return DELIVERY_OPTIONS.map(o => o.label)
|
||||||
@@ -218,31 +263,36 @@ export default {
|
|||||||
return PRESCRIPTION_TYPE_OPTIONS.map(o => o.label)
|
return PRESCRIPTION_TYPE_OPTIONS.map(o => o.label)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
// 仅用于 page-flex 高度 calc;layout 已 paddingTop,勿再绑到 header 的 relative top
|
||||||
|
const sys = uni.getSystemInfoSync()
|
||||||
|
const statusBarHeight = this.$statusBarHeight || sys.statusBarHeight || 0
|
||||||
|
const navbarHeight = this.$navbarHeight || 44
|
||||||
|
this.navTopOffset = statusBarHeight + navbarHeight
|
||||||
|
},
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.applyOrderFilter(loadOrderFilter())
|
this.applyOrderFilter(loadOrderFilter())
|
||||||
getOrderStatusOption().then(w => {
|
getOrderStatusOption().then(w => {
|
||||||
const opts = (w.data && (w.data.items || w.data)) || []
|
const opts = (w.data && (w.data.items || w.data)) || []
|
||||||
if (Array.isArray(opts) && opts.length) {
|
if (Array.isArray(opts) && opts.length) {
|
||||||
this.statusOptions = [{ label: '全部', value: '' }].concat(opts.map(o => ({
|
this.statusOptions = [{ label: '全部', value: '', count: 0 }].concat(opts.map(o => ({
|
||||||
label: o.label || o.name,
|
label: o.label || o.name,
|
||||||
value: o.value != null ? o.value : o.id,
|
value: o.value != null ? o.value : o.id,
|
||||||
|
count: 0,
|
||||||
})))
|
})))
|
||||||
|
if (this.statusIndex >= this.statusOptions.length) this.statusIndex = 0
|
||||||
}
|
}
|
||||||
|
this.loadStatusCount()
|
||||||
})
|
})
|
||||||
this.checkDateEndAndReload()
|
this.checkDateEndAndReload()
|
||||||
},
|
},
|
||||||
/** 离开/隐藏时关掉处方抽屉,避免 page-container 残留白屏 */
|
|
||||||
onHide() {
|
onHide() {
|
||||||
this.closePrescriptionPopup()
|
this.closePrescriptionPopup()
|
||||||
},
|
},
|
||||||
onUnload() {
|
onUnload() {
|
||||||
this.closePrescriptionPopup()
|
this.closePrescriptionPopup()
|
||||||
},
|
},
|
||||||
onReachBottom() {
|
|
||||||
this.load(this.page + 1, true)
|
|
||||||
},
|
|
||||||
methods: {
|
methods: {
|
||||||
/** 关闭处方抽屉 */
|
|
||||||
closePrescriptionPopup() {
|
closePrescriptionPopup() {
|
||||||
const pop = this.$refs.prescriptionPopup
|
const pop = this.$refs.prescriptionPopup
|
||||||
if (pop && typeof pop.onClose === 'function') {
|
if (pop && typeof pop.onClose === 'function') {
|
||||||
@@ -303,14 +353,13 @@ export default {
|
|||||||
dateEnd: this.dateEnd,
|
dateEnd: this.dateEnd,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
rowDisplay(item) {
|
|
||||||
return mapOrderListRow(item)
|
|
||||||
},
|
|
||||||
reload() {
|
reload() {
|
||||||
this.persistOrderFilter()
|
this.persistOrderFilter()
|
||||||
this.page = 1
|
this.page = 1
|
||||||
|
this.hasMore = true
|
||||||
this.load(1, false)
|
this.load(1, false)
|
||||||
this.loadSaleAmount()
|
this.loadSaleAmount()
|
||||||
|
this.loadStatusCount()
|
||||||
},
|
},
|
||||||
loadSaleAmount() {
|
loadSaleAmount() {
|
||||||
const params = buildOrderSaleAmountParams(this.filterValues())
|
const params = buildOrderSaleAmountParams(this.filterValues())
|
||||||
@@ -326,21 +375,76 @@ export default {
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
loadStatusCount() {
|
||||||
|
const params = buildOrderStatusCountParams(this.filterValues())
|
||||||
|
getOrderStatusCount(params).then((w) => {
|
||||||
|
if (!w.ok || !w.data) return
|
||||||
|
const total = Number(w.data.total || 0)
|
||||||
|
const map = {}
|
||||||
|
const items = w.data.items || []
|
||||||
|
items.forEach((it) => {
|
||||||
|
map[Number(it.id)] = Number(it.count || 0)
|
||||||
|
})
|
||||||
|
this.statusOptions = (this.statusOptions || []).map((s, i) => {
|
||||||
|
const isAll = i === 0 || s.value === '' || s.value == null
|
||||||
|
return {
|
||||||
|
...s,
|
||||||
|
count: isAll ? total : (map[Number(s.value)] || 0),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
},
|
||||||
load(page, append) {
|
load(page, append) {
|
||||||
this.loading = true
|
this.loading = true
|
||||||
|
const pageSize = this.pageSize || 15
|
||||||
const params = buildOrderListParams({
|
const params = buildOrderListParams({
|
||||||
page,
|
page,
|
||||||
pageSize: 15,
|
pageSize,
|
||||||
...this.filterValues(),
|
...this.filterValues(),
|
||||||
})
|
})
|
||||||
getOrderList(params).then(w => {
|
getOrderList(params).then(w => {
|
||||||
const items = withWxKey((w.data && w.data.items) || [], 'id', 'ord')
|
// 只写入卡片瘦字段,避免把处方 content / 明细整包 setData
|
||||||
|
const rawItems = (w.data && w.data.items) || []
|
||||||
|
const items = rawItems.map((raw) => toOrderCardRow(raw))
|
||||||
this.list = append ? this.list.concat(items) : items
|
this.list = append ? this.list.concat(items) : items
|
||||||
this.page = page
|
this.page = page
|
||||||
}).finally(() => { this.loading = false })
|
this.hasMore = rawItems.length >= pageSize
|
||||||
|
}).finally(() => {
|
||||||
|
this.loading = false
|
||||||
|
if (this.isRefreshing) this.isRefreshing = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onStatus(e) {
|
onRefresherRefresh() {
|
||||||
this.statusIndex = Number(e.detail.value)
|
this.isRefreshing = true
|
||||||
|
this.reload()
|
||||||
|
},
|
||||||
|
onScrollToLower() {
|
||||||
|
if (this.loading || !this.hasMore) return
|
||||||
|
this.load(this.page + 1, true)
|
||||||
|
},
|
||||||
|
onStatusTabChange(index) {
|
||||||
|
if (this.tabChanging) return
|
||||||
|
this.tabChanging = true
|
||||||
|
const idx = typeof index === 'object' ? Number(index.index != null ? index.index : index) : Number(index)
|
||||||
|
if (!Number.isNaN(idx) && idx !== this.statusIndex) {
|
||||||
|
this.statusIndex = idx
|
||||||
|
this.reload()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onStatusSwiperChange(e) {
|
||||||
|
if (this.tabChanging) return
|
||||||
|
this.tabChanging = true
|
||||||
|
const idx = Number((e.detail && e.detail.current) || 0)
|
||||||
|
if (idx !== this.statusIndex) {
|
||||||
|
this.statusIndex = idx
|
||||||
|
this.reload()
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false
|
||||||
|
})
|
||||||
},
|
},
|
||||||
onDelivery(e) {
|
onDelivery(e) {
|
||||||
this.deliveryIndex = Number(e.detail.value)
|
this.deliveryIndex = Number(e.detail.value)
|
||||||
@@ -358,14 +462,13 @@ export default {
|
|||||||
this.$refs.prescriptionPopup.open(pId)
|
this.$refs.prescriptionPopup.open(pId)
|
||||||
},
|
},
|
||||||
openPatient(item) {
|
openPatient(item) {
|
||||||
const d = this.rowDisplay(item)
|
const upId = Number(item.upId || 0)
|
||||||
const upId = Number(d.upId || 0)
|
|
||||||
if (!upId) {
|
if (!upId) {
|
||||||
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
uni.showToast({ title: '缺少就诊人信息', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
this.patientDetailUpId = upId
|
this.patientDetailUpId = upId
|
||||||
this.patientDetailName = d.patientName !== '-' ? d.patientName : ''
|
this.patientDetailName = item.patientName !== '-' ? item.patientName : ''
|
||||||
this.patientDetailVisible = true
|
this.patientDetailVisible = true
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -381,16 +484,45 @@ $color-text-muted: #86909C;
|
|||||||
|
|
||||||
.order-list-container {
|
.order-list-container {
|
||||||
background-color: $color-bg-base;
|
background-color: $color-bg-base;
|
||||||
min-height: 100vh;
|
height: 100%;
|
||||||
padding-bottom: 60rpx;
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.page-flex {
|
||||||
|
/* height 由行内 calc(100vh - navTopOffset) 控制,避免再叠 layout 的 paddingTop */
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
min-height: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.rx-flex-fixed {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.order-swiper {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.order-scroll {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.rx-swiper-placeholder {
|
||||||
|
min-height: 200rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 筛选区 */
|
/* 筛选区 */
|
||||||
|
.tabs-wrap {
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 16rpx 16rpx 0 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
.quick-filter {
|
.quick-filter {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
padding: 24rpx;
|
padding: 24rpx;
|
||||||
margin-bottom: 16rpx;
|
margin-bottom: 16rpx;
|
||||||
border-radius: 16rpx;
|
border-radius: 0 0 16rpx 16rpx;
|
||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06);
|
||||||
}
|
}
|
||||||
.filter-row {
|
.filter-row {
|
||||||
@@ -657,54 +789,17 @@ $color-text-muted: #86909C;
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ================= 核心:完美吸顶方案 ================= */
|
/* ================= 顶部筛选区(flex 布局下不再用 sticky,由 page-flex 固定) ================= */
|
||||||
/* 原生 sticky 结合 Vue 计算出的精确导航栏高度(top),严丝合缝拦截在导航栏正下方 */
|
|
||||||
.native-sticky-header {
|
.native-sticky-header {
|
||||||
position: -webkit-sticky;
|
position: relative;
|
||||||
position: sticky;
|
z-index: 100;
|
||||||
z-index: 100; /* 高层级,保证滑动时覆盖在下方的卡片上 */
|
background-color: $color-bg-base;
|
||||||
background-color: $color-bg-base; /* 必须有底色用于防穿透遮挡 */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-solid-bg {
|
.header-solid-bg {
|
||||||
/* 使用 padding 将内容向内挤,防止滑动过程的透明间隙穿透问题 */
|
|
||||||
padding: 16rpx 24rpx 16rpx;
|
padding: 16rpx 24rpx 16rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 筛选项 UI 美化 */
|
|
||||||
.quick-filter {
|
|
||||||
background: #fff;
|
|
||||||
padding: 16rpx 20rpx;
|
|
||||||
border-radius: 20rpx;
|
|
||||||
box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.04);
|
|
||||||
}
|
|
||||||
.filter-row {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 16rpx;
|
|
||||||
margin-bottom: 24rpx;
|
|
||||||
&:last-child { margin-bottom: 0; }
|
|
||||||
}
|
|
||||||
.date-row {
|
|
||||||
margin-bottom: 0;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
.filter-label {
|
|
||||||
font-size: 28rpx;
|
|
||||||
color: $color-text-body;
|
|
||||||
min-width: 130rpx;
|
|
||||||
}
|
|
||||||
.filter-input, .filter-picker {
|
|
||||||
flex: 1;
|
|
||||||
font-size: 26rpx;
|
|
||||||
padding: 14rpx 20rpx;
|
|
||||||
background: #F7F8FA;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
color: $color-text-main;
|
|
||||||
}
|
|
||||||
.date-picker { text-align: center; }
|
|
||||||
.sep { color: $color-text-muted; font-size: 24rpx; }
|
|
||||||
|
|
||||||
/* 重新设计的紧凑型查询按钮 */
|
/* 重新设计的紧凑型查询按钮 */
|
||||||
.btn-query {
|
.btn-query {
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
>
|
>
|
||||||
<view class="chinese-medicine-modal page-bg">
|
<view class="chinese-medicine-modal page-bg">
|
||||||
<view class="modal-header white">
|
<view class="modal-header white">
|
||||||
<d-text text="选择中药" className="fs-32 font-bold color-title"></d-text>
|
<d-text :text="modalTitleText" className="fs-32 font-bold color-title"></d-text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view class="modal-content">
|
<view class="modal-content">
|
||||||
@@ -286,6 +286,12 @@ export default {
|
|||||||
wayPanelDrugIndex: -1
|
wayPanelDrugIndex: -1
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
/** 抽屉标题:选择中药(N种),随 selectedDrugs 动态变更 */
|
||||||
|
modalTitleText() {
|
||||||
|
return '选择中药(' + (this.selectedDrugs || []).length + '种)';
|
||||||
|
},
|
||||||
|
},
|
||||||
watch: {
|
watch: {
|
||||||
value(newVal) {
|
value(newVal) {
|
||||||
this.show = newVal;
|
this.show = newVal;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view class="safe-area-inset-bottom page-bg">
|
<!-- page-flex:整页纵向 flex,swiper 吃满剩余高度,避免死高度裁切 -->
|
||||||
|
<view class="safe-area-inset-bottom page-bg page-flex">
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<page-container
|
<page-container
|
||||||
:if="pageBackGuardShow"
|
:if="pageBackGuardShow"
|
||||||
@@ -8,36 +9,29 @@
|
|||||||
/>
|
/>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- 导航栏 -->
|
<!-- 导航栏 -->
|
||||||
<u-navbar class="navbar" :is-back="true" :title="navbarTitle" :custom-back="boundCustomBack" title-color="#000" background="{ background: '#fff' }">
|
<u-navbar class="navbar rx-flex-fixed" :is-back="true" :title="navbarTitle" :custom-back="boundCustomBack" title-color="#000" background="{ background: '#fff' }">
|
||||||
</u-navbar>
|
</u-navbar>
|
||||||
|
|
||||||
<!-- 处方类型Tab切换 -->
|
<!-- 处方类型 Tab:与用户端订单列表一致,u-tabs + 下方 swiper 双向同步 -->
|
||||||
<view v-if="!isSalespersonTransferMode" class="tabs flex p-32 flex-ali-center flex-jus-sp shadow-sm" style="overflow-x: auto; position: relative; z-index: 10;">
|
<view v-if="!isSalespersonTransferMode" class="tabs-wrap shadow-sm rx-flex-fixed">
|
||||||
<u-button
|
<u-tabs
|
||||||
:throttle-time="0"
|
:list="prescriptionTabsList"
|
||||||
:plain="true"
|
:is-scroll="true"
|
||||||
:type="activeCategory === category.value ? 'success' : 'default'"
|
:current="swiperCurrent"
|
||||||
v-for="(category, index) in prescriptionCategories"
|
active-color="#6ACDBB"
|
||||||
:key="category.value"
|
inactive-color="#606266"
|
||||||
:custom-style="tabsBtnStyle"
|
:bold="true"
|
||||||
@click="handleSwitchCategory(category.value)"
|
@change="onRxTabChange"
|
||||||
:hairline="true"
|
></u-tabs>
|
||||||
:hoverStayTime="0"
|
|
||||||
:style="{
|
|
||||||
display: (index === 0 && identity === 2) || identity === 1 ? 'block' : 'none',
|
|
||||||
marginLeft: '10rpx',
|
|
||||||
}">
|
|
||||||
{{ category.label }}
|
|
||||||
</u-button>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12">
|
<view v-if="!isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12 rx-flex-fixed">
|
||||||
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
|
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-if="!isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
v-if="!isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
||||||
class="special-rx-card mx-32 m-t-12"
|
class="special-rx-card mx-32 m-t-12 rx-flex-fixed"
|
||||||
>
|
>
|
||||||
<view class="special-rx-card__inner flex-row flex-ali-center">
|
<view class="special-rx-card__inner flex-row flex-ali-center">
|
||||||
<u-image
|
<u-image
|
||||||
@@ -87,17 +81,17 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center">
|
<view v-if="!isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center rx-flex-fixed">
|
||||||
<u-button size="mini" :type="feeCategory === 1 ? 'success' : 'default'" @click="feeCategory = 1">自费</u-button>
|
<u-button size="mini" :type="feeCategory === 1 ? 'success' : 'default'" @click="feeCategory = 1">自费</u-button>
|
||||||
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
|
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-if="!isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12">
|
<view v-if="!isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12 rx-flex-fixed">
|
||||||
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 诊断输入区域(传方模式不展示) -->
|
<!-- 诊断输入区域(传方模式不展示) -->
|
||||||
<view v-if="!isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32">
|
<view v-if="!isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
|
||||||
<view class="top_title flex-row flex-ali-center">
|
<view class="top_title flex-row flex-ali-center">
|
||||||
<image :src="require('@/static/image/js.png')" class="size-32"></image>
|
<image :src="require('@/static/image/js.png')" class="size-32"></image>
|
||||||
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
|
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
|
||||||
@@ -124,8 +118,19 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 滚动内容区域 -->
|
<!-- 滚动内容:swiper flex:1 吃满剩余高度(对齐订单列表 order-swiper,不算死高度) -->
|
||||||
<scroll-view :style="scrollStyle" scroll-y="true">
|
<swiper
|
||||||
|
class="rx-order-swiper"
|
||||||
|
:current="swiperCurrent"
|
||||||
|
:disable-touch="isSalespersonTransferMode || swiperCategories.length < 2"
|
||||||
|
@change="onRxSwiperChange"
|
||||||
|
>
|
||||||
|
<swiper-item
|
||||||
|
v-for="(cat, idx) in swiperCategories"
|
||||||
|
:key="cat.value"
|
||||||
|
>
|
||||||
|
<scroll-view scroll-y class="rx-order-scroll">
|
||||||
|
<block v-if="swiperCurrent === idx">
|
||||||
|
|
||||||
<!-- 在线复诊 + 中药:证候/治法/中医疾病(接口 diseases_id、method_id、syndrome_id,与 PC 在线问诊处方一致) -->
|
<!-- 在线复诊 + 中药:证候/治法/中医疾病(接口 diseases_id、method_id、syndrome_id,与 PC 在线问诊处方一致) -->
|
||||||
<view v-if="activeCategory === 1 && isOnlineRevisit" class="white p-32 radius-12 mx-32 m-t-24 flex-col">
|
<view v-if="activeCategory === 1 && isOnlineRevisit" class="white p-32 radius-12 mx-32 m-t-24 flex-col">
|
||||||
@@ -249,7 +254,7 @@
|
|||||||
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||||
<view class="flex-row flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
<view class="rp-icon">Rp</view>
|
<view class="rp-icon">Rp</view>
|
||||||
<text class="fs-28 text-gray m-l-8">中药处方</text>
|
<text class="fs-28 text-gray m-l-8">中药处方(已选 {{ currentDrugs.length }} 种)</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row" style="height: 50rpx;">
|
<view class="flex-row" style="height: 50rpx;">
|
||||||
<u-button
|
<u-button
|
||||||
@@ -280,7 +285,7 @@
|
|||||||
<view class="white radius-12 p-32" v-if="currentDrugs.length > 0">
|
<view class="white radius-12 p-32" v-if="currentDrugs.length > 0">
|
||||||
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||||
<view class="flex-col">
|
<view class="flex-col">
|
||||||
<text class="fs-28 font-bold color-title">药材清单 (共{{currentDrugs.length}}味)</text>
|
<text class="fs-28 font-bold color-title">药材清单(共{{currentDrugs.length}}种)</text>
|
||||||
<text
|
<text
|
||||||
v-if="!isSpecialPrescriptionCartLocked"
|
v-if="!isSpecialPrescriptionCartLocked"
|
||||||
class="fs-22 text-gray m-t-4"
|
class="fs-22 text-gray m-t-4"
|
||||||
@@ -328,7 +333,8 @@
|
|||||||
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||||
<view class="flex-row flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
<view class="rp-icon">Rp</view>
|
<view class="rp-icon">Rp</view>
|
||||||
<text class="fs-28 text-gray m-l-8">产品/器械</text>
|
<!-- 与当前 Tab 名一致,避免 3/5/6/7 共用模板时写死「产品/器械」 -->
|
||||||
|
<text class="fs-28 text-gray m-l-8">{{ activeCategoryLabel }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row m-l-163" style="height: 50rpx;">
|
<view class="flex-row m-l-163" style="height: 50rpx;">
|
||||||
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
<view v-if="!isSpecialPrescriptionCartLocked" class="m-l-16">
|
||||||
@@ -482,7 +488,11 @@
|
|||||||
<u-icon name="shield-check" color="#B0B6C2" size="28" class="m-r-8"></u-icon>
|
<u-icon name="shield-check" color="#B0B6C2" size="28" class="m-r-8"></u-icon>
|
||||||
<text class="fs-24 color-sub">请确认患者已在实体医院就诊,并有明确诊断</text>
|
<text class="fs-24 color-sub">请确认患者已在实体医院就诊,并有明确诊断</text>
|
||||||
</view>
|
</view>
|
||||||
|
</block>
|
||||||
|
<view v-else class="rx-swiper-placeholder"></view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
|
||||||
<!-- 底部操作栏 -->
|
<!-- 底部操作栏 -->
|
||||||
<view class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
<view class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
||||||
@@ -632,6 +642,7 @@ import {
|
|||||||
saveWestCommonPrescriptionApi,
|
saveWestCommonPrescriptionApi,
|
||||||
saveChineseCommonPrescriptionApi,
|
saveChineseCommonPrescriptionApi,
|
||||||
getCurrentStoreTypeApi,
|
getCurrentStoreTypeApi,
|
||||||
|
getPrescriptionTypeOptionsApi,
|
||||||
getPriceAdjustConfigApi,
|
getPriceAdjustConfigApi,
|
||||||
getSalespersonTransferByRegisterApi,
|
getSalespersonTransferByRegisterApi,
|
||||||
getPrescriptionInfoApi,
|
getPrescriptionInfoApi,
|
||||||
@@ -711,13 +722,19 @@ export default {
|
|||||||
pendingReusePrescriptionId: '',
|
pendingReusePrescriptionId: '',
|
||||||
patientInfo: null,
|
patientInfo: null,
|
||||||
prescriptionCategories: [
|
prescriptionCategories: [
|
||||||
{ label: '中药', value: 1 },
|
{ label: '中药', value: 1, icon: '', icon_text: '' },
|
||||||
{ label: '西(中成)药', value: 2 },
|
{ label: '西(中成)药', value: 2, icon: '', icon_text: '' },
|
||||||
{ label: '保健食品', value: 3 },
|
{ label: '保健食品', value: 3, icon: '', icon_text: '' },
|
||||||
{ label: '产品服务包', value: 5 },
|
{ label: '产品服务包', value: 5, icon: '', icon_text: '' },
|
||||||
{ label: '非药品', value: 6 },
|
{ label: '非药品', value: 6, icon: '', icon_text: '' },
|
||||||
{ label: '医疗器械', value: 7 }
|
{ label: '医疗器械', value: 7, icon: '', icon_text: '' }
|
||||||
],
|
],
|
||||||
|
/** 后端下发的默认处方类型;无本地草稿/锁定时使用 */
|
||||||
|
prescriptionTypeDefault: 2,
|
||||||
|
/** swiper / u-tabs 当前下标;默认与 activeCategory=2(西药,列表第 2 项)对齐,避免首屏 Tab/内容错位 */
|
||||||
|
swiperCurrent: 1,
|
||||||
|
/** 防止 tabs 与 swiper 互相触发形成回路(同订单列表) */
|
||||||
|
tabChanging: false,
|
||||||
activeCategory: 2,
|
activeCategory: 2,
|
||||||
currentDrugs: [],
|
currentDrugs: [],
|
||||||
diagnoses: [],
|
diagnoses: [],
|
||||||
@@ -848,6 +865,39 @@ export default {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
/** 可见处方类型:identity=2 仅中药;其余展示后端下发的全部 */
|
||||||
|
visiblePrescriptionCategories() {
|
||||||
|
const list = Array.isArray(this.prescriptionCategories) ? this.prescriptionCategories : [];
|
||||||
|
if (Number(this.identity) === 2) {
|
||||||
|
return list.filter((c) => Number(c.value) === 1);
|
||||||
|
}
|
||||||
|
return list;
|
||||||
|
},
|
||||||
|
/** swiper 用分类:传方模式仅中药一项 */
|
||||||
|
swiperCategories() {
|
||||||
|
if (this.isSalespersonTransferMode) {
|
||||||
|
return [{ label: '中药', value: 1, icon: '', icon_text: '' }];
|
||||||
|
}
|
||||||
|
return this.visiblePrescriptionCategories;
|
||||||
|
},
|
||||||
|
/** u-tabs 需要 { name };有 icon_text 时拼到 name 后便于看见角标;始终返回数组避免 list 非数组警告 */
|
||||||
|
prescriptionTabsList() {
|
||||||
|
const list = this.visiblePrescriptionCategories || [];
|
||||||
|
return list.map((c) => {
|
||||||
|
let name = c.label || '';
|
||||||
|
if (c.icon_text) name = `${name} ${c.icon_text}`;
|
||||||
|
return { name, value: c.value, icon: c.icon || '' };
|
||||||
|
}) || [];
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 当前处方类型显示名:简单产品列表等共用区块按 Tab label 展示,
|
||||||
|
* 避免写死「产品/器械」导致保健食品等类型文案不对
|
||||||
|
*/
|
||||||
|
activeCategoryLabel() {
|
||||||
|
const list = this.visiblePrescriptionCategories || this.prescriptionCategories || [];
|
||||||
|
const hit = list.find((c) => Number(c.value) === Number(this.activeCategory));
|
||||||
|
return (hit && hit.label) || '产品';
|
||||||
|
},
|
||||||
allowInsuranceCategory() {
|
allowInsuranceCategory() {
|
||||||
return Number(this.registerStoreInfo?.allow_insurance_category ?? 0) === 1;
|
return Number(this.registerStoreInfo?.allow_insurance_category ?? 0) === 1;
|
||||||
},
|
},
|
||||||
@@ -965,25 +1015,6 @@ export default {
|
|||||||
shouldEnablePageBackGuard() {
|
shouldEnablePageBackGuard() {
|
||||||
return this.hasPrescriptionOverlay;
|
return this.hasPrescriptionOverlay;
|
||||||
},
|
},
|
||||||
scrollStyle() {
|
|
||||||
try {
|
|
||||||
const sys = uni.getSystemInfoSync();
|
|
||||||
const rpxRatio = sys.windowWidth / 750;
|
|
||||||
const topRpx = this.isSalespersonTransferMode ? 500 : 600;
|
|
||||||
const bottomRpx = 200;
|
|
||||||
const heightPx = sys.windowHeight - topRpx * rpxRatio - bottomRpx * rpxRatio;
|
|
||||||
return {
|
|
||||||
height: `${Math.max(Math.floor(heightPx), 200)}px`,
|
|
||||||
marginBottom: '200rpx',
|
|
||||||
};
|
|
||||||
} catch (e) {
|
|
||||||
const topRpx = this.isSalespersonTransferMode ? 500 : 600;
|
|
||||||
return {
|
|
||||||
height: `calc(100vh - ${topRpx}rpx)`,
|
|
||||||
marginBottom: '200rpx',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onlineTcmDiseasesLabel() {
|
onlineTcmDiseasesLabel() {
|
||||||
const data = this.traditionalTcmData;
|
const data = this.traditionalTcmData;
|
||||||
const id = this.onlineTcmDiseasesId;
|
const id = this.onlineTcmDiseasesId;
|
||||||
@@ -1038,6 +1069,8 @@ export default {
|
|||||||
created() {
|
created() {
|
||||||
// u-navbar 在小程序内 bind($parent) 可能拿不到页面实例,用箭头函数固定 this
|
// u-navbar 在小程序内 bind($parent) 可能拿不到页面实例,用箭头函数固定 this
|
||||||
this.boundCustomBack = () => this.handleCustomBack();
|
this.boundCustomBack = () => this.handleCustomBack();
|
||||||
|
// 按默认 activeCategory 立刻对齐 swiper 下标,避免首屏 Tab 在中药、内容按西药渲染
|
||||||
|
this.syncSwiperCurrentFromCategory();
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
this.isSalespersonTransferMode = String(options.salesperson_transfer) === '1';
|
this.isSalespersonTransferMode = String(options.salesperson_transfer) === '1';
|
||||||
@@ -1114,11 +1147,13 @@ export default {
|
|||||||
await this.loadPatientInfo();
|
await this.loadPatientInfo();
|
||||||
await this.loadRegisterOrderType();
|
await this.loadRegisterOrderType();
|
||||||
await this.loadBasicConfigData();
|
await this.loadBasicConfigData();
|
||||||
|
await this.loadPrescriptionTypeOptions();
|
||||||
await this.loadRegisterStoreInfo();
|
await this.loadRegisterStoreInfo();
|
||||||
// 无 URL/上次切换/草稿时,按诊所类型兜底中药或西药 Tab
|
// 无 URL/上次切换/草稿时,用后端 default 兜底
|
||||||
this.applyDefaultCategoryByClinicType();
|
this.applyDefaultCategoryFromOptions();
|
||||||
}
|
}
|
||||||
this.loadCurrentCategoryDrugs();
|
this.loadCurrentCategoryDrugs();
|
||||||
|
this.syncSwiperCurrentFromCategory();
|
||||||
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
|
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
|
||||||
await this.hydrateChineseProcessRuleListsFromConfig();
|
await this.hydrateChineseProcessRuleListsFromConfig();
|
||||||
}
|
}
|
||||||
@@ -1177,6 +1212,101 @@ export default {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 拉取后端处方类型列表与默认选中;失败时保留本地兜底列表
|
||||||
|
*/
|
||||||
|
async loadPrescriptionTypeOptions() {
|
||||||
|
try {
|
||||||
|
const params = {};
|
||||||
|
if (this.registerId) params.register_id = this.registerId;
|
||||||
|
const res = await getPrescriptionTypeOptionsApi(params);
|
||||||
|
// req 已解包为业务 data:{ default, list }
|
||||||
|
const data = res || {};
|
||||||
|
const list = Array.isArray(data.list) ? data.list : [];
|
||||||
|
if (list.length) {
|
||||||
|
this.prescriptionCategories = list.map((item) => ({
|
||||||
|
value: Number(item.value),
|
||||||
|
label: item.label || '',
|
||||||
|
icon: item.icon || '',
|
||||||
|
icon_text: item.icon_text || '',
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
const def = Number(data.default);
|
||||||
|
if (def) this.prescriptionTypeDefault = def;
|
||||||
|
} catch (e) {
|
||||||
|
console.error('加载处方类型失败:', e);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
||||||
|
* 使用后端下发的 default。优先级:URL > 上次切换 > 草稿 > 接口 default
|
||||||
|
*/
|
||||||
|
applyDefaultCategoryFromOptions() {
|
||||||
|
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
||||||
|
if (!this.registerId) return;
|
||||||
|
if (PrescriptionStorage.getActiveCategory(this.registerId)) return;
|
||||||
|
if (PrescriptionStorage.hasDraftWithDrugs(this.registerId)) return;
|
||||||
|
const next = Number(this.prescriptionTypeDefault) || 0;
|
||||||
|
if (!next || next === this.activeCategory) return;
|
||||||
|
this.activeCategory = next;
|
||||||
|
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||||
|
this.focusActiveTab();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
||||||
|
* 按诊所类型设中药(1)/西药(2)。优先级:URL > 上次切换 > 草稿 > clinic_type
|
||||||
|
* @deprecated 已由 applyDefaultCategoryFromOptions 替代,保留兼容
|
||||||
|
*/
|
||||||
|
applyDefaultCategoryByClinicType() {
|
||||||
|
this.applyDefaultCategoryFromOptions();
|
||||||
|
},
|
||||||
|
/** 将当前 Tab 滚入可视区(u-tabs is-scroll 已自动聚焦,此处同步 swiper 下标) */
|
||||||
|
focusActiveTab() {
|
||||||
|
this.syncSwiperCurrentFromCategory();
|
||||||
|
},
|
||||||
|
/** 根据 activeCategory 同步 swiperCurrent */
|
||||||
|
syncSwiperCurrentFromCategory() {
|
||||||
|
const list = this.swiperCategories || [];
|
||||||
|
const idx = list.findIndex((c) => Number(c.value) === Number(this.activeCategory));
|
||||||
|
const next = idx >= 0 ? idx : 0;
|
||||||
|
if (next === this.swiperCurrent) return;
|
||||||
|
// 程序化改 current 会触发 swiper @change,用 tabChanging 挡住回路
|
||||||
|
this.tabChanging = true;
|
||||||
|
this.swiperCurrent = next;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** Tab 点击切换(与订单列表 onTabChange 一致) */
|
||||||
|
onRxTabChange(index) {
|
||||||
|
if (this.tabChanging) return;
|
||||||
|
this.tabChanging = true;
|
||||||
|
const idx = typeof index === 'object' ? Number(index.index ?? index) : Number(index);
|
||||||
|
const list = this.swiperCategories || [];
|
||||||
|
const cat = list[idx];
|
||||||
|
if (cat) {
|
||||||
|
this.swiperCurrent = idx;
|
||||||
|
this.handleSwitchCategory(cat.value, { skipFocus: true });
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
/** swiper 滑动切换 */
|
||||||
|
onRxSwiperChange(e) {
|
||||||
|
if (this.tabChanging) return;
|
||||||
|
this.tabChanging = true;
|
||||||
|
const idx = Number((e.detail && e.detail.current) || 0);
|
||||||
|
const list = this.swiperCategories || [];
|
||||||
|
const cat = list[idx];
|
||||||
|
if (cat) {
|
||||||
|
this.swiperCurrent = idx;
|
||||||
|
this.handleSwitchCategory(cat.value, { skipFocus: true });
|
||||||
|
}
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tabChanging = false;
|
||||||
|
});
|
||||||
|
},
|
||||||
restoreFromLocalStorage() {
|
restoreFromLocalStorage() {
|
||||||
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
|
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
|
||||||
this.activeCategory = this._presetActiveCategory;
|
this.activeCategory = this._presetActiveCategory;
|
||||||
@@ -1198,28 +1328,6 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
|
||||||
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
|
||||||
* 按诊所类型设中药(1)/西药(2)。优先级:URL > 上次切换 > 草稿 > clinic_type
|
|
||||||
*/
|
|
||||||
applyDefaultCategoryByClinicType() {
|
|
||||||
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
|
||||||
if (!this.registerId) return;
|
|
||||||
// 最后切换的分类优先,不再用诊所类型覆盖
|
|
||||||
if (PrescriptionStorage.getActiveCategory(this.registerId)) return;
|
|
||||||
// 任一类有草稿则沿用 restore 结果
|
|
||||||
if (PrescriptionStorage.hasDraftWithDrugs(this.registerId)) return;
|
|
||||||
const clinicType = Number(this.registerStoreInfo?.clinic_type ?? 0);
|
|
||||||
let next = 0;
|
|
||||||
if (clinicType === 2) {
|
|
||||||
next = 1; // 中医诊所 → 中药
|
|
||||||
} else if (clinicType === 1) {
|
|
||||||
next = 2; // 西医诊所 → 西(中成)药
|
|
||||||
}
|
|
||||||
if (!next || next === this.activeCategory) return;
|
|
||||||
this.activeCategory = next;
|
|
||||||
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
|
||||||
},
|
|
||||||
loadCurrentCategoryDrugs() {
|
loadCurrentCategoryDrugs() {
|
||||||
const data = PrescriptionStorage.loadPrescriptionData(this.activeCategory, this.registerId);
|
const data = PrescriptionStorage.loadPrescriptionData(this.activeCategory, this.registerId);
|
||||||
if (data) {
|
if (data) {
|
||||||
@@ -1501,10 +1609,15 @@ export default {
|
|||||||
this.$toast('加载传方失败');
|
this.$toast('加载传方失败');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async handleSwitchCategory(category) {
|
async handleSwitchCategory(category, options = {}) {
|
||||||
this.saveToLocalStorage();
|
this.saveToLocalStorage();
|
||||||
this.activeCategory = category;
|
this.activeCategory = category;
|
||||||
this.loadCurrentCategoryDrugs();
|
this.loadCurrentCategoryDrugs();
|
||||||
|
if (!options.skipFocus) {
|
||||||
|
this.focusActiveTab();
|
||||||
|
} else {
|
||||||
|
this.syncSwiperCurrentFromCategory();
|
||||||
|
}
|
||||||
if (category === 1) {
|
if (category === 1) {
|
||||||
await this.checkSalespersonTransfer();
|
await this.checkSalespersonTransfer();
|
||||||
await this.checkAndShowTransferTip();
|
await this.checkAndShowTransferTip();
|
||||||
@@ -2736,7 +2849,17 @@ export default {
|
|||||||
/* 基础与通用样式 */
|
/* 基础与通用样式 */
|
||||||
.page-bg {
|
.page-bg {
|
||||||
background-color: #F4F6F8;
|
background-color: #F4F6F8;
|
||||||
min-height: 100vh;
|
}
|
||||||
|
/* 整页纵向 flex:顶部 chrome 不收缩,swiper 吃满剩余高度 */
|
||||||
|
.page-flex {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.rx-flex-fixed {
|
||||||
|
flex-shrink: 0;
|
||||||
}
|
}
|
||||||
.safe-area-inset-bottom { padding-bottom: env(safe-area-inset-bottom); }
|
.safe-area-inset-bottom { padding-bottom: env(safe-area-inset-bottom); }
|
||||||
.mx-32 { margin-left: 32rpx; margin-right: 32rpx; }
|
.mx-32 { margin-left: 32rpx; margin-right: 32rpx; }
|
||||||
@@ -2812,9 +2935,26 @@ export default {
|
|||||||
border-radius: 6rpx 2rpx 6rpx 2rpx;
|
border-radius: 6rpx 2rpx 6rpx 2rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs {
|
.tabs-wrap {
|
||||||
background: #fff;
|
background: #fff;
|
||||||
border-bottom: 1px solid #F0F2F5;
|
border-bottom: 1px solid #F0F2F5;
|
||||||
|
position: relative;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
.rx-order-swiper {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
min-height: 0;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
/* 内部滚动区铺满 swiper;底部留白避开 fixed 操作栏 */
|
||||||
|
.rx-order-scroll {
|
||||||
|
height: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-bottom: calc(200rpx + env(safe-area-inset-bottom));
|
||||||
|
}
|
||||||
|
.rx-swiper-placeholder {
|
||||||
|
min-height: 400rpx;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 现代卡片设计 (西药 / 产品) */
|
/* 现代卡片设计 (西药 / 产品) */
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ export class PrescriptionStorage {
|
|||||||
try {
|
try {
|
||||||
const key = this.getStorageKey(category, registerId);
|
const key = this.getStorageKey(category, registerId);
|
||||||
uni.setStorageSync(key, JSON.stringify(data));
|
uni.setStorageSync(key, JSON.stringify(data));
|
||||||
console.log('保存处方数据成功:', key, data);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('保存处方数据失败:', error);
|
console.error('保存处方数据失败:', error);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user