患者档案和进货单bug
This commit is contained in:
@@ -310,7 +310,7 @@ const handleCurrentChange = (newPage) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询患者信息
|
// 查询患者信息
|
||||||
const handlePatientSearch = async () => {
|
const handlePatientSearch = async () => {
|
||||||
// 检查是否至少填写了一个查询条件
|
// 检查是否至少填写了一个查询条件
|
||||||
if (!searchForm.patientName && !searchForm.idCard && !searchForm.phoneNumber) {
|
if (!searchForm.patientName && !searchForm.idCard && !searchForm.phoneNumber) {
|
||||||
ElMessage.warning('请至少输入一个查询条件')
|
ElMessage.warning('请至少输入一个查询条件')
|
||||||
@@ -338,7 +338,7 @@ const handlePatientSearch = async () => {
|
|||||||
total.value = response.data.total || 0
|
total.value = response.data.total || 0
|
||||||
selectedPatient.value = null // 查询时重置选中状态
|
selectedPatient.value = null // 查询时重置选中状态
|
||||||
|
|
||||||
// 如果只有一条记录且是第一页,自动显示患者信息
|
// 如果只有一条记录且是第一页,自动选中
|
||||||
if (response.data.records.length === 1 && currentPage.value === 1) {
|
if (response.data.records.length === 1 && currentPage.value === 1) {
|
||||||
const patient = response.data.records[0]
|
const patient = response.data.records[0]
|
||||||
// 确保patient对象中同时包含id和patientId字段
|
// 确保patient对象中同时包含id和patientId字段
|
||||||
@@ -346,21 +346,9 @@ const handlePatientSearch = async () => {
|
|||||||
patient.patientId = patient.id;
|
patient.patientId = patient.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 直接构建并设置患者信息,无需用户手动确认
|
// 设置为选中状态,但不自动确认
|
||||||
const outpatientNo = patient.identifierNo || patient.cardNo || patient.card_number || patient.就诊卡号 || patient.outpatientNumber || patient.outpatientNo || patient.门诊号码 || patient.卡号 || patient.card || patient.patientNo || patient.patient_id;
|
selectedPatient.value = patient
|
||||||
const gender = patient.genderEnum_enumText || patient.gender || patient.sex || patient.性别 || patient.xb || patient.sexCode || patient.GENDER || patient.SEX;
|
ElMessage.warning('已自动选中唯一患者,请点击确定')
|
||||||
|
|
||||||
patientInfo.value = {
|
|
||||||
outpatientNo: outpatientNo,
|
|
||||||
patientName: patient.patientName || patient.name,
|
|
||||||
idCard: patient.idCard || patient.id_card || patient.idNo,
|
|
||||||
phoneNumber: patient.phoneNumber || patient.phone || patient.mobile || patient.mobilePhone,
|
|
||||||
gender: gender,
|
|
||||||
age: patient.age,
|
|
||||||
patientId: patient.patientId || patient.id || outpatientNo
|
|
||||||
}
|
|
||||||
|
|
||||||
ElMessage.success('已自动选中唯一患者')
|
|
||||||
} else {
|
} else {
|
||||||
// 如果有多条记录或不是第一页,显示患者列表供选择
|
// 如果有多条记录或不是第一页,显示患者列表供选择
|
||||||
// 确保每条患者记录都包含patientId字段,优先使用id字段
|
// 确保每条患者记录都包含patientId字段,优先使用id字段
|
||||||
@@ -371,7 +359,7 @@ const handlePatientSearch = async () => {
|
|||||||
showPatientList.value = true
|
showPatientList.value = true
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 无数据时重置总条数
|
// 无数据时重置总条数
|
||||||
total.value = 0
|
total.value = 0
|
||||||
ElMessage.warning('未查询到患者信息')
|
ElMessage.warning('未查询到患者信息')
|
||||||
}
|
}
|
||||||
@@ -380,8 +368,9 @@ const handlePatientSearch = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// 错误处理
|
// 使用公共错误处理函数处理错误信息
|
||||||
ElMessage.error('查询失败,请稍后重试');
|
const errorMessage = processErrorMessage(error, '查询失败,请稍后重试');
|
||||||
|
ElMessage.error(errorMessage);
|
||||||
// 可以在这里添加错误监控或日志记录
|
// 可以在这里添加错误监控或日志记录
|
||||||
console.error('患者查询错误:', error);
|
console.error('患者查询错误:', error);
|
||||||
} finally {
|
} finally {
|
||||||
@@ -504,7 +493,15 @@ onMounted(() => {
|
|||||||
document.addEventListener('keydown', handleKeydown)
|
document.addEventListener('keydown', handleKeydown)
|
||||||
|
|
||||||
// 设置默认患者数据,方便用户直接进行换卡操作
|
// 设置默认患者数据,方便用户直接进行换卡操作
|
||||||
|
patientInfo.value = {
|
||||||
|
outpatientNo: '20231001001',
|
||||||
|
patientName: '张三',
|
||||||
|
idCard: '110101199001011234',
|
||||||
|
phoneNumber: '13800138000',
|
||||||
|
gender: '男',
|
||||||
|
age: '33岁',
|
||||||
|
patientId: '1001'
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
// 组件卸载时移除键盘事件监听
|
// 组件卸载时移除键盘事件监听
|
||||||
@@ -514,7 +511,7 @@ onUnmounted(() => {
|
|||||||
|
|
||||||
// 移除了调试功能
|
// 移除了调试功能
|
||||||
|
|
||||||
// 选择患者(仅设置选中状态)
|
// 选择患者(仅设置选中状态)
|
||||||
const selectPatient = (row) => {
|
const selectPatient = (row) => {
|
||||||
selectedPatient.value = row
|
selectedPatient.value = row
|
||||||
ElMessage.warning('已选择患者,请点击确定')
|
ElMessage.warning('已选择患者,请点击确定')
|
||||||
@@ -550,7 +547,7 @@ const confirmSelectPatient = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 重置表单
|
// 重置表单
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
searchForm.patientName = ''
|
searchForm.patientName = ''
|
||||||
searchForm.idCard = ''
|
searchForm.idCard = ''
|
||||||
searchForm.phoneNumber = ''
|
searchForm.phoneNumber = ''
|
||||||
@@ -565,8 +562,8 @@ const resetForm = () => {
|
|||||||
total.value = 0
|
total.value = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// 格式化日期为YYYY-MM-DD格式
|
// 格式化日期为YYYY-MM-DD格式
|
||||||
const formatDate = (date) => {
|
const formatDate = (date) => {
|
||||||
if (!date) return ''
|
if (!date) return ''
|
||||||
// 如果已经是YYYY-MM-DD格式,直接返回
|
// 如果已经是YYYY-MM-DD格式,直接返回
|
||||||
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
if (typeof date === 'string' && /^\d{4}-\d{2}-\d{2}$/.test(date)) {
|
||||||
|
|||||||
808
openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue
Normal file
808
openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue
Normal file
@@ -0,0 +1,808 @@
|
|||||||
|
<template>
|
||||||
|
<div class="inspection-management">
|
||||||
|
<!-- 左侧导航 -->
|
||||||
|
<div class="side-nav">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in navItems"
|
||||||
|
:key="index"
|
||||||
|
:class="['nav-item', { active: activeNavIndex === index }]"
|
||||||
|
@click="activeNavIndex = index"
|
||||||
|
>
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧主内容 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<div class="header-actions">
|
||||||
|
<button class="add-new-btn" @click="addNewRow">
|
||||||
|
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||||
|
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||||
|
</svg>
|
||||||
|
新增
|
||||||
|
</button>
|
||||||
|
<button class="test-btn" @click="testComponentFunctionality">
|
||||||
|
测试组件
|
||||||
|
</button>
|
||||||
|
<!-- 错误提示区域 -->
|
||||||
|
<div v-if="hasError" class="error-message">
|
||||||
|
{{ errorMessage }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-container">
|
||||||
|
<table class="data-table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>行</th>
|
||||||
|
<th>*大类编码</th>
|
||||||
|
<th>*大类项目名称</th>
|
||||||
|
<th>*执行科室</th>
|
||||||
|
<th>序号</th>
|
||||||
|
<th>备注</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="(row, rowIndex) in tableData" :key="rowIndex">
|
||||||
|
<td>{{ row.rowNum }}</td>
|
||||||
|
<!-- 大类编码 -->
|
||||||
|
<td>
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="row.code"
|
||||||
|
class="editable-input"
|
||||||
|
placeholder="请输入大类编码"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ row.code || '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<!-- 大类项目名称 -->
|
||||||
|
<td>
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="row.name"
|
||||||
|
class="editable-input"
|
||||||
|
placeholder="请输入大类项目名称"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ row.name || '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<!-- 执行科室 -->
|
||||||
|
<td>
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<select
|
||||||
|
v-model="row.department"
|
||||||
|
class="editable-select"
|
||||||
|
>
|
||||||
|
<option value="">请选择科室</option>
|
||||||
|
<option v-for="dept in departments" :key="dept" :value="dept">
|
||||||
|
{{ dept }}
|
||||||
|
</option>
|
||||||
|
</select>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ row.department || '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<!-- 序号 -->
|
||||||
|
<td>
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="row.orderNum"
|
||||||
|
class="editable-input"
|
||||||
|
placeholder="请输入序号"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ row.orderNum || '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<!-- 备注 -->
|
||||||
|
<td>
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="row.remark"
|
||||||
|
class="editable-input"
|
||||||
|
placeholder="请输入备注"
|
||||||
|
>
|
||||||
|
</template>
|
||||||
|
<span v-else>{{ row.remark || '-' }}</span>
|
||||||
|
</td>
|
||||||
|
<!-- 操作 -->
|
||||||
|
<td class="action-cell">
|
||||||
|
<template v-if="editingIndex === rowIndex">
|
||||||
|
<div class="action-btn confirm-btn" @click="saveRow" title="保存"></div>
|
||||||
|
<div class="action-btn delete-btn" @click="cancelEdit" title="取消"></div>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<div
|
||||||
|
class="action-btn confirm-btn"
|
||||||
|
@click="toggleConfirm(rowIndex)"
|
||||||
|
:class="{ 'confirmed': row.isConfirmed }"
|
||||||
|
:title="row.isConfirmed ? '取消确认' : '确认'"
|
||||||
|
></div>
|
||||||
|
<div class="action-btn edit-btn" @click="editRow(rowIndex)" title="编辑"></div>
|
||||||
|
<div class="action-btn add-btn" @click="addChildRow(rowIndex)" title="添加子项"></div>
|
||||||
|
<div class="action-btn delete-btn" @click="deleteRow(rowIndex)" title="删除"></div>
|
||||||
|
</template>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 页码区域 -->
|
||||||
|
<div class="pagination">
|
||||||
|
<div class="page-btn" @click="changePage(currentPage - 1)" :disabled="currentPage === 1">上一页</div>
|
||||||
|
<div
|
||||||
|
v-for="page in totalPages"
|
||||||
|
:key="page"
|
||||||
|
class="page-btn"
|
||||||
|
:class="{ active: currentPage === page }"
|
||||||
|
@click="changePage(page)"
|
||||||
|
>
|
||||||
|
{{ page }}
|
||||||
|
</div>
|
||||||
|
<div class="page-btn" @click="changePage(currentPage + 1)" :disabled="currentPage === totalPages">下一页</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, reactive, onMounted, watch } from 'vue'
|
||||||
|
|
||||||
|
// 组件名称
|
||||||
|
const name = 'InspectionManagement'
|
||||||
|
|
||||||
|
// 加载状态
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const hasError = ref(false)
|
||||||
|
const errorMessage = ref('')
|
||||||
|
|
||||||
|
// 全局消息提示函数
|
||||||
|
const showMessage = (message, type = 'info') => {
|
||||||
|
// 在实际项目中,这里可以使用Element Plus等UI库的消息组件
|
||||||
|
// 这里使用简单的实现
|
||||||
|
const messageContainer = document.createElement('div')
|
||||||
|
messageContainer.className = `message-toast message-${type}`
|
||||||
|
messageContainer.textContent = message
|
||||||
|
|
||||||
|
// 样式
|
||||||
|
Object.assign(messageContainer.style, {
|
||||||
|
position: 'fixed',
|
||||||
|
top: '20px',
|
||||||
|
right: '20px',
|
||||||
|
padding: '12px 20px',
|
||||||
|
borderRadius: '4px',
|
||||||
|
color: '#fff',
|
||||||
|
fontSize: '14px',
|
||||||
|
zIndex: '9999',
|
||||||
|
boxShadow: '0 2px 8px rgba(0, 0, 0, 0.15)',
|
||||||
|
transition: 'all 0.3s ease',
|
||||||
|
opacity: '0',
|
||||||
|
transform: 'translateX(100%)'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 根据类型设置背景色
|
||||||
|
const bgColors = {
|
||||||
|
success: '#52c41a',
|
||||||
|
warning: '#faad14',
|
||||||
|
error: '#ff4d4f',
|
||||||
|
info: '#1890ff'
|
||||||
|
}
|
||||||
|
messageContainer.style.backgroundColor = bgColors[type] || bgColors.info
|
||||||
|
|
||||||
|
document.body.appendChild(messageContainer)
|
||||||
|
|
||||||
|
// 显示动画
|
||||||
|
setTimeout(() => {
|
||||||
|
messageContainer.style.opacity = '1'
|
||||||
|
messageContainer.style.transform = 'translateX(0)'
|
||||||
|
}, 10)
|
||||||
|
|
||||||
|
// 自动关闭
|
||||||
|
setTimeout(() => {
|
||||||
|
messageContainer.style.opacity = '0'
|
||||||
|
messageContainer.style.transform = 'translateX(100%)'
|
||||||
|
setTimeout(() => {
|
||||||
|
document.body.removeChild(messageContainer)
|
||||||
|
}, 300)
|
||||||
|
}, 3000)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式数据
|
||||||
|
const activeNavIndex = ref(0)
|
||||||
|
const navItems = ref(['检验类型', '检验项目', '套餐设置'])
|
||||||
|
const departments = ref(['医学检验科', '病理科', '放射科', '超声科', '心电图室'])
|
||||||
|
const editingIndex = ref(-1)
|
||||||
|
const currentPage = ref(1)
|
||||||
|
const totalPages = ref(3)
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = reactive([
|
||||||
|
{ rowNum: 1, code: '10', name: '临检(镜检)', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 1, code: '08', name: '临检(尿液)', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 2, code: '06', name: '免疫组化', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 3, code: '04', name: '免疫', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 4, code: '02', name: '常规', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 5, code: '09', name: '病理', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 6, code: '07', name: '临检(血液)', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 7, code: '05', name: '外送', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 8, code: '03', name: '妇科', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false },
|
||||||
|
{ rowNum: 9, code: '01', name: '生化', department: '医学检验科', orderNum: '999999', remark: '', isConfirmed: false }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 编辑行
|
||||||
|
const editRow = (index) => {
|
||||||
|
editingIndex.value = index
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存行
|
||||||
|
const saveRow = () => {
|
||||||
|
// 获取正在编辑的行数据
|
||||||
|
const currentRow = tableData[editingIndex.value]
|
||||||
|
if (currentRow) {
|
||||||
|
// 确保必填字段不为空
|
||||||
|
if (!currentRow.code) {
|
||||||
|
showMessage('请输入大类编码', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!currentRow.name) {
|
||||||
|
showMessage('请输入大类项目名称', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!currentRow.department) {
|
||||||
|
showMessage('请选择执行科室', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 重置序号为数字格式
|
||||||
|
if (currentRow.orderNum && !/^\d+$/.test(currentRow.orderNum)) {
|
||||||
|
showMessage('序号必须为数字', 'warning')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查编码唯一性
|
||||||
|
const duplicateIndex = tableData.findIndex((row, idx) =>
|
||||||
|
row.code === currentRow.code && idx !== editingIndex.value
|
||||||
|
)
|
||||||
|
if (duplicateIndex !== -1) {
|
||||||
|
showMessage('编码已存在,请使用其他编码', 'error')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修复updateRowNumbers函数的逻辑错误
|
||||||
|
updateRowNumbers()
|
||||||
|
|
||||||
|
// 保存成功提示
|
||||||
|
showMessage('保存成功', 'success')
|
||||||
|
}
|
||||||
|
// 重置编辑状态
|
||||||
|
editingIndex.value = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消编辑
|
||||||
|
const cancelEdit = () => {
|
||||||
|
editingIndex.value = -1
|
||||||
|
}
|
||||||
|
|
||||||
|
// 切换确认状态
|
||||||
|
const toggleConfirm = (index) => {
|
||||||
|
tableData[index].isConfirmed = !tableData[index].isConfirmed
|
||||||
|
// 根据确认状态显示不同提示
|
||||||
|
showMessage(
|
||||||
|
tableData[index].isConfirmed ? '确认成功' : '已取消确认',
|
||||||
|
'info'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除行
|
||||||
|
const deleteRow = (index) => {
|
||||||
|
if (confirm('确定要删除这条记录吗?删除后无法恢复。')) {
|
||||||
|
// 记录要删除行的行号
|
||||||
|
const rowNumToDelete = tableData[index].rowNum
|
||||||
|
// 删除该行
|
||||||
|
tableData.splice(index, 1)
|
||||||
|
|
||||||
|
// 重新计算页码(简单实现,实际项目中可能需要更复杂的逻辑)
|
||||||
|
const itemsPerPage = 10
|
||||||
|
totalPages.value = Math.ceil(tableData.length / itemsPerPage)
|
||||||
|
if (currentPage.value > totalPages.value && currentPage.value > 1) {
|
||||||
|
currentPage.value = totalPages.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新行号
|
||||||
|
updateRowNumbers()
|
||||||
|
|
||||||
|
// 删除成功提示
|
||||||
|
showMessage('删除成功', 'success')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加新行
|
||||||
|
const addNewRow = () => {
|
||||||
|
// 计算新行的行号
|
||||||
|
let maxRowNum = 0
|
||||||
|
tableData.forEach(row => {
|
||||||
|
if (row.rowNum > maxRowNum) {
|
||||||
|
maxRowNum = row.rowNum
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const newRow = {
|
||||||
|
rowNum: maxRowNum + 1,
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
department: '医学检验科',
|
||||||
|
orderNum: '',
|
||||||
|
remark: '',
|
||||||
|
isConfirmed: false
|
||||||
|
}
|
||||||
|
|
||||||
|
tableData.push(newRow)
|
||||||
|
|
||||||
|
// 重新计算页码
|
||||||
|
const itemsPerPage = 10
|
||||||
|
totalPages.value = Math.ceil(tableData.length / itemsPerPage)
|
||||||
|
currentPage.value = totalPages.value // 自动跳转到最后一页
|
||||||
|
|
||||||
|
// 添加成功提示
|
||||||
|
showMessage('新增记录成功', 'success')
|
||||||
|
|
||||||
|
editRow(tableData.length - 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加子行
|
||||||
|
const addChildRow = (index) => {
|
||||||
|
// 查找当前行组的最大子行号
|
||||||
|
let maxChildNum = 0
|
||||||
|
const baseRowNum = tableData[index].rowNum
|
||||||
|
|
||||||
|
tableData.forEach(row => {
|
||||||
|
if (row.rowNum === baseRowNum && row !== tableData[index]) {
|
||||||
|
maxChildNum++
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const newChildRow = {
|
||||||
|
rowNum: baseRowNum,
|
||||||
|
code: '',
|
||||||
|
name: '',
|
||||||
|
department: '医学检验科',
|
||||||
|
orderNum: '',
|
||||||
|
remark: '',
|
||||||
|
isConfirmed: false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在当前行后面插入子行
|
||||||
|
tableData.splice(index + 1, 0, newChildRow)
|
||||||
|
|
||||||
|
// 添加子行成功提示
|
||||||
|
showMessage('添加子项成功', 'success')
|
||||||
|
|
||||||
|
editRow(index + 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新行号
|
||||||
|
const updateRowNumbers = () => {
|
||||||
|
try {
|
||||||
|
// 按行号分组
|
||||||
|
const rowGroups = {}
|
||||||
|
|
||||||
|
tableData.forEach(row => {
|
||||||
|
if (!rowGroups[row.rowNum]) {
|
||||||
|
rowGroups[row.rowNum] = []
|
||||||
|
}
|
||||||
|
rowGroups[row.rowNum].push(row)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重新分配行号,保持原有组的完整性
|
||||||
|
const sortedRowNums = Object.keys(rowGroups).map(num => parseInt(num)).sort((a, b) => a - b)
|
||||||
|
const newTableData = []
|
||||||
|
|
||||||
|
sortedRowNums.forEach((_, groupIndex) => {
|
||||||
|
const rows = rowGroups[sortedRowNums[groupIndex]]
|
||||||
|
rows.forEach(row => {
|
||||||
|
row.rowNum = groupIndex + 1 // 从1开始重新编号
|
||||||
|
newTableData.push(row)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 清空并重新填充数据,保持响应性
|
||||||
|
tableData.splice(0, tableData.length, ...newTableData)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('更新行号时出错:', error)
|
||||||
|
showMessage('行号更新失败', 'error')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 组件初始化和测试
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
isLoading.value = true
|
||||||
|
hasError.value = false
|
||||||
|
|
||||||
|
// 模拟异步数据加载
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500))
|
||||||
|
|
||||||
|
// 初始化测试 - 验证数据结构
|
||||||
|
if (!Array.isArray(tableData)) {
|
||||||
|
throw new Error('表格数据结构错误')
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化成功提示
|
||||||
|
showMessage('组件初始化成功', 'info')
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('组件初始化失败:', error)
|
||||||
|
hasError.value = true
|
||||||
|
errorMessage.value = error.message || '组件初始化失败'
|
||||||
|
showMessage('组件加载失败,请刷新页面重试', 'error')
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听表格数据变化
|
||||||
|
watch(() => tableData.length, (newLength, oldLength) => {
|
||||||
|
console.log(`表格数据变化: ${oldLength} -> ${newLength}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 验证组件功能的测试函数
|
||||||
|
const testComponentFunctionality = () => {
|
||||||
|
const tests = []
|
||||||
|
|
||||||
|
// 测试数据结构
|
||||||
|
tests.push({
|
||||||
|
name: '数据结构验证',
|
||||||
|
passed: Array.isArray(tableData) && tableData.every(row =>
|
||||||
|
typeof row === 'object' && row !== null
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试编辑状态管理
|
||||||
|
tests.push({
|
||||||
|
name: '编辑状态管理',
|
||||||
|
passed: typeof editingIndex.value === 'number'
|
||||||
|
})
|
||||||
|
|
||||||
|
// 测试分页功能
|
||||||
|
tests.push({
|
||||||
|
name: '分页功能',
|
||||||
|
passed: typeof currentPage.value === 'number' &&
|
||||||
|
typeof totalPages.value === 'number' &&
|
||||||
|
currentPage.value >= 1 &&
|
||||||
|
totalPages.value >= 1
|
||||||
|
})
|
||||||
|
|
||||||
|
// 输出测试结果
|
||||||
|
const passedTests = tests.filter(test => test.passed)
|
||||||
|
console.log('组件功能测试结果:', {
|
||||||
|
total: tests.length,
|
||||||
|
passed: passedTests.length,
|
||||||
|
failed: tests.length - passedTests.length,
|
||||||
|
details: tests
|
||||||
|
})
|
||||||
|
|
||||||
|
// 显示测试结果提示
|
||||||
|
if (passedTests.length === tests.length) {
|
||||||
|
showMessage('组件功能测试通过', 'success')
|
||||||
|
} else {
|
||||||
|
showMessage(`组件功能测试失败: ${tests.length - passedTests.length} 项测试未通过`, 'error')
|
||||||
|
}
|
||||||
|
|
||||||
|
return tests
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更改页码
|
||||||
|
const changePage = (page) => {
|
||||||
|
if (page >= 1 && page <= totalPages.value) {
|
||||||
|
currentPage.value = page
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/* 头部操作区样式 */
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new-btn {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: rgba(24, 144, 255, 0.1);
|
||||||
|
color: #1890FF;
|
||||||
|
border: 1px solid rgba(24, 144, 255, 0.3);
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-new-btn:hover {
|
||||||
|
background-color: rgba(24, 144, 255, 0.2);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(24, 144, 255, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 测试按钮样式 */
|
||||||
|
.test-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
background-color: #722ed1;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.test-btn:hover {
|
||||||
|
background-color: #9254de;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 8px rgba(114, 46, 209, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 错误信息样式 */
|
||||||
|
.error-message {
|
||||||
|
color: #ff4d4f;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-left: 10px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background-color: rgba(255, 77, 79, 0.1);
|
||||||
|
border-radius: 4px;
|
||||||
|
border: 1px solid rgba(255, 77, 79, 0.2);
|
||||||
|
}
|
||||||
|
/* 编辑按钮样式 */
|
||||||
|
.edit-btn {
|
||||||
|
background: rgba(250, 173, 20, 0.1);
|
||||||
|
color: #FAAD14;
|
||||||
|
}
|
||||||
|
/* 编辑状态输入框样式 */
|
||||||
|
.editable-input {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #E8E8E8;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.editable-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #1890FF;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
/* 编辑状态选择框样式 */
|
||||||
|
.editable-select {
|
||||||
|
width: 100%;
|
||||||
|
border: 1px solid #E8E8E8;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.editable-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #1890FF;
|
||||||
|
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||||
|
}
|
||||||
|
/* CSS Reset 和全局样式 */
|
||||||
|
.inspection-management {
|
||||||
|
display: flex;
|
||||||
|
min-height: 100vh;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 左侧导航菜单 */
|
||||||
|
.side-nav {
|
||||||
|
width: 160px;
|
||||||
|
background: #FFFFFF;
|
||||||
|
border-right: 1px solid #e8e8e8;
|
||||||
|
padding: 16px 0;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item {
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin: 4px 8px;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
border-radius: 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item:hover {
|
||||||
|
background: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item.active {
|
||||||
|
background: #1890FF;
|
||||||
|
color: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 右侧主内容区 */
|
||||||
|
.main-content {
|
||||||
|
flex: 1;
|
||||||
|
margin-left: 160px;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表格样式 */
|
||||||
|
.table-container {
|
||||||
|
overflow-x: auto;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
min-width: 800px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th, .data-table td {
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 14px;
|
||||||
|
height: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table th {
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table td {
|
||||||
|
font-weight: 400;
|
||||||
|
border-bottom: 1px solid #E8E8E8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table tr:hover td {
|
||||||
|
background-color: #E6F7FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 操作按钮样式 */
|
||||||
|
.action-cell {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn {
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
border-radius: 4px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
margin: 0 2px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn::before {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn {
|
||||||
|
background: rgba(82, 196, 26, 0.1);
|
||||||
|
color: #52C41A;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn.confirmed {
|
||||||
|
background: #52C41A;
|
||||||
|
color: #FFFFFF;
|
||||||
|
box-shadow: 0 2px 4px rgba(82, 196, 26, 0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-btn::before {
|
||||||
|
content: '✓';
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn {
|
||||||
|
background: rgba(24, 144, 255, 0.1);
|
||||||
|
color: #1890FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-btn::before {
|
||||||
|
content: '+';
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn {
|
||||||
|
background: rgba(255, 77, 79, 0.1);
|
||||||
|
color: #FF4D4F;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-btn::before {
|
||||||
|
content: '×';
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-btn:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页码区域 */
|
||||||
|
.pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 16px;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn {
|
||||||
|
padding: 8px 16px;
|
||||||
|
margin: 0 4px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 14px;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn:hover {
|
||||||
|
background: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn.active {
|
||||||
|
background: #1890FF;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-btn[disabled] {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.side-nav {
|
||||||
|
width: 60px;
|
||||||
|
padding: 16px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-item span {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin-left: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.side-nav {
|
||||||
|
width: 50px;
|
||||||
|
padding: 16px 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
margin-left: 50px;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -386,7 +386,15 @@
|
|||||||
:prop="`localTableData.${scope.$index}.startTime`"
|
:prop="`localTableData.${scope.$index}.startTime`"
|
||||||
:rules="rules.startTime"
|
:rules="rules.startTime"
|
||||||
>
|
>
|
||||||
<span>
|
<el-date-picker
|
||||||
|
v-if="localTableData[scope.$index].isEditing"
|
||||||
|
v-model="localTableData[scope.$index].startTime"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择生产日期"
|
||||||
|
style="width: 180px"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/>
|
||||||
|
<span v-else-if="localTableData[scope.$index].isViewing">
|
||||||
{{ parseTime(localTableData[scope.$index].startTime, '{y}-{m}-{d}') || '' }}
|
{{ parseTime(localTableData[scope.$index].startTime, '{y}-{m}-{d}') || '' }}
|
||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -396,7 +404,15 @@
|
|||||||
<el-table-column prop="endTime" label="失效日期" width="180">
|
<el-table-column prop="endTime" label="失效日期" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<el-form-item :prop="`localTableData.${scope.$index}.endTime`" :rules="rules.endTime">
|
<el-form-item :prop="`localTableData.${scope.$index}.endTime`" :rules="rules.endTime">
|
||||||
<span>
|
<el-date-picker
|
||||||
|
v-if="localTableData[scope.$index].isEditing"
|
||||||
|
v-model="localTableData[scope.$index].endTime"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择失效日期"
|
||||||
|
style="width: 180px"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
/>
|
||||||
|
<span v-else-if="localTableData[scope.$index].isViewing">
|
||||||
{{ parseTime(localTableData[scope.$index].endTime, '{y}-{m}-{d}') || '' }}
|
{{ parseTime(localTableData[scope.$index].endTime, '{y}-{m}-{d}') || '' }}
|
||||||
</span>
|
</span>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
@@ -1778,4 +1794,4 @@ defineExpose({
|
|||||||
.table-container {
|
.table-container {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
Reference in New Issue
Block a user