套餐设置功能前后端内容基本完成(细节未处理)
This commit is contained in:
79
openhis-ui-vue3/src/api/system/inspectionPackage.js
Normal file
79
openhis-ui-vue3/src/api/system/inspectionPackage.js
Normal file
@@ -0,0 +1,79 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询检验套餐列表
|
||||
export function listInspectionPackage(query) {
|
||||
return request({
|
||||
url: '/system/inspection-package/list',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
// 查询检验套餐详细
|
||||
export function getInspectionPackage(packageId) {
|
||||
return request({
|
||||
url: '/system/inspection-package/' + packageId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 新增检验套餐基本信息
|
||||
export function addInspectionPackage(data) {
|
||||
return request({
|
||||
url: '/system/inspection-package',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 修改检验套餐基本信息
|
||||
export function updateInspectionPackage(data) {
|
||||
return request({
|
||||
url: '/system/inspection-package',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 删除检验套餐
|
||||
export function delInspectionPackage(packageId) {
|
||||
return request({
|
||||
url: '/system/inspection-package/' + packageId,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
// 保存检验套餐明细数据
|
||||
export function saveInspectionPackageDetails(data) {
|
||||
return request({
|
||||
url: '/system/inspection-package/details',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
// 查询检验套餐明细列表
|
||||
export function listInspectionPackageDetails(packageId) {
|
||||
return request({
|
||||
url: '/system/inspection-package/details/' + packageId,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除检验套餐明细
|
||||
export function delInspectionPackageDetails(detailIds) {
|
||||
return request({
|
||||
url: '/system/inspection-package/details',
|
||||
method: 'delete',
|
||||
data: detailIds
|
||||
})
|
||||
}
|
||||
|
||||
// 批量保存检验套餐明细
|
||||
export function batchSaveInspectionPackageDetails(data) {
|
||||
return request({
|
||||
url: '/system/inspection-package/details/batch',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
@@ -389,6 +389,9 @@
|
||||
<button class="btn btn-icon" @click="refreshPage">
|
||||
<i>↻</i> 刷新
|
||||
</button>
|
||||
<button class="btn btn-secondary" @click="resetForm">
|
||||
<i>🔄</i> 重置
|
||||
</button>
|
||||
<button class="btn btn-success" @click="handlePackageManagement">套餐管理</button>
|
||||
</div>
|
||||
<button class="btn btn-lg" @click="handleSave">保存</button>
|
||||
@@ -445,7 +448,7 @@
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label">卫生机构</span>
|
||||
<input type="text" class="form-control" :value="userStore.orgName" readonly>
|
||||
<input type="text" class="form-control" :value="userStore.orgName || '测试机构'" readonly>
|
||||
</div>
|
||||
<div class="form-item">
|
||||
<span class="form-label">套餐金额</span>
|
||||
@@ -616,18 +619,25 @@
|
||||
</td>
|
||||
<td>
|
||||
<template v-if="editingRowId === index">
|
||||
<input type="number" v-model.number="item.quantity" placeholder="数量" style="width: 100%;" @input="updateItemAmount(item)" :style="inputStyle">
|
||||
<input type="number" v-model.number="item.quantity" placeholder="数量" style="width: 100%;" @input="updateItemAmount(item)" :style="inputStyle" min="0" step="1">
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.quantity || '-' }}
|
||||
</template>
|
||||
</td>
|
||||
<td>{{ item.unit || '-' }}</td>
|
||||
<td>{{ item.unitPrice.toFixed(2) }}</td>
|
||||
<td>
|
||||
<template v-if="editingRowId === index">
|
||||
<input type="number" v-model.number="item.unitPrice" placeholder="单价" style="width: 100%;" @input="updateItemAmount(item)" :style="inputStyle" min="0" step="0.01">
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.unitPrice.toFixed(2) }}
|
||||
</template>
|
||||
</td>
|
||||
<td>{{ item.amount.toFixed(2) }}</td>
|
||||
<td>
|
||||
<template v-if="editingRowId === index">
|
||||
<input type="number" v-model.number="item.serviceFee" placeholder="服务费" style="width: 100%;" step="0.01" @input="updateItemTotalAmount(item)" :style="inputStyle">
|
||||
<input type="number" v-model.number="item.serviceFee" placeholder="服务费" style="width: 100%;" step="0.01" @input="updateItemTotalAmount(item)" :style="inputStyle" min="0">
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ item.serviceFee.toFixed(2) }}
|
||||
@@ -644,20 +654,32 @@
|
||||
</td>
|
||||
<td class="action-cell">
|
||||
<div class="table-actions" style="display: flex; gap: 5px;">
|
||||
<div class="action-btn edit-btn" title="编辑" @click="handleEditItem(index)">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="action-btn delete-btn" title="删除" @click="deletePackageItem(index)">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</div>
|
||||
<template v-if="editingRowId === index">
|
||||
<!-- 编辑模式下的保存和取消按钮 -->
|
||||
<div class="action-btn confirm-btn" title="保存" @click="handleEditItem(index)">
|
||||
<span style="font-size: 12px;">✓</span>
|
||||
</div>
|
||||
<div class="action-btn cancel-btn" title="取消" @click="cancelEditItem(index)">
|
||||
<span style="font-size: 12px;">✕</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-else>
|
||||
<!-- 非编辑模式下的编辑和删除按钮 -->
|
||||
<div class="action-btn edit-btn" title="编辑" @click="handleEditItem(index)">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
|
||||
<path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<div class="action-btn delete-btn" title="删除" @click="deletePackageItem(index)">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="3 6 5 6 21 6"></polyline>
|
||||
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path>
|
||||
<line x1="10" y1="11" x2="10" y2="17"></line>
|
||||
<line x1="14" y1="11" x2="14" y2="17"></line>
|
||||
</svg>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -672,13 +694,14 @@
|
||||
<script setup>
|
||||
import useUserStore from '@/store/modules/user';
|
||||
import { ref, reactive, onMounted, watch, computed, nextTick, getCurrentInstance } from 'vue';
|
||||
import { ElMessage, ElAutocomplete, ElMessageBox } from 'element-plus';
|
||||
import { ElMessage, ElAutocomplete, ElMessageBox, ElLoading } from 'element-plus';
|
||||
import * as echarts from 'echarts';
|
||||
import { useRouter, useRoute } from 'vue-router';
|
||||
import { formatDate } from '@/utils/index';
|
||||
import request from '@/utils/request';
|
||||
import { listInspectionType, getInspectionType, addInspectionType, updateInspectionType, delInspectionType } from '@/api/system/inspectionType';
|
||||
import { listLisGroup } from '@/api/system/checkType';
|
||||
import { addInspectionPackage, saveInspectionPackageDetails, batchSaveInspectionPackageDetails, listInspectionPackage, getInspectionPackage, listInspectionPackageDetails } from '@/api/system/inspectionPackage';
|
||||
import { getDiagnosisTreatmentList } from '@/views/catalog/diagnosistreatment/components/diagnosistreatment';
|
||||
import { getLocationTree } from '@/views/charge/outpatientregistration/components/outpatientregistration';
|
||||
|
||||
@@ -1137,7 +1160,28 @@ const handleEditItem = (index) => {
|
||||
return;
|
||||
}
|
||||
if (editingRowId.value === index) {
|
||||
// 保存编辑
|
||||
// 保存编辑 - 验证并计算金额
|
||||
const item = packageItems.value[index];
|
||||
if (!item.name || item.name.trim() === '') {
|
||||
ElMessage.error('请输入项目名称');
|
||||
return;
|
||||
}
|
||||
if (!item.unit || item.unit.trim() === '') {
|
||||
ElMessage.error('请输入单位');
|
||||
return;
|
||||
}
|
||||
if (item.quantity <= 0) {
|
||||
ElMessage.error('数量必须大于0');
|
||||
return;
|
||||
}
|
||||
if (item.unitPrice <= 0) {
|
||||
ElMessage.error('单价必须大于0');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重新计算金额
|
||||
updateItemAmount(item);
|
||||
|
||||
editingRowId.value = null;
|
||||
ElMessage.success('保存成功');
|
||||
} else {
|
||||
@@ -1146,6 +1190,12 @@ const handleEditItem = (index) => {
|
||||
}
|
||||
};
|
||||
|
||||
// 取消编辑项目
|
||||
const cancelEditItem = (index) => {
|
||||
editingRowId.value = null;
|
||||
ElMessage.info('已取消编辑');
|
||||
};
|
||||
|
||||
// 计算单个项目的服务费(基于折扣后的金额)
|
||||
const calculateItemServiceFee = (item) => {
|
||||
if (!generateServiceFee.value) return 0;
|
||||
@@ -1601,14 +1651,274 @@ const exportTable = () => {
|
||||
|
||||
// 套餐相关方法
|
||||
const handleSave = () => {
|
||||
// 验证套餐级别是否选择
|
||||
// 验证必填字段
|
||||
if (!packageLevel.value) {
|
||||
alert('请选择套餐级别');
|
||||
ElMessage.error('请选择套餐级别');
|
||||
return;
|
||||
}
|
||||
|
||||
// 这里可以添加其他表单验证和保存逻辑
|
||||
console.log('保存表单数据');
|
||||
|
||||
if (!packageName.value || packageName.value.trim() === '') {
|
||||
ElMessage.error('请输入套餐名称');
|
||||
return;
|
||||
}
|
||||
|
||||
if (packageItems.value.length === 0) {
|
||||
ElMessage.error('请至少添加一个检验项目');
|
||||
return;
|
||||
}
|
||||
|
||||
// 验证套餐明细数据
|
||||
for (let i = 0; i < packageItems.value.length; i++) {
|
||||
const item = packageItems.value[i];
|
||||
if (!item.name || item.name.trim() === '') {
|
||||
ElMessage.error(`第${i + 1}行:请输入项目名称`);
|
||||
return;
|
||||
}
|
||||
if (!item.unit || item.unit.trim() === '') {
|
||||
ElMessage.error(`第${i + 1}行:请输入单位`);
|
||||
return;
|
||||
}
|
||||
if (item.quantity <= 0) {
|
||||
ElMessage.error(`第${i + 1}行:数量必须大于0`);
|
||||
return;
|
||||
}
|
||||
if (item.unitPrice <= 0) {
|
||||
ElMessage.error(`第${i + 1}行:单价必须大于0`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 准备基本信息数据
|
||||
const basicInfo = {
|
||||
packageCategory: packageCategory.value,
|
||||
packageLevel: packageLevel.value,
|
||||
packageName: packageName.value.trim(),
|
||||
department: department.value,
|
||||
userId: userStore.nickName, // 制单人
|
||||
discount: discount.value || 0,
|
||||
isDisabled: isDisabled.value,
|
||||
showPackageName: showPackageName.value,
|
||||
generateServiceFee: generateServiceFee.value,
|
||||
enablePackagePrice: enablePackagePrice.value,
|
||||
packageAmount: packageAmount.value,
|
||||
serviceFee: serviceFee.value,
|
||||
lisGroup: '', // 从下拉框获取
|
||||
bloodVolume: bloodVolume.value,
|
||||
remarks: remarks.value,
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString()
|
||||
};
|
||||
|
||||
// 如果是科室套餐,添加科室信息
|
||||
if (packageLevel.value === '科室套餐' && department.value) {
|
||||
basicInfo.departmentId = department.value;
|
||||
}
|
||||
|
||||
// 如果是个人套餐,添加用户信息
|
||||
if (packageLevel.value === '个人套餐') {
|
||||
basicInfo.userId = userStore.userId || userStore.nickName;
|
||||
}
|
||||
|
||||
// 准备明细数据
|
||||
const detailData = packageItems.value.map(item => ({
|
||||
packageName: packageName.value.trim(),
|
||||
itemName: item.name,
|
||||
dosage: item.dosage,
|
||||
route: item.route,
|
||||
frequency: item.frequency,
|
||||
days: item.days,
|
||||
quantity: item.quantity,
|
||||
unit: item.unit,
|
||||
unitPrice: item.unitPrice,
|
||||
amount: item.amount,
|
||||
serviceFee: item.serviceFee,
|
||||
totalAmount: item.totalAmount,
|
||||
origin: item.origin,
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString()
|
||||
}));
|
||||
|
||||
console.log('准备保存的基本信息:', basicInfo);
|
||||
console.log('准备保存的明细数据:', detailData);
|
||||
|
||||
// 调用保存API(暂时使用模拟保存,后续对接真实API)
|
||||
savePackageData(basicInfo, detailData);
|
||||
};
|
||||
|
||||
// 保存套餐数据到数据库
|
||||
const savePackageData = async (basicInfo, detailData) => {
|
||||
try {
|
||||
// 显示保存进度
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在保存...',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
});
|
||||
|
||||
// 1. 先保存基本信息
|
||||
const basicResponse = await addInspectionPackage(basicInfo);
|
||||
|
||||
if (basicResponse.code !== 200) {
|
||||
throw new Error(basicResponse.msg || '保存基本信息失败');
|
||||
}
|
||||
|
||||
const packageId = basicResponse.data.packageId || basicResponse.data.id;
|
||||
|
||||
// 2. 批量保存明细数据,关联套餐ID
|
||||
const detailDataWithPackageId = detailData.map(item => ({
|
||||
...item,
|
||||
packageId: packageId
|
||||
}));
|
||||
|
||||
const detailResponse = await batchSaveInspectionPackageDetails({
|
||||
packageId: packageId,
|
||||
details: detailDataWithPackageId
|
||||
});
|
||||
|
||||
if (detailResponse.code !== 200) {
|
||||
throw new Error(detailResponse.msg || '保存明细数据失败');
|
||||
}
|
||||
|
||||
// 关闭加载提示
|
||||
loading.close();
|
||||
|
||||
ElMessage.success('保存成功');
|
||||
|
||||
// 保存成功后重置表单
|
||||
doResetForm();
|
||||
|
||||
} catch (error) {
|
||||
console.error('保存失败:', error);
|
||||
|
||||
// 处理不同类型的错误
|
||||
let errorMessage = '保存失败,请重试';
|
||||
|
||||
if (error.response) {
|
||||
// 服务器返回错误状态码
|
||||
const status = error.response.status;
|
||||
const data = error.response.data;
|
||||
|
||||
if (status === 400) {
|
||||
errorMessage = data.msg || '请求参数错误';
|
||||
} else if (status === 401) {
|
||||
errorMessage = '未授权,请重新登录';
|
||||
} else if (status === 403) {
|
||||
errorMessage = '没有权限执行此操作';
|
||||
} else if (status === 500) {
|
||||
errorMessage = '服务器内部错误';
|
||||
} else {
|
||||
errorMessage = data.msg || `请求失败 (${status})`;
|
||||
}
|
||||
} else if (error.request) {
|
||||
// 网络错误
|
||||
errorMessage = '网络连接失败,请检查网络设置';
|
||||
} else {
|
||||
// 其他错误
|
||||
errorMessage = error.message || '保存失败,请重试';
|
||||
}
|
||||
|
||||
ElMessage.error(errorMessage);
|
||||
}
|
||||
};
|
||||
|
||||
// 重置表单(带确认对话框)
|
||||
const resetForm = () => {
|
||||
ElMessageBox.confirm('确定要重置表单吗?所有未保存的数据将丢失。', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
doResetForm();
|
||||
ElMessage.success('表单已重置');
|
||||
}).catch(() => {
|
||||
// 用户取消重置
|
||||
});
|
||||
};
|
||||
|
||||
// 执行表单重置(不带确认对话框,用于保存成功后自动重置)
|
||||
const doResetForm = () => {
|
||||
// 重置基本信息
|
||||
packageLevel.value = '';
|
||||
packageName.value = '';
|
||||
department.value = '';
|
||||
discount.value = '';
|
||||
isDisabled.value = false;
|
||||
showPackageName.value = true;
|
||||
generateServiceFee.value = true;
|
||||
enablePackagePrice.value = true;
|
||||
packageAmount.value = 0.00;
|
||||
serviceFee.value = 0.00;
|
||||
bloodVolume.value = '';
|
||||
remarks.value = '';
|
||||
|
||||
// 清空明细数据
|
||||
packageItems.value = [];
|
||||
|
||||
// 重新计算金额
|
||||
calculateAmounts();
|
||||
};
|
||||
|
||||
// 加载检验套餐数据(用于编辑现有套餐)
|
||||
const loadInspectionPackage = async (packageId) => {
|
||||
try {
|
||||
const loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在加载套餐数据...',
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
});
|
||||
|
||||
// 获取基本信息
|
||||
const basicResponse = await getInspectionPackage(packageId);
|
||||
if (basicResponse.code !== 200) {
|
||||
throw new Error(basicResponse.msg || '加载基本信息失败');
|
||||
}
|
||||
|
||||
// 获取明细数据
|
||||
const detailResponse = await listInspectionPackageDetails(packageId);
|
||||
if (detailResponse.code !== 200) {
|
||||
throw new Error(detailResponse.msg || '加载明细数据失败');
|
||||
}
|
||||
|
||||
const basicData = basicResponse.data;
|
||||
const detailData = detailResponse.data || [];
|
||||
|
||||
// 填充基本信息
|
||||
packageLevel.value = basicData.packageLevel;
|
||||
packageName.value = basicData.packageName;
|
||||
department.value = basicData.department;
|
||||
discount.value = basicData.discount || '';
|
||||
isDisabled.value = basicData.isDisabled || false;
|
||||
showPackageName.value = basicData.showPackageName !== false;
|
||||
generateServiceFee.value = basicData.generateServiceFee !== false;
|
||||
enablePackagePrice.value = basicData.enablePackagePrice !== false;
|
||||
packageAmount.value = basicData.packageAmount || 0.00;
|
||||
serviceFee.value = basicData.serviceFee || 0.00;
|
||||
bloodVolume.value = basicData.bloodVolume || '';
|
||||
remarks.value = basicData.remarks || '';
|
||||
|
||||
// 填充明细数据
|
||||
packageItems.value = detailData.map(item => ({
|
||||
name: item.itemName || item.name,
|
||||
dosage: item.dosage || '',
|
||||
route: item.route || '',
|
||||
frequency: item.frequency || '',
|
||||
days: item.days || '',
|
||||
quantity: item.quantity || 1,
|
||||
unit: item.unit || '',
|
||||
unitPrice: parseFloat(item.unitPrice || 0),
|
||||
amount: parseFloat(item.amount || 0),
|
||||
serviceFee: parseFloat(item.serviceFee || 0),
|
||||
totalAmount: parseFloat(item.totalAmount || 0),
|
||||
origin: item.origin || ''
|
||||
}));
|
||||
|
||||
loading.close();
|
||||
ElMessage.success('套餐数据加载成功');
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载套餐数据失败:', error);
|
||||
ElMessage.error(error.message || '加载套餐数据失败');
|
||||
}
|
||||
};
|
||||
|
||||
const handlePackageManagement = () => {
|
||||
@@ -1940,6 +2250,12 @@ watch(packageItems, (newVal) => {
|
||||
pointer-events: auto;
|
||||
}
|
||||
|
||||
.cancel-btn {
|
||||
background-color: #FFA500;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 分页 */
|
||||
.pagination {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user