套餐设置套餐管理完善
This commit is contained in:
@@ -135,10 +135,15 @@ export function updateAuthRole(data) {
|
||||
}
|
||||
|
||||
// 查询部门下拉树结构
|
||||
export function deptTreeSelect() {
|
||||
// 默认只显示科室类型(typeEnum=2),如果需要其他类型,可以传入 params 覆盖
|
||||
export function deptTreeSelect(params = {}) {
|
||||
return request({
|
||||
url: '/base-data-manage/organization/organization',
|
||||
method: 'get'
|
||||
method: 'get',
|
||||
params: {
|
||||
typeEnum: 2, // 默认只显示科室
|
||||
...params // 允许外部传入参数覆盖默认值
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -469,7 +469,8 @@ async function getDiseaseTreatmentList() {
|
||||
// 诊疗目录分类查询下拉树结d构
|
||||
async function getImplDepartList() {
|
||||
try {
|
||||
const res = await getImplementDepartmentList();
|
||||
// 只查询科室类型(typeEnum=2),不包含专业等其他类型
|
||||
const res = await getImplementDepartmentList({ typeEnum: 2 });
|
||||
if (res.code === 200) {
|
||||
if (res.data.records.length > 0) {
|
||||
organization.value = res.data.records.map((res) => {
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, index) in tableData"
|
||||
v-for="(row, index) in pagedTypeRows"
|
||||
:key="row.id"
|
||||
:class="{ 'editing': editingRowId === row.id }"
|
||||
>
|
||||
@@ -148,12 +148,16 @@
|
||||
</div>
|
||||
|
||||
<!-- 页码区域 -->
|
||||
<div class="pagination">
|
||||
<div class="page-btn">上一页</div>
|
||||
<div class="page-btn active">1</div>
|
||||
<div class="page-btn">2</div>
|
||||
<div class="page-btn">3</div>
|
||||
<div class="page-btn">下一页</div>
|
||||
<div class="pagination" v-if="typeTotalPages > 1">
|
||||
<div class="page-btn" :class="{ disabled: typeCurrentPage === 1 }" @click="typePrevPage">上一页</div>
|
||||
<div
|
||||
v-for="p in typePageButtons"
|
||||
:key="p"
|
||||
class="page-btn"
|
||||
:class="{ active: p === typeCurrentPage }"
|
||||
@click="typeGoPage(p)"
|
||||
>{{ p }}</div>
|
||||
<div class="page-btn" :class="{ disabled: typeCurrentPage >= typeTotalPages }" @click="typeNextPage">下一页</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -239,7 +243,7 @@
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in filteredInspectionItems"
|
||||
v-for="(item, index) in pagedInspectionItems"
|
||||
:key="item.id"
|
||||
:class="{ 'editing': editingRowId === item.id }"
|
||||
>
|
||||
@@ -372,12 +376,16 @@
|
||||
</div>
|
||||
|
||||
<!-- 页码区域 -->
|
||||
<div class="pagination">
|
||||
<div class="page-btn">上一页</div>
|
||||
<div class="page-btn active">1</div>
|
||||
<div class="page-btn">2</div>
|
||||
<div class="page-btn">3</div>
|
||||
<div class="page-btn">下一页</div>
|
||||
<div class="pagination" v-if="inspectionTotalPages > 1">
|
||||
<div class="page-btn" :class="{ disabled: inspectionCurrentPage === 1 }" @click="inspectionPrevPage">上一页</div>
|
||||
<div
|
||||
v-for="p in inspectionPageButtons"
|
||||
:key="p"
|
||||
class="page-btn"
|
||||
:class="{ active: p === inspectionCurrentPage }"
|
||||
@click="inspectionGoPage(p)"
|
||||
>{{ p }}</div>
|
||||
<div class="page-btn" :class="{ disabled: inspectionCurrentPage >= inspectionTotalPages }" @click="inspectionNextPage">下一页</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -687,9 +695,9 @@
|
||||
|
||||
<script setup>
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import {computed, onMounted, ref, watch} from 'vue';
|
||||
import {computed, nextTick, onActivated, onMounted, ref, watch} from 'vue';
|
||||
import {ElLoading, ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {useRoute, useRouter} from 'vue-router';
|
||||
import {onBeforeRouteUpdate, useRoute, useRouter} from 'vue-router';
|
||||
import {
|
||||
addInspectionType,
|
||||
delInspectionType,
|
||||
@@ -785,6 +793,97 @@ const activeNav = ref(0);
|
||||
// 检验类型数据
|
||||
const tableData = ref([]);
|
||||
|
||||
// ==============================
|
||||
// 分页(检验类型 tab)
|
||||
// ==============================
|
||||
const typeCurrentPage = ref(1);
|
||||
// 每页条数:按需求固定为10
|
||||
const typePageSize = ref(10);
|
||||
const typeTotalPages = computed(() => {
|
||||
const total = tableData.value.length;
|
||||
return Math.max(1, Math.ceil(total / typePageSize.value));
|
||||
});
|
||||
const typePageButtons = computed(() => {
|
||||
const total = typeTotalPages.value;
|
||||
return Array.from({ length: total }, (_, i) => i + 1);
|
||||
});
|
||||
// 按“大类编码(code)”排序后再分页展示(不要按序号 sortOrder 排)
|
||||
function parseCodeParts(code) {
|
||||
const s = (code ?? '').toString().trim();
|
||||
// 支持 1、01、3-001、3_001 等:用 -/_ 分隔
|
||||
const parts = s.split(/[-_]/g);
|
||||
const mainRaw = parts[0] ?? '';
|
||||
const subRaw = parts[1] ?? '';
|
||||
const mainNum = Number.parseInt(mainRaw, 10);
|
||||
const subNum = Number.parseInt(subRaw, 10);
|
||||
return {
|
||||
raw: s,
|
||||
mainRaw,
|
||||
subRaw,
|
||||
mainIsNum: !Number.isNaN(mainNum),
|
||||
subIsNum: !Number.isNaN(subNum),
|
||||
mainNum,
|
||||
subNum
|
||||
};
|
||||
}
|
||||
|
||||
const sortedTypeRows = computed(() => {
|
||||
return [...tableData.value].sort((a, b) => {
|
||||
const pa = parseCodeParts(a?.code);
|
||||
const pb = parseCodeParts(b?.code);
|
||||
|
||||
// 先按主编码:数字优先按数值,否则按字符串
|
||||
if (pa.mainIsNum && pb.mainIsNum) {
|
||||
if (pa.mainNum !== pb.mainNum) return pa.mainNum - pb.mainNum;
|
||||
} else {
|
||||
const c = pa.mainRaw.localeCompare(pb.mainRaw, 'zh-Hans-CN', { numeric: true, sensitivity: 'base' });
|
||||
if (c !== 0) return c;
|
||||
}
|
||||
|
||||
// 同主编码时:无子编码的排在前(大类在子类之前)
|
||||
const aHasSub = !!pa.subRaw;
|
||||
const bHasSub = !!pb.subRaw;
|
||||
if (aHasSub !== bHasSub) return aHasSub ? 1 : -1;
|
||||
|
||||
// 再按子编码
|
||||
if (pa.subIsNum && pb.subIsNum) {
|
||||
if (pa.subNum !== pb.subNum) return pa.subNum - pb.subNum;
|
||||
} else {
|
||||
const c = pa.subRaw.localeCompare(pb.subRaw, 'zh-Hans-CN', { numeric: true, sensitivity: 'base' });
|
||||
if (c !== 0) return c;
|
||||
}
|
||||
|
||||
// 最后兜底:按原字符串
|
||||
return pa.raw.localeCompare(pb.raw, 'zh-Hans-CN', { numeric: true, sensitivity: 'base' });
|
||||
});
|
||||
});
|
||||
|
||||
const pagedTypeRows = computed(() => {
|
||||
const start = (typeCurrentPage.value - 1) * typePageSize.value;
|
||||
return sortedTypeRows.value.slice(start, start + typePageSize.value);
|
||||
});
|
||||
function typeGoPage(p) {
|
||||
if (p < 1 || p > typeTotalPages.value) return;
|
||||
typeCurrentPage.value = p;
|
||||
}
|
||||
function typePrevPage() {
|
||||
if (typeCurrentPage.value <= 1) return;
|
||||
typeCurrentPage.value -= 1;
|
||||
}
|
||||
function typeNextPage() {
|
||||
if (typeCurrentPage.value >= typeTotalPages.value) return;
|
||||
typeCurrentPage.value += 1;
|
||||
}
|
||||
watch(
|
||||
() => tableData.value.length,
|
||||
() => {
|
||||
// 数据变化后,确保当前页有效
|
||||
if (typeCurrentPage.value > typeTotalPages.value) {
|
||||
typeCurrentPage.value = 1;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 获取检验类型列表 - 从后端API获取
|
||||
const getInspectionTypeList = () => {
|
||||
listInspectionType().then(data => {
|
||||
@@ -989,6 +1088,44 @@ const filteredInspectionItems = computed(() => {
|
||||
});
|
||||
});
|
||||
|
||||
// ==============================
|
||||
// 分页(检验项目 tab)
|
||||
// ==============================
|
||||
const inspectionCurrentPage = ref(1);
|
||||
// 每页条数:按需求固定为10
|
||||
const inspectionPageSize = ref(10);
|
||||
const inspectionTotalPages = computed(() => {
|
||||
const total = filteredInspectionItems.value.length;
|
||||
return Math.max(1, Math.ceil(total / inspectionPageSize.value));
|
||||
});
|
||||
const inspectionPageButtons = computed(() => {
|
||||
const total = inspectionTotalPages.value;
|
||||
return Array.from({ length: total }, (_, i) => i + 1);
|
||||
});
|
||||
const pagedInspectionItems = computed(() => {
|
||||
const start = (inspectionCurrentPage.value - 1) * inspectionPageSize.value;
|
||||
return filteredInspectionItems.value.slice(start, start + inspectionPageSize.value);
|
||||
});
|
||||
function inspectionGoPage(p) {
|
||||
if (p < 1 || p > inspectionTotalPages.value) return;
|
||||
inspectionCurrentPage.value = p;
|
||||
}
|
||||
function inspectionPrevPage() {
|
||||
if (inspectionCurrentPage.value <= 1) return;
|
||||
inspectionCurrentPage.value -= 1;
|
||||
}
|
||||
function inspectionNextPage() {
|
||||
if (inspectionCurrentPage.value >= inspectionTotalPages.value) return;
|
||||
inspectionCurrentPage.value += 1;
|
||||
}
|
||||
watch(
|
||||
() => filteredInspectionItems.value.length,
|
||||
() => {
|
||||
// 过滤条件变化后,回到第一页,避免出现“当前页没数据但页码还在后面”的体验
|
||||
inspectionCurrentPage.value = 1;
|
||||
}
|
||||
);
|
||||
|
||||
// 执行过滤
|
||||
const filterItems = () => {
|
||||
// 过滤逻辑已经在computed属性中实现,这里可以添加额外的逻辑
|
||||
@@ -1371,7 +1508,9 @@ const addNewRow = () => {
|
||||
ElMessage.warning('请先保存或取消当前正在编辑的行');
|
||||
return;
|
||||
}
|
||||
const newRow = { id: Date.now(), code: '', name: '', department: departments.value[0], sortOrder: tableData.value.length + 1, remark: '' };
|
||||
// department 在表格里是字符串(科室名称);这里不要直接塞对象,否则后续 trim/校验会异常
|
||||
const defaultDeptName = departments.value?.[0]?.name || '';
|
||||
const newRow = { id: Date.now(), code: '', name: '', department: defaultDeptName, sortOrder: tableData.value.length + 1, remark: '' };
|
||||
tableData.value.push(newRow);
|
||||
editingRowId.value = newRow.id;
|
||||
};
|
||||
@@ -1391,41 +1530,28 @@ const handleConfirm = (row) => {
|
||||
// 确保sortOrder字段存在且为数字类型
|
||||
sortOrder: row.sortOrder ? Number(row.sortOrder) : 0
|
||||
};
|
||||
|
||||
// 兼容 department 可能是对象的情况(例如 el-tree-select 返回节点对象)
|
||||
if (submitData.department && typeof submitData.department === 'object') {
|
||||
submitData.department = submitData.department.name || submitData.department.label || '';
|
||||
}
|
||||
|
||||
console.log('原始row数据:', row);
|
||||
console.log('提交前的submitData:', submitData);
|
||||
|
||||
// 验证必填字段,如果为空则删除该行
|
||||
// 验证必填字段:为空则提示并保留该行(不要直接删行,否则用户“点确定就没了”体验很差)
|
||||
if (!submitData.code || submitData.code.trim() === '') {
|
||||
// 删除空的编辑行
|
||||
const index = tableData.value.findIndex(r => r.id === row.id);
|
||||
if (index !== -1) {
|
||||
tableData.value.splice(index, 1);
|
||||
editingRowId.value = null;
|
||||
ElMessage.info('已删除空行');
|
||||
}
|
||||
ElMessage.warning('请输入大类编码');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!submitData.name || submitData.name.trim() === '') {
|
||||
// 删除空的编辑行
|
||||
const index = tableData.value.findIndex(r => r.id === row.id);
|
||||
if (index !== -1) {
|
||||
tableData.value.splice(index, 1);
|
||||
editingRowId.value = null;
|
||||
ElMessage.info('已删除空行');
|
||||
}
|
||||
ElMessage.warning('请输入大类项目名称');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!submitData.department || submitData.department.trim() === '') {
|
||||
// 删除空的编辑行
|
||||
const index = tableData.value.findIndex(r => r.id === row.id);
|
||||
if (index !== -1) {
|
||||
tableData.value.splice(index, 1);
|
||||
editingRowId.value = null;
|
||||
ElMessage.info('已删除空行');
|
||||
}
|
||||
if (!submitData.department || String(submitData.department).trim() === '') {
|
||||
ElMessage.warning('请选择执行科室');
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1486,7 +1612,40 @@ const handleAdd = (row, index) => {
|
||||
ElMessage.warning('请先保存或取消当前正在编辑的行');
|
||||
return;
|
||||
}
|
||||
const newRow = { id: Date.now(), code: '', name: '', department: row.department, sortOrder: row.sortOrder + 1, remark: '' };
|
||||
|
||||
// 行内“+”:在当前大类下新增子类,编码按“父类编码-001”递增生成
|
||||
const parentCode = (row?.code ?? '').toString().trim();
|
||||
if (!parentCode) {
|
||||
ElMessage.warning('请先填写并保存当前大类编码,再新增子类');
|
||||
return;
|
||||
}
|
||||
|
||||
// 找到该父类下已有的子类编码,取最大序号 + 1
|
||||
// 支持如:1-001、01-002 等
|
||||
const prefix = `${parentCode}-`;
|
||||
let maxSeq = 0;
|
||||
for (const r of tableData.value) {
|
||||
const code = (r?.code ?? '').toString();
|
||||
if (code.startsWith(prefix)) {
|
||||
const suffix = code.slice(prefix.length);
|
||||
const n = Number.parseInt(suffix, 10);
|
||||
if (!Number.isNaN(n)) {
|
||||
maxSeq = Math.max(maxSeq, n);
|
||||
}
|
||||
}
|
||||
}
|
||||
const nextSeq = maxSeq + 1;
|
||||
const childCode = `${parentCode}-${String(nextSeq).padStart(3, '0')}`;
|
||||
|
||||
const newRow = {
|
||||
id: Date.now(),
|
||||
code: childCode,
|
||||
name: '',
|
||||
department: row.department,
|
||||
// 序号字段保持原逻辑:插入到父类下一行,默认沿用父类序号
|
||||
sortOrder: row.sortOrder,
|
||||
remark: ''
|
||||
};
|
||||
tableData.value.splice(index + 1, 0, newRow);
|
||||
editingRowId.value = newRow.id;
|
||||
};
|
||||
@@ -2069,22 +2228,52 @@ onMounted(() => {
|
||||
loadObservationItems();
|
||||
// 加载检验套餐明细项目
|
||||
loadPackageItemsFromAPI();
|
||||
// 检查URL参数,如果有tab参数则切换到对应导航项
|
||||
const query = router.currentRoute.value.query;
|
||||
if (query.tab === '0' || query.tab === '1' || query.tab === '2') {
|
||||
activeNav.value = parseInt(query.tab);
|
||||
}
|
||||
// 检查URL参数中是否有packageId,如果有则加载套餐数据(用于编辑或查看)
|
||||
if (query.packageId) {
|
||||
// 切换到套餐设置标签页
|
||||
activeNav.value = 2;
|
||||
// 加载套餐数据
|
||||
loadInspectionPackage(query.packageId);
|
||||
}
|
||||
// 初始化计算套餐金额和服务费
|
||||
// 初始化计算套餐金额和服务费
|
||||
calculateAmounts();
|
||||
});
|
||||
|
||||
/**
|
||||
* 关键修复:
|
||||
* 从“套餐管理”跳转到这里时,通常只是改变 query(packageId/mode/tab),组件不会重新挂载,
|
||||
* 所以仅靠 onMounted 读取一次 query 会导致“查看/修改”出现空白,刷新才正常。
|
||||
* 这里监听 query 变化,自动切换 tab 并加载套餐数据。
|
||||
*/
|
||||
watch(
|
||||
() => route.query.tab,
|
||||
(tab) => {
|
||||
if (tab === '0' || tab === '1' || tab === '2') {
|
||||
activeNav.value = parseInt(String(tab));
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
|
||||
const applyRouteForPackage = async (query) => {
|
||||
const packageId = query?.packageId;
|
||||
if (!packageId) return;
|
||||
activeNav.value = 2;
|
||||
await nextTick();
|
||||
loadInspectionPackage(String(packageId));
|
||||
};
|
||||
|
||||
watch(
|
||||
() => route.query.packageId,
|
||||
async () => {
|
||||
await applyRouteForPackage(route.query);
|
||||
},
|
||||
{ immediate: true, flush: 'post' }
|
||||
);
|
||||
|
||||
// 兜底:如果该页面被 keep-alive 缓存,从别的页面返回时不会触发 onMounted
|
||||
onActivated(() => {
|
||||
applyRouteForPackage(route.query);
|
||||
});
|
||||
|
||||
// 兜底:同一路由复用/仅 query 变化时,确保能触发加载
|
||||
onBeforeRouteUpdate((to) => {
|
||||
applyRouteForPackage(to.query);
|
||||
});
|
||||
|
||||
// 监听生成服务费选项变更
|
||||
watch(generateServiceFee, (newVal) => {
|
||||
calculateAmounts();
|
||||
@@ -2422,6 +2611,12 @@ watch(packageItems, (newVal) => {
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.page-btn.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 套餐设置样式 */
|
||||
.top-bar {
|
||||
height: 48px;
|
||||
|
||||
Reference in New Issue
Block a user