更新
This commit is contained in:
@@ -931,31 +931,75 @@
|
||||
</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="用量">
|
||||
<template v-if="detailPrescription.dosage_amount">
|
||||
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}袋
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
|
||||
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>—</template>
|
||||
<div class="flex flex-col gap-1 text-sm leading-relaxed">
|
||||
<div>
|
||||
<span v-if="detailHasAuxHerbs" class="text-gray-500 mr-1">主方:</span>
|
||||
<template v-if="detailPrescription.dosage_amount">
|
||||
{{ detailPrescription.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailPrescription.dosage_bag_count) > 0 ? Number(detailPrescription.dosage_bag_count) : 1 }}袋
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片' && detailPrescription.need_decoction !== null" class="ml-2 text-gray-500">
|
||||
({{ detailPrescription.need_decoction ? '代煎' : '不代煎' }})
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>—</template>
|
||||
</div>
|
||||
<div v-if="detailHasAuxHerbs && detailAuxUsage">
|
||||
<span class="text-gray-500 mr-1">辅方:</span>
|
||||
<template v-if="detailAuxUsage.dosage_amount != null && detailAuxUsage.dosage_amount !== 0">
|
||||
{{ detailAuxUsage.dosage_amount }}{{ detailPrescription.dosage_unit || 'g' }}
|
||||
<template v-if="detailPrescription.prescription_type === '浓缩水丸'">
|
||||
· {{ Number(detailAuxUsage.dosage_bag_count) > 0 ? Number(detailAuxUsage.dosage_bag_count) : 1 }}袋
|
||||
</template>
|
||||
<span v-if="detailPrescription.prescription_type === '饮片'" class="ml-2 text-gray-500">
|
||||
({{ detailAuxUsage.need_decoction ? '代煎' : '不代煎' }})
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>—</template>
|
||||
</div>
|
||||
</div>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="服用方式">
|
||||
<div class="flex flex-col gap-1.5 text-sm leading-relaxed">
|
||||
<div>
|
||||
<span class="text-gray-500">每天次数:</span>
|
||||
{{ detailPrescription.times_per_day ? detailPrescription.times_per_day + ' 次' : '—' }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-500">处方开立:</span>
|
||||
{{
|
||||
detailPrescription.usage_days != null && detailPrescription.usage_days !== ''
|
||||
? detailPrescription.usage_days + ' 天'
|
||||
: '—'
|
||||
}}
|
||||
</div>
|
||||
<template v-if="detailHasAuxHerbs">
|
||||
<div>
|
||||
<span class="text-gray-500 mr-1">主方:</span>
|
||||
每天
|
||||
{{ detailPrescription.times_per_day ? detailPrescription.times_per_day + ' 次' : '— 次' }}
|
||||
· 处方开立
|
||||
{{
|
||||
detailPrescription.usage_days != null && detailPrescription.usage_days !== ''
|
||||
? detailPrescription.usage_days + ' 天'
|
||||
: '— 天'
|
||||
}}
|
||||
</div>
|
||||
<div v-if="detailAuxUsage">
|
||||
<span class="text-gray-500 mr-1">辅方:</span>
|
||||
每天
|
||||
{{ detailAuxUsage.times_per_day ? detailAuxUsage.times_per_day + ' 次' : '— 次' }}
|
||||
· 处方开立
|
||||
{{
|
||||
detailAuxUsage.usage_days != null && Number(detailAuxUsage.usage_days) > 0
|
||||
? detailAuxUsage.usage_days + ' 天'
|
||||
: '— 天'
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<div>
|
||||
<span class="text-gray-500">每天次数:</span>
|
||||
{{ detailPrescription.times_per_day ? detailPrescription.times_per_day + ' 次' : '—' }}
|
||||
</div>
|
||||
<div>
|
||||
<span class="text-gray-500">处方开立:</span>
|
||||
{{
|
||||
detailPrescription.usage_days != null && detailPrescription.usage_days !== ''
|
||||
? detailPrescription.usage_days + ' 天'
|
||||
: '—'
|
||||
}}
|
||||
</div>
|
||||
</template>
|
||||
<div>
|
||||
<span class="text-gray-500">订单设置:</span>
|
||||
{{
|
||||
@@ -2426,9 +2470,9 @@
|
||||
</div>
|
||||
|
||||
<div class="rx-text">
|
||||
<p v-if="prescriptionTabType === 'internal'">主方服法:{{ rxUsageText }}</p>
|
||||
<p v-if="prescriptionTabType === 'internal' || rxAuxUsageText">主服法:{{ rxUsageText }}</p>
|
||||
<p v-else>服法:{{ rxUsageText }}</p>
|
||||
<p v-if="prescriptionTabType === 'internal' && rxAuxUsageText">辅方服法:{{ rxAuxUsageText }}</p>
|
||||
<p v-if="rxAuxUsageText">辅服法:{{ rxAuxUsageText }}</p>
|
||||
<p v-if="rxAdviceText">医嘱:{{ rxAdviceText }}</p>
|
||||
<p v-if="rxRemarkText">备注:{{ rxRemarkText }}</p>
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">
|
||||
@@ -3856,6 +3900,20 @@ const detailLinkedAppointmentChannelText = computed(() => {
|
||||
|
||||
const detailRxHerbs = computed(() => normalizeSlipHerbs(detailPrescription.value?.herbs))
|
||||
|
||||
/** 详情:是否含辅方药材(用于决定是否展示辅方用量 / 用法) */
|
||||
const detailHasAuxHerbs = computed(() => {
|
||||
const herbs = detailPrescription.value?.herbs
|
||||
if (!Array.isArray(herbs)) return false
|
||||
return herbs.some((h: any) => normalizeSlipFormulaType(h?.formula_type) === '辅方')
|
||||
})
|
||||
|
||||
/** 详情:辅方用法(aux_usage JSON 经规范化后的对象,与处方类型一致) */
|
||||
const detailAuxUsage = computed(() => {
|
||||
const rx = detailPrescription.value as any
|
||||
if (!rx) return null
|
||||
return normalizeSlipAuxUsageForm(rx.aux_usage, rx.prescription_type || '浓缩水丸')
|
||||
})
|
||||
|
||||
/** false=无权限;true/缺省兼容旧接口(旧版未下发该字段时仍展示药材) */
|
||||
const detailHerbsVisible = computed(() => detailData.value?.prescription_detail_herbs_visible !== false)
|
||||
|
||||
@@ -5571,6 +5629,22 @@ const slipPillGrams = computed(() => {
|
||||
return days * times * doseG * bags
|
||||
})
|
||||
|
||||
/** 辅方出丸克数:仅含辅方药材且为浓缩水丸时计算;公式同主方,用 aux_usage 字段 */
|
||||
const slipAuxPillGrams = computed(() => {
|
||||
const d = prescriptionViewData.value as any
|
||||
if (!d || (d.prescription_type || '浓缩水丸') !== '浓缩水丸') return null
|
||||
if (!slipAuxHerbs.value.length) return null
|
||||
const aux = normalizeSlipAuxUsageForm(d.aux_usage, d.prescription_type || '浓缩水丸')
|
||||
/** 辅方疗程优先用业务订单 medication_days(与主方共享一次配药周期),缺省回退辅方 usage_days */
|
||||
const days = Number(d.medication_days ?? aux.usage_days)
|
||||
const times = Number(aux.times_per_day)
|
||||
const doseG = Number(aux.dosage_amount)
|
||||
const bags = Number(aux.dosage_bag_count) > 0 ? Number(aux.dosage_bag_count) : 1
|
||||
if (!Number.isFinite(days) || !Number.isFinite(times) || !Number.isFinite(doseG)) return null
|
||||
if (days <= 0 || times <= 0 || doseG <= 0) return null
|
||||
return days * times * doseG * bags
|
||||
})
|
||||
|
||||
/* ============================================================
|
||||
* A4 处方笺(药房联)相关计算 —— 与 consumer/prescription/index.vue 保持一致
|
||||
* ============================================================ */
|
||||
@@ -5684,10 +5758,14 @@ const rxAuxUsageText = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
/** 出丸:优先用 slipPillGrams(每袋用量×袋数×每天次数×服用天数),否则按药材总量×剂数 */
|
||||
/** 出丸:优先用 slipPillGrams / slipAuxPillGrams(含辅方时相加),否则按药材总量×剂数 */
|
||||
const rxOutPelletGrams = computed(() => {
|
||||
const pill = slipPillGrams.value
|
||||
if (typeof pill === 'number' && isFinite(pill) && pill > 0) return formatNum(pill)
|
||||
const main = slipPillGrams.value
|
||||
const aux = slipAuxPillGrams.value
|
||||
const mainNum = typeof main === 'number' && isFinite(main) && main > 0 ? main : 0
|
||||
const auxNum = typeof aux === 'number' && isFinite(aux) && aux > 0 ? aux : 0
|
||||
const sum = mainNum + auxNum
|
||||
if (sum > 0) return formatNum(sum)
|
||||
const v = prescriptionViewData.value as any
|
||||
if (!v) return ''
|
||||
const explicit = v.out_pellet || v.total_weight
|
||||
@@ -5723,7 +5801,17 @@ const rxPharmacyRemarkText = computed(() => {
|
||||
|
||||
const rxOutPelletText = computed(() => {
|
||||
const out = rxOutPelletGrams.value
|
||||
return out ? `${out}克` : ''
|
||||
if (!out) return ''
|
||||
/** 含辅方时附带「主方 X克 + 辅方 Y克」明细,便于药房核对 */
|
||||
const main = slipPillGrams.value
|
||||
const aux = slipAuxPillGrams.value
|
||||
if (
|
||||
typeof main === 'number' && isFinite(main) && main > 0 &&
|
||||
typeof aux === 'number' && isFinite(aux) && aux > 0
|
||||
) {
|
||||
return `${out}克(主方 ${formatNum(main)}克 + 辅方 ${formatNum(aux)}克)`
|
||||
}
|
||||
return `${out}克`
|
||||
})
|
||||
|
||||
function formatNum(n: number): string {
|
||||
|
||||
@@ -1437,6 +1437,16 @@ class YejiStatsLogic
|
||||
* @param int[] $deptIdsForAdmin
|
||||
* @param array<int, true> $tableRowSet
|
||||
* @param array<int, array<string, mixed>> $deptById
|
||||
*
|
||||
* 多链路 admin 的归属选择:按**原始 admin_dept 链路的深度**择优(更深 = 更具体的岗位归属),
|
||||
* 而非按解析出的表格行深度。否则同层中心兄弟节点在父视图(如 [郑州一中心, 郑州二中心])会
|
||||
* 以 id 大小决胜,而子视图(钻取到某中心后行集变为其子组)则只剩唯一候选——同一条挂号在两个
|
||||
* 视图被路由到不同行,导致 admin「已完成挂号」父行 < 子行合计。
|
||||
*
|
||||
* 向下兜底(resolveFirstTableRowUnderDept)按「链路深度 ≥ 最浅表格行深度 - 1」收窄:
|
||||
* 仅当链路本身就是用户筛选根(与表格行同层或刚刚高一层)时才允许向下归到首个子表格行;
|
||||
* 否则像「只挂在甄养堂(root) / 一中心」等过宽链路,会在不同视图被任意攀附到第一个子节点,
|
||||
* 同一挂号父视图归到第一个中心、子视图归到该中心第一个子组 → 父子合计错位。
|
||||
*/
|
||||
private static function mapAdminDeptLinksToTableRow(array $deptIdsForAdmin, array $tableRowDeptIds, array $tableRowSet, array $deptById): int
|
||||
{
|
||||
@@ -1449,7 +1459,7 @@ class YejiStatsLogic
|
||||
}
|
||||
$hit = self::resolveDeptToNearestTableRowUpward($d, $tableRowSet, $deptById);
|
||||
if ($hit > 0) {
|
||||
$depth = self::deptDepthFromRoot($hit, $deptById);
|
||||
$depth = self::deptDepthFromRoot($d, $deptById);
|
||||
if ($depth > $bestDepth || ($depth === $bestDepth && ($best === 0 || $hit < $best))) {
|
||||
$best = $hit;
|
||||
$bestDepth = $depth;
|
||||
@@ -1459,11 +1469,22 @@ class YejiStatsLogic
|
||||
if ($best > 0) {
|
||||
return $best;
|
||||
}
|
||||
$minTableRowDepth = PHP_INT_MAX;
|
||||
foreach ($tableRowDeptIds as $tid) {
|
||||
$td = self::deptDepthFromRoot((int) $tid, $deptById);
|
||||
if ($td > 0 && $td < $minTableRowDepth) {
|
||||
$minTableRowDepth = $td;
|
||||
}
|
||||
}
|
||||
$linkDepthFloor = $minTableRowDepth === PHP_INT_MAX ? PHP_INT_MAX : ($minTableRowDepth - 1);
|
||||
foreach ($deptIdsForAdmin as $d) {
|
||||
$d = (int) $d;
|
||||
if ($d <= 0) {
|
||||
continue;
|
||||
}
|
||||
if (self::deptDepthFromRoot($d, $deptById) < $linkDepthFloor) {
|
||||
continue;
|
||||
}
|
||||
$hit = self::resolveFirstTableRowUnderDept($d, $tableRowDeptIds, $deptById);
|
||||
if ($hit > 0) {
|
||||
return $hit;
|
||||
@@ -1618,10 +1639,31 @@ class YejiStatsLogic
|
||||
$aids[] = $d;
|
||||
}
|
||||
}
|
||||
$aids = array_values(array_unique($aids));
|
||||
$adminToTable = ($tableRowDeptIds !== [] && $deptById !== [])
|
||||
? self::batchMapPerformanceAdminsToTableDept($aids, $tableRowDeptIds, $deptById)
|
||||
: [];
|
||||
|
||||
/**
|
||||
* 视图无关的「该 admin 是否有 admin_dept 归属」。
|
||||
* 用途:仅当 admin 完全无部门归属时才允许由"医助"回退到"接诊医生"。
|
||||
* 否则父视图能解析到 admin 的中心行 → 计入 assistant;子视图(钻取另一中心)
|
||||
* adminToTable / adminToPrimary 双双 0 时会回退到 doctor,造成同一挂号在父视图归 assistant、
|
||||
* 在子视图归 doctor,父行 < 子行合计。
|
||||
*
|
||||
* @var array<int, true> $adminHasAnyDept
|
||||
*/
|
||||
$adminHasAnyDept = [];
|
||||
if ($aids !== []) {
|
||||
$linkedAids = Db::name('admin_dept')->whereIn('admin_id', $aids)->column('admin_id');
|
||||
foreach ($linkedAids as $lid) {
|
||||
$lid = (int) $lid;
|
||||
if ($lid > 0) {
|
||||
$adminHasAnyDept[$lid] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$byDept = [];
|
||||
foreach ($rows as $r) {
|
||||
$effId = (int) ($r['eff_a'] ?? 0);
|
||||
@@ -1657,6 +1699,14 @@ class YejiStatsLogic
|
||||
$primary = $p;
|
||||
break;
|
||||
}
|
||||
/**
|
||||
* 视图无关地有部门归属,仅是当前视图行集 / primaryDeptIds 不含 →
|
||||
* 视为该 admin 的归属(视图外不可见),不再回退到下一候选(医生),
|
||||
* 以避免父子视图归属分裂。narrowTotalsToTable 视图下 unmapped 会被自动隐藏。
|
||||
*/
|
||||
if (isset($adminHasAnyDept[$aid])) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($primary > 0) {
|
||||
@@ -4058,7 +4108,14 @@ class YejiStatsLogic
|
||||
if ($candidates === []) {
|
||||
return 0;
|
||||
}
|
||||
/** @var int[] $matching */
|
||||
/**
|
||||
* 每个命中的表格行追加 ['rid'=>表格行 id, 'link_depth'=>原始 admin_dept 链路深度]。
|
||||
* 用链路深度(而非表格行深度)择优,保证父视图(同层中心兄弟节点行集)与子视图(钻取单中心后行集为其子组)
|
||||
* 对同一 admin 的归属选择一致——否则同 admin 在父行被路由到「错」的中心,在子行被路由到正确中心子组,
|
||||
* 表现为父行计数 < 子行合计。
|
||||
*
|
||||
* @var array<int, array{rid: int, link_depth: int}> $matching
|
||||
*/
|
||||
$matching = [];
|
||||
foreach ($tableRowDeptIds as $rid) {
|
||||
$rid = (int) $rid;
|
||||
@@ -4069,6 +4126,7 @@ class YejiStatsLogic
|
||||
if ($flip === []) {
|
||||
continue;
|
||||
}
|
||||
$bestLinkDepth = -1;
|
||||
foreach ($candidates as $aid => $_) {
|
||||
$aid = (int) $aid;
|
||||
if ($aid <= 0) {
|
||||
@@ -4077,11 +4135,16 @@ class YejiStatsLogic
|
||||
foreach ($adminDeptIdList[$aid] ?? [] as $d) {
|
||||
$d = (int) $d;
|
||||
if ($d > 0 && isset($flip[$d])) {
|
||||
$matching[] = $rid;
|
||||
break 2;
|
||||
$linkDepth = self::deptDepthFromRoot($d, $deptById);
|
||||
if ($linkDepth > $bestLinkDepth) {
|
||||
$bestLinkDepth = $linkDepth;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($bestLinkDepth >= 0) {
|
||||
$matching[] = ['rid' => $rid, 'link_depth' => $bestLinkDepth];
|
||||
}
|
||||
}
|
||||
if ($matching === []) {
|
||||
foreach ($candidates as $aid => $_) {
|
||||
@@ -4099,8 +4162,9 @@ class YejiStatsLogic
|
||||
}
|
||||
$best = 0;
|
||||
$bestDepth = -1;
|
||||
foreach ($matching as $rid) {
|
||||
$depth = self::deptDepthFromRoot($rid, $deptById);
|
||||
foreach ($matching as $m) {
|
||||
$rid = $m['rid'];
|
||||
$depth = $m['link_depth'];
|
||||
if ($depth > $bestDepth || ($depth === $bestDepth && ($best === 0 || $rid < $best))) {
|
||||
$best = $rid;
|
||||
$bestDepth = $depth;
|
||||
|
||||
Reference in New Issue
Block a user