需求56 检查项目设置-》检查类型维护;在check_type表中增加一个parent_id字段用于父行与子行绑定;修改执行科室下拉字典的数据来源
This commit is contained in:
@@ -52,6 +52,9 @@ public class CheckType {
|
||||
/** 更新时间 */
|
||||
private LocalDateTime updateTime;
|
||||
|
||||
/** 父级ID */
|
||||
private Long parentId;
|
||||
|
||||
/**
|
||||
* 禁用逻辑删除,因为数据库表中没有delete_flag字段
|
||||
*/
|
||||
|
||||
@@ -84,10 +84,10 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in tableData"
|
||||
<tr
|
||||
v-for="(item, index) in tableData"
|
||||
:key="index"
|
||||
:class="{ 'editing-row': item.editing, 'child-row': item.row.includes('.') }"
|
||||
:class="{ 'editing-row': item.editing, 'child-row': !!item.parentId }"
|
||||
@click="handleRowClick(index)"
|
||||
>
|
||||
<td>{{ item.row }}</td>
|
||||
@@ -177,7 +177,7 @@
|
||||
✕
|
||||
</button>
|
||||
<button
|
||||
v-if="!item.row.includes('.')"
|
||||
v-if="!item.parentId"
|
||||
class="btn btn-add"
|
||||
@click.stop="handleAdd(index)"
|
||||
title="添加子项"
|
||||
@@ -190,7 +190,7 @@
|
||||
✏️
|
||||
</button>
|
||||
<button
|
||||
v-if="!item.row.includes('.')"
|
||||
v-if="!item.parentId"
|
||||
class="btn btn-add"
|
||||
@click.stop="handleAdd(index)"
|
||||
title="添加子项"
|
||||
@@ -567,6 +567,7 @@
|
||||
import {computed, onMounted, reactive, ref} from 'vue';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
import {getDicts} from '@/api/system/dict/data';
|
||||
import {getList as getDeptList} from '@/views/basicmanage/organization/components/api';
|
||||
import {
|
||||
addCheckMethod,
|
||||
addCheckPart,
|
||||
@@ -677,14 +678,105 @@ const currentSearchParams = computed(() => {
|
||||
// 按钮悬停状态
|
||||
const hoverAddButton = ref(false);
|
||||
|
||||
// 排序函数:确保父行在前,子行在父行后面
|
||||
function sortTableDataByParentChild(data) {
|
||||
// 分离父行和子行
|
||||
const parentRows = [];
|
||||
const childRows = [];
|
||||
|
||||
data.forEach(item => {
|
||||
// parentId 为 null/undefined/0 的都是父行
|
||||
if (item.parentId) {
|
||||
childRows.push(item);
|
||||
} else {
|
||||
parentRows.push(item);
|
||||
}
|
||||
});
|
||||
|
||||
// 按编码排序父行
|
||||
parentRows.sort((a, b) => {
|
||||
const codeA = a.code || '';
|
||||
const codeB = b.code || '';
|
||||
return codeA.localeCompare(codeB, undefined, { numeric: true });
|
||||
});
|
||||
|
||||
// 构建最终排序结果
|
||||
const sortedData = [];
|
||||
let rowNum = 1;
|
||||
|
||||
parentRows.forEach(parent => {
|
||||
// 添加父行
|
||||
parent.row = String(rowNum);
|
||||
sortedData.push(parent);
|
||||
|
||||
// 查找并添加该父行的所有子行
|
||||
const children = childRows.filter(child => child.parentId === parent.id);
|
||||
|
||||
// 按子行编码排序
|
||||
children.sort((a, b) => {
|
||||
const codeA = a.code || '';
|
||||
const codeB = b.code || '';
|
||||
return codeA.localeCompare(codeB, undefined, { numeric: true });
|
||||
});
|
||||
|
||||
// 添加子行,并设置子行行号
|
||||
children.forEach((child, index) => {
|
||||
child.row = parent.row + '.' + (index + 1);
|
||||
sortedData.push(child);
|
||||
});
|
||||
|
||||
rowNum++;
|
||||
});
|
||||
|
||||
return sortedData;
|
||||
}
|
||||
|
||||
// 从数据库获取所有检查相关数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
// 获取科室字典数据
|
||||
const deptResponse = await getDicts('dept');
|
||||
if (deptResponse && deptResponse.data) {
|
||||
// 保存完整的科室字典数据,包含编码和名称
|
||||
departments.value = deptResponse.data;
|
||||
// 1. 获取科室分类字典,找到“医学影像科”对应的 value
|
||||
let imageClassValues = [];
|
||||
try {
|
||||
const dictRes = await getDicts('organization_class');
|
||||
if (dictRes && dictRes.data) {
|
||||
imageClassValues = dictRes.data
|
||||
.filter(item => item.dictLabel.includes('医学影像科'))
|
||||
.map(item => item.dictValue);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('获取字典失败', e);
|
||||
}
|
||||
|
||||
// 2. 获取科室列表
|
||||
const deptResponse = await getDeptList({ pageNo: 1, pageSize: 1000 });
|
||||
|
||||
if (deptResponse && (deptResponse.data || deptResponse.records)) {
|
||||
const records = deptResponse.data?.records || deptResponse.records || [];
|
||||
|
||||
// 3. 过滤
|
||||
const filteredDepts = records.filter(item => {
|
||||
// 匹配字典值
|
||||
let matchClass = false;
|
||||
if (item.classEnum && imageClassValues.length > 0) {
|
||||
// classEnum 可能是 "1" 或 "1,2"
|
||||
const itemClasses = String(item.classEnum).split(',');
|
||||
matchClass = itemClasses.some(c => imageClassValues.includes(c));
|
||||
}
|
||||
|
||||
// 匹配文本
|
||||
const matchText = item.classEnum_dictText && item.classEnum_dictText.includes('医学影像科');
|
||||
|
||||
// 匹配名称 (保底) - 放宽匹配条件
|
||||
const matchName = ['超声', '放射', '内镜', '心电'].some(k => item.name && item.name.includes(k));
|
||||
|
||||
return matchClass || matchText || matchName;
|
||||
});
|
||||
|
||||
// 4. 赋值
|
||||
departments.value = filteredDepts.map(item => ({
|
||||
dictValue: item.name,
|
||||
dictLabel: item.name
|
||||
}));
|
||||
}
|
||||
|
||||
// 获取检查类型数据(从数据字典获取)
|
||||
@@ -713,19 +805,24 @@ onMounted(async () => {
|
||||
if (typeTableResponse && typeTableResponse.data) {
|
||||
// 构建检查类型表格数据
|
||||
checkTypeData.splice(0, checkTypeData.length);
|
||||
typeTableResponse.data.forEach((item, index) => {
|
||||
checkTypeData.push({
|
||||
id: item.id, // 保存id字段,用于判断是新增还是修改
|
||||
row: (index + 1).toString(),
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
selected: true,
|
||||
department: item.department || '',
|
||||
number: item.number || '999999',
|
||||
remark: item.remark || '',
|
||||
actions: true
|
||||
});
|
||||
const tempData = typeTableResponse.data.map((item) => ({
|
||||
id: item.id, // 保存id字段,用于判断是新增还是修改
|
||||
row: '', // 行号将在排序后重新分配
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
selected: true,
|
||||
department: item.department || '',
|
||||
number: item.number || '999999',
|
||||
remark: item.remark || '',
|
||||
parentId: item.parentId || null, // 父行的 parentId 保持为 null
|
||||
actions: true
|
||||
}));
|
||||
|
||||
// 排序数据,确保父行在前,子行在父行后
|
||||
const sortedData = sortTableDataByParentChild(tempData);
|
||||
sortedData.forEach(item => {
|
||||
checkTypeData.push(item);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -820,29 +917,31 @@ async function loadMenuData(menu) {
|
||||
case '检查类型':
|
||||
// 清空检查类型数据
|
||||
checkTypeData.splice(0, checkTypeData.length);
|
||||
|
||||
const typeResponse = await listCheckType();
|
||||
if (typeResponse && typeResponse.data) {
|
||||
// 确保data是数组类型
|
||||
const typeData = Array.isArray(typeResponse.data) ? typeResponse.data : [];
|
||||
|
||||
// 获取所有不重复的检查类型值
|
||||
checkTypes.value = [...new Set(typeData.map(item => item.type))];
|
||||
|
||||
typeData.forEach((item, index) => {
|
||||
// 直接使用数据库中的department值,不进行转换
|
||||
checkTypeData.push({
|
||||
id: item.id, // 保存id字段,用于判断是新增还是修改
|
||||
row: (index + 1).toString(),
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
selected: true,
|
||||
department: item.department || '',
|
||||
number: item.number || '999999',
|
||||
remark: item.remark || '',
|
||||
actions: true
|
||||
});
|
||||
// 构建临时数据
|
||||
const tempData = typeData.map((item) => ({
|
||||
id: item.id, // 保存id字段,用于判断是新增还是修改
|
||||
row: '', // 行号将在排序后重新分配
|
||||
code: item.code,
|
||||
name: item.name,
|
||||
type: item.type,
|
||||
selected: true,
|
||||
department: item.department || '',
|
||||
number: item.number || '999999',
|
||||
remark: item.remark || '',
|
||||
parentId: item.parentId || null, // 父行的 parentId 保持为 null
|
||||
actions: true
|
||||
}));
|
||||
|
||||
// 排序数据,确保父行在前,子行在父行后
|
||||
const sortedData = sortTableDataByParentChild(tempData);
|
||||
sortedData.forEach(item => {
|
||||
checkTypeData.push(item);
|
||||
});
|
||||
}
|
||||
break;
|
||||
@@ -985,7 +1084,11 @@ function handleEdit(index) {
|
||||
// 处理取消编辑按钮点击
|
||||
function handleCancelEdit(index) {
|
||||
const item = tableData.value[index];
|
||||
item.editing = false;
|
||||
if (item.isNew) {
|
||||
tableData.value.splice(index, 1);
|
||||
} else {
|
||||
item.editing = false;
|
||||
}
|
||||
}
|
||||
|
||||
// 处理确认按钮点击
|
||||
@@ -1038,11 +1141,24 @@ async function handleConfirm(index) {
|
||||
orderNum: item.orderNum,
|
||||
remark: item.remark
|
||||
};
|
||||
console.log('准备新增检查方法:', addData);
|
||||
const newItem = await addCheckMethod(addData);
|
||||
console.log('新增检查方法返回结果:', newItem);
|
||||
// 将新增的id赋值给本地数据
|
||||
item.id = newItem.id;
|
||||
const response = await addCheckMethod(addData);
|
||||
|
||||
// 将新增的id赋值给本地数据,兼容不同的返回格式
|
||||
let newId = null;
|
||||
if (response) {
|
||||
// 尝试多种可能的返回格式
|
||||
newId = response.id ||
|
||||
response.data?.id ||
|
||||
response.data?.data?.id ||
|
||||
(typeof response.data === 'number' ? response.data : null) ||
|
||||
(typeof response.data === 'string' ? response.data : null);
|
||||
}
|
||||
|
||||
if (newId) {
|
||||
item.id = newId;
|
||||
} else {
|
||||
ElMessage.warning('数据已保存,但未能获取ID,删除功能可能受影响');
|
||||
}
|
||||
}
|
||||
} else if (activeMenu.value === '检查部位') {
|
||||
// 检查部位的保存逻辑
|
||||
@@ -1094,7 +1210,8 @@ async function handleConfirm(index) {
|
||||
selected: item.selected,
|
||||
department: item.department,
|
||||
number: item.number,
|
||||
remark: item.remark
|
||||
remark: item.remark,
|
||||
parentId: item.parentId || null // 父行的 parentId 设为 null
|
||||
};
|
||||
await updateCheckType(updateData);
|
||||
} else {
|
||||
@@ -1106,19 +1223,25 @@ async function handleConfirm(index) {
|
||||
selected: item.selected,
|
||||
department: item.department,
|
||||
number: item.number,
|
||||
remark: item.remark
|
||||
remark: item.remark,
|
||||
parentId: item.parentId || null // 父行的 parentId 设为 null
|
||||
};
|
||||
console.log('准备新增检查类型:', addData);
|
||||
const newItem = await addCheckType(addData);
|
||||
console.log('新增检查类型返回结果:', newItem);
|
||||
// 将新增的id赋值给本地数据
|
||||
item.id = newItem.id;
|
||||
const response = await addCheckType(addData);
|
||||
// 将新增的id赋值给本地数据,兼容不同的返回格式
|
||||
const newItem = response.data || response;
|
||||
item.id = newItem.id || newItem;
|
||||
console.log('保存的ID:', item.id);
|
||||
}
|
||||
}
|
||||
// 退出编辑状态
|
||||
tableData.value[index].editing = false;
|
||||
if (item.isNew) {
|
||||
item.isNew = false;
|
||||
}
|
||||
// 显示保存成功提示
|
||||
ElMessage.success(`第 ${item.row} 行数据已保存`);
|
||||
// 保存成功后自动刷新列表
|
||||
await loadMenuData(activeMenu.value);
|
||||
} catch (error) {
|
||||
console.error('保存失败,错误信息:', error);
|
||||
console.error('保存失败,错误详情:', error.response ? error.response.data : error);
|
||||
@@ -1128,26 +1251,72 @@ async function handleConfirm(index) {
|
||||
|
||||
// 处理删除按钮点击
|
||||
async function handleDelete(index) {
|
||||
ElMessageBox.confirm('确定要删除这一行吗?', '系统提示', {
|
||||
const item = tableData.value[index];
|
||||
|
||||
// 如果是新增但未保存的行(没有ID或标记为新增),直接从列表中移除
|
||||
// 注意:必须严格检查 ID 是否为有效值
|
||||
const hasValidId = item.id &&
|
||||
item.id !== null &&
|
||||
item.id !== undefined &&
|
||||
item.id !== '' &&
|
||||
String(item.id).trim() !== '' &&
|
||||
String(item.id) !== 'undefined' &&
|
||||
String(item.id) !== 'null';
|
||||
|
||||
if (!hasValidId || item.isNew) {
|
||||
ElMessageBox.confirm('该行数据尚未保存,确定要删除吗?', '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
tableData.value.splice(index, 1);
|
||||
ElMessage.success('删除成功');
|
||||
}).catch(() => {
|
||||
console.log('用户取消删除操作');
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 检查是否为父行
|
||||
const isParentRow = activeMenu.value === '检查类型' && !item.parentId;
|
||||
|
||||
// 如果是父行,查找所有子行
|
||||
let childRows = [];
|
||||
if (isParentRow && item.id) {
|
||||
childRows = tableData.value.filter(row => row.parentId === item.id);
|
||||
}
|
||||
|
||||
// 构建确认消息
|
||||
let confirmMessage = '确定要删除这一行吗?';
|
||||
if (isParentRow && childRows.length > 0) {
|
||||
confirmMessage = `确定要删除这一行吗?\n该操作将同时删除 ${childRows.length} 个子行数据。`;
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(confirmMessage, '系统提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(async () => {
|
||||
const item = tableData.value[index];
|
||||
try {
|
||||
// 如果有id,调用API删除数据库中的数据
|
||||
if (item.id) {
|
||||
if (activeMenu.value === '检查方法') {
|
||||
await delCheckMethod(item.id);
|
||||
} else if (activeMenu.value === '检查部位') {
|
||||
await delCheckPart(item.id);
|
||||
} else {
|
||||
await delCheckType(item.id);
|
||||
}
|
||||
// 确保 ID 是有效的数字或字符串
|
||||
const validId = String(item.id).trim();
|
||||
if (!validId || validId === 'undefined' || validId === 'null') {
|
||||
ElMessage.error('删除失败,无效的ID');
|
||||
return;
|
||||
}
|
||||
// 从数组中删除该行数据
|
||||
tableData.value.splice(index, 1);
|
||||
ElMessage.success('删除成功!');
|
||||
|
||||
// 只需要删除当前行,数据库会通过外键级联自动删除子行
|
||||
if (activeMenu.value === '检查方法') {
|
||||
await delCheckMethod(validId);
|
||||
} else if (activeMenu.value === '检查部位') {
|
||||
await delCheckPart(validId);
|
||||
} else {
|
||||
await delCheckType(validId);
|
||||
}
|
||||
|
||||
// 删除成功,刷新列表数据
|
||||
await loadMenuData(activeMenu.value);
|
||||
ElMessage.success(`删除成功!${isParentRow && childRows.length > 0 ? `已删除 1 个父行和 ${childRows.length} 个子行` : ''}`);
|
||||
} catch (error) {
|
||||
ElMessage.error('删除失败,请稍后重试');
|
||||
}
|
||||
@@ -1181,6 +1350,7 @@ function handleAddNewRow() {
|
||||
department: '',
|
||||
number: '999999',
|
||||
remark: '',
|
||||
parentId: null, // 父行的 parentId 设为 null,避免外键约束错误
|
||||
editing: true, // 新行默认进入编辑状态
|
||||
isNew: true, // 标记为新增行
|
||||
actions: true
|
||||
@@ -1240,37 +1410,49 @@ function handleAddNewRow() {
|
||||
function handleAdd(index) {
|
||||
const parentRow = tableData.value[index];
|
||||
|
||||
if (!parentRow.id) {
|
||||
ElMessage.warning('请先保存父行数据后再添加子行');
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找该父行的所有现有子行,确定下一个子行号
|
||||
const parentRowPrefix = parentRow.row + '.';
|
||||
let maxChildNum = 0;
|
||||
const children = tableData.value.filter(item => item.parentId === parentRow.id);
|
||||
const nextChildNum = children.length + 1;
|
||||
|
||||
tableData.value.forEach((item, idx) => {
|
||||
if (item.row.startsWith(parentRowPrefix) && idx > index) {
|
||||
const childNum = parseInt(item.row.split('.').pop());
|
||||
if (childNum > maxChildNum) {
|
||||
maxChildNum = childNum;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const nextChildNum = maxChildNum + 1;
|
||||
|
||||
// 创建子行数据,继承父行的编码
|
||||
// 生成默认子行编码
|
||||
const childCode = parentRow.code ? `${parentRow.code}_${String(nextChildNum).padStart(2, '0')}` : '';
|
||||
|
||||
// 创建子行数据
|
||||
const childRow = {
|
||||
row: parentRow.row + '.' + nextChildNum, // 子行编号
|
||||
code: parentRow.code, // 继承父行编码
|
||||
row: parentRow.row + '.' + nextChildNum, // 子行行号显示
|
||||
code: childCode,
|
||||
name: '',
|
||||
type: '',
|
||||
type: parentRow.type,
|
||||
selected: false,
|
||||
department: '',
|
||||
department: parentRow.department,
|
||||
number: '',
|
||||
remark: '',
|
||||
parentId: parentRow.id,
|
||||
editing: true,
|
||||
actions: false
|
||||
isNew: true,
|
||||
actions: true
|
||||
};
|
||||
|
||||
// 在父行后插入子行
|
||||
tableData.value.splice(index + 1, 0, childRow);
|
||||
// 找到父行的最后一个子行位置,在其后插入新子行
|
||||
let insertIndex = index + 1;
|
||||
for (let i = index + 1; i < tableData.value.length; i++) {
|
||||
const item = tableData.value[i];
|
||||
// 如果是该父行的子行,继续向后查找
|
||||
if (item.parentId === parentRow.id) {
|
||||
insertIndex = i + 1;
|
||||
} else {
|
||||
// 遇到非子行,停止查找
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 在找到的位置插入子行
|
||||
tableData.value.splice(insertIndex, 0, childRow);
|
||||
}
|
||||
|
||||
// 处理搜索功能
|
||||
|
||||
Reference in New Issue
Block a user