1173
openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue
Executable file
1173
openhis-ui-vue3/src/views/maintainSystem/Inspection/PackageManagement.vue
Executable file
File diff suppressed because it is too large
Load Diff
3076
openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue
Executable file
3076
openhis-ui-vue3/src/views/maintainSystem/Inspection/index.vue
Executable file
File diff suppressed because it is too large
Load Diff
556
openhis-ui-vue3/src/views/maintainSystem/chargeConfig/index.vue
Executable file
556
openhis-ui-vue3/src/views/maintainSystem/chargeConfig/index.vue
Executable file
@@ -0,0 +1,556 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<div class="config-header">
|
||||
<el-button type="primary" @click="handleDefault">默认(M)</el-button>
|
||||
<el-button type="success" @click="handleSave">保存(S)</el-button>
|
||||
<el-button type="danger" @click="handleClose">关闭(X)</el-button>
|
||||
</div>
|
||||
|
||||
<el-tabs v-model="activeTab" class="config-tabs">
|
||||
<el-tab-pane label="划价收费1" name="tab1">
|
||||
<div class="tab-content">划价收费1相关配置</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="划价收费2" name="tab2">
|
||||
<div class="tab-content">划价收费2相关配置</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="挂号处理" name="tab3">
|
||||
<el-form ref="formRef" :model="formData" label-width="150px" class="config-form compact-form">
|
||||
<!-- 第一行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="病历本费用(元)" prop="medicalRecordFee">
|
||||
<el-input-number v-model="formData.medicalRecordFee" style="width: 150px" :controls-position="'right'" :step="1" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="medicalRecordFlag" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.medicalRecordFlag" style="margin-right: 8px;" />
|
||||
<span>病历费入账标志</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第二行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="就诊卡费(元)" prop="patientCardFee">
|
||||
<el-input-number v-model="formData.patientCardFee" style="width: 150px" :controls-position="'right'" :step="1" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="isNightShift" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.isNightShift" style="margin-right: 8px;" />
|
||||
<span>是否启用晚班</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第三行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="" prop="patientCardFlag" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.patientCardFlag" style="margin-right: 8px;" />
|
||||
<span>就诊卡记账标志</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="上午接诊起始时间" prop="morningStartTime">
|
||||
<el-input v-model="formData.morningStartTime" style="width: 150px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第四行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="" prop="autoGenerateOutpatientNo" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.autoGenerateOutpatientNo" style="margin-right: 8px;" />
|
||||
<span>自动产生门诊号</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="allowModifyOutpatientNo" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.allowModifyOutpatientNo" style="margin-right: 8px;" />
|
||||
<span>建档时是否允许修改门诊号</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第五行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="下午起始时间" prop="afternoonStartTime">
|
||||
<el-input v-model="formData.afternoonStartTime" style="width: 150px" />
|
||||
</el-form-item>
|
||||
<el-form-item label="晚上起始时间" prop="eveningStartTime" v-if="formData.isNightShift">
|
||||
<el-input v-model="formData.eveningStartTime" style="width: 150px" />
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第六行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="挂号有效期(天)" prop="registrationValidity">
|
||||
<el-input-number v-model="formData.registrationValidity" style="width: 150px" :controls-position="'right'" :step="1" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="挂号单据模式" prop="registrationDocumentMode">
|
||||
<el-select v-model="formData.registrationDocumentMode" style="width: 150px">
|
||||
<el-option label="使用发票" value="使用发票" />
|
||||
<el-option label="普通单据" value="其他模式" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第七行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="" prop="exemptFlag" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.exemptFlag" style="margin-right: 8px;" />
|
||||
<span>减免标志</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="consultationFlag" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.consultationFlag" style="margin-right: 8px;" />
|
||||
<span>义诊标志</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="enableHolidayFeeFloat" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.enableHolidayFeeFloat" style="margin-right: 8px;" />
|
||||
<span>启用法定节假日挂号费浮动</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第八行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="监护人规定年龄(岁)" prop="guardianAge">
|
||||
<el-input-number v-model="formData.guardianAge" style="width: 150px" :controls-position="'right'" :step="1" :min="0" />
|
||||
</el-form-item>
|
||||
<el-form-item label="" prop="enableDoubleScreen" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.enableDoubleScreen" style="margin-right: 8px;" />
|
||||
<span>门诊挂号启用双屏</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
|
||||
<!-- 第九行 -->
|
||||
<div class="form-row">
|
||||
<el-form-item label="" prop="optionalRegistrationType" checkbox-label>
|
||||
<template #label>
|
||||
<div class="checkbox-label-container">
|
||||
<el-checkbox v-model="formData.optionalRegistrationType" style="margin-right: 8px;" />
|
||||
<span>挂号类型可选择</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-form-item>
|
||||
</div>
|
||||
</el-form>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="挂号预约" name="tab4">
|
||||
<div class="tab-content">挂号预约相关配置</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="打印设置" name="tab5">
|
||||
<el-checkbox v-model="formData.isPrint">是否打印挂号单</el-checkbox>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="其他选项" name="tab6">
|
||||
<div class="tab-content">其他选项相关配置</div>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="病人账户" name="tab7">
|
||||
<div class="tab-content">病人账户相关配置</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from 'vue';
|
||||
import {useRouter} from 'vue-router';
|
||||
import {ElMessage} from 'element-plus';
|
||||
import {addConfig, listConfig, updateConfig} from '@/api/system/config';
|
||||
|
||||
const router = useRouter();
|
||||
const formRef = ref(null);
|
||||
const activeTab = ref('tab1');
|
||||
|
||||
// 表单数据
|
||||
const formData = reactive({
|
||||
medicalRecordFee: '1',
|
||||
patientCardFee: '1',
|
||||
medicalRecordFlag: true,
|
||||
patientCardFlag: false,
|
||||
autoGenerateOutpatientNo: true,
|
||||
allowModifyOutpatientNo: false,
|
||||
enableHolidayFeeFloat: true,
|
||||
enableDoubleScreen: true,
|
||||
isNightShift: false,
|
||||
exemptFlag: false,
|
||||
consultationFlag: false,
|
||||
optionalRegistrationType: false,
|
||||
morningStartTime: '08:00',
|
||||
afternoonStartTime: '13:00',
|
||||
eveningStartTime: '22:00',
|
||||
registrationValidity: '5',
|
||||
registrationDocumentMode: '使用发票',
|
||||
guardianAge: '16',
|
||||
isPrint: false,
|
||||
});
|
||||
|
||||
// 加载配置数据
|
||||
const loadConfigData = async (showSuccessMessage = true) => {
|
||||
try {
|
||||
// 调用系统配置API获取数据,不设置configType过滤条件以获取所有配置
|
||||
const response = await listConfig({ pageSize: 1000 });
|
||||
|
||||
// 处理响应数据,兼容不同的返回格式
|
||||
let configs = [];
|
||||
if (response && response.data) {
|
||||
if (Array.isArray(response.data)) {
|
||||
configs = response.data;
|
||||
} else if (response.data.rows && Array.isArray(response.data.rows)) {
|
||||
configs = response.data.rows;
|
||||
} else {
|
||||
console.error('返回数据格式不符合预期:', response);
|
||||
}
|
||||
} else if (response && response.rows && Array.isArray(response.rows)) {
|
||||
configs = response.rows;
|
||||
} else {
|
||||
console.error('API返回空响应:', response);
|
||||
}
|
||||
|
||||
console.log('loadConfigData - 获取到的配置列表:', configs);
|
||||
console.log('loadConfigData - 配置总数:', configs.length);
|
||||
|
||||
// 将配置数据映射到表单
|
||||
if (configs && configs.length > 0) {
|
||||
configs.forEach(config => {
|
||||
const { configKey, configValue } = config;
|
||||
|
||||
// 处理布尔类型字段
|
||||
if (configKey in formData) {
|
||||
if (typeof formData[configKey] === 'boolean') {
|
||||
formData[configKey] = configValue === '1';
|
||||
} else {
|
||||
formData[configKey] = configValue;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (showSuccessMessage) {
|
||||
ElMessage.success('配置数据加载成功');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载配置数据失败:', error);
|
||||
ElMessage.warning('无法加载配置数据,使用默认值');
|
||||
}
|
||||
};
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
loadConfigData();
|
||||
});
|
||||
|
||||
// 默认按钮点击事件
|
||||
const handleDefault = () => {
|
||||
// 重置为默认值
|
||||
Object.assign(formData, {
|
||||
medicalRecordFee: '1',
|
||||
patientCardFee: '1',
|
||||
medicalRecordFlag: true,
|
||||
patientCardFlag: false,
|
||||
autoGenerateOutpatientNo: true,
|
||||
allowModifyOutpatientNo: false,
|
||||
enableHolidayFeeFloat: true,
|
||||
enableDoubleScreen: true,
|
||||
isNightShift: false,
|
||||
exemptFlag: false,
|
||||
consultationFlag: false,
|
||||
optionalRegistrationType: false,
|
||||
morningStartTime: '08:00',
|
||||
afternoonStartTime: '13:00',
|
||||
eveningStartTime: '22:00',
|
||||
registrationValidity: '5',
|
||||
registrationDocumentMode: '使用发票',
|
||||
guardianAge: '16',
|
||||
isPrint: false,
|
||||
});
|
||||
ElMessage.success('已恢复默认值');
|
||||
};
|
||||
|
||||
// 保存按钮点击事件
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// 这里可以添加表单验证逻辑
|
||||
if (formRef.value) {
|
||||
await formRef.value.validate();
|
||||
}
|
||||
|
||||
// 将表单数据转换为系统配置格式,并添加必要的默认值
|
||||
const configData = [
|
||||
{ configKey: 'medicalRecordFee', configValue: formData.medicalRecordFee, configName: '病历本费用', configType: 'Y' },
|
||||
{ configKey: 'patientCardFee', configValue: formData.patientCardFee, configName: '就诊卡费', configType: 'Y' },
|
||||
{ configKey: 'medicalRecordFlag', configValue: formData.medicalRecordFlag ? '1' : '0', configName: '病历费入账标志', configType: 'Y' },
|
||||
{ configKey: 'isNightShift', configValue: formData.isNightShift ? '1' : '0', configName: '是否启用晚班', configType: 'Y' },
|
||||
{ configKey: 'patientCardFlag', configValue: formData.patientCardFlag ? '1' : '0', configName: '就诊卡记账标志', configType: 'Y' },
|
||||
{ configKey: 'morningStartTime', configValue: formData.morningStartTime, configName: '上午接诊起始时间', configType: 'Y' },
|
||||
{ configKey: 'autoGenerateOutpatientNo', configValue: formData.autoGenerateOutpatientNo ? '1' : '0', configName: '自动产生门诊号', configType: 'Y' },
|
||||
{ configKey: 'allowModifyOutpatientNo', configValue: formData.allowModifyOutpatientNo ? '1' : '0', configName: '建档时是否允许修改门诊号', configType: 'Y' },
|
||||
{ configKey: 'afternoonStartTime', configValue: formData.afternoonStartTime, configName: '下午起始时间', configType: 'Y' },
|
||||
{ configKey: 'eveningStartTime', configValue: formData.eveningStartTime, configName: '晚上起始时间', configType: 'Y' },
|
||||
{ configKey: 'registrationValidity', configValue: formData.registrationValidity, configName: '挂号有效期(天)', configType: 'Y' },
|
||||
{ configKey: 'registrationDocumentMode', configValue: formData.registrationDocumentMode, configName: '挂号单据模式', configType: 'Y' },
|
||||
{ configKey: 'exemptFlag', configValue: formData.exemptFlag ? '1' : '0', configName: '减免标志', configType: 'Y' },
|
||||
{ configKey: 'consultationFlag', configValue: formData.consultationFlag ? '1' : '0', configName: '义诊标志', configType: 'Y' },
|
||||
{ configKey: 'enableHolidayFeeFloat', configValue: formData.enableHolidayFeeFloat ? '1' : '0', configName: '启用法定节假日挂号费浮动', configType: 'Y' },
|
||||
{ configKey: 'guardianAge', configValue: formData.guardianAge, configName: '监护人规定年龄(岁)', configType: 'Y' },
|
||||
{ configKey: 'enableDoubleScreen', configValue: formData.enableDoubleScreen ? '1' : '0', configName: '门诊挂号启用双屏', configType: 'Y' },
|
||||
{ configKey: 'optionalRegistrationType', configValue: formData.optionalRegistrationType ? '1' : '0', configName: '挂号类型可选择', configType: 'Y' },
|
||||
{ configKey: 'isPrint', configValue: formData.isPrint ? '1' : '0', configName: '是否打印挂号单', configType: 'Y' },
|
||||
];
|
||||
|
||||
let successCount = 0;
|
||||
let failedParams = [];
|
||||
|
||||
try {
|
||||
// 先获取所有配置,避免重复调用API
|
||||
const allConfigsResponse = await listConfig({ pageSize: 1000 });
|
||||
console.log('handleSave - listConfig返回完整结果:', JSON.stringify(allConfigsResponse));
|
||||
|
||||
// 检查返回结果结构
|
||||
let allConfigs = [];
|
||||
if (allConfigsResponse.code === 200) {
|
||||
if (allConfigsResponse.data && Array.isArray(allConfigsResponse.data)) {
|
||||
allConfigs = allConfigsResponse.data;
|
||||
} else if (allConfigsResponse.data && allConfigsResponse.data.rows) {
|
||||
allConfigs = allConfigsResponse.data.rows;
|
||||
} else if (Array.isArray(allConfigsResponse.rows)) {
|
||||
allConfigs = allConfigsResponse.rows;
|
||||
} else if (Array.isArray(allConfigsResponse)) {
|
||||
allConfigs = allConfigsResponse;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('handleSave - 最终获取到的所有配置:', allConfigs);
|
||||
console.log('handleSave - 配置总数:', allConfigs.length);
|
||||
|
||||
// 构建配置项映射表,方便快速查找
|
||||
const configMap = new Map();
|
||||
allConfigs.forEach(config => {
|
||||
configMap.set(config.configKey, config);
|
||||
console.log('handleSave - 添加到映射表:', config.configKey);
|
||||
});
|
||||
|
||||
// 调用系统配置API保存每个参数
|
||||
for (const config of configData) {
|
||||
try {
|
||||
const existingConfig = configMap.get(config.configKey);
|
||||
|
||||
console.log(`处理参数: ${config.configName} (${config.configKey})`);
|
||||
console.log(`现有配置:`, existingConfig);
|
||||
console.log(`要保存的值:`, config.configValue);
|
||||
|
||||
if (existingConfig && existingConfig.configId) {
|
||||
// 如果存在则更新,保留原有数据的configId和创建时间
|
||||
console.log(`更新参数 ${config.configKey},使用configId: ${existingConfig.configId}`);
|
||||
const updateResult = await updateConfig({
|
||||
...config,
|
||||
configId: existingConfig.configId,
|
||||
createTime: existingConfig.createTime,
|
||||
remark: existingConfig.remark || '收费系统配置参数',
|
||||
configType: existingConfig.configType || 'N'
|
||||
});
|
||||
console.log(`更新结果:`, updateResult);
|
||||
} else {
|
||||
// 如果不存在则新增,添加默认备注
|
||||
console.log(`新增参数 ${config.configKey}`);
|
||||
const addResult = await addConfig({
|
||||
...config,
|
||||
remark: '收费系统配置参数',
|
||||
configType: 'N'
|
||||
});
|
||||
console.log(`新增结果:`, addResult);
|
||||
}
|
||||
successCount++;
|
||||
} catch (paramError) {
|
||||
console.error(`保存参数 ${config.configName} (${config.configKey}) 失败:`, paramError);
|
||||
console.error(`错误详情:`, paramError.response || paramError);
|
||||
failedParams.push(config.configName);
|
||||
// 继续处理下一个参数,不中断整体流程
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取配置列表失败:', error);
|
||||
ElMessage.error('获取配置列表失败,无法保存参数');
|
||||
return;
|
||||
}
|
||||
|
||||
// 根据保存结果显示相应消息
|
||||
if (failedParams.length === 0) {
|
||||
ElMessage.success(`保存成功`);
|
||||
// 保存成功后重新加载数据,确保页面显示最新配置
|
||||
loadConfigData(false); // 不显示加载成功消息
|
||||
} else if (successCount > 0) {
|
||||
ElMessage.warning(`${successCount} 个参数保存成功,但以下 ${failedParams.length} 个参数保存失败: ${failedParams.join(', ')}`);
|
||||
// 部分成功也重新加载数据
|
||||
loadConfigData(false); // 不显示加载成功消息
|
||||
} else {
|
||||
ElMessage.error(`所有参数保存失败,请检查系统配置`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存过程中发生错误:', error);
|
||||
ElMessage.error('保存操作异常,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭按钮点击事件
|
||||
const handleClose = () => {
|
||||
router.back();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.app-container {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.config-header {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.config-header .el-button {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.config-tabs {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.config-form {
|
||||
padding: 10px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
flex-wrap: nowrap; /* 禁止行内元素换行 */
|
||||
overflow-x: hidden; /* 防止溢出 */
|
||||
}
|
||||
|
||||
.config-form .el-form-item {
|
||||
margin-bottom: 0;
|
||||
margin-right: 60px; /* 增加间距 */
|
||||
min-width: 350px; /* 增加最小宽度 */
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-shrink: 0; /* 禁止压缩 */
|
||||
}
|
||||
|
||||
/* 紧凑表单样式 */
|
||||
.compact-form {
|
||||
label-width: 150px !important;
|
||||
}
|
||||
|
||||
.compact-form .el-form-item {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* 所有表单标签的统一样式 */
|
||||
.config-form .el-form-item__label {
|
||||
font-weight: 500;
|
||||
width: 220px !important; /* 足够宽的标签宽度 */
|
||||
text-align: right;
|
||||
padding-right: 15px;
|
||||
white-space: nowrap !important; /* 强制不换行 */
|
||||
overflow: visible !important; /* 允许内容溢出(确保不截断) */
|
||||
text-overflow: clip !important; /* 不显示省略号 */
|
||||
flex-shrink: 0 !important; /* 禁止压缩 */
|
||||
font-size: 14px !important; /* 统一字体大小 */
|
||||
line-height: 32px !important; /* 统一行高 */
|
||||
height: 32px !important; /* 统一高度 */
|
||||
margin: 0 !important; /* 清除默认边距 */
|
||||
padding: 0 15px 0 0 !important; /* 统一内边距 */
|
||||
}
|
||||
|
||||
/* 复选框标签样式 */
|
||||
.compact-form .el-form-item[checkbox-label] .el-form-item__label {
|
||||
padding-right: 10px;
|
||||
width: 150px !important;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.compact-form .el-form-item[checkbox-label] .el-form-item__content {
|
||||
margin-left: 0 !important;
|
||||
}
|
||||
|
||||
/* 自定义复选框标签容器 */
|
||||
.checkbox-label-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.checkbox-label-container span {
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 空标签的样式,用于对齐 */
|
||||
.config-form .el-form-item__label:empty {
|
||||
width: 18px !important;
|
||||
padding-right: 5px !important;
|
||||
}
|
||||
|
||||
.config-form .el-form-item--medium .el-form-item__content {
|
||||
line-height: 32px;
|
||||
flex-shrink: 0;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.config-form .el-checkbox {
|
||||
margin-left: 0;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 确保输入框与标签对齐 */
|
||||
.config-form .el-input {
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.config-form .el-input__wrapper {
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.tab-content {
|
||||
padding: 10px;
|
||||
color: #606266;
|
||||
text-align: center;
|
||||
line-height: 60px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,814 @@
|
||||
<template>
|
||||
<div class="package-management">
|
||||
<!-- 顶部筛选栏 -->
|
||||
<div class="filter-section">
|
||||
<el-form :model="queryParams" :inline="true" label-width="80px">
|
||||
<el-form-item label="日期">
|
||||
<el-date-picker
|
||||
v-model="dateRange"
|
||||
type="daterange"
|
||||
range-separator="-"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="卫生机构">
|
||||
<el-select v-model="queryParams.organization" placeholder="请选择机构" style="width: 150px" clearable filterable>
|
||||
<el-option
|
||||
v-for="org in organizationOptions"
|
||||
:key="org.value"
|
||||
:label="org.label"
|
||||
:value="org.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="套餐名称">
|
||||
<el-input
|
||||
v-model="queryParams.packageName"
|
||||
placeholder="请输入套餐名称"
|
||||
style="width: 200px"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="套餐级别">
|
||||
<el-select v-model="queryParams.packageLevel" placeholder="请选择套餐级别" style="width: 150px" clearable>
|
||||
<el-option
|
||||
v-for="item in packageLevelOptions"
|
||||
:key="item.dictValue"
|
||||
:label="item.dictLabel"
|
||||
:value="item.dictValue"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="套餐类别">
|
||||
<el-select v-model="queryParams.packageType" placeholder="请选择套餐类别" style="width: 150px" clearable>
|
||||
<el-option label="检查套餐" value="检查套餐" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="科室">
|
||||
<el-select v-model="queryParams.department" placeholder="请选择科室" style="width: 150px" clearable filterable>
|
||||
<el-option
|
||||
v-for="dept in departments"
|
||||
:key="dept.dictValue"
|
||||
:label="dept.dictLabel"
|
||||
:value="dept.deptCode || dept.busNoPrefix || dept.rawOrg?.busNo || dept.dictLabel"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="用户">
|
||||
<el-input
|
||||
v-model="queryParams.user"
|
||||
placeholder="请输入用户名称"
|
||||
style="width: 150px"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="handleQuery" icon="Search">查询</el-button>
|
||||
<el-button @click="handleReset" icon="Refresh">重置</el-button>
|
||||
<el-button type="success" @click="handleAdd" icon="Plus">新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
|
||||
<!-- 表格展示区 -->
|
||||
<div class="table-section">
|
||||
<div class="table-wrapper">
|
||||
<el-table
|
||||
:data="tableData"
|
||||
border
|
||||
style="width: 100%"
|
||||
v-loading="loading"
|
||||
:max-height="600"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="organization" label="卫生机构" width="120" align="center" />
|
||||
<el-table-column prop="maintainDate" label="日期" width="120" align="center" />
|
||||
<el-table-column prop="packageName" label="套餐名称" min-width="150" show-overflow-tooltip />
|
||||
<el-table-column prop="packageType" label="套餐类别" width="100" align="center" />
|
||||
<el-table-column prop="packageLevel" label="套餐级别" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ getLevelLabel(row.packageLevel) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="department" label="科室" width="150" align="center">
|
||||
<template #default="{ row }">
|
||||
<span :title="row.department && /^[A-Z]\d{2}$/.test(row.department.trim()) ? '旧编码格式,建议编辑套餐重新选择科室' : ''">
|
||||
{{ getDeptName(row.department) }}
|
||||
</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="user" label="用户" width="100" align="center" />
|
||||
<el-table-column prop="packagePrice" label="金额" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ (row.packagePrice || 0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="serviceFee" label="服务费" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ (row.serviceFee || 0).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总金额" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
{{ ((row.packagePrice || 0) + (row.serviceFee || 0)).toFixed(2) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="组合套餐" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.packagePriceEnabled === 1 ? 'success' : 'danger'">
|
||||
{{ row.packagePriceEnabled === 1 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="显示套餐名" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.showPackageName === 1 ? 'success' : 'info'">
|
||||
{{ row.showPackageName === 1 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="启用标志" width="100" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag :type="row.isDisabled === 0 ? 'success' : 'danger'">
|
||||
{{ row.isDisabled === 0 ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="操作人" width="100" align="center" />
|
||||
<el-table-column label="操作" width="180" align="center" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<div class="actions">
|
||||
<el-button
|
||||
class="btn btn-edit"
|
||||
size="small"
|
||||
circle
|
||||
@click="handleEdit(row)"
|
||||
title="编辑"
|
||||
>
|
||||
✏️
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn btn-view"
|
||||
size="small"
|
||||
circle
|
||||
@click="handleView(row)"
|
||||
title="查看"
|
||||
>
|
||||
👁️
|
||||
</el-button>
|
||||
<el-button
|
||||
class="btn btn-delete"
|
||||
size="small"
|
||||
circle
|
||||
@click="handleDelete(row)"
|
||||
title="删除"
|
||||
>
|
||||
✕
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部分页 -->
|
||||
<div class="pagination-section">
|
||||
<el-pagination
|
||||
v-model:current-page="queryParams.pageNo"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:page-sizes="[10, 20, 50, 100]"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleQuery"
|
||||
@current-change="handleQuery"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {getDicts} from '@/api/system/dict/data'
|
||||
import {listDept} from '@/api/system/dept'
|
||||
import {delCheckPackage, getCheckPackage, listCheckPackage} from '@/api/system/checkType'
|
||||
import {getTenantPage} from '@/api/system/tenant'
|
||||
import request from '@/utils/request'
|
||||
import useUserStore from '@/store/modules/user'
|
||||
|
||||
// 定义emit事件
|
||||
const emit = defineEmits(['switch-to-settings'])
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
//删除
|
||||
function handleDelete(row) {
|
||||
const currentUser = userStore.name
|
||||
console.log('当前用户:', currentUser, '套餐创建者:', row.creator)
|
||||
//只有创建者本人才能删除(creator为空时不能删除)
|
||||
if(!row.creator){
|
||||
ElMessage.warning('该套餐创建者未知,无法删除')
|
||||
return
|
||||
}
|
||||
if(row.creator !== currentUser){
|
||||
ElMessage.warning(`该套餐由"${row.creator}"创建,您没有权限删除`)
|
||||
return
|
||||
}
|
||||
|
||||
ElMessageBox.confirm(
|
||||
`确认删除套餐ID:${row.id} - ${row.packageName} 吗?删除后将无法恢复`,
|
||||
'确认删除',
|
||||
{
|
||||
confirmButtonText:'确定删除',
|
||||
cancelButtonText:'取消',
|
||||
type: 'warning',
|
||||
buttonSize:'default'
|
||||
}
|
||||
).then(async () => {
|
||||
try{const response = await delCheckPackage(row.id)
|
||||
|
||||
if(response && response.code === 200 || response.code === 0){
|
||||
ElMessage.success('删除成功')
|
||||
handleQuery()
|
||||
}else{
|
||||
ElMessage.error(response?.msg || response?.message || '删除失败')
|
||||
}
|
||||
}catch(error){
|
||||
console.error('删除失败:',error)
|
||||
const errorMsg = error?.response?.data?.msg || error?.message || ''
|
||||
if(errorMsg.includes('foreign key') || errorMsg.includes('violates foreign key')){
|
||||
ElMessage.warning('该套餐已被使用,无法删除')
|
||||
}else{
|
||||
ElMessage.error('删除失败:'+(error.message || '未知错误'))
|
||||
}
|
||||
}
|
||||
}).catch(() => {})
|
||||
}
|
||||
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
organization: '',
|
||||
packageName: '',
|
||||
packageLevel: '',
|
||||
packageType: '',
|
||||
department: '',
|
||||
user: ''
|
||||
})
|
||||
|
||||
// 日期范围
|
||||
const dateRange = ref([])
|
||||
|
||||
// 表格数据
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
// 套餐级别选项
|
||||
const packageLevelOptions = ref([])
|
||||
// 科室选项
|
||||
const departments = ref([])
|
||||
// 卫生机构选项
|
||||
const organizationOptions = ref([])
|
||||
|
||||
// 初始化数据
|
||||
onMounted(async () => {
|
||||
// 获取套餐级别字典
|
||||
try {
|
||||
const levelResponse = await getDicts('examination_item_package_level')
|
||||
if (levelResponse && levelResponse.data) {
|
||||
packageLevelOptions.value = levelResponse.data
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取套餐级别字典失败:', error)
|
||||
}
|
||||
|
||||
// 获取科室列表 - 使用Organization完整API(包含编码和名称)
|
||||
try {
|
||||
// 使用Organization完整API获取科室列表(包含busNo编码)
|
||||
const orgResponse = await request({
|
||||
url: '/base-data-manage/organization/organization',
|
||||
method: 'get',
|
||||
params: {
|
||||
pageNo: 1,
|
||||
pageSize: 1000 // 获取足够多的数据
|
||||
}
|
||||
})
|
||||
let orgList = []
|
||||
if (orgResponse) {
|
||||
if (orgResponse.data) {
|
||||
if (orgResponse.data.records && Array.isArray(orgResponse.data.records)) {
|
||||
orgList = orgResponse.data.records
|
||||
} else if (Array.isArray(orgResponse.data)) {
|
||||
orgList = orgResponse.data
|
||||
}
|
||||
} else if (Array.isArray(orgResponse)) {
|
||||
orgList = orgResponse
|
||||
}
|
||||
}
|
||||
|
||||
// 展开树结构,过滤出科室类型(typeEnum=2)
|
||||
if (orgList && orgList.length > 0) {
|
||||
const flattenList = []
|
||||
function flatten(nodes) {
|
||||
nodes.forEach(node => {
|
||||
if (node.typeEnum === 2 && node.busNo && node.name) {
|
||||
flattenList.push(node)
|
||||
}
|
||||
if (node.children && node.children.length > 0) {
|
||||
flatten(node.children)
|
||||
}
|
||||
})
|
||||
}
|
||||
flatten(orgList)
|
||||
orgList = flattenList
|
||||
}
|
||||
|
||||
if (orgList && orgList.length > 0) {
|
||||
departments.value = orgList.map(org => {
|
||||
const busNo = (org.busNo || org.code || '').trim()
|
||||
const name = (org.name || org.deptName || '').trim()
|
||||
const busNoPrefix = busNo ? busNo.split('.')[0] : ''
|
||||
return {
|
||||
dictValue: name,
|
||||
dictLabel: name,
|
||||
deptId: org.id || org.deptId,
|
||||
deptCode: busNo || name,
|
||||
busNoPrefix: busNoPrefix,
|
||||
rawOrg: org
|
||||
}
|
||||
})
|
||||
} else {
|
||||
// 如果Organization API没有数据,使用系统部门API
|
||||
const deptResponse = await listDept()
|
||||
|
||||
let deptList = []
|
||||
if (deptResponse) {
|
||||
if (Array.isArray(deptResponse)) {
|
||||
deptList = deptResponse
|
||||
} else if (deptResponse.data) {
|
||||
if (Array.isArray(deptResponse.data)) {
|
||||
deptList = deptResponse.data
|
||||
} else if (deptResponse.data.data && Array.isArray(deptResponse.data.data)) {
|
||||
deptList = deptResponse.data.data
|
||||
}
|
||||
} else if (deptResponse.code === 200 && deptResponse.data) {
|
||||
deptList = Array.isArray(deptResponse.data) ? deptResponse.data : []
|
||||
}
|
||||
}
|
||||
|
||||
if (deptList && deptList.length > 0) {
|
||||
departments.value = deptList.map(dept => ({
|
||||
dictValue: dept.deptName || dept.name,
|
||||
dictLabel: dept.deptName || dept.name,
|
||||
deptId: dept.deptId || dept.id,
|
||||
deptCode: dept.deptName || dept.name
|
||||
}))
|
||||
} else {
|
||||
// 如果获取失败,尝试使用字典方式
|
||||
try {
|
||||
const dictResponse = await getDicts('dept')
|
||||
if (dictResponse && dictResponse.data) {
|
||||
departments.value = dictResponse.data
|
||||
}
|
||||
} catch (dictError) {
|
||||
console.error('获取科室字典失败:', dictError)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取科室列表失败:', error)
|
||||
// 如果获取失败,尝试使用字典方式
|
||||
try {
|
||||
const dictResponse = await getDicts('dept')
|
||||
if (dictResponse && dictResponse.data) {
|
||||
departments.value = dictResponse.data
|
||||
}
|
||||
} catch (dictError) {
|
||||
console.error('获取科室字典也失败:', dictError)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取卫生机构列表
|
||||
try {
|
||||
const tenantResponse = await getTenantPage({ pageNum: 1, pageSize: 100 })
|
||||
if (tenantResponse && tenantResponse.code === 200) {
|
||||
const tenantData = tenantResponse.data || {}
|
||||
let tenantList = []
|
||||
if (Array.isArray(tenantData)) {
|
||||
tenantList = tenantData
|
||||
} else if (tenantData.records) {
|
||||
tenantList = tenantData.records
|
||||
} else if (tenantData.rows) {
|
||||
tenantList = tenantData.rows
|
||||
} else if (tenantData.list) {
|
||||
tenantList = tenantData.list
|
||||
}
|
||||
// 过滤启用的机构
|
||||
organizationOptions.value = tenantList
|
||||
.filter(item => item && item.status === '0')
|
||||
.map(item => ({
|
||||
value: item.tenantName || item.name || item.orgName || String(item.id),
|
||||
label: item.tenantName || item.name || item.orgName || String(item.id)
|
||||
}))
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取卫生机构列表失败:', error)
|
||||
}
|
||||
|
||||
// 加载列表数据
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
// 获取级别标签
|
||||
function getLevelLabel(value) {
|
||||
const item = packageLevelOptions.value.find(i => i.dictValue === value)
|
||||
return item ? item.dictLabel : value
|
||||
}
|
||||
|
||||
// 获取科室名称(根据编码或名称查找)
|
||||
function getDeptName(deptValue) {
|
||||
if (!deptValue) return ''
|
||||
|
||||
// 去除前后空格
|
||||
const trimmedValue = String(deptValue).trim()
|
||||
if (!trimmedValue) return ''
|
||||
|
||||
// 如果科室列表为空,直接返回原值
|
||||
if (!departments.value || departments.value.length === 0) {
|
||||
return trimmedValue
|
||||
}
|
||||
|
||||
// 先尝试精确匹配编码(去除所有空格,转大写)
|
||||
let dept = departments.value.find(d => {
|
||||
const code = String(d.deptCode || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const searchValue = trimmedValue.replace(/\s+/g, '').toUpperCase()
|
||||
return code && code === searchValue
|
||||
})
|
||||
|
||||
// 如果找不到,尝试通过busNo前缀匹配(优先使用存储的前缀)
|
||||
if (!dept) {
|
||||
dept = departments.value.find(d => {
|
||||
const busNoPrefix = String(d.busNoPrefix || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const searchValue = trimmedValue.replace(/\s+/g, '').toUpperCase()
|
||||
return busNoPrefix && busNoPrefix === searchValue
|
||||
})
|
||||
}
|
||||
|
||||
// 如果找不到,尝试通过原始busNo精确匹配
|
||||
if (!dept) {
|
||||
dept = departments.value.find(d => {
|
||||
const rawBusNo = String(d.rawOrg?.busNo || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const searchValue = trimmedValue.replace(/\s+/g, '').toUpperCase()
|
||||
return rawBusNo && rawBusNo === searchValue
|
||||
})
|
||||
}
|
||||
|
||||
// 如果找不到,尝试层级编码匹配(busNo可能是 "A01.001",搜索值是 "A01")
|
||||
if (!dept) {
|
||||
dept = departments.value.find(d => {
|
||||
const rawBusNo = String(d.rawOrg?.busNo || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const searchValue = trimmedValue.replace(/\s+/g, '').toUpperCase()
|
||||
if (!rawBusNo) return false
|
||||
// 如果busNo包含点号,取点号前的部分进行匹配
|
||||
const busNoPrefix = rawBusNo.split('.')[0]
|
||||
// 匹配方式:前缀匹配、完全匹配、或者搜索值作为前缀
|
||||
return busNoPrefix === searchValue ||
|
||||
rawBusNo.startsWith(searchValue + '.') ||
|
||||
rawBusNo === searchValue
|
||||
})
|
||||
}
|
||||
|
||||
// 如果找不到,尝试匹配名称
|
||||
if (!dept) {
|
||||
dept = departments.value.find(d => {
|
||||
const label = String(d.dictLabel || '').trim()
|
||||
const value = String(d.dictValue || '').trim()
|
||||
return label === trimmedValue || value === trimmedValue
|
||||
})
|
||||
}
|
||||
|
||||
// 如果还是找不到,尝试部分匹配(编码可能不完整,或者编码格式不同)
|
||||
if (!dept) {
|
||||
dept = departments.value.find(d => {
|
||||
const code = String(d.deptCode || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const rawBusNo = String(d.rawOrg?.busNo || '').trim().replace(/\s+/g, '').toUpperCase()
|
||||
const searchValue = trimmedValue.replace(/\s+/g, '').toUpperCase()
|
||||
if (!code && !rawBusNo) return false
|
||||
|
||||
// 尝试多种匹配方式
|
||||
return (code && code.includes(searchValue)) ||
|
||||
(code && searchValue.includes(code)) ||
|
||||
(rawBusNo && rawBusNo.includes(searchValue)) ||
|
||||
(rawBusNo && searchValue.includes(rawBusNo)) ||
|
||||
(rawBusNo && rawBusNo.startsWith(searchValue)) ||
|
||||
(code && code.length >= 3 && searchValue.length >= 3 && code.substring(0, 3) === searchValue.substring(0, 3))
|
||||
})
|
||||
}
|
||||
|
||||
if (dept && dept.dictLabel) {
|
||||
return dept.dictLabel
|
||||
}
|
||||
|
||||
// 无法匹配时,返回原始编码值
|
||||
const isOldFormat = /^[A-Z]\d{2}$/.test(trimmedValue)
|
||||
if (isOldFormat) {
|
||||
return trimmedValue + ' (旧格式)'
|
||||
}
|
||||
|
||||
return trimmedValue
|
||||
}
|
||||
|
||||
// 查询
|
||||
async function handleQuery() {
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 构建查询参数
|
||||
const params = {
|
||||
...queryParams
|
||||
}
|
||||
|
||||
// 处理日期范围
|
||||
if (dateRange.value && dateRange.value.length === 2) {
|
||||
params.startDate = dateRange.value[0]
|
||||
params.endDate = dateRange.value[1]
|
||||
}
|
||||
|
||||
const response = await listCheckPackage(params)
|
||||
|
||||
if (response && response.data) {
|
||||
// 处理不同的响应格式
|
||||
if (Array.isArray(response.data)) {
|
||||
tableData.value = response.data
|
||||
total.value = response.data.length
|
||||
} else if (response.data.records) {
|
||||
tableData.value = response.data.records
|
||||
total.value = response.data.total || 0
|
||||
} else {
|
||||
tableData.value = []
|
||||
total.value = 0
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('查询失败:', error)
|
||||
ElMessage.error('查询失败: ' + (error.message || '未知错误'))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 重置
|
||||
function handleReset() {
|
||||
queryParams.pageNo = 1
|
||||
queryParams.pageSize = 10
|
||||
queryParams.organization = ''
|
||||
queryParams.packageName = ''
|
||||
queryParams.packageLevel = ''
|
||||
queryParams.packageType = ''
|
||||
queryParams.department = ''
|
||||
queryParams.user = ''
|
||||
dateRange.value = []
|
||||
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 新增
|
||||
function handleAdd() {
|
||||
emit('switch-to-settings', { mode: 'add', data: null })
|
||||
}
|
||||
|
||||
// 编辑
|
||||
async function handleEdit(row) {
|
||||
try {
|
||||
const response = await getCheckPackage(row.id)
|
||||
|
||||
if (response && (response.code === 200 || response.code === 0) && response.data) {
|
||||
emit('switch-to-settings', { mode: 'edit', data: response.data })
|
||||
} else {
|
||||
ElMessage.error('加载套餐数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载套餐数据失败:', error)
|
||||
ElMessage.error('数据加载失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 查看
|
||||
async function handleView(row) {
|
||||
try {
|
||||
const response = await getCheckPackage(row.id)
|
||||
|
||||
if (response && (response.code === 200 || response.code === 0) && response.data) {
|
||||
emit('switch-to-settings', { mode: 'view', data: response.data })
|
||||
} else {
|
||||
ElMessage.error('加载套餐数据失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载套餐数据失败:', error)
|
||||
ElMessage.error('数据加载失败,请重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 查询参数
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.package-management {
|
||||
padding: 24px;
|
||||
background-color: #f5f7fa;
|
||||
min-height: 100vh;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 16px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.filter-section :deep(.el-form-item) {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.table-section {
|
||||
background: white;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
max-height: calc(100vh - 400px);
|
||||
min-height: 400px;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 20px;
|
||||
}
|
||||
|
||||
/* 统一的操作按钮样式 */
|
||||
.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-view {
|
||||
background: linear-gradient(135deg, #722ED1 0%, #9254DE 100%);
|
||||
}
|
||||
|
||||
.btn-view:hover {
|
||||
background: linear-gradient(135deg, #531DAE 0%, #722ED1 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%);
|
||||
}
|
||||
|
||||
/* 优化滚动条样式 - 支持水平和垂直滚动 */
|
||||
.table-wrapper {
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #c1c1c1 #f1f1f1;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar-track {
|
||||
background: #f1f1f1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar-thumb {
|
||||
background: #c1c1c1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar-thumb:hover {
|
||||
background: #a8a8a8;
|
||||
}
|
||||
|
||||
/* 确保表格可以水平滚动 */
|
||||
.table-wrapper :deep(.el-table) {
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.table-wrapper :deep(.el-table__body-wrapper) {
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 1200px) {
|
||||
.filter-section :deep(.el-form) {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.package-management {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.filter-section,
|
||||
.table-section,
|
||||
.pagination-section {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
max-height: calc(100vh - 350px);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
2481
openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/index.vue
Executable file
2481
openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/index.vue
Executable file
File diff suppressed because it is too large
Load Diff
930
openhis-ui-vue3/src/views/maintainSystem/lisGroup/index.vue
Executable file
930
openhis-ui-vue3/src/views/maintainSystem/lisGroup/index.vue
Executable file
@@ -0,0 +1,930 @@
|
||||
<template>
|
||||
<div class="lis-group-maintain">
|
||||
|
||||
<!-- 标题区域 -->
|
||||
<div class="header">
|
||||
<h2>LIS分组维护</h2>
|
||||
</div>
|
||||
|
||||
<!-- 表格展示区域 -->
|
||||
<div class="table-container">
|
||||
<table class="data-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 50px;">行</th>
|
||||
<th style="width: 150px;">卫生机构</th>
|
||||
<th style="width: 150px;">日期</th>
|
||||
<th style="width: 200px;">LIS分组名称</th>
|
||||
<th style="width: 120px;">采血管</th>
|
||||
<th style="width: 300px;">备注</th>
|
||||
<th style="width: 150px;">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(item, index) in tableData"
|
||||
:key="item.id || index"
|
||||
:class="{ 'editing-row': item.editing, 'success-row': item.success, 'deleting-row': item.deleting }"
|
||||
>
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>
|
||||
<template v-if="item.editing">
|
||||
<input
|
||||
type="text"
|
||||
v-model="item.healthInstitution"
|
||||
disabled
|
||||
>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.healthInstitution }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="item.editing">
|
||||
<input type="text" v-model="item.date" disabled>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.date }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="item.editing">
|
||||
<input
|
||||
type="text"
|
||||
v-model="item.lisGroupName"
|
||||
placeholder="请输入分组名称"
|
||||
:class="{ 'error-input': item.errors?.lisGroupName, 'focus-target': item.isNew }"
|
||||
>
|
||||
<span v-if="item.errors?.lisGroupName" class="error-message">{{ item.errors.lisGroupName }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.lisGroupName }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="item.editing">
|
||||
<select
|
||||
v-model="item.bloodCollectionTube"
|
||||
:class="{ 'error-input': item.errors?.bloodCollectionTube }"
|
||||
>
|
||||
<option value="">请选择采血管</option>
|
||||
<option v-for="tube in bloodTubeOptions" :key="tube" :value="tube">
|
||||
{{ tube }}
|
||||
</option>
|
||||
</select>
|
||||
<span v-if="item.errors?.bloodCollectionTube" class="error-message">{{ item.errors.bloodCollectionTube }}</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.bloodCollectionTube }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="item.editing">
|
||||
<input type="text" v-model="item.remark" placeholder="请输入备注信息">
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.remark || '' }}
|
||||
</template>
|
||||
</td>
|
||||
<td>
|
||||
<div class="actions">
|
||||
<template v-if="!item.editing">
|
||||
<button class="btn btn-edit" @click="startEdit(item)">✏️</button>
|
||||
<button class="btn btn-primary" @click="addRow">+</button>
|
||||
<button class="btn btn-delete" @click="deleteRow(index)">🗑</button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<button class="btn btn-confirm" @click="confirmEdit(item)">√</button>
|
||||
<button class="btn btn-cancel" @click="cancelEdit(item)">×</button>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- 分页区域 -->
|
||||
<div class="pagination">
|
||||
<button class="pagination-btn" @click="prevPage" :disabled="currentPage <= 1">‹</button>
|
||||
<span>{{ currentPage }}</span>
|
||||
<button class="pagination-btn" @click="nextPage" :disabled="currentPage >= totalPages">›</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import {computed, nextTick, onMounted, reactive, ref} from 'vue'
|
||||
import {addLisGroup, delLisGroup, listLisGroup, updateLisGroup} from '@/api/system/checkType'
|
||||
import {getDicts} from "@/api/system/dict/data";
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import {ElMessage, ElMessageBox} from 'element-plus';
|
||||
|
||||
export default {
|
||||
name: 'LisGroupMaintain',
|
||||
setup() {
|
||||
// 获取用户store
|
||||
const userStore = useUserStore();
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false)
|
||||
|
||||
// 采血管选项
|
||||
const bloodTubeOptions = ref([]);
|
||||
|
||||
// 加载采血管颜色字典数据
|
||||
const loadBloodTubeDict = async () => {
|
||||
try {
|
||||
const response = await getDicts('test_the_color_of_the_blood_collection_tube');
|
||||
if (response.code === 200 && response.data) {
|
||||
// 根据实际返回数据格式调整
|
||||
bloodTubeOptions.value = response.data.map(item => item.label || item.dictLabel);
|
||||
}
|
||||
} catch (error) {
|
||||
// 如果字典获取失败,使用默认选项作为备选
|
||||
bloodTubeOptions.value = ['黄管', '紫管', '蓝管'];
|
||||
}
|
||||
};
|
||||
|
||||
// 分页相关数据
|
||||
const currentPage = ref(1)
|
||||
const pageSize = ref(10)
|
||||
const totalItems = ref(2) // 总数据量,实际应用中应该从后端获取
|
||||
|
||||
// 总页数
|
||||
const totalPages = computed(() => {
|
||||
return Math.ceil(totalItems.value / pageSize.value)
|
||||
})
|
||||
|
||||
// 获取当前用户的卫生机构名称
|
||||
const getCurrentHospital = () => {
|
||||
return userStore.hospitalName || userStore.orgName || '演示医院';
|
||||
};
|
||||
|
||||
// 表格数据
|
||||
const tableData = reactive([])
|
||||
|
||||
// 获取当前日期
|
||||
const getCurrentDate = () => {
|
||||
const date = new Date()
|
||||
const year = date.getFullYear()
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0')
|
||||
const day = String(date.getDate()).padStart(2, '0')
|
||||
return `${year}-${month}-${day}`
|
||||
}
|
||||
|
||||
// 加载LIS分组数据
|
||||
const loadLisGroups = async () => {
|
||||
// 确保采血管字典数据已加载
|
||||
if (bloodTubeOptions.value.length === 0) {
|
||||
await loadBloodTubeDict();
|
||||
}
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 构建查询参数
|
||||
const query = {
|
||||
pageNum: currentPage.value,
|
||||
pageSize: pageSize.value
|
||||
}
|
||||
|
||||
// 调用接口获取数据
|
||||
const response = await listLisGroup(query)
|
||||
|
||||
// 清空现有数据
|
||||
tableData.splice(0, tableData.length)
|
||||
totalItems.value = 0
|
||||
|
||||
// 处理明确的数据结构
|
||||
if (response && response.code === 200) {
|
||||
// 清空现有数据
|
||||
tableData.splice(0, tableData.length);
|
||||
|
||||
// 获取最内层的数据结构
|
||||
const innerResponse = response.data;
|
||||
if (innerResponse && innerResponse.code === 200) {
|
||||
const data = innerResponse.data;
|
||||
if (data && Array.isArray(data.records)) {
|
||||
// 更新总记录数
|
||||
totalItems.value = data.total || 0;
|
||||
|
||||
// 处理每条记录
|
||||
data.records.forEach((item, index) => {
|
||||
const formattedItem = {
|
||||
id: item.id,
|
||||
healthInstitution: item.hospital,
|
||||
date: item.date,
|
||||
lisGroupName: item.groupName,
|
||||
bloodCollectionTube: item.tube,
|
||||
remark: item.remark || '',
|
||||
editing: false
|
||||
};
|
||||
|
||||
tableData.push(formattedItem);
|
||||
});
|
||||
} else {
|
||||
totalItems.value = 0;
|
||||
}
|
||||
} else {
|
||||
totalItems.value = 0;
|
||||
}
|
||||
} else {
|
||||
// 非成功状态码
|
||||
const errorMsg = response.msg || response.message || `请求失败,状态码: ${response.code}`
|
||||
showMessage(errorMsg, 'error')
|
||||
totalItems.value = 0;
|
||||
}
|
||||
} catch (error) {
|
||||
// 提取错误信息
|
||||
let errorMessage = '加载LIS分组数据失败,请稍后重试'
|
||||
|
||||
if (error.response) {
|
||||
// 服务器响应了,但状态码不是2xx
|
||||
if (error.response.data) {
|
||||
errorMessage = error.response.data.msg ||
|
||||
error.response.data.message ||
|
||||
error.response.data.error ||
|
||||
`服务器错误 (${error.response.status})`
|
||||
} else {
|
||||
errorMessage = `服务器错误 (${error.response.status})`
|
||||
}
|
||||
} else if (error.request) {
|
||||
// 请求已发出,但未收到响应
|
||||
errorMessage = '网络错误,服务器未响应,请检查网络连接'
|
||||
} else if (error.message) {
|
||||
// 请求配置出错
|
||||
errorMessage = error.message
|
||||
}
|
||||
|
||||
// 显示错误信息
|
||||
showMessage(errorMessage, 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 开始编辑
|
||||
const startEdit = (item) => {
|
||||
// 存储原始数据,用于取消操作
|
||||
item.originalData = { ...item }
|
||||
item.editing = true
|
||||
}
|
||||
|
||||
// 验证数据
|
||||
const validateData = (item) => {
|
||||
const errors = {}
|
||||
|
||||
// 清除之前的错误信息
|
||||
delete item.errors
|
||||
|
||||
// 验证LIS分组名称
|
||||
if (!item.lisGroupName || item.lisGroupName.trim() === '') {
|
||||
errors.lisGroupName = 'LIS分组名称不能为空'
|
||||
} else if (item.lisGroupName.length > 50) {
|
||||
errors.lisGroupName = 'LIS分组名称不能超过50个字符'
|
||||
}
|
||||
|
||||
// 验证采血管
|
||||
if (!item.bloodCollectionTube) {
|
||||
errors.bloodCollectionTube = '请选择采血管'
|
||||
}
|
||||
|
||||
// 验证备注长度
|
||||
if (item.remark && item.remark.length > 200) {
|
||||
errors.remark = '备注信息不能超过200个字符'
|
||||
}
|
||||
|
||||
if (Object.keys(errors).length > 0) {
|
||||
item.errors = errors
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// 显示提示信息
|
||||
const showMessage = (message, type = 'success') => {
|
||||
// 使用Element Plus的消息组件
|
||||
if (type === 'error') {
|
||||
ElMessage.error(message)
|
||||
} else {
|
||||
ElMessage.success(message)
|
||||
}
|
||||
}
|
||||
|
||||
// 确认编辑
|
||||
const confirmEdit = async (item) => {
|
||||
// 验证数据
|
||||
if (!validateData(item)) {
|
||||
showMessage('请检查并修正错误信息', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否有重复的分组名称(在同一个卫生机构内)
|
||||
const duplicate = tableData.find(data =>
|
||||
data.id !== item.id &&
|
||||
data.healthInstitution === item.healthInstitution &&
|
||||
data.lisGroupName === item.lisGroupName
|
||||
)
|
||||
|
||||
if (duplicate) {
|
||||
if (!item.errors) item.errors = {}
|
||||
item.errors.lisGroupName = '该分组名称已存在'
|
||||
showMessage('分组名称重复,请修改', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
loading.value = true
|
||||
|
||||
// 准备提交数据(使用后端期望的字段名)
|
||||
const submitData = {
|
||||
id: item.id,
|
||||
hospital: item.healthInstitution, // 前端字段映射到后端字段
|
||||
date: item.date,
|
||||
groupName: item.lisGroupName, // 前端字段映射到后端字段
|
||||
tube: item.bloodCollectionTube, // 前端字段映射到后端字段
|
||||
remark: item.remark
|
||||
}
|
||||
|
||||
let response
|
||||
// 判断是新增还是修改
|
||||
if (item.isNew) {
|
||||
// 新增
|
||||
response = await addLisGroup(submitData)
|
||||
} else {
|
||||
// 修改
|
||||
response = await updateLisGroup(submitData)
|
||||
}
|
||||
|
||||
// 处理响应
|
||||
if (response.code === 200) {
|
||||
// 保存编辑
|
||||
item.editing = false
|
||||
delete item.originalData
|
||||
delete item.errors
|
||||
|
||||
// 先检查是否是新增记录,再删除isNew属性
|
||||
const isNewRecord = item.isNew
|
||||
delete item.isNew
|
||||
|
||||
// 添加保存成功样式,包括新增记录
|
||||
item.success = true
|
||||
// 0.5秒后移除成功样式
|
||||
setTimeout(() => {
|
||||
item.success = false
|
||||
}, 500)
|
||||
|
||||
showMessage('保存成功')
|
||||
|
||||
// 修改完成后重新请求后端本页数据,确保数据准确性
|
||||
setTimeout(() => {
|
||||
loadLisGroups()
|
||||
}, 500)
|
||||
} else {
|
||||
showMessage(`保存失败: ${response.msg || '未知错误'}`, 'error')
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage(`保存失败: ${error.message || '未知错误'}`, 'error')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 取消编辑
|
||||
const cancelEdit = (item) => {
|
||||
try {
|
||||
if (item.isNew) {
|
||||
// 如果是新增的行,从数组中移除
|
||||
const index = tableData.findIndex(row => row.id === item.id)
|
||||
if (index > -1) {
|
||||
tableData.splice(index, 1)
|
||||
// 更新总数
|
||||
if (totalItems.value > 0) {
|
||||
totalItems.value--
|
||||
}
|
||||
}
|
||||
} else if (item.originalData) {
|
||||
// 恢复原始数据
|
||||
Object.assign(item, item.originalData)
|
||||
delete item.originalData
|
||||
item.editing = false
|
||||
} else {
|
||||
item.editing = false
|
||||
}
|
||||
} catch (error) {
|
||||
showMessage('取消编辑失败', 'error')
|
||||
}
|
||||
}
|
||||
|
||||
// 添加新行
|
||||
const addRow = async () => {
|
||||
// 检查是否已有正在编辑的行
|
||||
const editingRow = tableData.find(item => item.editing)
|
||||
if (editingRow) {
|
||||
showMessage('请先完成当前行的编辑', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
const newId = String(Math.max(...tableData.map(item => parseInt(item.id) || 0), 0) + 1)
|
||||
// 从用户信息中获取当前卫生机构
|
||||
const currentHospital = getCurrentHospital()
|
||||
tableData.push({
|
||||
id: '',
|
||||
healthInstitution: currentHospital, // 默认当前操作工号的卫生机构
|
||||
date: getCurrentDate(), // 默认当前系统时间
|
||||
lisGroupName: '',
|
||||
bloodCollectionTube: '',
|
||||
remark: '',
|
||||
editing: true,
|
||||
errors: null,
|
||||
isNew: true // 标记为新记录
|
||||
})
|
||||
|
||||
// 等待DOM更新后聚焦到第一个输入框
|
||||
await nextTick()
|
||||
const focusElement = document.querySelector('.focus-target')
|
||||
if (focusElement) {
|
||||
focusElement.focus()
|
||||
// 清除focus-target类,避免下次添加行时影响
|
||||
focusElement.classList.remove('focus-target')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除行
|
||||
const deleteRow = async (index) => {
|
||||
const item = tableData[index]
|
||||
// 检查是否正在编辑
|
||||
if (item.editing) {
|
||||
showMessage('请先完成或取消编辑', 'error')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// 使用Element Plus的确认对话框
|
||||
await ElMessageBox.confirm(
|
||||
`确定要删除"${item.lisGroupName}"这条记录吗?`,
|
||||
'删除确认',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
)
|
||||
|
||||
loading.value = true
|
||||
|
||||
// 添加删除行标记,触发渐隐效果
|
||||
item.deleting = true
|
||||
|
||||
// 等待300ms,让渐隐效果完成
|
||||
await new Promise(resolve => setTimeout(resolve, 300))
|
||||
|
||||
// 如果是新添加的记录,直接从数组中移除
|
||||
if (item.isNew) {
|
||||
tableData.splice(index, 1)
|
||||
showMessage('删除成功')
|
||||
totalItems.value--
|
||||
} else {
|
||||
// 调用API删除
|
||||
const response = await delLisGroup(item.id)
|
||||
|
||||
if (response.code === 200) {
|
||||
// 更新总数
|
||||
totalItems.value--
|
||||
|
||||
// 检查当前页码是否仍然有效
|
||||
const newTotalPages = Math.ceil(totalItems.value / pageSize.value)
|
||||
if (currentPage.value > newTotalPages && newTotalPages > 0) {
|
||||
// 如果当前页码大于总页数且总页数大于0,调整为最后一页
|
||||
currentPage.value = newTotalPages
|
||||
}
|
||||
|
||||
// 重新加载当前页数据,保持页码不变
|
||||
loadLisGroups()
|
||||
showMessage('删除成功')
|
||||
} else {
|
||||
showMessage(`删除失败: ${response.msg || '未知错误'}`, 'error')
|
||||
// 如果删除失败,移除删除标记
|
||||
item.deleting = false
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (error !== 'cancel') {
|
||||
showMessage(`删除失败: ${error.message || '未知错误'}`, 'error')
|
||||
// 如果删除失败,移除删除标记
|
||||
if (tableData[index]) {
|
||||
tableData[index].deleting = false
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 分页方法
|
||||
const prevPage = () => {
|
||||
if (currentPage.value > 1) {
|
||||
currentPage.value--
|
||||
// 调用加载数据方法
|
||||
loadLisGroups()
|
||||
}
|
||||
}
|
||||
|
||||
const nextPage = () => {
|
||||
if (currentPage.value < totalPages.value) {
|
||||
currentPage.value++
|
||||
// 调用加载数据方法
|
||||
loadLisGroups()
|
||||
}
|
||||
}
|
||||
|
||||
const goToPage = (page) => {
|
||||
if (page >= 1 && page <= totalPages.value) {
|
||||
currentPage.value = page
|
||||
// 调用加载数据方法
|
||||
loadLisGroups()
|
||||
}
|
||||
}
|
||||
|
||||
// 组件挂载时加载数据
|
||||
onMounted(() => {
|
||||
// 加载采血管字典数据
|
||||
loadBloodTubeDict();
|
||||
// 加载表格数据
|
||||
loadLisGroups()
|
||||
})
|
||||
|
||||
return {
|
||||
tableData,
|
||||
bloodTubeOptions,
|
||||
startEdit,
|
||||
confirmEdit,
|
||||
cancelEdit,
|
||||
addRow,
|
||||
deleteRow,
|
||||
currentPage,
|
||||
totalPages,
|
||||
prevPage,
|
||||
nextPage,
|
||||
goToPage,
|
||||
loading
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.lis-group-maintain {
|
||||
padding: 20px;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.header {
|
||||
height: 56px;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
margin-bottom: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.header h2 {
|
||||
font-size: 18px;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin-bottom: 20px;
|
||||
overflow-x: auto;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 12px;
|
||||
border: 1px solid #e8e8e8;
|
||||
word-break: break-all;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
background-color: #fafafa;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.editing-row {
|
||||
background-color: #f0f8ff;
|
||||
}
|
||||
|
||||
/* 保存成功行样式 */
|
||||
.success-row {
|
||||
background-color: #f6ffed !important;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.actions {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.pagination-section {
|
||||
height: 48px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 分页样式 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.pagination span {
|
||||
font-size: 14px;
|
||||
margin: 0 16px;
|
||||
background-color: #1890FF;
|
||||
color: white;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.pagination-btn:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.pagination-number.active {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.pagination-number.active:hover {
|
||||
background-color: #40a9ff;
|
||||
border-color: #40a9ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
padding: 0;
|
||||
margin-right: 5px;
|
||||
border: 1px solid #d9d9d9;
|
||||
background-color: #fff;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
border-color: #40a9ff;
|
||||
color: #40a9ff;
|
||||
}
|
||||
|
||||
.btn-primary {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
|
||||
.btn-primary:hover {
|
||||
background-color: #40a9ff;
|
||||
border-color: #40a9ff;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-confirm {
|
||||
background-color: #52c41a;
|
||||
color: #fff;
|
||||
border-color: #52c41a;
|
||||
}
|
||||
|
||||
.btn-confirm:hover {
|
||||
background-color: #73d13d;
|
||||
border-color: #73d13d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-cancel {
|
||||
background-color: #f5f5f5;
|
||||
color: #333;
|
||||
border-color: #d9d9d9;
|
||||
}
|
||||
|
||||
.btn-edit {
|
||||
background-color: #faad14;
|
||||
color: #fff;
|
||||
border-color: #faad14;
|
||||
}
|
||||
|
||||
.btn-edit:hover {
|
||||
background-color: #ffc53d;
|
||||
border-color: #ffc53d;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.btn-delete {
|
||||
background-color: #ff4d4f;
|
||||
color: #fff;
|
||||
border-color: #ff4d4f;
|
||||
}
|
||||
|
||||
.btn-delete:hover {
|
||||
background-color: #ff7875;
|
||||
border-color: #ff7875;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
input, select {
|
||||
width: 100%;
|
||||
padding: 4px 6px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 2px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
input:disabled {
|
||||
background-color: #f5f5f5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
/* 错误状态样式 */
|
||||
.error-input {
|
||||
border-color: #ff4d4f !important;
|
||||
}
|
||||
|
||||
.error-input:focus {
|
||||
border-color: #ff4d4f !important;
|
||||
box-shadow: 0 0 0 2px rgba(255, 77, 79, 0.2) !important;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #ff4d4f;
|
||||
margin-top: 2px;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
/* 错误提示区域样式 */
|
||||
.error-message-container {
|
||||
padding: 12px 16px;
|
||||
background-color: #fff2f0;
|
||||
border: 1px solid #ffccc7;
|
||||
border-radius: 2px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.error-text {
|
||||
color: #ff4d4f;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 加载状态样式 */
|
||||
.loading-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(255, 255, 255, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 确保页面容器相对定位,使加载遮罩正常工作 */
|
||||
.lis-group-maintain {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 12px 8px;
|
||||
text-align: center;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
}
|
||||
|
||||
.data-table th {
|
||||
background-color: #fafafa;
|
||||
font-weight: 500;
|
||||
color: rgba(0, 0, 0, 0.85);
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.lis-group-maintain {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.data-table {
|
||||
font-size: 12px;
|
||||
border-collapse: collapse;
|
||||
min-width: 768px; /* 确保表格在小屏幕上可以横向滚动 */
|
||||
}
|
||||
|
||||
.data-table th,
|
||||
.data-table td {
|
||||
padding: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
font-size: 11px;
|
||||
margin-right: 2px;
|
||||
}
|
||||
|
||||
.pagination-btn {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
font-size: 12px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 平滑过渡效果 */
|
||||
.btn,
|
||||
.pagination-btn,
|
||||
.editing-row,
|
||||
tr {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 删除行渐隐效果 */
|
||||
.deleting-row {
|
||||
opacity: 0;
|
||||
height: 0;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 美化输入框和选择框的焦点样式 */
|
||||
input:focus,
|
||||
select:focus {
|
||||
outline: none;
|
||||
border-color: #40a9ff;
|
||||
box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user