更新
This commit is contained in:
@@ -20,7 +20,7 @@ export function orderTodayRevenue() {
|
||||
return request.get({ url: '/order.order/todayRevenue' })
|
||||
}
|
||||
|
||||
// 订单统计(-1全部已支付类型合计,0退款,1挂号费,2问诊费,3药品费用,4首付,5尾款,6其他)
|
||||
// 订单统计(-1全部已支付类型合计,0退款,1~8:含全部费用与驼奶费用等)
|
||||
export function orderStats(params?: { order_type?: number; days?: number }) {
|
||||
return request.get({ url: '/order.order/orderStats', params })
|
||||
}
|
||||
@@ -80,6 +80,11 @@ export function orderExport(params: any) {
|
||||
return request.get({ url: '/order.order/export', params }, { isReturnDefaultResponse: true })
|
||||
}
|
||||
|
||||
// 拆分订单(待支付 / 付呗待审核)
|
||||
export function orderSplit(params: { id: number; amounts: number[]; order_types: number[] }) {
|
||||
return request.post({ url: '/order.order/split', params })
|
||||
}
|
||||
|
||||
// 搜索患者(从诊单表)
|
||||
export function searchPatients(params: any) {
|
||||
return request.get({ url: '/tcm.diagnosis/searchPatient', params })
|
||||
|
||||
@@ -328,7 +328,12 @@ export function prescriptionOrderWithdraw(params: { id: number }) {
|
||||
}
|
||||
|
||||
/** 业务订单物流轨迹(快递100 + 顺丰/京东官网链接) */
|
||||
export function prescriptionOrderLogisticsTrace(params: { id: number; express_company?: string }) {
|
||||
export function prescriptionOrderLogisticsTrace(params: {
|
||||
id: number
|
||||
express_company?: string
|
||||
/** 顺丰等:收件电话(建议完整 11 位,与面单一致;至少后 4 位) */
|
||||
phone_tail?: string
|
||||
}) {
|
||||
return request.get({ url: '/tcm.prescriptionOrder/logisticsTrace', params })
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<template>
|
||||
<el-date-picker
|
||||
v-model="content"
|
||||
type="datetimerange"
|
||||
:type="pickerType"
|
||||
range-separator="-"
|
||||
start-placeholder="开始时间"
|
||||
end-placeholder="结束时间"
|
||||
value-format="YYYY-MM-DD HH:mm:ss"
|
||||
:start-placeholder="startPlaceholder"
|
||||
:end-placeholder="endPlaceholder"
|
||||
:value-format="valueFormat"
|
||||
clearable
|
||||
></el-date-picker>
|
||||
</template>
|
||||
@@ -15,10 +15,18 @@ const props = withDefaults(
|
||||
defineProps<{
|
||||
startTime?: string
|
||||
endTime?: string
|
||||
pickerType?: 'daterange' | 'datetimerange'
|
||||
valueFormat?: string
|
||||
startPlaceholder?: string
|
||||
endPlaceholder?: string
|
||||
}>(),
|
||||
{
|
||||
startTime: '',
|
||||
endTime: ''
|
||||
endTime: '',
|
||||
pickerType: 'datetimerange',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
startPlaceholder: '开始时间',
|
||||
endPlaceholder: '结束时间'
|
||||
}
|
||||
)
|
||||
const emit = defineEmits(['update:startTime', 'update:endTime'])
|
||||
|
||||
@@ -1152,6 +1152,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
<!-- 处方业务订单(zyt_tcm_prescription_order,非支付单) -->
|
||||
<template>
|
||||
<div class="prescription-order-page prescription-order-list px-4 py-4 pb-8">
|
||||
<header class="po-page-head">
|
||||
<!-- <header class="po-page-head">
|
||||
<div>
|
||||
<h1 class="po-page-title">处方业务订单</h1>
|
||||
<p class="po-page-desc">履约与双审;点「详情」可同屏查看<strong>处方单</strong>与<strong>关联支付单</strong>。</p>
|
||||
</div>
|
||||
</header>
|
||||
</header> -->
|
||||
|
||||
<el-card class="!border-none shadow-sm mb-4" shadow="never">
|
||||
<div class="flex flex-col gap-3">
|
||||
@@ -58,6 +58,38 @@
|
||||
<span class="font-medium">{{ tab.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="text-gray-500 text-sm font-medium w-20 shrink-0">供货方式</span>
|
||||
<div
|
||||
v-for="tab in supplyModeTabs"
|
||||
:key="'sm-' + String(tab.value)"
|
||||
:class="[
|
||||
'px-4 py-1.5 rounded-lg cursor-pointer transition-all duration-200 select-none text-sm',
|
||||
queryParams.supply_mode === tab.value
|
||||
? 'bg-primary text-white shadow-md'
|
||||
: 'bg-gray-50 text-gray-600 hover:bg-gray-100'
|
||||
]"
|
||||
@click="handleSupplyModeTabClick(tab.value)"
|
||||
>
|
||||
<span class="font-medium">{{ tab.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2 flex-wrap">
|
||||
<span class="text-gray-500 text-sm font-medium w-20 shrink-0">快递方式</span>
|
||||
<div
|
||||
v-for="tab in expressCompanyTabs"
|
||||
:key="'ec-' + String(tab.value)"
|
||||
:class="[
|
||||
'px-4 py-1.5 rounded-lg cursor-pointer transition-all duration-200 select-none text-sm',
|
||||
queryParams.express_company === tab.value
|
||||
? 'bg-primary text-white shadow-md'
|
||||
: 'bg-gray-50 text-gray-600 hover:bg-gray-100'
|
||||
]"
|
||||
@click="handleExpressCompanyTabClick(tab.value)"
|
||||
>
|
||||
<span class="font-medium">{{ tab.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
@@ -97,12 +129,12 @@
|
||||
<p class="relative mt-2 text-xs text-slate-500 leading-relaxed">
|
||||
{{ listStats.periodLine }}
|
||||
</p>
|
||||
<p class="relative mt-1.5 text-xs text-slate-400 leading-relaxed">
|
||||
<!-- <p class="relative mt-1.5 text-xs text-slate-400 leading-relaxed">
|
||||
{{ listStats.orderSplitHint }}
|
||||
</p>
|
||||
<p class="relative mt-0.5 text-xs text-slate-400 leading-relaxed">
|
||||
{{ listStats.scopeHint }}
|
||||
</p>
|
||||
</p> -->
|
||||
</div>
|
||||
<div
|
||||
class="po-stat-surface po-stat-surface--pay relative overflow-hidden rounded-xl border border-slate-200/90 px-5 py-4 shadow-sm"
|
||||
@@ -135,9 +167,9 @@
|
||||
<p class="relative mt-2 text-xs text-slate-500 leading-relaxed">
|
||||
{{ listStats.periodLine }}
|
||||
</p>
|
||||
<p class="relative mt-1.5 text-xs text-slate-400 leading-relaxed">
|
||||
<!-- <p class="relative mt-1.5 text-xs text-slate-400 leading-relaxed">
|
||||
业绩 = 除业务订单已取消(4)外关联支付金额;下方为合计与已取消明细
|
||||
</p>
|
||||
</p> -->
|
||||
</div>
|
||||
</div>
|
||||
<el-form class="ls-form" :model="queryParams" inline>
|
||||
@@ -145,6 +177,10 @@
|
||||
<DaterangePicker
|
||||
v-model:startTime="queryParams.start_time"
|
||||
v-model:endTime="queryParams.end_time"
|
||||
picker-type="daterange"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
class="!w-full"
|
||||
/>
|
||||
</el-form-item>
|
||||
@@ -172,6 +208,14 @@
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="w-[220px]" label="快递">
|
||||
<el-input
|
||||
v-model="queryParams.express_keyword"
|
||||
placeholder="快递单号 / 公司"
|
||||
clearable
|
||||
@keyup.enter="resetPage"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item class="w-[200px]" label="医生">
|
||||
<el-select
|
||||
v-model="queryParams.doctor_id"
|
||||
@@ -228,11 +272,63 @@
|
||||
|
||||
<el-card v-loading="pager.loading" class="!border-none mt-4 po-table-card shadow-sm" shadow="never">
|
||||
<div class="po-table-toolbar">
|
||||
<span class="po-table-title text-gray-800">订单列表</span>
|
||||
<span class="po-table-hint text-gray-400">共 {{ pager.count }} 条</span>
|
||||
<div class="po-table-toolbar__title-wrap">
|
||||
<span class="po-table-title text-gray-800">订单列表</span>
|
||||
<span class="po-table-hint text-gray-400">共 {{ pager.count }} 条</span>
|
||||
</div>
|
||||
<div class="po-focus-board">
|
||||
<div
|
||||
class="po-focus-board__item is-pending-rx"
|
||||
:class="{ 'is-active': activeFocusKey === 'pendingRx' }"
|
||||
@click="handleFocusBoardSearch('pendingRx')"
|
||||
>
|
||||
<span class="po-focus-board__label">待处方审核</span>
|
||||
<strong class="po-focus-board__value">{{ focusStats.pendingRx }}</strong>
|
||||
</div>
|
||||
<div
|
||||
class="po-focus-board__item is-pending-pay"
|
||||
:class="{ 'is-active': activeFocusKey === 'pendingPay' }"
|
||||
@click="handleFocusBoardSearch('pendingPay')"
|
||||
>
|
||||
<span class="po-focus-board__label">待支付审核</span>
|
||||
<strong class="po-focus-board__value">{{ focusStats.pendingPay }}</strong>
|
||||
</div>
|
||||
<div
|
||||
class="po-focus-board__item is-pending-ship"
|
||||
:class="{ 'is-active': activeFocusKey === 'pendingShip' }"
|
||||
@click="handleFocusBoardSearch('pendingShip')"
|
||||
>
|
||||
<span class="po-focus-board__label">待发货</span>
|
||||
<strong class="po-focus-board__value">{{ focusStats.pendingShip }}</strong>
|
||||
</div>
|
||||
<div
|
||||
class="po-focus-board__item is-risk"
|
||||
:class="{ 'is-active': activeFocusKey === 'risk' }"
|
||||
@click="handleFocusBoardSearch('risk')"
|
||||
>
|
||||
<span class="po-focus-board__label">重点异常</span>
|
||||
<strong class="po-focus-board__value">{{ focusStats.risk }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="pager.lists" size="default" stripe class="po-data-table">
|
||||
<el-table-column label="订单号" prop="order_no" min-width="150" show-overflow-tooltip />
|
||||
<el-table :data="pager.lists" size="default" stripe class="po-data-table" :row-class-name="orderRowClassName">
|
||||
<el-table-column label="订单号" min-width="178" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<div class="po-order-no-cell">
|
||||
<div class="po-order-no-main">{{ row.order_no || '—' }}</div>
|
||||
<div class="po-order-no-sub">
|
||||
<el-tag
|
||||
size="small"
|
||||
effect="plain"
|
||||
:type="String(row.gancao_reciperl_order_no || '').trim() ? 'success' : 'info'"
|
||||
>
|
||||
{{ String(row.gancao_reciperl_order_no || '').trim() ? '甘草' : '自营' }}
|
||||
</el-tag>
|
||||
<span class="text-gray-400">#{{ row.id }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="处方ID" prop="prescription_id" width="86" />
|
||||
<el-table-column label="诊单ID" prop="diagnosis_id" width="86" />
|
||||
<el-table-column label="收货人" min-width="100">
|
||||
@@ -294,23 +390,23 @@
|
||||
{{ row.internal_cost != null && row.internal_cost !== '' ? `¥${row.internal_cost}` : '—' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="处方审核" width="96">
|
||||
<el-table-column label="处方审核" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="auditTagType(row.prescription_audit_status)" size="small">
|
||||
<el-tag :type="auditTagType(row.prescription_audit_status)" size="small" :class="statusTagClass('rx', row)">
|
||||
{{ auditStatusText(row.prescription_audit_status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="支付单审核" width="102">
|
||||
<el-table-column label="支付单审核" width="110">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="auditTagType(row.payment_slip_audit_status)" size="small">
|
||||
<el-tag :type="auditTagType(row.payment_slip_audit_status)" size="small" :class="statusTagClass('pay', row)">
|
||||
{{ auditStatusText(row.payment_slip_audit_status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="履约" width="96">
|
||||
<el-table-column label="履约" width="108">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="fulfillmentTagType(row.fulfillment_status)" size="small">
|
||||
<el-tag :type="fulfillmentTagType(row.fulfillment_status)" size="small" :class="statusTagClass('fulfillment', row)">
|
||||
{{ fulfillmentText(row.fulfillment_status) }}
|
||||
</el-tag>
|
||||
</template>
|
||||
@@ -878,7 +974,19 @@
|
||||
<span class="font-medium text-[15px]">履约与收货信息</span>
|
||||
</template>
|
||||
<el-descriptions :column="3" border size="small" class="po-desc">
|
||||
<el-descriptions-item label="诊单ID">{{ detailData.diagnosis_id }}</el-descriptions-item>
|
||||
<el-descriptions-item label="诊单ID">
|
||||
<span class="font-mono">{{ detailData.diagnosis_id }}</span>
|
||||
<el-button
|
||||
v-if="Number(detailData.diagnosis_id) > 0"
|
||||
type="primary"
|
||||
link
|
||||
size="small"
|
||||
class="ml-2 align-baseline"
|
||||
@click="openDiagnosisPatientDetailFromOrder"
|
||||
>
|
||||
查看患者信息
|
||||
</el-button>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ formatTime(detailData.create_time) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="收货人">{{ detailData.recipient_name }}</el-descriptions-item>
|
||||
|
||||
@@ -968,9 +1076,17 @@
|
||||
<span class="font-medium text-[15px]">物流轨迹</span>
|
||||
</template>
|
||||
<p class="po-logistics-tip text-gray-400 text-xs mb-3 font-normal">
|
||||
优先读库,无记录时走快递100;顺丰建议手机后四位正确(已自动带入收货手机)。
|
||||
优先读库,无记录时走快递100。顺丰会校验单号+电话(快递100要求完整手机号更易通过);默认预填订单收货手机,若仍提示验证码错误请改成与顺丰面单一致的号码。
|
||||
</p>
|
||||
<div class="flex flex-wrap items-center gap-3 mb-4">
|
||||
<el-input
|
||||
v-model="logisticsTracePhoneTail"
|
||||
clearable
|
||||
maxlength="20"
|
||||
placeholder="收件手机(建议11位,与面单一致)"
|
||||
class="w-[180px]"
|
||||
@keyup.enter="fetchLogisticsTrace"
|
||||
/>
|
||||
<el-select v-model="detailLogisticsExpress" placeholder="承运商" style="width: 140px">
|
||||
<el-option label="自动识别" value="auto" />
|
||||
<el-option label="顺丰速运" value="sf" />
|
||||
@@ -1380,6 +1496,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
@@ -1654,6 +1771,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="支付金额" prop="pay_amount">
|
||||
@@ -2132,11 +2250,14 @@
|
||||
</el-card>
|
||||
</div>
|
||||
</el-drawer>
|
||||
|
||||
<!-- 与 consumer/prescription/index、诊单 edit 同一套界面:只读诊单与全部分页签 -->
|
||||
<TcmDiagnosisEditView ref="diagnosisViewRef" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup name="prescriptionOrderList">
|
||||
import { computed, onMounted, reactive, ref, nextTick, watch } from 'vue'
|
||||
import { computed, onMounted, reactive, ref, nextTick, watch, defineAsyncComponent } from 'vue'
|
||||
import { Refresh, Loading, ArrowDown, InfoFilled, Search, Calendar, Document, Link as LinkIcon, Wallet } from '@element-plus/icons-vue'
|
||||
import DaterangePicker from '@/components/daterange-picker/index.vue'
|
||||
import {
|
||||
@@ -2172,8 +2293,24 @@ import { ElMessageBox } from 'element-plus'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
|
||||
const TcmDiagnosisEditView = defineAsyncComponent(() => import('@/views/tcm/diagnosis/edit.vue'))
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const diagnosisViewRef = ref<{
|
||||
openViewOnly: (id: number) => Promise<void>
|
||||
} | null>(null)
|
||||
|
||||
/** 打开关联诊单抽屉(仅查看,与 tcm/diagnosis/edit openViewOnly 一致) */
|
||||
function openDiagnosisPatientDetailFromOrder() {
|
||||
const id = Number(detailData.value?.diagnosis_id)
|
||||
if (!id) {
|
||||
feedback.msgWarning('本订单未关联诊单,无法查看患者诊单信息')
|
||||
return
|
||||
}
|
||||
void diagnosisViewRef.value?.openViewOnly(id)
|
||||
}
|
||||
|
||||
/** 诊间医助角色(与 DiagnosisLists 等一致) */
|
||||
const TCM_ASSISTANT_ROLE_ID = 2
|
||||
/** 与 server/config/project.php prescription_audit_roles 默认一致,可处方审核的角色 */
|
||||
@@ -2358,10 +2495,35 @@ const fulfillmentStatusTabs = [
|
||||
]
|
||||
|
||||
function handleFulfillmentStatusTabClick(value: number | '') {
|
||||
activeFocusKey.value = ''
|
||||
queryParams.fulfillment_status = value
|
||||
resetPage()
|
||||
}
|
||||
|
||||
const activeFocusKey = ref<'' | 'pendingRx' | 'pendingPay' | 'pendingShip' | 'risk'>('')
|
||||
|
||||
const supplyModeTabs = [
|
||||
{ label: '全部', value: '' as '' | 'gancao' | 'self' },
|
||||
{ label: '甘草', value: 'gancao' as const },
|
||||
{ label: '自营', value: 'self' as const }
|
||||
]
|
||||
|
||||
function handleSupplyModeTabClick(value: '' | 'gancao' | 'self') {
|
||||
queryParams.supply_mode = value
|
||||
resetPage()
|
||||
}
|
||||
|
||||
const expressCompanyTabs = [
|
||||
{ label: '全部', value: '' as '' | 'sf' | 'jd' },
|
||||
{ label: '顺丰', value: 'sf' as const },
|
||||
{ label: '京东', value: 'jd' as const }
|
||||
]
|
||||
|
||||
function handleExpressCompanyTabClick(value: '' | 'sf' | 'jd') {
|
||||
queryParams.express_company = value
|
||||
resetPage()
|
||||
}
|
||||
|
||||
const queryParams = reactive({
|
||||
/** 创建时间区间(与列表筛选一致;不影响「今日」统计) */
|
||||
start_time: '' as string,
|
||||
@@ -2370,6 +2532,10 @@ const queryParams = reactive({
|
||||
prescription_id: '' as string | number,
|
||||
/** 诊单患者姓名/手机号(后台关联 diagnosis) */
|
||||
patient_keyword: '',
|
||||
/** 快递检索:快递单号 / 快递公司 */
|
||||
express_keyword: '',
|
||||
/** 快递方式快捷筛选:顺丰/京东 */
|
||||
express_company: '' as '' | 'sf' | 'jd',
|
||||
/** 开方医生(后台:关联处方 creator_id) */
|
||||
doctor_id: '' as number | '',
|
||||
/** 诊单医助(后台:关联诊单 assistant_id) */
|
||||
@@ -2378,7 +2544,9 @@ const queryParams = reactive({
|
||||
assistant_dept_id: '' as number | '',
|
||||
fulfillment_status: '' as number | '',
|
||||
prescription_audit_status: '' as number | '',
|
||||
payment_slip_audit_status: '' as number | ''
|
||||
payment_slip_audit_status: '' as number | '',
|
||||
/** 供货方式:甘草(已传甘草药方单号)/ 自营(无甘草单号) */
|
||||
supply_mode: '' as '' | 'gancao' | 'self'
|
||||
})
|
||||
|
||||
const rxAuditTabs = [
|
||||
@@ -2395,14 +2563,47 @@ const payAuditTabs = [
|
||||
]
|
||||
|
||||
function handleRxAuditTabClick(val: number | '') {
|
||||
activeFocusKey.value = ''
|
||||
queryParams.prescription_audit_status = val
|
||||
resetPage()
|
||||
}
|
||||
function handlePayAuditTabClick(val: number | '') {
|
||||
activeFocusKey.value = ''
|
||||
queryParams.payment_slip_audit_status = val
|
||||
resetPage()
|
||||
}
|
||||
|
||||
function handleFocusBoardSearch(key: 'pendingRx' | 'pendingPay' | 'pendingShip' | 'risk') {
|
||||
// 再点同一卡片:取消该快捷筛选
|
||||
if (activeFocusKey.value === key) {
|
||||
activeFocusKey.value = ''
|
||||
queryParams.prescription_audit_status = ''
|
||||
queryParams.payment_slip_audit_status = ''
|
||||
queryParams.fulfillment_status = ''
|
||||
resetPage()
|
||||
return
|
||||
}
|
||||
|
||||
activeFocusKey.value = key
|
||||
// 快捷筛选统一先清掉三类状态条件,再设置目标条件
|
||||
queryParams.prescription_audit_status = ''
|
||||
queryParams.payment_slip_audit_status = ''
|
||||
queryParams.fulfillment_status = ''
|
||||
|
||||
if (key === 'pendingRx') {
|
||||
queryParams.prescription_audit_status = 0
|
||||
} else if (key === 'pendingPay') {
|
||||
queryParams.prescription_audit_status = 1
|
||||
queryParams.payment_slip_audit_status = 0
|
||||
} else if (key === 'pendingShip') {
|
||||
queryParams.fulfillment_status = 2
|
||||
} else if (key === 'risk') {
|
||||
// 重点异常优先按“审核驳回”检索
|
||||
queryParams.payment_slip_audit_status = 2
|
||||
}
|
||||
resetPage()
|
||||
}
|
||||
|
||||
async function fetchLists(params: Record<string, unknown>) {
|
||||
const p: Record<string, unknown> = { ...params }
|
||||
if (p.prescription_id === '' || p.prescription_id === undefined) {
|
||||
@@ -2419,6 +2620,9 @@ async function fetchLists(params: Record<string, unknown>) {
|
||||
if (p.payment_slip_audit_status === '' || p.payment_slip_audit_status === undefined) {
|
||||
delete p.payment_slip_audit_status
|
||||
}
|
||||
if (!p.supply_mode || p.supply_mode === '') {
|
||||
delete p.supply_mode
|
||||
}
|
||||
if (p.doctor_id === '' || p.doctor_id === undefined || p.doctor_id === null) {
|
||||
delete p.doctor_id
|
||||
} else {
|
||||
@@ -2436,9 +2640,19 @@ async function fetchLists(params: Record<string, unknown>) {
|
||||
}
|
||||
if (!p.order_no) delete p.order_no
|
||||
if (!String(p.patient_keyword || '').trim()) delete p.patient_keyword
|
||||
if (!String(p.express_keyword || '').trim()) delete p.express_keyword
|
||||
if (!p.express_company || p.express_company === '') delete p.express_company
|
||||
if (!String(p.start_time || '').trim() || !String(p.end_time || '').trim()) {
|
||||
delete p.start_time
|
||||
delete p.end_time
|
||||
} else {
|
||||
// 当前页面按“日”筛选:请求接口时展开为整天区间,避免结束日只落在 00:00:00。
|
||||
const st = String(p.start_time || '').trim()
|
||||
const et = String(p.end_time || '').trim()
|
||||
if (/^\d{4}-\d{2}-\d{2}$/.test(st) && /^\d{4}-\d{2}-\d{2}$/.test(et)) {
|
||||
p.start_time = `${st} 00:00:00`
|
||||
p.end_time = `${et} 23:59:59`
|
||||
}
|
||||
}
|
||||
return prescriptionOrderLists(p)
|
||||
}
|
||||
@@ -2541,6 +2755,44 @@ const listStats = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const focusStats = computed(() => {
|
||||
const ex = pager.extend as Record<string, unknown> | undefined
|
||||
const n = (v: unknown) => {
|
||||
const x = Number(v)
|
||||
if (!Number.isFinite(x) || x < 0) return 0
|
||||
return Math.trunc(x)
|
||||
}
|
||||
const pendingRx = n(ex?.focus_pending_rx_count)
|
||||
const pendingPay = n(ex?.focus_pending_pay_count)
|
||||
const pendingShip = n(ex?.focus_pending_ship_count)
|
||||
const risk = n(ex?.focus_risk_count)
|
||||
return { pendingRx, pendingPay, pendingShip, risk }
|
||||
})
|
||||
|
||||
function statusTagClass(type: 'rx' | 'pay' | 'fulfillment', row: any) {
|
||||
const rx = Number(row?.prescription_audit_status)
|
||||
const pay = Number(row?.payment_slip_audit_status)
|
||||
const fs = Number(row?.fulfillment_status)
|
||||
if (type === 'rx' && rx === 0) return 'po-status-tag is-focus'
|
||||
if (type === 'pay' && rx === 1 && pay === 0) return 'po-status-tag is-focus'
|
||||
if (type === 'fulfillment' && fs === 2) return 'po-status-tag is-focus'
|
||||
if ((type === 'rx' && rx === 2) || (type === 'pay' && pay === 2) || (type === 'fulfillment' && fs === 4)) {
|
||||
return 'po-status-tag is-risk'
|
||||
}
|
||||
return 'po-status-tag'
|
||||
}
|
||||
|
||||
function orderRowClassName({ row }: { row: any }) {
|
||||
const rx = Number(row?.prescription_audit_status)
|
||||
const pay = Number(row?.payment_slip_audit_status)
|
||||
const fs = Number(row?.fulfillment_status)
|
||||
const phoneMismatch = recipientVsPrescriptionPhoneMismatch(row?.recipient_phone, row?.prescription_phone)
|
||||
if (rx === 2 || pay === 2 || phoneMismatch) return 'po-row-risk'
|
||||
if (rx === 0 || (rx === 1 && pay === 0) || fs === 2) return 'po-row-focus'
|
||||
if (fs === 3 || fs === 6) return 'po-row-done'
|
||||
return ''
|
||||
}
|
||||
|
||||
watch(
|
||||
showPrescriptionAuditFilter,
|
||||
(show) => {
|
||||
@@ -2553,17 +2805,21 @@ watch(
|
||||
)
|
||||
|
||||
function handleReset() {
|
||||
activeFocusKey.value = ''
|
||||
queryParams.start_time = ''
|
||||
queryParams.end_time = ''
|
||||
queryParams.order_no = ''
|
||||
queryParams.prescription_id = ''
|
||||
queryParams.patient_keyword = ''
|
||||
queryParams.express_keyword = ''
|
||||
queryParams.express_company = ''
|
||||
queryParams.doctor_id = ''
|
||||
queryParams.assistant_id = ''
|
||||
queryParams.assistant_dept_id = ''
|
||||
queryParams.fulfillment_status = ''
|
||||
queryParams.prescription_audit_status = ''
|
||||
queryParams.payment_slip_audit_status = ''
|
||||
queryParams.supply_mode = ''
|
||||
resetParams()
|
||||
}
|
||||
|
||||
@@ -2575,7 +2831,8 @@ function feeTypeText(t: number | undefined) {
|
||||
4: '首付费用',
|
||||
5: '尾款费用',
|
||||
6: '其他费用',
|
||||
7: '全部费用'
|
||||
7: '全部费用',
|
||||
8: '驼奶费用'
|
||||
}
|
||||
return m[Number(t)] ?? '—'
|
||||
}
|
||||
@@ -2974,6 +3231,8 @@ function formatOrderTime(v: unknown) {
|
||||
const detailLogisticsExpress = ref<string>('auto')
|
||||
const logisticsTraceLoading = ref(false)
|
||||
const logisticsTracePayload = ref<Record<string, any> | null>(null)
|
||||
/** 顺丰/快递100:与运单一致的收件手机后四位(可覆盖订单收货手机) */
|
||||
const logisticsTracePhoneTail = ref('')
|
||||
|
||||
const detailLogs = ref<any[]>([])
|
||||
|
||||
@@ -3048,10 +3307,15 @@ async function fetchLogisticsTrace() {
|
||||
if (!id) return
|
||||
logisticsTraceLoading.value = true
|
||||
try {
|
||||
const res: any = await prescriptionOrderLogisticsTrace({
|
||||
const digits = String(logisticsTracePhoneTail.value || '').replace(/\D/g, '')
|
||||
const params: { id: number; express_company?: string; phone_tail?: string } = {
|
||||
id,
|
||||
express_company: detailLogisticsExpress.value
|
||||
})
|
||||
}
|
||||
if (digits.length >= 4) {
|
||||
params.phone_tail = digits
|
||||
}
|
||||
const res: any = await prescriptionOrderLogisticsTrace(params)
|
||||
logisticsTracePayload.value = (res?.data ?? res) as Record<string, any>
|
||||
} catch {
|
||||
logisticsTracePayload.value = null
|
||||
@@ -3135,6 +3399,7 @@ async function openDetail(id: number) {
|
||||
detailUnlinkedPayOrders.value = []
|
||||
logisticsTracePayload.value = null
|
||||
detailLogisticsExpress.value = 'auto'
|
||||
logisticsTracePhoneTail.value = ''
|
||||
detailLogs.value = []
|
||||
|
||||
detailVisible.value = true
|
||||
@@ -3145,6 +3410,8 @@ async function openDetail(id: number) {
|
||||
detailData.value = d
|
||||
if (d) {
|
||||
detailLogisticsExpress.value = String(d.express_company || 'auto') || 'auto'
|
||||
const dig = String(d.recipient_phone || '').replace(/\D/g, '')
|
||||
logisticsTracePhoneTail.value = dig.length >= 4 ? dig : ''
|
||||
if (String(d.tracking_number || '').trim()) {
|
||||
fetchLogisticsTrace()
|
||||
}
|
||||
@@ -4605,21 +4872,142 @@ async function downloadPrescriptionSlipPdf() {
|
||||
|
||||
.po-table-toolbar {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 14px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
.po-table-toolbar__title-wrap {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.po-table-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.po-focus-board {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(106px, 1fr));
|
||||
gap: 8px;
|
||||
width: min(100%, 520px);
|
||||
}
|
||||
|
||||
.po-focus-board__item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--el-border-color-lighter);
|
||||
background: #fff;
|
||||
padding: 7px 10px;
|
||||
cursor: pointer;
|
||||
transition: all 0.18s ease;
|
||||
}
|
||||
|
||||
.po-focus-board__item:hover {
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 8px 20px -14px rgba(15, 23, 42, 0.38);
|
||||
}
|
||||
|
||||
.po-focus-board__item.is-active {
|
||||
box-shadow: 0 0 0 2px color-mix(in srgb, var(--el-color-primary) 24%, transparent) inset;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.po-focus-board__label {
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-secondary);
|
||||
}
|
||||
|
||||
.po-focus-board__value {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
color: var(--el-text-color-primary);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.po-focus-board__item.is-pending-rx {
|
||||
border-color: color-mix(in srgb, var(--el-color-warning) 34%, #fff);
|
||||
background: color-mix(in srgb, var(--el-color-warning-light-9) 66%, #fff);
|
||||
}
|
||||
|
||||
.po-focus-board__item.is-pending-pay {
|
||||
border-color: color-mix(in srgb, #2563eb 30%, #fff);
|
||||
background: color-mix(in srgb, #dbeafe 62%, #fff);
|
||||
}
|
||||
|
||||
.po-focus-board__item.is-pending-ship {
|
||||
border-color: color-mix(in srgb, var(--el-color-primary) 30%, #fff);
|
||||
background: color-mix(in srgb, var(--el-color-primary-light-9) 64%, #fff);
|
||||
}
|
||||
|
||||
.po-focus-board__item.is-risk {
|
||||
border-color: color-mix(in srgb, var(--el-color-danger) 34%, #fff);
|
||||
background: color-mix(in srgb, var(--el-color-danger-light-9) 66%, #fff);
|
||||
}
|
||||
|
||||
@media (max-width: 1024px) {
|
||||
.po-focus-board {
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.po-data-table :deep(th.el-table__cell) {
|
||||
font-weight: 600;
|
||||
background: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.po-order-no-cell {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.po-order-no-main {
|
||||
font-weight: 600;
|
||||
color: var(--el-text-color-primary);
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.po-order-no-sub {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.po-status-tag {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.po-status-tag.is-focus {
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--el-color-warning) 24%, transparent) inset;
|
||||
}
|
||||
|
||||
.po-status-tag.is-risk {
|
||||
box-shadow: 0 0 0 1px color-mix(in srgb, var(--el-color-danger) 30%, transparent) inset;
|
||||
}
|
||||
|
||||
.po-data-table :deep(.el-table__row.po-row-focus > td.el-table__cell) {
|
||||
background: color-mix(in srgb, var(--el-color-warning-light-9) 38%, #fff) !important;
|
||||
}
|
||||
|
||||
.po-data-table :deep(.el-table__row.po-row-risk > td.el-table__cell) {
|
||||
background: color-mix(in srgb, var(--el-color-danger-light-9) 42%, #fff) !important;
|
||||
}
|
||||
|
||||
.po-data-table :deep(.el-table__row.po-row-done > td.el-table__cell) {
|
||||
background: color-mix(in srgb, var(--el-color-success-light-9) 24%, #fff) !important;
|
||||
}
|
||||
|
||||
/* 改版 Drawer 样式 */
|
||||
.po-detail-drawer :deep(.el-drawer__header) {
|
||||
margin-bottom: 0;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -49,6 +49,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -199,7 +200,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" width="280" fixed="right">
|
||||
<el-table-column label="操作" width="340" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
v-perms="['order.order/detail']"
|
||||
@@ -217,6 +218,15 @@
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="row.status === 1 || row.status === 2 || (row.status === 5 && row.payment_method === 'fubei')"
|
||||
v-perms="['order.order/split']"
|
||||
type="primary"
|
||||
link
|
||||
@click="openSplitOrder(row)"
|
||||
>
|
||||
拆分
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-perms="['order.order/wxQrcode']"
|
||||
@@ -542,6 +552,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
@@ -640,6 +651,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
@@ -648,6 +660,67 @@
|
||||
<el-button type="primary" :loading="editOrderLoading" @click="submitEditOrder">保存</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<el-dialog v-model="splitDialogVisible" title="拆分订单" width="620px" destroy-on-close @closed="splitSource = null">
|
||||
<template v-if="splitSource">
|
||||
<p class="text-sm text-gray-600 mb-3">
|
||||
原订单号 <span class="font-mono">{{ splitSource.order_no }}</span>,金额
|
||||
<span class="text-red-500 font-semibold">¥{{ splitSource.amount }}</span
|
||||
>。每笔子单可分别选择<strong>订单类型</strong>;子单继承患者、创建人及企微收款信息;<strong>已支付</strong>子单继承支付方式、支付时间与交易号;原单将软删除;若已关联业务订单,关联会同步到全部子单。
|
||||
</p>
|
||||
<el-form label-width="88px" @submit.prevent>
|
||||
<el-form-item label="拆分管数">
|
||||
<el-input-number
|
||||
v-model="splitPartCount"
|
||||
:min="2"
|
||||
:max="20"
|
||||
:step="1"
|
||||
step-strictly
|
||||
@change="redistributeSplitAmounts"
|
||||
/>
|
||||
<span class="text-xs text-gray-400 ml-2">2~20 笔</span>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="space-y-2 mb-3 max-h-[320px] overflow-y-auto pr-1">
|
||||
<div v-for="(_amt, idx) in splitAmounts" :key="idx" class="flex items-center gap-2 flex-wrap">
|
||||
<span class="text-sm text-gray-500 w-14 shrink-0">第 {{ idx + 1 }} 笔</span>
|
||||
<el-select v-model="splitOrderTypes[idx]" placeholder="订单类型" class="w-[136px] shrink-0">
|
||||
<el-option label="挂号费" :value="1" />
|
||||
<el-option label="问诊费" :value="2" />
|
||||
<el-option label="药品费用" :value="3" />
|
||||
<el-option label="首付费用" :value="4" />
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
<el-input-number
|
||||
v-model="splitAmounts[idx]"
|
||||
:min="0.01"
|
||||
:step="0.01"
|
||||
:precision="2"
|
||||
class="flex-1 min-w-[140px]"
|
||||
controls-position="right"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<el-alert
|
||||
:type="splitSumMatches ? 'success' : 'warning'"
|
||||
:closable="false"
|
||||
show-icon
|
||||
>
|
||||
<template #title>
|
||||
子单合计 ¥{{ splitSum.toFixed(2) }},
|
||||
{{ splitSumMatches ? '与原价一致,可提交' : '须与原价完全一致方可拆分' }}
|
||||
</template>
|
||||
</el-alert>
|
||||
</template>
|
||||
<template #footer>
|
||||
<el-button @click="splitDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="splitLoading" :disabled="!splitSumMatches" @click="submitSplitOrder">
|
||||
确定拆分
|
||||
</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
<!-- 小程序码弹窗 -->
|
||||
<el-dialog
|
||||
v-model="qrcodeDialogVisible"
|
||||
@@ -705,7 +778,8 @@ import {
|
||||
wechatPay,
|
||||
orderActionLogs,
|
||||
orderActionLogStats,
|
||||
orderBatchAssignAssistant
|
||||
orderBatchAssignAssistant,
|
||||
orderSplit
|
||||
} from '@/api/order'
|
||||
import { getAssistants } from '@/api/tcm'
|
||||
import { generateOrderQrcode } from '@/api/tcm'
|
||||
@@ -1109,8 +1183,9 @@ const getOrderTypeText = (type: number) => {
|
||||
3: '药品费用',
|
||||
4: '首付费用',
|
||||
5: '尾款费用',
|
||||
6:'其他费用',
|
||||
7:'全部费用',
|
||||
6: '其他费用',
|
||||
7: '全部费用',
|
||||
8: '驼奶费用',
|
||||
}
|
||||
return typeMap[type] || '未知'
|
||||
}
|
||||
@@ -1210,6 +1285,68 @@ const submitEditOrder = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
/** 拆分订单(待支付 / 已支付 / 付呗待审核) */
|
||||
const splitDialogVisible = ref(false)
|
||||
const splitSource = ref<any>(null)
|
||||
const splitPartCount = ref(2)
|
||||
const splitAmounts = ref<number[]>([])
|
||||
const splitOrderTypes = ref<number[]>([])
|
||||
const splitLoading = ref(false)
|
||||
|
||||
const redistributeSplitAmounts = () => {
|
||||
if (!splitSource.value) return
|
||||
const total = Number(splitSource.value.amount)
|
||||
const n = Math.max(2, Math.min(20, Math.floor(Number(splitPartCount.value) || 2)))
|
||||
splitPartCount.value = n
|
||||
const cents = Math.round(total * 100)
|
||||
const base = Math.floor(cents / n)
|
||||
const rem = cents % n
|
||||
const next: number[] = []
|
||||
for (let i = 0; i < n; i++) {
|
||||
const c = base + (i < rem ? 1 : 0)
|
||||
next.push(c / 100)
|
||||
}
|
||||
const prevTypes = [...splitOrderTypes.value]
|
||||
const defaultType = Number(splitSource.value.order_type ?? 6)
|
||||
splitOrderTypes.value = next.map((_, i) => (i < prevTypes.length ? prevTypes[i]! : defaultType))
|
||||
splitAmounts.value = next
|
||||
}
|
||||
|
||||
const openSplitOrder = (row: any) => {
|
||||
splitSource.value = row
|
||||
splitPartCount.value = 2
|
||||
redistributeSplitAmounts()
|
||||
splitDialogVisible.value = true
|
||||
}
|
||||
|
||||
const splitSum = computed(() => splitAmounts.value.reduce((s, x) => s + Number(x || 0), 0))
|
||||
|
||||
const splitSumMatches = computed(() => {
|
||||
if (!splitSource.value) return false
|
||||
const orig = Number(splitSource.value.amount)
|
||||
return Math.abs(splitSum.value - orig) < 0.0001
|
||||
})
|
||||
|
||||
const submitSplitOrder = async () => {
|
||||
if (!splitSource.value || !splitSumMatches.value) return
|
||||
splitLoading.value = true
|
||||
try {
|
||||
await orderSplit({
|
||||
id: splitSource.value.id,
|
||||
amounts: splitAmounts.value.map((x) => Number(x)),
|
||||
order_types: splitOrderTypes.value.map((x) => Number(x))
|
||||
})
|
||||
feedback.msgSuccess('拆分成功')
|
||||
splitDialogVisible.value = false
|
||||
getLists()
|
||||
fetchTodayRevenue()
|
||||
} catch (e: any) {
|
||||
if (e?.msg) feedback.msgError(e.msg)
|
||||
} finally {
|
||||
splitLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const handleDetail = async (row: any) => {
|
||||
try {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="blood-record-list">
|
||||
<div class="mb-4">
|
||||
<el-button type="primary" @click="handleAdd">添加记录</el-button>
|
||||
<el-button v-if="!readOnly" type="primary" @click="handleAdd">添加记录</el-button>
|
||||
<el-select v-model="trendDays" @change="handleTrendDaysChange" class="ml-2" style="width: 150px">
|
||||
<el-option label="最近7天" :value="7" />
|
||||
<el-option label="最近30天" :value="30" />
|
||||
@@ -62,7 +62,7 @@
|
||||
<el-table-column prop="western_medicine" label="西药" show-overflow-tooltip />
|
||||
<el-table-column prop="insulin" label="胰岛素" show-overflow-tooltip />
|
||||
<el-table-column prop="remark" label="备注" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<el-table-column v-if="!readOnly" label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||
@@ -185,6 +185,10 @@ const props = defineProps({
|
||||
patientId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="case-record-list">
|
||||
<div class="mb-3 flex justify-end">
|
||||
<div v-if="!readOnly" class="mb-3 flex justify-end">
|
||||
<el-button type="primary" size="small" @click="handleOpenPrescription">开方</el-button>
|
||||
</div>
|
||||
<el-table :data="caseList" border v-loading="loading">
|
||||
@@ -46,6 +46,11 @@ const props = defineProps({
|
||||
diagnosisId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
/** 只读:隐藏开方(与诊单 openViewOnly 一致) */
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="diet-record-list">
|
||||
<div class="mb-4">
|
||||
<div v-if="!readOnly" class="mb-4">
|
||||
<el-button type="primary" @click="handleAdd">添加记录</el-button>
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="note" label="备注" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<el-table-column v-if="!readOnly" label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||
@@ -125,6 +125,10 @@ const props = defineProps({
|
||||
patientId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="exercise-record-list">
|
||||
<div class="mb-4">
|
||||
<div v-if="!readOnly" class="mb-4">
|
||||
<el-button type="primary" @click="handleAdd">添加记录</el-button>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="note" label="备注" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<el-table-column v-if="!readOnly" label="操作" width="150" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button link type="primary" @click="handleEdit(row)">编辑</el-button>
|
||||
<el-button link type="danger" @click="handleDelete(row)">删除</el-button>
|
||||
@@ -99,6 +99,10 @@ const props = defineProps({
|
||||
patientId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
readOnly: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -2,14 +2,57 @@
|
||||
<div class="edit-drawer">
|
||||
<el-drawer
|
||||
v-model="visible"
|
||||
:title="drawerTitle"
|
||||
size="60%"
|
||||
:before-close="handleClose"
|
||||
:z-index="viewOnly ? 4000 : 1500"
|
||||
:modal="true"
|
||||
class="diagnosis-drawer"
|
||||
class="diagnosis-drawer tcm-diagnosis-drawer"
|
||||
>
|
||||
<el-tabs v-model="activeTab" class="diagnosis-tabs">
|
||||
<template #header>
|
||||
<div class="tcm-dd-header">
|
||||
<div class="tcm-dd-header__row">
|
||||
<h2 class="tcm-dd-header__title">{{ drawerTitle }}</h2>
|
||||
<el-tag
|
||||
v-if="viewOnly"
|
||||
type="info"
|
||||
effect="plain"
|
||||
round
|
||||
size="small"
|
||||
class="tcm-dd-header__badge"
|
||||
>
|
||||
只读
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else-if="mode === 'add'"
|
||||
type="success"
|
||||
effect="plain"
|
||||
round
|
||||
size="small"
|
||||
class="tcm-dd-header__badge"
|
||||
>
|
||||
新建
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-else
|
||||
type="warning"
|
||||
effect="plain"
|
||||
round
|
||||
size="small"
|
||||
class="tcm-dd-header__badge"
|
||||
>
|
||||
编辑
|
||||
</el-tag>
|
||||
</div>
|
||||
<p v-if="formData.patient_name || formData.id" class="tcm-dd-header__sub">
|
||||
<span v-if="formData.patient_name" class="tcm-dd-header__name">{{ formData.patient_name }}</span>
|
||||
<span v-if="formData.id" class="tcm-dd-header__id">
|
||||
<span v-if="formData.patient_name" class="tcm-dd-header__sep">·</span>
|
||||
诊单 {{ formData.patient_id || formData.id }}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</template>
|
||||
<el-tabs v-model="activeTab" class="diagnosis-tabs tcm-diagnosis-tabs">
|
||||
<!-- 基本信息标签页 -->
|
||||
<el-tab-pane label="病历" name="basic">
|
||||
<el-form
|
||||
@@ -499,8 +542,8 @@
|
||||
|
||||
<el-form-item label="病史补充" prop="remark">
|
||||
<el-input
|
||||
v-mode="formData.remark"
|
||||
type="ltextarea"
|
||||
v-model="formData.remark"
|
||||
type="textarea"
|
||||
:rows="2"
|
||||
placeholder="请输入病史补充"
|
||||
/>
|
||||
@@ -513,6 +556,7 @@
|
||||
<NoteTimeline
|
||||
:notes="diagNotes"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:readonly="viewOnly"
|
||||
@refresh="fetchDiagNotes"
|
||||
/>
|
||||
</div>
|
||||
@@ -524,6 +568,7 @@
|
||||
v-if="formData.id"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:patient-id="Number(formData.patient_id)"
|
||||
:read-only="viewOnly"
|
||||
/>
|
||||
<el-empty v-else description="请先保存诊单后再添加血糖血压记录" />
|
||||
</el-tab-pane>
|
||||
@@ -534,6 +579,7 @@
|
||||
v-if="formData.id"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:patient-id="Number(formData.patient_id)"
|
||||
:read-only="viewOnly"
|
||||
/>
|
||||
<el-empty v-else description="请先保存诊单后再添加饮食记录" />
|
||||
</el-tab-pane>
|
||||
@@ -544,6 +590,7 @@
|
||||
v-if="formData.id"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:patient-id="Number(formData.patient_id)"
|
||||
:read-only="viewOnly"
|
||||
/>
|
||||
<el-empty v-else description="请先保存诊单后再添加运动打卡记录" />
|
||||
</el-tab-pane>
|
||||
@@ -555,6 +602,7 @@
|
||||
<case-record-list
|
||||
ref="caseRecordListRef"
|
||||
:diagnosis-id="Number(formData.id)"
|
||||
:read-only="viewOnly"
|
||||
@view="handleViewCase"
|
||||
@openPrescription="handleOpenPrescription"
|
||||
/>
|
||||
@@ -625,15 +673,18 @@
|
||||
<tcm-prescription ref="prescriptionRef" @success="caseRecordListRef?.refresh?.()" />
|
||||
|
||||
<template #footer>
|
||||
<div class="flex justify-end gap-3 px-4 pb-4">
|
||||
<el-button @click="handleClose">{{ viewOnly ? '关闭' : '取消' }}</el-button>
|
||||
<div class="tcm-dd-footer">
|
||||
<el-button class="tcm-dd-footer__btn" @click="handleClose">
|
||||
{{ viewOnly ? '关闭' : '取消' }}
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="!viewOnly && activeTab === 'basic'"
|
||||
class="tcm-dd-footer__btn"
|
||||
type="primary"
|
||||
@click="handleSubmit"
|
||||
:loading="submitting"
|
||||
>
|
||||
确定
|
||||
保存病历
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
@@ -1139,6 +1190,7 @@ const handleViewCase = (row: any) => {
|
||||
|
||||
// 开方:传入当前诊单数据,包含详细病历(深拷贝避免响应式引用)
|
||||
const handleOpenPrescription = () => {
|
||||
if (viewOnly.value) return
|
||||
const data = {
|
||||
id: formData.value.id,
|
||||
diagnosis_id: formData.value.id,
|
||||
@@ -1245,14 +1297,24 @@ defineExpose({
|
||||
}
|
||||
|
||||
:deep(.el-divider) {
|
||||
margin: 24px 0 20px;
|
||||
|
||||
margin: 28px 0 22px;
|
||||
border-color: transparent;
|
||||
|
||||
&::before,
|
||||
&::after {
|
||||
border-top-color: #e2e8f0;
|
||||
}
|
||||
|
||||
.el-divider__text {
|
||||
font-size: 13px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
background: #f5f7fa;
|
||||
padding: 0 16px;
|
||||
color: #0f172a;
|
||||
letter-spacing: -0.01em;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
|
||||
padding: 7px 16px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid #e2e8f0;
|
||||
box-shadow: 0 1px 2px rgb(15 23 42 / 6%);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1373,24 +1435,111 @@ defineExpose({
|
||||
color: #909399;
|
||||
}
|
||||
|
||||
// 输入框样式优化
|
||||
:deep(.el-input__inner),
|
||||
// 输入圆角:医疗表单更易扫读
|
||||
:deep(.el-input__wrapper) {
|
||||
border-radius: 10px;
|
||||
transition:
|
||||
box-shadow 0.2s ease,
|
||||
border-color 0.2s ease;
|
||||
}
|
||||
|
||||
:deep(.el-textarea__inner) {
|
||||
border-radius: 4px;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
// 选择器样式优化
|
||||
:deep(.el-select) {
|
||||
.el-input__inner {
|
||||
border-radius: 4px;
|
||||
}
|
||||
:deep(.el-select .el-input__wrapper) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
// 日期选择器样式优化
|
||||
:deep(.el-date-editor) {
|
||||
.el-input__inner {
|
||||
border-radius: 4px;
|
||||
}
|
||||
:deep(.el-date-editor .el-input__wrapper) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
:deep(.el-input-number .el-input__wrapper) {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.tcm-dd-header {
|
||||
min-width: 0;
|
||||
flex: 1;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.tcm-dd-header__row {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 8px 10px;
|
||||
}
|
||||
|
||||
.tcm-dd-header__title {
|
||||
margin: 0;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #0f172a;
|
||||
line-height: 1.3;
|
||||
letter-spacing: -0.02em;
|
||||
}
|
||||
|
||||
.tcm-dd-header__badge {
|
||||
flex-shrink: 0;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.tcm-dd-header__sub {
|
||||
margin: 6px 0 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.45;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.tcm-dd-header__name {
|
||||
font-weight: 600;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.tcm-dd-header__id {
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
}
|
||||
|
||||
.tcm-dd-header__sep {
|
||||
margin: 0 4px;
|
||||
color: #94a3b8;
|
||||
}
|
||||
|
||||
.tcm-dd-footer {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
padding: 14px 20px 18px;
|
||||
box-sizing: border-box;
|
||||
border-top: 1px solid rgb(226 232 240 / 90%);
|
||||
background: linear-gradient(180deg, rgb(248 250 252 / 95%) 0%, #fff 40%);
|
||||
}
|
||||
|
||||
.tcm-dd-footer__btn {
|
||||
min-height: 40px;
|
||||
padding: 10px 20px;
|
||||
font-weight: 600;
|
||||
border-radius: 10px;
|
||||
transition:
|
||||
transform 0.15s ease,
|
||||
box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.tcm-dd-footer__btn:focus-visible {
|
||||
outline: 2px solid var(--el-color-primary);
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.tcm-dd-footer__btn {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1415,5 +1564,209 @@ defineExpose({
|
||||
.el-select-dropdown {
|
||||
z-index: 3000 !important;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
/**
|
||||
* 诊单抽屉壳层(挂到 body 时通过 .diagnosis-drawer 限定作用域)
|
||||
*/
|
||||
.diagnosis-drawer.tcm-diagnosis-drawer {
|
||||
.el-drawer__header {
|
||||
align-items: flex-start !important;
|
||||
border-bottom: 1px solid #e2e8f0;
|
||||
background: linear-gradient(180deg, #fff 0%, #f8fafc 100%);
|
||||
}
|
||||
|
||||
.el-drawer__body {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.el-drawer__footer {
|
||||
padding: 0 !important;
|
||||
background: #fff;
|
||||
box-shadow: 0 -4px 24px rgb(15 23 42 / 7%);
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__nav-wrap {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__nav-wrap::-webkit-scrollbar {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__nav {
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
float: none !important;
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__item {
|
||||
flex: 0 0 auto;
|
||||
padding: 0 14px;
|
||||
font-weight: 500;
|
||||
color: #64748b;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__item.is-active {
|
||||
color: var(--el-color-primary);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.tcm-diagnosis-tabs .el-tabs__active-bar {
|
||||
height: 3px;
|
||||
border-radius: 3px 3px 0 0;
|
||||
}
|
||||
|
||||
.tcm-dd-footer {
|
||||
box-shadow: none;
|
||||
border-top: 1px solid #e2e8f0;
|
||||
background: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.diagnosis-drawer.tcm-diagnosis-drawer .tcm-diagnosis-tabs .el-tabs__item {
|
||||
transition: none;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 窄屏诊单抽屉:drawer 通过 teleport 挂到 body,scoped 无法作用到内部,此处用抽屉 class 做 H5 适配。
|
||||
*/
|
||||
@media screen and (max-width: 768px) {
|
||||
.diagnosis-drawer.el-drawer {
|
||||
width: 100% !important;
|
||||
max-width: 100vw;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-drawer__header {
|
||||
padding: 12px 14px !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-drawer__body {
|
||||
padding: 10px 12px 12px !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-drawer__footer {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .tcm-dd-footer {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 10px;
|
||||
padding: 12px 14px calc(12px + env(safe-area-inset-bottom, 0px)) !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .tcm-dd-footer__btn {
|
||||
width: 100%;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .tcm-dd-header__title {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .diagnosis-tabs .el-tabs__header {
|
||||
margin: 0 0 10px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-tabs__item {
|
||||
padding: 0 12px;
|
||||
font-size: 13px;
|
||||
min-height: 44px;
|
||||
line-height: 44px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-tabs__nav-wrap::after {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
/* 表单:标签在上、控件全宽,避免 160px 标签 + 三列栅格把内容挤没 */
|
||||
.diagnosis-drawer .el-form-item {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: stretch !important;
|
||||
margin-bottom: 14px !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-form-item__label {
|
||||
width: 100% !important;
|
||||
max-width: 100% !important;
|
||||
height: auto !important;
|
||||
line-height: 1.4 !important;
|
||||
text-align: left !important;
|
||||
justify-content: flex-start !important;
|
||||
margin: 0 0 6px !important;
|
||||
padding: 0 !important;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-form-item__content {
|
||||
margin-left: 0 !important;
|
||||
max-width: 100% !important;
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-row {
|
||||
margin-left: 0 !important;
|
||||
margin-right: 0 !important;
|
||||
row-gap: 0;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-col,
|
||||
.diagnosis-drawer .el-col-12,
|
||||
.diagnosis-drawer .el-col-8,
|
||||
.diagnosis-drawer .el-col-20,
|
||||
.diagnosis-drawer [class*='el-col-'] {
|
||||
flex: 0 0 100% !important;
|
||||
max-width: 100% !important;
|
||||
width: 100% !important;
|
||||
padding-left: 0 !important;
|
||||
padding-right: 0 !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-input-number {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-radio-group {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap !important;
|
||||
gap: 8px 16px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-radio {
|
||||
margin-right: 0 !important;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-radio-button:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-checkbox-group {
|
||||
display: flex !important;
|
||||
flex-wrap: wrap !important;
|
||||
gap: 8px 10px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-divider {
|
||||
margin: 18px 0 14px;
|
||||
}
|
||||
|
||||
.diagnosis-drawer .el-divider__text {
|
||||
font-size: 13px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -438,6 +438,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -399,6 +399,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="订单金额" prop="amount">
|
||||
|
||||
@@ -372,6 +372,7 @@
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
<el-option label="首付费用" :value="4" />
|
||||
<el-option label="尾款费用" :value="5" />
|
||||
<el-option label="其他费用" :value="6" />
|
||||
<el-option label="全部费用" :value="7" />
|
||||
<el-option label="驼奶费用" :value="8" />
|
||||
<el-option label="退款统计" :value="0" />
|
||||
</el-select>
|
||||
<el-select v-model="orderStatsDays" class="wb-select" @change="loadOrderStats">
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user