772 lines
20 KiB
Vue
772 lines
20 KiB
Vue
<template>
|
||
<div class="check-project-settings">
|
||
<!-- 左侧导航栏 -->
|
||
<div class="sidebar" style="margin-top: 100px;">
|
||
<!-- 明确列出所有导航项 -->
|
||
<button
|
||
class="menu-item active"
|
||
@click="handleMenuClick('检查类型')"
|
||
style="display: block; width: 100%; height: 40px; padding: 8px; text-align: center;"
|
||
>
|
||
检查类型
|
||
</button>
|
||
<button
|
||
class="menu-item"
|
||
@click="handleMenuClick('检查方法')"
|
||
style="display: block; width: 100%; height: 40px; padding: 8px; text-align: center;"
|
||
>
|
||
检查方法
|
||
</button>
|
||
<button
|
||
class="menu-item"
|
||
@click="handleMenuClick('检查部位')"
|
||
style="display: block; width: 100%; height: 40px; padding: 8px; text-align: center;"
|
||
>
|
||
检查部位
|
||
</button>
|
||
<button
|
||
class="menu-item"
|
||
@click="handleMenuClick('套餐设置')"
|
||
style="display: block; width: 100%; height: 40px; padding: 8px; text-align: center;"
|
||
>
|
||
套餐设置
|
||
</button>
|
||
</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-else>
|
||
<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('.') }"
|
||
@click="handleRowClick(item)"
|
||
>
|
||
<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="type in checkTypes"
|
||
:key="type"
|
||
:value="type"
|
||
>
|
||
{{ type }}
|
||
</option>
|
||
</select>
|
||
</template>
|
||
<template v-else>
|
||
<span v-if="item.type">{{ 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.actions">
|
||
<button class="btn btn-confirm" @click.stop="handleConfirm(index)">
|
||
✓
|
||
</button>
|
||
<button
|
||
v-if="!item.row.includes('.')"
|
||
class="btn btn-add"
|
||
@click.stop="handleAdd(index)"
|
||
>
|
||
+
|
||
</button>
|
||
<button class="btn btn-delete" @click.stop="handleDelete(index)">
|
||
🗑
|
||
</button>
|
||
</template>
|
||
<template v-else>
|
||
<button class="btn btn-confirm" @click.stop="handleConfirm(index)">
|
||
✓
|
||
</button>
|
||
<button class="btn btn-delete" @click.stop="handleDelete(index)">
|
||
🗑
|
||
</button>
|
||
</template>
|
||
</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
</div>
|
||
|
||
<!-- 分页区域 -->
|
||
<div class="pagination">
|
||
<button class="pagination-btn">‹</button>
|
||
<span>1</span>
|
||
<button class="pagination-btn">›</button>
|
||
</div>
|
||
</template>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, reactive, onMounted } from 'vue';
|
||
import { ElMessage } from 'element-plus';
|
||
import { getDicts } from '@/api/system/dict/data';
|
||
import { listCheckType, listCheckMethod, listCheckPart, listCheckPackage, addCheckType, updateCheckType, delCheckType } 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 checkMethods = ref([]);
|
||
const checkParts = ref([]);
|
||
const checkPackages = ref([]);
|
||
const departments = ref([]);
|
||
|
||
// 表格数据
|
||
const tableData = reactive([]);
|
||
|
||
// 从数据库获取所有检查相关数据
|
||
onMounted(async () => {
|
||
try {
|
||
// 获取科室字典数据
|
||
const deptResponse = await getDicts('dept');
|
||
if (deptResponse && deptResponse.data) {
|
||
// 保存完整的科室字典数据,包含编码和名称
|
||
departments.value = deptResponse.data;
|
||
}
|
||
|
||
// 获取检查类型数据
|
||
const typeResponse = await listCheckType();
|
||
if (typeResponse && typeResponse.data) {
|
||
// 将数据库返回的检查类型数据转换为表格所需格式
|
||
const types = typeResponse.data;
|
||
// 获取所有不重复的检查类型值
|
||
checkTypes.value = [...new Set(types.map(item => item.type))];
|
||
|
||
// 构建表格数据
|
||
tableData.splice(0, tableData.length);
|
||
types.forEach((item, index) => {
|
||
tableData.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) {
|
||
checkMethods.value = methodResponse.data;
|
||
}
|
||
|
||
// 获取检查部位数据
|
||
const partResponse = await listCheckPart();
|
||
if (partResponse && partResponse.data) {
|
||
checkParts.value = partResponse.data;
|
||
}
|
||
|
||
// 获取检查套餐数据
|
||
const packageResponse = await listCheckPackage();
|
||
if (packageResponse && packageResponse.data) {
|
||
checkPackages.value = packageResponse.data;
|
||
}
|
||
|
||
} 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 {
|
||
tableData.splice(0, tableData.length);
|
||
|
||
switch(menu) {
|
||
case '检查类型':
|
||
const typeResponse = await listCheckType();
|
||
if (typeResponse && typeResponse.data) {
|
||
// 获取所有不重复的检查类型值
|
||
checkTypes.value = [...new Set(typeResponse.data.map(item => item.type))];
|
||
|
||
typeResponse.data.forEach((item, index) => {
|
||
// 直接使用数据库中的department值,不进行转换
|
||
tableData.push({
|
||
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 '检查方法':
|
||
const methodResponse = await listCheckMethod();
|
||
if (methodResponse && methodResponse.data) {
|
||
|
||
}
|
||
break;
|
||
|
||
case '检查部位':
|
||
const partResponse = await listCheckPart();
|
||
if (partResponse && partResponse.data) {
|
||
|
||
}
|
||
break;
|
||
|
||
case '套餐设置':
|
||
const packageResponse = await listCheckPackage();
|
||
if (packageResponse && packageResponse.data) {
|
||
|
||
}
|
||
break;
|
||
}
|
||
} catch (error) {
|
||
console.error('加载菜单数据失败:', error);
|
||
alert(`加载${menu}数据失败,请检查网络或服务状态`);
|
||
}
|
||
}
|
||
|
||
// 处理行点击,进入编辑状态
|
||
function handleRowClick(item) {
|
||
if (!item.editing) {
|
||
item.editing = true;
|
||
}
|
||
}
|
||
|
||
// 处理确认按钮点击
|
||
async function handleConfirm(index) {
|
||
const item = tableData[index];
|
||
try {
|
||
// 根据是否有id判断是新增还是修改
|
||
if (item.id) {
|
||
// 修改操作
|
||
await updateCheckType(item);
|
||
} else {
|
||
// 新增操作
|
||
const newItem = await addCheckType(item);
|
||
// 将新增的id赋值给本地数据
|
||
item.id = newItem.id;
|
||
}
|
||
// 退出编辑状态
|
||
tableData[index].editing = false;
|
||
// 显示保存成功提示
|
||
alert(`第 ${item.row} 行数据已保存`);
|
||
} catch (error) {
|
||
console.error('保存失败:', error);
|
||
alert('保存失败,请稍后重试');
|
||
}
|
||
}
|
||
|
||
// 处理删除按钮点击
|
||
async function handleDelete(index) {
|
||
if (confirm('确定要删除这一行吗?')) {
|
||
const item = tableData[index];
|
||
try {
|
||
// 如果有id,调用API删除数据库中的数据
|
||
if (item.id) {
|
||
await delCheckType(item.id);
|
||
}
|
||
// 从数组中删除该行数据
|
||
tableData.splice(index, 1);
|
||
alert('删除成功!');
|
||
} catch (error) {
|
||
console.error('删除失败:', error);
|
||
alert('删除失败,请稍后重试');
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理添加新行按钮点击
|
||
function handleAddNewRow() {
|
||
// 生成新行的行号
|
||
const newRowNumber = tableData.length + 1;
|
||
|
||
// 添加新行数据
|
||
tableData.push({
|
||
row: newRowNumber.toString(),
|
||
code: '',
|
||
name: '',
|
||
type: '',
|
||
selected: true,
|
||
department: '',
|
||
number: '999999',
|
||
remark: '',
|
||
actions: true,
|
||
editing: true, // 新行默认进入编辑状态
|
||
isNew: true // 标记为新增行
|
||
});
|
||
}
|
||
|
||
// 处理添加按钮点击
|
||
function handleAdd(index) {
|
||
const parentRow = tableData[index];
|
||
|
||
// 创建子行数据,继承父行的编码
|
||
const childRow = {
|
||
row: parentRow.row + '.1', // 子行编号
|
||
code: parentRow.code, // 继承父行编码
|
||
name: '',
|
||
type: '',
|
||
selected: false,
|
||
department: '',
|
||
number: '',
|
||
remark: '',
|
||
editing: true,
|
||
actions: false
|
||
};
|
||
|
||
// 在父行后插入子行
|
||
tableData.splice(index + 1, 0, childRow);
|
||
}
|
||
</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: 160px;
|
||
background-color: #FFFFFF;
|
||
height: 100vh;
|
||
position: fixed;
|
||
left: 0;
|
||
top: 0;
|
||
padding: 16px 8px;
|
||
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.05);
|
||
z-index: 10;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.menu-item {
|
||
display: block;
|
||
width: 100%;
|
||
padding: 8px 12px;
|
||
margin-bottom: 8px;
|
||
background-color: #FFFFFF;
|
||
color: #000000;
|
||
border-radius: 4px;
|
||
text-decoration: none;
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 24px;
|
||
border: none;
|
||
cursor: pointer;
|
||
text-align: left;
|
||
transition: all 0.2s ease;
|
||
}
|
||
|
||
.menu-item:hover {
|
||
background-color: rgba(24, 144, 255, 0.1);
|
||
}
|
||
|
||
.menu-item.active {
|
||
background-color: #1890FF;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
/* 主内容区样式 */
|
||
.content {
|
||
flex: 1;
|
||
margin-left: 160px;
|
||
padding: 24px;
|
||
}
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 20px;
|
||
font-weight: 500;
|
||
color: #000000;
|
||
margin: 0;
|
||
}
|
||
|
||
.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: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
|
||
border: 1px solid #D9D9D9;
|
||
}
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
border-spacing: 0;
|
||
table-layout: fixed;
|
||
min-width: 1200px;
|
||
}
|
||
|
||
th {
|
||
background-color: #fafafa;
|
||
height: 40px;
|
||
padding: 8px 16px;
|
||
font-size: 14px;
|
||
font-weight: 500;
|
||
line-height: 24px;
|
||
text-align: left;
|
||
border-bottom: 1px solid #D9D9D9;
|
||
}
|
||
|
||
td {
|
||
padding: 8px 16px;
|
||
height: 40px;
|
||
font-size: 14px;
|
||
font-weight: 400;
|
||
line-height: 24px;
|
||
border-bottom: 1px solid #e8e8e8;
|
||
}
|
||
|
||
/* 操作按钮样式 */
|
||
.actions {
|
||
display: flex;
|
||
justify-content: flex-end;
|
||
gap: 8px;
|
||
}
|
||
|
||
.btn {
|
||
width: 28px;
|
||
height: 28px;
|
||
border-radius: 50%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
cursor: pointer;
|
||
border: none;
|
||
transition: all 0.2s ease;
|
||
font-size: 16px;
|
||
font-weight: bold;
|
||
}
|
||
|
||
.btn-confirm {
|
||
background-color: #1890FF;
|
||
color: white;
|
||
}
|
||
|
||
.btn-add {
|
||
background-color: #1890FF;
|
||
color: white;
|
||
}
|
||
|
||
.btn-delete {
|
||
background-color: #FF4D4F;
|
||
color: white;
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* 特殊状态样式 */
|
||
.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: 60px;
|
||
}
|
||
|
||
.menu-item span {
|
||
display: none;
|
||
}
|
||
|
||
.content {
|
||
margin-left: 60px;
|
||
padding: 16px;
|
||
}
|
||
}
|
||
</style> |