Files
his/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/index.vue
2026-01-16 16:32:36 +08:00

1954 lines
59 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="check-project-settings">
<!-- 左侧导航栏 -->
<div class="sidebar">
<div class="sidebar-header">
<h3>检查项目管理</h3>
</div>
<div class="sidebar-menu">
<!-- 明确列出所有导航项 -->
<button
class="menu-item active"
@click="handleMenuClick('检查类型')"
>
<span class="menu-icon">📋</span>
<span class="menu-text">检查类型</span>
</button>
<button
class="menu-item"
@click="handleMenuClick('检查方法')"
>
<span class="menu-icon">🔬</span>
<span class="menu-text">检查方法</span>
</button>
<button
class="menu-item"
@click="handleMenuClick('检查部位')"
>
<span class="menu-icon">🎯</span>
<span class="menu-text">检查部位</span>
</button>
<button
class="menu-item"
@click="handleMenuClick('套餐设置')"
>
<span class="menu-icon">📦</span>
<span class="menu-text">套餐设置</span>
</button>
</div>
</div>
<!-- 主内容区域 -->
<div class="content">
<!-- 套餐管理和套餐设置 -->
<template v-if="activeMenu === '套餐设置'">
<!-- 套餐设置界面默认显示 -->
<PackageSettings
v-if="packageView === 'settings'"
:mode="packageMode"
:package-data="currentPackageData"
@switch-to-management="handleSwitchToManagement"
@save-success="handleSaveSuccess"
/>
<!-- 套餐管理界面列表 -->
<PackageManagement
v-else
@switch-to-settings="handleSwitchToSettings"
/>
</template>
<!-- 检查类型的表格视图 -->
<template v-if="activeMenu === '检查类型'">
<div class="header">
<h1>{{ activeMenu }}管理</h1>
<div class="header-actions">
<button class="btn btn-add-new" @click="handleAddNewRow">+
</button>
</div>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th style="width: 50px;"></th>
<th style="width: 100px;">*编码</th>
<th style="width: 150px;">*名称</th>
<th style="width: 150px;">*检查类型</th>
<th style="width: 120px;">选择部位</th>
<th style="width: 150px;">*执行科室</th>
<th style="width: 100px;">序号</th>
<th style="width: 150px;">备注</th>
<th style="width: 120px;">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in tableData"
:key="index"
:class="{ 'editing-row': item.editing, 'child-row': item.row.includes('.') }"
>
<td>{{ item.row }}</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入编码" v-model="item.code">
</template>
<template v-else>
{{ item.code }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入名称" v-model="item.name">
</template>
<template v-else>
{{ item.name }}
</template>
</td>
<td>
<template v-if="item.editing">
<select v-model="item.type" :class="{ 'placeholder-text': !item.type }">
<option value="">选择检查类型</option>
<option
v-for="dict in inspectionTypeDicts"
:key="dict.dictValue"
:value="dict.dictValue"
>
{{ dict.dictLabel }}
</option>
</select>
</template>
<template v-else>
<span v-if="item.type">{{ getInspectionTypeLabel(item.type) }}</span>
<span v-else class="placeholder-text">选择检查类型</span>
</template>
</td>
<td class="checkbox-container">
<input type="checkbox" v-model="item.selected" @click.stop>
</td>
<td>
<template v-if="item.editing">
<select v-model="item.department" :class="{ 'placeholder-text': !item.department }">
<option value="">选择执行科室</option>
<option
v-if="item.department"
:value="item.department"
>
{{ item.department }}
</option>
<option
v-for="dept in departments"
:key="dept.dictValue"
:value="dept.dictLabel"
>
{{ dept.dictLabel }}
</option>
</select>
</template>
<template v-else>
<span v-if="item.department">{{ item.department }}</span>
<span v-else class="placeholder-text">选择执行科室</span>
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" v-model="item.number">
</template>
<template v-else>
{{ item.number }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入备注" v-model="item.remark">
</template>
<template v-else>
{{ item.remark || '' }}
</template>
</td>
<td class="actions">
<template v-if="item.editing">
<button class="btn btn-confirm" @click.stop="handleConfirm(index)" title="保存">
</button>
<button class="btn btn-cancel" @click.stop="handleCancelEdit(index)" title="取消">
</button>
<button
v-if="!item.row.includes('.')"
class="btn btn-add"
@click.stop="handleAdd(index)"
title="添加子项"
>
+
</button>
</template>
<template v-else>
<button class="btn btn-edit" @click.stop="handleEdit(index)" title="修改">
</button>
<button
v-if="!item.row.includes('.')"
class="btn btn-add"
@click.stop="handleAdd(index)"
title="添加子项"
>
+
</button>
<button class="btn btn-delete" @click.stop="handleDelete(index)" title="删除">
</button>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<!-- 检查方法的表格视图 -->
<template v-else-if="activeMenu === '检查方法'">
<div class="header">
<h1>{{ activeMenu }}管理</h1>
</div>
<div class="search-bar search-bar-method">
<div class="search-filters">
<div class="search-item">
<label>检查类型</label>
<el-select v-model="searchParamsMethod.checkType" placeholder="选择检查类型" style="width: 150px">
<el-option
v-for="dict in inspectionTypeDicts"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
>
</el-option>
</el-select>
</div>
<div class="search-item">
<label>名称</label>
<el-input placeholder="名称/编码" v-model="searchParamsMethod.name" />
</div>
<div class="search-item">
<label>费用套餐</label>
<el-select v-model="searchParamsMethod.packageName" placeholder="选择使用套餐" style="width: 150px">
<el-option
v-for="pkg in checkPackages"
:key="pkg.id"
:label="pkg.name"
:value="pkg.name"
>
</el-option>
</el-select>
</div>
</div>
<div class="search-actions">
<el-button type="primary" @mouseenter="hoverAddButton = true" @mouseleave="hoverAddButton = false" @click="handleAddNewRow">新增</el-button>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button type="primary" @click="handleReset">重置</el-button>
<el-button type="success" @click="handleExport">导出表格</el-button>
</div>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th style="width: 50px;"></th>
<th style="width: 100px;">代码</th>
<th style="width: 150px;">名称</th>
<th style="width: 150px;">检查类型</th>
<th style="width: 150px;">套餐名称</th>
<th style="width: 100px;">曝光次数</th>
<th style="width: 100px;">序号</th>
<th style="width: 150px;">备注</th>
<th style="width: 120px;">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in checkMethodData"
:key="index"
:class="{ 'editing-row': item.editing }"
@click="handleEdit(index)"
>
<td>{{ item.row }}</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入编码" v-model="item.code">
</template>
<template v-else>
{{ item.code }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入方法名称" v-model="item.name">
</template>
<template v-else>
{{ item.name }}
</template>
</td>
<td>
<template v-if="item.editing">
<select v-model="item.checkType">
<option value="">选择检查类型</option>
<option
v-for="dict in inspectionTypeDicts"
:key="dict.dictValue"
:value="dict.dictValue"
>
{{ dict.dictLabel }}
</option>
</select>
</template>
<template v-else>
{{ getInspectionTypeLabel(item.checkType) || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入套餐名称" v-model="item.packageName">
</template>
<template v-else>
{{ item.packageName || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" v-model="item.exposureNum">
</template>
<template v-else>
{{ item.exposureNum || 0}}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" v-model="item.orderNum">
</template>
<template v-else>
{{ item.orderNum || '0' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入备注" v-model="item.remark">
</template>
<template v-else>
{{ item.remark || '' }}
</template>
</td>
<td class="actions">
<template v-if="item.editing">
<button class="btn btn-confirm" @click.stop="handleConfirm(index)" title="保存">
</button>
<button class="btn btn-cancel" @click.stop="handleCancelEdit(index)" title="取消">
</button>
</template>
<template v-else>
<button class="btn btn-edit" @click.stop="handleEdit(index)" title="编辑">
</button>
<button class="btn btn-confirm" @click.stop="handleConfirm(index)" title="保存">
</button>
<button class="btn btn-delete" @click.stop="handleDelete(index)" title="删除">
</button>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<!-- 检查部位的表格视图 -->
<template v-else-if="activeMenu === '检查部位'">
<div class="header">
<h1>{{ activeMenu }}管理</h1>
</div>
<div class="search-bar search-bar-part">
<div class="search-item">
<label>检查类型</label>
<el-select v-model="searchParamsPart.checkType" placeholder="选择检查类型" style="width: 150px">
<el-option
v-for="dict in inspectionTypeDicts"
:key="dict.dictValue"
:label="dict.dictLabel"
:value="dict.dictValue"
>
</el-option>
</el-select>
</div>
<div class="search-item">
<label>名称</label>
<el-input placeholder="名称/编码" v-model="searchParamsPart.name" />
</div>
<div class="search-item">
<label>费用套餐</label>
<el-input placeholder="费用套餐" v-model="searchParamsPart.packageName" />
</div>
<div class="search-actions">
<el-button type="primary" @click="handleAddNewRow">新增</el-button>
<el-button type="primary" @click="handleSearch">查询</el-button>
<el-button type="primary" @click="handleReset">重置</el-button>
<el-button type="success" @click="handleExport">导出表格</el-button>
</div>
</div>
<div class="table-container">
<table>
<thead>
<tr>
<th style="width: 50px;"></th>
<th style="width: 100px;">*编码</th>
<th style="width: 100px;">*名称</th>
<th style="width: 120px;">检查类型</th>
<th style="width: 80px;">曝光次数</th>
<th style="width: 120px;">费用套餐</th>
<th style="width: 80px;">金额</th>
<th style="width: 50px;">序号</th>
<th style="width: 120px;">服务范围</th>
<th style="width: 120px;">下级医技类型</th>
<th style="width: 150px;">备注</th>
<th style="width: 120px;">操作</th>
</tr>
</thead>
<tbody>
<tr
v-for="(item, index) in checkPartData"
:key="index"
:class="{ 'editing-row': item.editing }"
@click="handleEdit(index)"
>
<td>{{ item.row }}</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入编码" v-model="item.code">
</template>
<template v-else>
{{ item.code }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入名称" v-model="item.name">
</template>
<template v-else>
{{ item.name }}
</template>
</td>
<td>
<template v-if="item.editing">
<select v-model="item.checkType">
<option value="">选择检查类型</option>
<option
v-for="dict in inspectionTypeDicts"
:key="dict.dictValue"
:value="dict.dictValue"
>
{{ dict.dictLabel }}
</option>
</select>
</template>
<template v-else>
{{ getInspectionTypeLabel(item.checkType) || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="number" min="0" placeholder="请输入曝光次数" v-model="item.exposureNum">
</template>
<template v-else>
{{ item.exposureNum || '0' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入费用套餐" v-model="item.packageName">
</template>
<template v-else>
{{ item.packageName || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="number" step="0.01" min="0" placeholder="请输入金额" v-model="item.price">
</template>
<template v-else>
{{ item.price || '0.00' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="number" min="0" placeholder="请输入序号" v-model="item.number">
</template>
<template v-else>
{{ item.number || '999999' }}
</template>
</td>
<td>
<template v-if="item.editing">
<select v-model="item.serviceScope">
<option value="">选择服务范围</option>
<option
v-for="dict in serviceScopeDicts"
:key="dict.dictValue"
:value="dict.dictValue"
>
{{ dict.dictLabel }}
</option>
</select>
</template>
<template v-else>
{{ getServiceScopeLabel(item.serviceScope) || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入下级医技类型" v-model="item.subType">
</template>
<template v-else>
{{ item.subType || '' }}
</template>
</td>
<td>
<template v-if="item.editing">
<input type="text" placeholder="请输入备注" v-model="item.remark">
</template>
<template v-else>
{{ item.remark || '' }}
</template>
</td>
<td class="actions">
<template v-if="item.editing">
<button class="btn btn-confirm" @click.stop="handleConfirm(index)" title="保存">
</button>
<button class="btn btn-cancel" @click.stop="handleCancelEdit(index)" title="取消">
</button>
</template>
<template v-else>
<button class="btn btn-edit" @click.stop="handleEdit(index)" title="编辑">
</button>
<button class="btn btn-confirm" @click.stop="handleConfirm(index)" title="保存">
</button>
<button class="btn btn-delete" @click.stop="handleDelete(index)" title="删除">
</button>
</template>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<!-- 分页区域 -->
<div class="pagination">
<button class="pagination-btn"></button>
<span>1</span>
<button class="pagination-btn"></button>
</div>
</div>
</div>
</template>
<script setup>
import {computed, onMounted, reactive, ref} from 'vue';
import {ElMessage, ElMessageBox} from 'element-plus';
import {getDicts} from '@/api/system/dict/data';
import {
addCheckMethod,
addCheckPart,
addCheckType,
delCheckMethod,
delCheckPart,
delCheckType,
exportCheckMethod,
exportCheckPart,
listCheckMethod,
listCheckPackage,
listCheckPart,
listCheckType,
searchCheckMethod,
searchCheckPart,
updateCheckMethod,
updateCheckPart,
updateCheckType
} from '@/api/system/checkType';
import PackageSettings from './components/PackageSettings.vue';
import PackageManagement from './components/PackageManagement.vue';
// 菜单数据
const menus = ['检查类型', '检查方法', '检查部位', '套餐设置'];
const activeMenu = ref('检查类型');
// 套餐视图状态: management-套餐管理列表, settings-套餐设置
const packageView = ref('management')
// 套餐设置模式: add-新增, edit-编辑, view-查看
const packageMode = ref('add')
// 当前编辑的套餐数据
const currentPackageData = ref(null)
// 检查类型和科室选项
const checkTypes = ref([]);
// 完整的检查类型字典数据
const inspectionTypeDicts = ref([]);
// 完整的服务范围字典数据
const serviceScopeDicts = ref([]);
const checkMethods = ref([]);
// 根据字典值获取检查类型标签
const getInspectionTypeLabel = (value) => {
const dictItem = inspectionTypeDicts.value.find(item => item.dictValue === value);
return dictItem ? dictItem.dictLabel : value;
};
// 根据字典值获取服务范围标签
const getServiceScopeLabel = (value) => {
const dictItem = serviceScopeDicts.value.find(item => item.dictValue === value);
return dictItem ? dictItem.dictLabel : value;
};
const checkParts = ref([]);
const checkPackages = ref([]);
const departments = ref([]);
// 表格数据 - 为每个菜单创建独立的数据存储
const checkTypeData = reactive([]);
const checkMethodData = reactive([]);
const checkPartData = reactive([]);
const packageData = reactive([]);
// 当前显示的表格数据
const tableData = computed(() => {
switch(activeMenu.value) {
case '检查类型':
return checkTypeData;
case '检查方法':
return checkMethodData;
case '检查部位':
return checkPartData;
case '套餐设置':
return packageData;
default:
return [];
}
});
// 搜索条件 - 为每个菜单创建独立的搜索参数
const searchParamsType = reactive({});
const searchParamsMethod = reactive({
checkType: '',
name: '',
packageName: ''
});
const searchParamsPart = reactive({
checkType: '',
name: '',
packageName: ''
});
// 获取当前菜单对应的搜索参数
const currentSearchParams = computed(() => {
switch(activeMenu.value) {
case '检查类型':
return searchParamsType;
case '检查方法':
return searchParamsMethod;
case '检查部位':
return searchParamsPart;
case '套餐设置':
return searchParamsMethod; // 套餐设置可能共享检查方法的搜索参数
default:
return {};
}
});
// 按钮悬停状态
const hoverAddButton = ref(false);
// 从数据库获取所有检查相关数据
onMounted(async () => {
try {
// 获取科室字典数据
const deptResponse = await getDicts('dept');
if (deptResponse && deptResponse.data) {
// 保存完整的科室字典数据,包含编码和名称
departments.value = deptResponse.data;
}
// 获取检查类型数据(从数据字典获取)
const typeResponse = await getDicts('inspection_type');
if (typeResponse && typeResponse.data) {
// 保存完整的字典数据
inspectionTypeDicts.value = typeResponse.data;
// 从数据字典获取检查类型值
checkTypes.value = typeResponse.data.map(item => item.dictValue);
} else {
checkTypes.value = [];
inspectionTypeDicts.value = [];
}
// 获取服务范围数据(从数据字典获取)
const scopeResponse = await getDicts('scope_of_services');
if (scopeResponse && scopeResponse.data) {
// 保存完整的服务范围字典数据
serviceScopeDicts.value = scopeResponse.data;
} else {
serviceScopeDicts.value = [];
}
// 获取检查类型表格数据仍然从API获取
const typeTableResponse = await listCheckType();
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 methodResponse = await listCheckMethod();
if (methodResponse && methodResponse.data) {
// 确保data是数组类型
checkMethods.value = Array.isArray(methodResponse.data) ? methodResponse.data : [];
} else {
checkMethods.value = [];
}
// 获取检查部位数据
const partResponse = await listCheckPart();
if (partResponse && partResponse.data) {
// 确保data是数组类型
checkParts.value = Array.isArray(partResponse.data) ? partResponse.data : [];
} else {
checkParts.value = [];
}
// 获取检查套餐数据
const packageResponse = await listCheckPackage();
if (packageResponse && packageResponse.data) {
// 确保data是数组类型
checkPackages.value = Array.isArray(packageResponse.data) ? packageResponse.data : [];
} else {
checkPackages.value = [];
}
} catch (error) {
console.error('获取数据失败:', error);
// 如果API调用失败显示友好提示
alert('获取检查类型数据失败,请检查网络或服务状态');
}
});
// 处理菜单点击
function handleMenuClick(menu) {
console.log('点击菜单:', menu);
console.log('当前activeMenu:', activeMenu.value);
activeMenu.value = menu;
console.log('更新后activeMenu:', activeMenu.value);
// 更新菜单激活状态
const menuItems = document.querySelectorAll('.menu-item');
menuItems.forEach(item => {
item.classList.remove('active');
if (item.textContent.trim() === menu) {
item.classList.add('active');
}
});
// 如果点击套餐设置,默认显示套餐设置界面
if (menu === '套餐设置') {
packageView.value = 'settings'
packageMode.value = 'add'
currentPackageData.value = null
console.log('显示套餐设置界面')
} else {
loadMenuData(menu);
}
}
// 从套餐设置切换到套餐管理界面
function handleSwitchToManagement() {
console.log('切换到套餐管理界面')
packageView.value = 'management'
}
// 从套餐管理切换到套餐设置界面(新增/编辑/查看)
function handleSwitchToSettings(params) {
console.log('切换到套餐设置界面:', params)
packageView.value = 'settings'
packageMode.value = params.mode
currentPackageData.value = params.data
}
// 保存成功后保持在套餐设置界面
function handleSaveSuccess() {
console.log('保存成功')
// 保存成功后保持在套餐设置界面,可以继续编辑或返回管理界面
ElMessage.success('保存成功')
}
// 根据菜单加载对应数据
async function loadMenuData(menu) {
try {
switch(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
});
});
}
break;
case '检查方法':
// 清空检查方法数据
checkMethodData.splice(0, checkMethodData.length);
// 构建检查方法的搜索参数
const methodParams = {
pageNo: 1,
pageSize: 100, // 默认获取100条数据可以根据需要调整
checkType: searchParamsMethod.checkType,
name: searchParamsMethod.name,
packageName: searchParamsMethod.packageName
};
try {
const methodResponse = await searchCheckMethod(methodParams);
// 确保data是数组适配不同的后端返回格式
let methodData = [];
if (methodResponse) {
if (Array.isArray(methodResponse)) {
methodData = methodResponse;
} else if (methodResponse.data && Array.isArray(methodResponse.data)) {
methodData = methodResponse.data;
} else if (methodResponse.data && methodResponse.data.data && Array.isArray(methodResponse.data.data)) {
methodData = methodResponse.data.data;
} else if (methodResponse.data && methodResponse.data.records) {
methodData = methodResponse.data.records;
}
}
// 处理数组数据
if (methodData.length > 0) {
methodData.forEach((item, index) => {
checkMethodData.push({
id: item.id, // 保存id字段用于判断是新增还是修改
row: (index + 1).toString(),
code: item.code,
name: item.name,
checkType: item.checkType || '',
packageName: item.packageName || '',
exposureNum: item.exposureNum || 0,
orderNum: item.orderNum || 0,
remark: item.remark || '',
actions: true
});
});
} else {
ElMessage.warning('未获取到检查方法数据');
}
} catch (error) {
ElMessage.error(`加载检查方法数据失败: ${error.message || '未知错误'}`);
}
break;
case '检查部位':
// 清空检查部位数据
checkPartData.splice(0, checkPartData.length);
// 构建检查部位的搜索参数
const partParams = {
pageNo: 1,
pageSize: 100, // 默认获取100条数据可以根据需要调整
checkType: searchParamsPart.checkType,
name: searchParamsPart.name,
packageName: searchParamsPart.packageName
};
try {
const partResponse = await searchCheckPart(partParams);
// 确保data是数组适配不同的后端返回格式
let partData = [];
if (partResponse) {
if (Array.isArray(partResponse)) {
partData = partResponse;
} else if (partResponse.data && Array.isArray(partResponse.data)) {
partData = partResponse.data;
} else if (partResponse.data && partResponse.data.data && Array.isArray(partResponse.data.data)) {
partData = partResponse.data.data;
} else if (partResponse.data && partResponse.data.records) {
partData = partResponse.data.records;
}
}
// 处理数组数据
if (partData.length > 0) {
partData.forEach((item, index) => {
checkPartData.push({
id: item.id, // 保存id字段用于判断是新增还是修改
row: (index + 1).toString(),
code: item.code,
name: item.name,
checkType: item.checkType || '',
exposureNum: item.exposureNum || 0,
packageName: item.packageName || '',
price: item.price || 0,
number: item.number || '999999',
serviceScope: item.serviceScope || '',
subType: item.subType || '',
remark: item.remark || '',
actions: true
});
});
} else {
ElMessage.warning('未获取到检查部位数据');
}
} catch (error) {
ElMessage.error(`加载检查部位数据失败: ${error.message || '未知错误'}`);
}
break;
case '套餐设置':
const packageResponse = await listCheckPackage();
if (packageResponse && packageResponse.data) {
}
break;
}
} catch (error) {
console.error('加载菜单数据失败:', error);
ElMessage.error(`加载${menu}数据失败,请检查网络或服务状态`);
}
}
// 处理编辑按钮点击
function handleEdit(index) {
const item = tableData.value[index];
item.editing = true;
}
// 处理取消编辑按钮点击
function handleCancelEdit(index) {
const item = tableData.value[index];
item.editing = false;
}
// 处理确认按钮点击
async function handleConfirm(index) {
const item = tableData.value[index];
try {
// 根据当前激活的菜单调用不同的API
if (activeMenu.value === '检查方法') {
// 检查方法的保存逻辑
if (item.id) {
// 修改操作:只传递必要字段
const updateData = {
id: item.id,
code: item.code,
name: item.name,
checkType: item.checkType,
packageName: item.packageName,
exposureNum: item.exposureNum,
orderNum: item.orderNum,
remark: item.remark
};
await updateCheckMethod(updateData);
} else {
// 新增操作:只传递必要字段
const addData = {
code: item.code,
name: item.name,
checkType: item.checkType,
packageName: item.packageName,
exposureNum: item.exposureNum,
orderNum: item.orderNum,
remark: item.remark
};
console.log('准备新增检查方法:', addData);
const newItem = await addCheckMethod(addData);
console.log('新增检查方法返回结果:', newItem);
// 将新增的id赋值给本地数据
item.id = newItem.id;
}
} else if (activeMenu.value === '检查部位') {
// 检查部位的保存逻辑
if (item.id) {
// 修改操作:只传递必要字段
const updateData = {
id: item.id,
code: item.code,
name: item.name,
checkType: item.checkType,
exposureNum: item.exposureNum,
packageName: item.packageName,
price: item.price,
number: item.number,
serviceScope: item.serviceScope,
subType: item.subType,
remark: item.remark
};
await updateCheckPart(updateData);
} else {
// 新增操作:只传递必要字段
const addData = {
code: item.code,
name: item.name,
checkType: item.checkType,
exposureNum: item.exposureNum,
packageName: item.packageName,
price: item.price,
number: item.number,
serviceScope: item.serviceScope,
subType: item.subType,
remark: item.remark
};
console.log('准备新增检查部位:', addData);
const newItem = await addCheckPart(addData);
console.log('新增检查部位返回结果:', newItem);
// 将新增的id赋值给本地数据
item.id = newItem.id;
}
} else {
// 检查类型的保存逻辑
if (item.id) {
// 修改操作:只传递必要字段
const updateData = {
id: item.id,
code: item.code,
name: item.name,
type: item.type,
selected: item.selected,
department: item.department,
number: item.number,
remark: item.remark
};
await updateCheckType(updateData);
} else {
// 新增操作:只传递必要字段
const addData = {
code: item.code,
name: item.name,
type: item.type,
selected: item.selected,
department: item.department,
number: item.number,
remark: item.remark
};
console.log('准备新增检查类型:', addData);
const newItem = await addCheckType(addData);
console.log('新增检查类型返回结果:', newItem);
// 将新增的id赋值给本地数据
item.id = newItem.id;
}
}
// 退出编辑状态
tableData.value[index].editing = false;
// 显示保存成功提示
ElMessage.success(`${item.row} 行数据已保存`);
} catch (error) {
console.error('保存失败,错误信息:', error);
console.error('保存失败,错误详情:', error.response ? error.response.data : error);
ElMessage.error('保存失败,请稍后重试');
}
}
// 处理删除按钮点击
async function handleDelete(index) {
ElMessageBox.confirm('确定要删除这一行吗?', '系统提示', {
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);
}
}
// 从数组中删除该行数据
tableData.value.splice(index, 1);
ElMessage.success('删除成功!');
} catch (error) {
ElMessage.error('删除失败,请稍后重试');
}
}).catch(() => {
// 用户取消删除操作
});
}
// 处理添加新行按钮点击
function handleAddNewRow() {
// 获取当前最大行号,为新建行生成行号
const maxRowNum = Math.max(
0,
...tableData.value.map(item => {
// 处理子行编号,如"1.1"只取主行号"1"
const rowParts = item.row.split('.');
return parseInt(rowParts[0]) || 0;
})
);
let newRow;
// 根据当前激活的菜单生成不同的初始数据结构
if (activeMenu.value === '检查类型') {
newRow = {
row: String(maxRowNum + 1),
code: '',
name: '',
type: '',
selected: true,
department: '',
number: '999999',
remark: '',
editing: true, // 新行默认进入编辑状态
isNew: true, // 标记为新增行
actions: true
};
} else if (activeMenu.value === '检查方法') {
newRow = {
row: String(maxRowNum + 1),
code: '',
name: '',
checkType: '',
packageName: '',
exposureNum: 0,
orderNum: 0,
remark: '',
editing: true, // 新行默认进入编辑状态
isNew: true, // 标记为新增行
actions: true
};
} else if (activeMenu.value === '检查部位') {
newRow = {
row: String(maxRowNum + 1),
code: '',
name: '',
checkType: '',
exposureNum: 0,
packageName: '',
price: 0,
number: '999999',
serviceScope: '',
subType: '',
remark: '',
editing: true, // 新行默认进入编辑状态
isNew: true, // 标记为新增行
actions: true
};
} else {
// 默认数据结构
newRow = {
row: String(maxRowNum + 1),
code: '',
name: '',
type: '',
selected: true,
department: '',
number: '999999',
remark: '',
editing: true, // 新行默认进入编辑状态
isNew: true, // 标记为新增行
actions: true
};
}
tableData.value.push(newRow);
}
// 处理添加按钮点击
function handleAdd(index) {
const parentRow = tableData.value[index];
// 创建子行数据,继承父行的编码
const childRow = {
row: parentRow.row + '.1', // 子行编号
code: parentRow.code, // 继承父行编码
name: '',
type: '',
selected: false,
department: '',
number: '',
remark: '',
editing: true,
actions: false
};
// 在父行后插入子行
tableData.value.splice(index + 1, 0, childRow);
}
// 处理搜索功能
async function handleSearch() {
try {
console.log('搜索条件:', currentSearchParams.value);
// ElMessage.info(`正在搜索${activeMenu.value}数据...`);
switch(activeMenu.value) {
case '检查方法':
// 清空检查方法数据
checkMethodData.splice(0, checkMethodData.length);
// 构建检查方法的搜索参数
const methodParams = {
pageNo: 1,
pageSize: 100, // 默认获取100条数据可以根据需要调整
checkType: searchParamsMethod.checkType,
name: searchParamsMethod.name,
packageName: searchParamsMethod.packageName
};
const methodResponse = await searchCheckMethod(methodParams);
// 确保data是数组适配不同的后端返回格式
let methodData = [];
if (methodResponse) {
if (Array.isArray(methodResponse)) {
methodData = methodResponse;
} else if (methodResponse.data && Array.isArray(methodResponse.data)) {
methodData = methodResponse.data;
} else if (methodResponse.data && methodResponse.data.data && Array.isArray(methodResponse.data.data)) {
methodData = methodResponse.data.data;
} else if (methodResponse.data && methodResponse.data.records) {
methodData = methodResponse.data.records;
}
}
// 处理数组数据
if (methodData.length > 0) {
methodData.forEach((item, index) => {
checkMethodData.push({
id: item.id, // 保存id字段用于判断是新增还是修改
row: (index + 1).toString(),
code: item.code,
name: item.name,
checkType: item.checkType || '',
packageName: item.packageName || '',
exposureNum: item.exposureNum || 0,
orderNum: item.orderNum || 0,
remark: item.remark || '',
actions: true
});
});
ElMessage.success(`搜索到${methodData.length}条检查方法数据`);
} else {
ElMessage.warning('未搜索到检查方法数据');
}
break;
case '检查部位':
// 清空检查部位数据
checkPartData.splice(0, checkPartData.length);
// 构建检查部位的搜索参数
const partParams = {
pageNo: 1,
pageSize: 100, // 默认获取100条数据可以根据需要调整
checkType: searchParamsPart.checkType,
name: searchParamsPart.name,
packageName: searchParamsPart.packageName
};
const partResponse = await searchCheckPart(partParams);
// 确保data是数组适配不同的后端返回格式
let partData = [];
if (partResponse) {
if (Array.isArray(partResponse)) {
partData = partResponse;
} else if (partResponse.data && Array.isArray(partResponse.data)) {
partData = partResponse.data;
} else if (partResponse.data && partResponse.data.data && Array.isArray(partResponse.data.data)) {
partData = partResponse.data.data;
} else if (partResponse.data && partResponse.data.records) {
partData = partResponse.data.records;
}
}
// 处理数组数据
if (partData.length > 0) {
partData.forEach((item, index) => {
checkPartData.push({
id: item.id, // 保存id字段用于判断是新增还是修改
row: (index + 1).toString(),
code: item.code,
name: item.name,
checkType: item.checkType || '',
exposureNum: item.exposureNum || 0,
packageName: item.packageName || '',
price: item.price || 0,
number: item.number || '999999',
serviceScope: item.serviceScope || '',
subType: item.subType || '',
remark: item.remark || '',
actions: true
});
});
ElMessage.success(`搜索到${partData.length}条检查部位数据`);
} else {
ElMessage.warning('未搜索到检查部位数据');
}
break;
default:
// 其他菜单使用原有的加载逻辑
await loadMenuData(activeMenu.value);
break;
}
} catch (error) {
console.error('搜索失败:', error);
ElMessage.error(`搜索${activeMenu.value}数据失败: ${error.message || '未知错误'}`);
}
}
// 处理重置功能
function handleReset() {
// 根据当前活动菜单重置对应的搜索条件
switch(activeMenu.value) {
case '检查类型':
for (const key in searchParamsType) {
searchParamsType[key] = '';
}
break;
case '检查方法':
searchParamsMethod.checkType = '';
searchParamsMethod.name = '';
searchParamsMethod.packageName = '';
break;
case '检查部位':
searchParamsPart.checkType = '';
searchParamsPart.name = '';
searchParamsPart.packageName = '';
break;
default:
break;
}
ElMessage.info('搜索条件已重置');
}
// 处理导出表格功能
function handleExport() {
console.log('导出表格:', activeMenu.value);
if (activeMenu.value === '检查方法') {
// 调用检查方法导出API
exportCheckMethod(searchParamsMethod).then(blobData => {
// 直接使用blobData创建下载链接因为response拦截器已经返回了res.data
const blob = new Blob([blobData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 设置文件名
link.setAttribute('download', `检查方法数据_${new Date().toISOString().slice(0, 10)}.xlsx`);
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
ElMessage.success('检查方法数据导出成功');
}).catch(error => {
console.error('导出检查方法数据失败:', error);
ElMessage.error('导出检查方法数据失败');
});
} else if (activeMenu.value === '检查部位') {
// 调用检查部位导出API
exportCheckPart(searchParamsPart).then(blobData => {
// 直接使用blobData创建下载链接因为response拦截器已经返回了res.data
const blob = new Blob([blobData], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const url = window.URL.createObjectURL(blob);
const link = document.createElement('a');
link.href = url;
// 设置文件名
link.setAttribute('download', `检查部位数据_${new Date().toISOString().slice(0, 10)}.xlsx`);
document.body.appendChild(link);
link.click();
// 清理
document.body.removeChild(link);
window.URL.revokeObjectURL(url);
ElMessage.success('检查部位数据导出成功');
}).catch(error => {
console.error('导出检查部位数据失败:', error);
ElMessage.error('导出检查部位数据失败');
});
} else {
// 其他菜单的导出逻辑可以在这里扩展
ElMessage.warning('该功能尚未实现');
}
}
</script>
<style scoped>
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
background-image: url("data:image/svg+xml;charset=UTF-8,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3e%3cpolyline points='6 9 12 15 18 9'%3e%3c/polyline%3e%3c/svg%3e");
background-repeat: no-repeat;
background-position: right 8px center;
background-size: 14px;
padding-right: 28px;
}
.check-project-settings {
display: flex;
min-height: 100vh;
background-color: #f5f7fa;
color: #000000;
}
/* 左侧导航栏样式 */
.sidebar {
width: 200px;
background: linear-gradient(180deg, #f8f9fa 0%, #ffffff 100%);
height: 100%;
position: relative;
padding: 24px 0;
box-shadow: 2px 0 8px rgba(0, 0, 0, 0.08);
overflow-y: auto;
flex-shrink: 0;
border-right: 1px solid #e8e8e8;
}
.sidebar-header {
padding: 0 20px 20px 20px;
margin-bottom: 12px;
border-bottom: 2px solid #1890FF;
}
.sidebar-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #1890FF;
letter-spacing: 0.5px;
}
.sidebar-menu {
display: flex;
flex-direction: column;
gap: 4px;
padding: 0 12px;
}
.menu-item {
display: flex;
align-items: center;
width: 100%;
padding: 14px 16px;
margin-bottom: 0;
background-color: transparent;
color: #333333;
border-radius: 8px;
text-decoration: none;
font-size: 15px;
font-weight: 500;
line-height: 1;
border: 1px solid transparent;
cursor: pointer;
text-align: left;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
}
.menu-item::before {
content: '';
position: absolute;
left: 0;
top: 50%;
transform: translateY(-50%);
width: 4px;
height: 0;
background: linear-gradient(180deg, #1890FF 0%, #40a9ff 100%);
border-radius: 0 4px 4px 0;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
}
.menu-item:hover {
background-color: rgba(24, 144, 255, 0.08);
border-color: rgba(24, 144, 255, 0.2);
transform: translateX(4px);
}
.menu-item:hover::before {
height: 20px;
}
.menu-item.active {
background: linear-gradient(135deg, #1890FF 0%, #40a9ff 100%);
color: #FFFFFF;
border-color: #1890FF;
box-shadow: 0 4px 12px rgba(24, 144, 255, 0.3);
}
.menu-item.active::before {
height: 24px;
}
.menu-icon {
font-size: 18px;
margin-right: 12px;
display: inline-flex;
align-items: center;
justify-content: center;
width: 24px;
height: 24px;
transition: transform 0.3s ease;
}
.menu-item:hover .menu-icon {
transform: scale(1.1);
}
.menu-text {
flex: 1;
letter-spacing: 0.3px;
}
/* 主内容区样式 */
.content {
flex: 1;
padding: 32px 32px 32px 32px;
min-width: 0;
overflow-x: auto;
overflow-y: auto;
background-color: #f8f9fa;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
padding: 20px 24px;
background: #ffffff;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
border: 1px solid #e8e8e8;
}
.header h1 {
font-size: 22px;
font-weight: 600;
color: #1890FF;
margin: 0;
letter-spacing: 0.5px;
}
/* 搜索栏样式 */
.search-bar {
background-color: #FFFFFF;
padding: 20px 24px;
border-radius: 12px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
border: 1px solid #e8e8e8;
display: flex;
align-items: center;
gap: 16px;
}
/* 检查方法搜索栏特定样式 */
.search-bar-method {
display: flex;
flex-direction: column;
gap: 16px;
}
.search-bar-method .search-filters {
display: flex;
align-items: center;
gap: 16px;
width: 100%;
}
.search-bar-method .search-actions {
margin-left: auto;
display: flex;
gap: 8px;
}
.search-item {
display: flex;
align-items: center;
gap: 8px;
}
.search-item label {
font-size: 14px;
color: #000000;
white-space: nowrap;
}
.search-item select,
.search-item input {
width: 150px;
height: 32px;
padding: 0 8px;
border: 1px solid #D9D9D9;
border-radius: 4px;
font-size: 14px;
}
.search-actions {
margin-left: auto;
display: flex;
gap: 8px;
}
.search-actions .btn {
width: auto;
height: 32px;
padding: 0 16px;
border-radius: 4px;
font-size: 14px;
cursor: pointer;
border: none;
transition: all 0.2s ease;
}
.btn-purple {
background-color: #722ED1;
color: white;
}
.btn-blue {
background-color: #1890FF;
color: white;
}
.btn-default {
background-color: #FFFFFF;
color: #000000;
border: 1px solid #D9D9D9;
}
.btn-green {
background-color: #52C41A;
color: white;
}
.search-actions .btn:hover {
opacity: 0.85;
}
.header-actions {
display: flex;
gap: 10px;
}
.btn-add-new {
background-color: #409eff;
color: white;
border: none;
padding: 8px 16px;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.3s;
}
.btn-add-new:hover {
background-color: #66b1ff;
}
/* 表格样式 */
.table-container {
width: 100%;
overflow-x: auto;
background: #FFFFFF;
border-radius: 12px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
border: 1px solid #e8e8e8;
}
table {
width: 100%;
border-collapse: collapse;
border-spacing: 0;
table-layout: fixed;
min-width: 1200px;
}
th {
background: linear-gradient(180deg, #f8f9fa 0%, #fafbfc 100%);
height: 48px;
padding: 12px 16px;
font-size: 14px;
font-weight: 600;
line-height: 24px;
text-align: center;
border-bottom: 2px solid #e8e8e8;
color: #1890FF;
letter-spacing: 0.3px;
}
td {
padding: 8px 16px;
height: 40px;
font-size: 14px;
font-weight: 400;
line-height: 24px;
border-bottom: 1px solid #e8e8e8;
text-align: center;
}
/* 操作按钮样式 */
.actions {
display: flex;
justify-content: center;
gap: 6px;
position: relative;
z-index: 10;
}
.btn {
width: 30px;
height: 30px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
font-size: 14px;
font-weight: bold;
position: relative;
z-index: 10;
color: white;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
}
.btn:hover {
transform: translateY(-2px) scale(1.1);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
}
.btn:active {
transform: translateY(0) scale(0.95);
}
.btn-confirm {
background: linear-gradient(135deg, #52C41A 0%, #73d13d 100%);
}
.btn-confirm:hover {
background: linear-gradient(135deg, #389E0D 0%, #52C41A 100%);
}
.btn-edit {
background: linear-gradient(135deg, #1890FF 0%, #40a9ff 100%);
}
.btn-edit:hover {
background: linear-gradient(135deg, #096DD9 0%, #1890FF 100%);
}
.btn-add {
background: linear-gradient(135deg, #1890FF 0%, #40a9ff 100%);
}
.btn-add:hover {
background: linear-gradient(135deg, #096DD9 0%, #1890FF 100%);
}
.btn-cancel {
background: linear-gradient(135deg, #FA8C16 0%, #ffa940 100%);
}
.btn-cancel:hover {
background: linear-gradient(135deg, #D46B08 0%, #FA8C16 100%);
}
.btn-delete {
background: linear-gradient(135deg, #FF4D4F 0%, #ff7875 100%);
z-index: 20;
pointer-events: auto;
}
.btn-delete:hover {
background: linear-gradient(135deg, #CF1322 0%, #FF4D4F 100%);
}
/* 特殊状态样式 */
.editing-row {
background-color: #E6F7FF;
cursor: text;
}
tr:not(.editing-row):hover {
background-color: rgba(24, 144, 255, 0.05);
cursor: pointer;
}
.child-row {
background-color: #f9f9f9;
}
.child-row td:first-child {
padding-left: 32px;
}
.checkbox-container {
display: flex;
justify-content: center;
}
input[type="checkbox"] {
width: 16px;
height: 16px;
}
input[type="text"], select {
height: 30px;
padding: 0 8px;
width: 100%;
border: 1px solid #D9D9D9;
border-radius: 4px;
font-size: 14px;
background-color: white;
color: #333;
}
input[type="text"]::placeholder {
color: #C0C4CC;
}
/* 分页样式 */
.pagination {
display: flex;
justify-content: flex-end;
align-items: center;
padding: 16px 0;
margin-top: 8px;
}
.pagination span {
font-size: 14px;
margin: 0 16px;
}
.pagination-btn {
background: none;
border: 1px solid #D9D9D9;
border-radius: 4px;
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s ease;
}
.pagination-btn:hover {
border-color: #1890FF;
color: #1890FF;
}
/* 禁用状态样式 */
.placeholder-text {
color: #C0C4CC;
}
/* 响应式设计 */
@media (max-width: 768px) {
.sidebar {
width: 70px;
padding: 16px 8px;
}
.sidebar-header {
padding: 0 8px 16px 8px;
}
.sidebar-header h3 {
font-size: 14px;
text-align: center;
}
.menu-text {
display: none;
}
.menu-icon {
margin-right: 0;
}
.menu-item {
justify-content: center;
padding: 12px;
}
.content {
padding: 20px 16px;
}
}
/* 平板适配 */
@media (min-width: 769px) and (max-width: 1024px) {
.sidebar {
width: 160px;
}
.content {
padding: 24px 20px;
}
}
</style>