Merge remote-tracking branch 'origin/develop' into develop
# Conflicts: # openhis-server-new/openhis-application/src/main/java/com/openhis/web/clinicalmanage/dto/OpScheduleDto.java
This commit is contained in:
@@ -49,6 +49,40 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 性别、出生日期、或实足年龄 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="7" class="form-item">
|
||||
<span class="form-label required">性别</span>
|
||||
<el-radio-group v-model="form.sex" class="gender-radio-group">
|
||||
<el-radio value="男">男</el-radio>
|
||||
<el-radio value="女">女</el-radio>
|
||||
<el-radio value="未知">未知</el-radio>
|
||||
</el-radio-group>
|
||||
</el-col>
|
||||
<el-col :span="10" class="form-item">
|
||||
<span class="form-label required">出生日期</span>
|
||||
<div class="birth-input-group">
|
||||
<el-input v-model="form.birthYear" class="birth-input year" placeholder="年" maxlength="4" />
|
||||
<span class="birth-separator">年</span>
|
||||
<el-input v-model="form.birthMonth" class="birth-input month" placeholder="月" maxlength="2" />
|
||||
<span class="birth-separator">月</span>
|
||||
<el-input v-model="form.birthDay" class="birth-input day" placeholder="日" maxlength="2" />
|
||||
<span class="birth-separator">日</span>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="7" class="form-item">
|
||||
<span class="form-label">或 实足年龄</span>
|
||||
<div class="age-input-group">
|
||||
<el-input v-model="form.age" class="age-input" placeholder="年龄" />
|
||||
<el-select v-model="form.ageUnit" class="age-unit-select">
|
||||
<el-option label="岁" value="岁" />
|
||||
<el-option label="月" value="月" />
|
||||
<el-option label="天" value="天" />
|
||||
</el-select>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 联系电话、紧急联系人电话 -->
|
||||
<el-row :gutter="16" class="form-row">
|
||||
<el-col :span="12" class="form-item">
|
||||
@@ -1992,4 +2026,53 @@ defineExpose({ show, showReport, close: handleClose });
|
||||
display: flex !important;
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
/* 性别单选按钮组 */
|
||||
.gender-radio-group {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
/* 出生日期输入组 */
|
||||
.birth-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 2px;
|
||||
}
|
||||
|
||||
.birth-input {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.birth-input.year {
|
||||
width: 70px;
|
||||
}
|
||||
|
||||
.birth-input.month,
|
||||
.birth-input.day {
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.birth-separator {
|
||||
color: #606266;
|
||||
font-size: 13px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
/* 年龄输入组 */
|
||||
.age-input-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.age-input {
|
||||
width: 70px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.age-unit-select {
|
||||
width: 65px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -278,16 +278,33 @@
|
||||
<el-input v-model="scope.row.applyPart" size="small" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="检查方法" min-width="120">
|
||||
<el-table-column label="检查方法" min-width="160">
|
||||
<template #default="scope">
|
||||
<!-- Bug #384修复: 显示检查方法名称,不显示套餐名称 -->
|
||||
<span v-if="scope.row.selectedMethod">
|
||||
{{ scope.row.selectedMethod.name }}
|
||||
</span>
|
||||
<span v-else-if="scope.row.methods && scope.row.methods.length > 0" style="color: #909399;">
|
||||
未选择
|
||||
</span>
|
||||
<span v-else style="color: #c0c4cc;">-</span>
|
||||
<el-select
|
||||
v-if="scope.row.methods && scope.row.methods.length > 1"
|
||||
:model-value="scope.row.selectedMethod"
|
||||
value-key="id"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
placeholder="选择方法"
|
||||
@update:model-value="(val) => onDetailMethodChange(scope.row, val)"
|
||||
>
|
||||
<el-option
|
||||
v-for="meth in scope.row.methods"
|
||||
:key="meth.id"
|
||||
:label="`${meth.name}${meth.packagePrice != null ? ' ¥' + formatDetailAmount(meth.packagePrice) : ''}`"
|
||||
:value="meth"
|
||||
/>
|
||||
</el-select>
|
||||
<template v-else>
|
||||
<span v-if="scope.row.selectedMethod">
|
||||
{{ scope.row.selectedMethod.name }}
|
||||
</span>
|
||||
<span v-else-if="scope.row.methods && scope.row.methods.length > 0" style="color: #909399;">
|
||||
未选择
|
||||
</span>
|
||||
<span v-else style="color: #c0c4cc;">-</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="单位" prop="unit" width="55" align="center" />
|
||||
@@ -412,58 +429,48 @@
|
||||
v-for="(item, idx) in selectedItems"
|
||||
:key="idx"
|
||||
class="selected-item-card"
|
||||
:class="{ 'is-expanded': item.expanded }"
|
||||
>
|
||||
<!-- Bug #384修复 + #426修复: 项目卡片头部,可展开/收起 -->
|
||||
<div class="card-header" @click="toggleItemExpand(item)">
|
||||
<el-tag v-if="item.isPackage || item.packageName" size="small" type="warning" style="margin-right: 4px; flex-shrink: 0;">套餐</el-tag>
|
||||
<span class="card-name">{{ item.name }}</span>
|
||||
<span class="card-price">¥{{ item.price }}</span>
|
||||
<!-- 展开/收起图标 -->
|
||||
<el-icon class="expand-icon" :class="{ expanded: item.expanded }">
|
||||
<ArrowRight />
|
||||
<el-tooltip :content="item.name" placement="top" :show-after="400">
|
||||
<span class="card-name">{{ item.name }}</span>
|
||||
</el-tooltip>
|
||||
<span class="card-price">¥{{ formatDetailAmount(item.price) }}</span>
|
||||
<el-icon :class="['expand-icon', { expanded: item.expanded }]">
|
||||
<ArrowDown v-if="!item.expanded" />
|
||||
<ArrowUp v-if="item.expanded" />
|
||||
</el-icon>
|
||||
<!-- 删除按钮 -->
|
||||
<el-button link type="danger" size="small" @click.stop="handleRemoveItem(idx, item)">
|
||||
<el-icon><Close /></el-icon>
|
||||
</el-button>
|
||||
</div>
|
||||
<!-- Bug #428修复 + #426修复: 展开后显示套餐明细或检查方法 -->
|
||||
<div v-show="item.expanded" class="expanded-content">
|
||||
<!-- 显示套餐明细 -->
|
||||
<div v-if="(item.isPackage || item.packageName) && item.packageDetails && item.packageDetails.length > 0" class="package-details-list">
|
||||
<div class="detail-row" v-for="detail in item.packageDetails" :key="detail.id">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
<span class="detail-info">数量: {{ detail.quantity }} 单价: ¥{{ detail.price }}</span>
|
||||
<!-- Bug #428: 有套餐 ID 时默认展开;加载中/空/明细均在本区域展示 -->
|
||||
<div v-if="item.expanded && shouldShowPackageBody(item)" class="selected-card-body">
|
||||
<div v-if="item.packageDetailsLoading" class="package-details-loading">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-if="getPackageDetailsList(item).length === 0" class="package-details-empty">
|
||||
暂无套餐明细
|
||||
</div>
|
||||
</div>
|
||||
<!-- 套餐明细加载中 -->
|
||||
<div v-else-if="(item.isPackage || item.packageName) && item.packageDetailsLoading" class="package-loading-hint">
|
||||
加载中...
|
||||
</div>
|
||||
<!-- 显示检查方法 -->
|
||||
<div v-else-if="item.methods && item.methods.length > 0" class="method-list">
|
||||
<div v-for="method in item.methods" :key="method.id" class="method-option">
|
||||
<el-checkbox :model-value="item.selectedMethod?.id === method.id" @change="(val) => selectMethodCheckbox(val, item, method)">
|
||||
<span class="method-name">{{ method.name }}</span>
|
||||
<span class="method-price">¥{{ method.packagePrice || item.price }}</span>
|
||||
</el-checkbox>
|
||||
</div>
|
||||
<!-- 选中方法后,显示对应的套餐明细 -->
|
||||
<div v-if="item.selectedMethod && item.methodPackageDetails && item.methodPackageDetails.length > 0" class="method-package-details">
|
||||
<div class="method-package-header">
|
||||
<span class="method-package-title">套餐明细 - {{ item.selectedMethod.name }}</span>
|
||||
</div>
|
||||
<div v-for="detail in item.methodPackageDetails" :key="detail.id" class="method-option">
|
||||
<el-checkbox v-model="detail.checked">
|
||||
<span class="method-name">{{ detail.name }}</span>
|
||||
<span class="method-price">数量: {{ detail.quantity }} ¥{{ detail.price }}</span>
|
||||
</el-checkbox>
|
||||
<div v-else class="package-details-list">
|
||||
<div class="package-details-head">套餐明细</div>
|
||||
<div
|
||||
v-for="(detail, dIdx) in getPackageDetailsList(item)"
|
||||
:key="detail.id ?? detail.itemCode ?? `d-${dIdx}`"
|
||||
class="detail-row"
|
||||
>
|
||||
<el-tooltip :content="detail.name" placement="top" :show-after="500">
|
||||
<span class="detail-name">{{ detail.name }}</span>
|
||||
</el-tooltip>
|
||||
<div class="detail-meta">
|
||||
<span class="detail-qty">×{{ detail.quantity || 1 }}</span>
|
||||
<span class="detail-price">¥{{ formatDetailAmount(detail.price) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.selectedMethod && item.methodPackageLoading" class="method-package-loading">
|
||||
加载套餐明细中...
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -477,7 +484,7 @@
|
||||
<script setup>
|
||||
import { ref, reactive, computed, watch, onMounted, nextTick } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { Printer, Delete, ArrowDown, ArrowUp, Close, ArrowRight } from '@element-plus/icons-vue';
|
||||
import { Printer, Delete, ArrowDown, ArrowUp, Close } from '@element-plus/icons-vue';
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import request from '@/utils/request';
|
||||
import { listCheckMethod, searchCheckMethod, listCheckPackage } from '@/api/system/checkType';
|
||||
@@ -568,21 +575,22 @@ handleResetSearch();
|
||||
|
||||
// 🔧 BugFix#426: 懒加载套餐明细
|
||||
async function loadPackageDetails(row, treeNode, resolve) {
|
||||
if (!row.isPackage || !row.packageId) {
|
||||
if (!row.packageId) {
|
||||
resolve([]);
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const res = await request({
|
||||
url: `/system/package/${row.packageId}/details`,
|
||||
url: `/system/check-type/package/${row.packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
if (res.code === 200 && res.data) {
|
||||
const children = res.data.map(item => ({
|
||||
...item,
|
||||
name: item.name || item.itemName,
|
||||
unit: item.unit || '次',
|
||||
price: item.price || item.itemPrice || 0,
|
||||
if (res.code === 200) {
|
||||
const list = parsePackageDetailsPayload(res);
|
||||
const children = list.map((child) => ({
|
||||
...child,
|
||||
name: child.name || child.itemName,
|
||||
unit: child.unit || '次',
|
||||
price: child.price ?? child.unitPrice ?? child.itemPrice ?? 0,
|
||||
quantity: row.quantity || 1,
|
||||
isPackageDetail: true
|
||||
}));
|
||||
@@ -597,16 +605,68 @@ async function loadPackageDetails(row, treeNode, resolve) {
|
||||
}
|
||||
|
||||
// #428修复 + #426修复: 为已选择项目加载套餐明细(通过packageId或packageName查询)
|
||||
/** 套餐明细挂在「部位」或已选的「检查方法」上(方法可带 packageId) */
|
||||
function getPackageCarrier(item) {
|
||||
return item?.selectedMethod?.packageId ? item.selectedMethod : item;
|
||||
}
|
||||
|
||||
function getPackageDetailsList(item) {
|
||||
// 明细挂在行对象上,避免仅写入 methods 内嵌对象时首帧不触发视图更新(体感需点两次才展开)
|
||||
if (Array.isArray(item?.packageDetailsDisplay)) {
|
||||
return item.packageDetailsDisplay;
|
||||
}
|
||||
const carrier = getPackageCarrier(item);
|
||||
return Array.isArray(carrier?.packageDetails) ? carrier.packageDetails : [];
|
||||
}
|
||||
|
||||
/** 有套餐 ID 的已选行才展示右侧套餐区(加载中 / 空 / 明细列表) */
|
||||
function shouldShowPackageBody(item) {
|
||||
return !!getPackageCarrier(item)?.packageId;
|
||||
}
|
||||
|
||||
/** 金额展示:统一两位小数 */
|
||||
function formatDetailAmount(value) {
|
||||
const n = Number(value ?? 0);
|
||||
return Number.isFinite(n) ? n.toFixed(2) : '0.00';
|
||||
}
|
||||
|
||||
/** 默认检查方法:优先与部位 packageId 一致的方法,否则取首个带套餐的方法,否则取第一个 */
|
||||
function pickDefaultMethod(methods, partItem) {
|
||||
if (!methods?.length) return null;
|
||||
if (methods.length === 1) return methods[0];
|
||||
const pid = partItem?.packageId ?? null;
|
||||
if (pid != null && pid !== '') {
|
||||
const matched = methods.find(
|
||||
(x) => x.packageId != null && String(x.packageId) === String(pid)
|
||||
);
|
||||
if (matched) return matched;
|
||||
}
|
||||
const withPkg = methods.find((x) => x.packageId != null);
|
||||
if (withPkg) return withPkg;
|
||||
return methods[0];
|
||||
}
|
||||
|
||||
function parsePackageDetailsPayload(res) {
|
||||
const raw =
|
||||
res?.data?.data ??
|
||||
res?.data?.records ??
|
||||
res?.data ??
|
||||
res?.rows ??
|
||||
res;
|
||||
if (!Array.isArray(raw)) return [];
|
||||
return raw;
|
||||
}
|
||||
|
||||
// #428: 为已选择项目加载套餐明细(后端:CheckTypeController /system/check-type/package/{id}/details)
|
||||
async function loadPackageDetailsForItem(item) {
|
||||
// 只要有 packageName 就认为是套餐,不强制要求 isPackage 或 packageId
|
||||
if (!item.packageName && !item.packageId) {
|
||||
const carrier = getPackageCarrier(item);
|
||||
let packageId = item.packageId || carrier?.packageId;
|
||||
if (!packageId && !item.packageName) {
|
||||
return;
|
||||
}
|
||||
item.packageDetailsLoading = true;
|
||||
try {
|
||||
let packageId = item.packageId;
|
||||
if (!packageId && item.packageName) {
|
||||
// CheckPart 没有 packageId 字段,需要通过 packageName 查询获取
|
||||
const pkgRes = await listCheckPackage({ packageName: item.packageName });
|
||||
let packages = pkgRes?.data || [];
|
||||
if (!Array.isArray(packages)) {
|
||||
@@ -614,31 +674,48 @@ async function loadPackageDetailsForItem(item) {
|
||||
}
|
||||
if (packages.length === 0) {
|
||||
item.packageDetails = [];
|
||||
item.packageDetailsDisplay = [];
|
||||
return;
|
||||
}
|
||||
packageId = packages[0].id;
|
||||
item.packageId = packageId;
|
||||
}
|
||||
if (!packageId) {
|
||||
item.packageDetails = [];
|
||||
item.packageDetailsDisplay = [];
|
||||
return;
|
||||
}
|
||||
const res = await request({
|
||||
url: `/system/package/${packageId}/details`,
|
||||
url: `/system/check-type/package/${packageId}/details`,
|
||||
method: 'get'
|
||||
});
|
||||
const list = parsePackageDetailsPayload(res);
|
||||
const mapped = list.map((detail) => ({
|
||||
...detail,
|
||||
name: detail.name || detail.itemName,
|
||||
unit: detail.unit || '次',
|
||||
price: detail.price ?? detail.unitPrice ?? detail.itemPrice ?? 0,
|
||||
quantity: detail.quantity || 1
|
||||
}));
|
||||
item.packageDetailsDisplay = mapped;
|
||||
carrier.packageDetails = mapped;
|
||||
if (res.code === 200 && res.data) {
|
||||
item.packageDetails = res.data.map(detail => ({
|
||||
...detail,
|
||||
name: detail.name || detail.itemName,
|
||||
unit: detail.unit || '次',
|
||||
price: detail.price || detail.unitPrice || 0,
|
||||
quantity: detail.quantity || 1
|
||||
}));
|
||||
item.packageDetails = Array.isArray(res.data)
|
||||
? res.data.map((detail) => ({
|
||||
...detail,
|
||||
name: detail.name || detail.itemName,
|
||||
unit: detail.unit || '次',
|
||||
price: detail.price || detail.unitPrice || 0,
|
||||
quantity: detail.quantity || 1
|
||||
}))
|
||||
: mapped;
|
||||
} else {
|
||||
item.packageDetails = [];
|
||||
item.packageDetails = mapped;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载套餐明细失败:', err);
|
||||
item.packageDetailsDisplay = [];
|
||||
carrier.packageDetails = [];
|
||||
item.packageDetails = [];
|
||||
} finally {
|
||||
item.packageDetailsLoading = false;
|
||||
@@ -1174,7 +1251,10 @@ function handleRowClick(row) {
|
||||
nationalCode: '', checked: true,
|
||||
methods: [],
|
||||
selectedMethod: null,
|
||||
expanded: false // Bug #384修复: 添加展开状态
|
||||
expanded: false,
|
||||
packageDetailsLoading: false,
|
||||
isPackage: false,
|
||||
packageId: null
|
||||
};
|
||||
// 加载该项目的检查方法
|
||||
if (m.bodyPartCode) {
|
||||
@@ -1205,6 +1285,13 @@ function handleRowClick(row) {
|
||||
item.packageId = item.selectedMethod.packageId;
|
||||
}
|
||||
}
|
||||
if (!item.selectedMethod && item.methods.length) {
|
||||
item.selectedMethod = pickDefaultMethod(item.methods, { packageId: item.packageId });
|
||||
}
|
||||
if (item.selectedMethod?.packageId) {
|
||||
item.packageId = item.selectedMethod.packageId;
|
||||
item.isPackage = true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('加载检查方法失败', err);
|
||||
@@ -1214,6 +1301,12 @@ function handleRowClick(row) {
|
||||
return item;
|
||||
}));
|
||||
selectedItems.value = itemsWithMethods;
|
||||
for (const it of selectedItems.value) {
|
||||
if (getPackageCarrier(it)?.packageId) {
|
||||
await loadPackageDetailsForItem(it);
|
||||
}
|
||||
it.expanded = !!getPackageCarrier(it)?.packageId;
|
||||
}
|
||||
syncCategoryChecked();
|
||||
// Bug #384修复: 回充后更新检查方法显示
|
||||
updateMethodDisplay();
|
||||
@@ -1375,7 +1468,7 @@ async function handleItemSelect(checked, item, cat) {
|
||||
}
|
||||
}
|
||||
|
||||
selectedItems.value.push({
|
||||
const newRow = {
|
||||
id: item.id, name: item.name,
|
||||
price: item.price, quantity: 1,
|
||||
serviceFee: item.serviceFee || 0,
|
||||
@@ -1386,11 +1479,27 @@ async function handleItemSelect(checked, item, cat) {
|
||||
checked: true,
|
||||
methods: methods,
|
||||
selectedMethod: null,
|
||||
expanded: false, // Bug #384修复: 新增展开状态,默认不展开
|
||||
isPackage: !!item.packageName, // Bug #428修复: 标记是否为套餐
|
||||
packageName: item.packageName || null, // Bug #426修复: 套餐名称,用于查找packageId
|
||||
packageId: item.packageId || null // Bug #428修复: 套餐ID
|
||||
});
|
||||
expanded: false,
|
||||
isPackage: !!(item.packageId || item.packageName),
|
||||
packageName: item.packageName || null,
|
||||
packageDetailsLoading: false,
|
||||
packageId: item.packageId || null
|
||||
};
|
||||
selectedItems.value.push(newRow);
|
||||
// 必须用数组里的响应式行,不能继续改局部 newRow:push 后列表内是 proxy,改 raw 对象不会触发右侧卡片更新(会一直卡在「加载中」)
|
||||
const row = selectedItems.value[selectedItems.value.length - 1];
|
||||
|
||||
// 右侧不再展示「检查方法」列表:自动选默认方法(保存、计价仍依赖 selectedMethod)
|
||||
if (methods.length >= 1) {
|
||||
row.selectedMethod = pickDefaultMethod(methods, item);
|
||||
}
|
||||
updateMethodDisplay();
|
||||
|
||||
// 有套餐 ID 时默认展开(先显示加载区,明细写入行对象 packageDetailsDisplay)
|
||||
row.expanded = !!getPackageCarrier(row)?.packageId;
|
||||
if (getPackageCarrier(row)?.packageId) {
|
||||
await loadPackageDetailsForItem(row);
|
||||
}
|
||||
|
||||
// 自动回填执行科室:按检查项目类型 → 检查类型管理里配置的执行科室
|
||||
if (selectedItems.value.length === 1 && cat?.performDeptName) {
|
||||
@@ -1414,16 +1523,23 @@ async function handleItemSelect(checked, item, cat) {
|
||||
// Bug #384修复 + #426修复: 展开/收起项目卡片
|
||||
async function toggleItemExpand(item) {
|
||||
item.expanded = !item.expanded;
|
||||
// 如果是展开且该项目是套餐(通过 isPackage 或 packageName 判断),加载套餐明细
|
||||
if (item.expanded && (item.isPackage || item.packageName) && (!item.packageDetails || item.packageDetails.length === 0) && !item.packageDetailsLoading) {
|
||||
await loadPackageDetailsForItem(item);
|
||||
}
|
||||
if (item.expanded && shouldShowPackageBody(item)) {
|
||||
if (getPackageDetailsList(item).length === 0 && !item.packageDetailsLoading) {
|
||||
await loadPackageDetailsForItem(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bug #384修复: 勾选框选择检查方法(单选逻辑)
|
||||
async function selectMethodCheckbox(checked, item, method) {
|
||||
if (checked) {
|
||||
item.selectedMethod = method;
|
||||
if (item.expanded && method.packageId) {
|
||||
loadPackageDetailsForItem(item);
|
||||
}
|
||||
// 动态加载该方法对应的套餐明细
|
||||
await loadMethodPackageDetails(item, method);
|
||||
} else {
|
||||
@@ -1483,6 +1599,28 @@ async function loadMethodPackageDetails(item, method) {
|
||||
}
|
||||
}
|
||||
|
||||
/** 检查明细表格中切换检查方法 */
|
||||
async function onDetailMethodChange(row, val) {
|
||||
row.selectedMethod = val || null;
|
||||
if (val?.packageId) {
|
||||
row.packageId = val.packageId;
|
||||
row.isPackage = true;
|
||||
}
|
||||
row.packageDetailsDisplay = undefined;
|
||||
const carrier = getPackageCarrier(row);
|
||||
if (carrier) {
|
||||
carrier.packageDetails = undefined;
|
||||
}
|
||||
updateMethodDisplay();
|
||||
row.expanded = !!getPackageCarrier(row)?.packageId;
|
||||
if (getPackageCarrier(row)?.packageId) {
|
||||
await loadPackageDetailsForItem(row);
|
||||
}
|
||||
nextTick(() => {
|
||||
form.totalAmount = totalAmountCalc.value;
|
||||
});
|
||||
}
|
||||
|
||||
// Bug #384修复: 更新检查方法显示字段(联动)
|
||||
function updateMethodDisplay() {
|
||||
// 找到第一个有选中检查方法的项目
|
||||
@@ -1627,7 +1765,7 @@ defineExpose({ getList });
|
||||
|
||||
/* 右:分类面板 */
|
||||
.category-panel {
|
||||
width: 380px;
|
||||
width: 420px;
|
||||
flex-shrink: 0;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
@@ -1763,8 +1901,11 @@ defineExpose({ getList });
|
||||
}
|
||||
|
||||
/* 已选择 tags */
|
||||
/* 已选择:加宽,避免套餐明细挤成一团 */
|
||||
.selected-panel {
|
||||
width: 140px; /* Bug #384修复: 加宽以适应展开内容 */
|
||||
width: 220px;
|
||||
min-width: 200px;
|
||||
max-width: 280px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -1772,9 +1913,11 @@ defineExpose({ getList });
|
||||
.selected-tags {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
padding-right: 2px;
|
||||
}
|
||||
.selected-tag {
|
||||
max-width: 100%;
|
||||
@@ -1787,30 +1930,40 @@ defineExpose({ getList });
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* Bug #384修复: 已选择项目卡片(可展开) */
|
||||
/* 已选择项目卡片 */
|
||||
.selected-item-card {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #F5F5F5;
|
||||
border-radius: 4px;
|
||||
background: #fff;
|
||||
border-radius: 6px;
|
||||
border: 1px solid #e4e7ed;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.selected-item-card .card-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px 10px;
|
||||
padding: 10px 10px;
|
||||
cursor: pointer;
|
||||
gap: 4px;
|
||||
gap: 8px;
|
||||
background: linear-gradient(180deg, #f8fafc 0%, #f0f4f8 100%);
|
||||
border-bottom: 1px solid transparent;
|
||||
}
|
||||
|
||||
.selected-item-card .card-header:hover {
|
||||
background: #E6F7FF;
|
||||
background: linear-gradient(180deg, #ecf5ff 0%, #e3eef8 100%);
|
||||
}
|
||||
|
||||
.selected-item-card.is-expanded .card-header {
|
||||
border-bottom-color: #ebeef5;
|
||||
}
|
||||
|
||||
.card-name {
|
||||
flex: 1;
|
||||
font-size: 12px;
|
||||
min-width: 0;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
@@ -1818,14 +1971,17 @@ defineExpose({ getList });
|
||||
}
|
||||
|
||||
.card-price {
|
||||
font-size: 12px;
|
||||
color: #1890FF;
|
||||
font-weight: 500;
|
||||
font-size: 13px;
|
||||
color: #409eff;
|
||||
font-weight: 600;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.expand-icon {
|
||||
font-size: 12px;
|
||||
font-size: 14px;
|
||||
color: #909399;
|
||||
transition: transform 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
transition: transform 0.2s;
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
@@ -1834,19 +1990,6 @@ defineExpose({ getList });
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
/* Bug #426修复: 展开内容容器 */
|
||||
.expanded-content {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* Bug #426修复: 套餐明细加载提示 */
|
||||
.package-loading-hint {
|
||||
padding: 8px 10px;
|
||||
font-size: 11px;
|
||||
color: #c0c4cc;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Bug #428修复: 套餐明细列表样式 */
|
||||
.package-details-list {
|
||||
padding: 6px 10px;
|
||||
@@ -1882,61 +2025,83 @@ defineExpose({ getList });
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Bug #384修复: 检查方法勾选框列表 */
|
||||
.method-list {
|
||||
padding: 6px 10px;
|
||||
background: #fff;
|
||||
border-top: 1px solid #e4e7ed;
|
||||
/* 展开区域 */
|
||||
.selected-card-body {
|
||||
background: #fafbfc;
|
||||
}
|
||||
|
||||
.package-details-loading,
|
||||
.package-details-empty {
|
||||
padding: 12px 10px;
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.package-details-empty {
|
||||
color: #c0c4cc;
|
||||
}
|
||||
|
||||
.package-details-list {
|
||||
padding: 10px 10px 12px;
|
||||
}
|
||||
|
||||
.package-details-head {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
color: #909399;
|
||||
letter-spacing: 0.02em;
|
||||
margin-bottom: 8px;
|
||||
padding-bottom: 6px;
|
||||
border-bottom: 1px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.detail-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 8px 12px;
|
||||
align-items: start;
|
||||
padding: 10px 0;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
}
|
||||
|
||||
.detail-row:last-of-type {
|
||||
border-bottom: none;
|
||||
padding-bottom: 2px;
|
||||
}
|
||||
|
||||
.detail-name {
|
||||
font-size: 12px;
|
||||
color: #303133;
|
||||
line-height: 1.5;
|
||||
word-break: break-word;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-meta {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-end;
|
||||
gap: 4px;
|
||||
flex-shrink: 0;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.method-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.method-option :deep(.el-checkbox__label) {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.method-option .method-name {
|
||||
.detail-qty {
|
||||
font-size: 11px;
|
||||
color: #606266;
|
||||
}
|
||||
|
||||
.method-option .method-price {
|
||||
font-size: 11px;
|
||||
color: #e6a23c;
|
||||
font-weight: 500;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
/* 选中方法后显示的套餐明细 */
|
||||
.method-package-details {
|
||||
margin-top: 4px;
|
||||
padding: 4px 0;
|
||||
border-top: 1px dashed #dcdfe6;
|
||||
}
|
||||
|
||||
.method-package-header {
|
||||
padding: 2px 0 4px 24px;
|
||||
}
|
||||
|
||||
.method-package-title {
|
||||
font-size: 10px;
|
||||
color: #909399;
|
||||
font-weight: 500;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.method-package-loading {
|
||||
padding: 4px 0 4px 24px;
|
||||
font-size: 10px;
|
||||
color: #c0c4cc;
|
||||
.detail-price {
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #e6a23c;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
/* 折叠组件细节 */
|
||||
|
||||
@@ -875,7 +875,7 @@ import { ArrowDown, Search, Memo, Minus, Plus, Edit, Delete } from '@element-plu
|
||||
import printUtils, { getPrinterList, PRINT_TEMPLATE, savePrinterToCache, } from '@/utils/printUtils';
|
||||
import Template from "@/views/inpatientDoctor/home/emr/components/template.vue";
|
||||
|
||||
const emit = defineEmits(['selectDiagnosis']);
|
||||
const emit = defineEmits(['selectDiagnosis', 'inspectionListRefresh']);
|
||||
const total = ref(0);
|
||||
const queryParams = ref({});
|
||||
const prescriptionList = ref([]);
|
||||
@@ -2084,6 +2084,21 @@ function getOrgList() {
|
||||
});
|
||||
}
|
||||
|
||||
/** 诊疗医嘱关联检验申请时 contentJson 含 applyNo */
|
||||
function getInspectionApplyNoFromAdviceRow(row) {
|
||||
if (!row || row.adviceType !== 3) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const raw = row.contentJson;
|
||||
const j = raw ? (typeof raw === 'string' ? JSON.parse(raw) : raw) : {};
|
||||
const no = j && j.applyNo != null ? String(j.applyNo).trim() : '';
|
||||
return no || null;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete() {
|
||||
let selectRows = prescriptionRef.value.getSelectionRows();
|
||||
console.log('BugFix#219: handleDelete called, selectRows=', selectRows);
|
||||
@@ -2263,12 +2278,31 @@ function handleDelete() {
|
||||
}
|
||||
|
||||
if (deleteList.length > 0) {
|
||||
savePrescription({ adviceSaveList: deleteList }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
getListInfo(false);
|
||||
}
|
||||
const hasLabLinked = deleteList.some((d) => {
|
||||
const row = normalRows.find((r) => r.requestId === d.requestId);
|
||||
return row && getInspectionApplyNoFromAdviceRow(row);
|
||||
});
|
||||
const runApiDelete = () => {
|
||||
savePrescription({ adviceSaveList: deleteList }).then((res) => {
|
||||
if (res.code == 200) {
|
||||
proxy.$modal.msgSuccess('删除成功');
|
||||
getListInfo(false);
|
||||
emit('inspectionListRefresh');
|
||||
}
|
||||
});
|
||||
};
|
||||
if (hasLabLinked) {
|
||||
proxy.$modal
|
||||
.confirm(
|
||||
'删除此医嘱将同时作废关联的检验申请单(检验页签中的同单申请及同单下相关医嘱)。是否继续?',
|
||||
'删除确认',
|
||||
{ type: 'warning' }
|
||||
)
|
||||
.then(runApiDelete)
|
||||
.catch(() => {});
|
||||
} else {
|
||||
runApiDelete();
|
||||
}
|
||||
} else if (consultationRows.length == 0) {
|
||||
proxy.$modal.msgWarning('所选医嘱不可删除,请先撤回后再删除');
|
||||
return;
|
||||
|
||||
Reference in New Issue
Block a user