Files
his/openhis-ui-vue3/src/views/maintainSystem/checkprojectSettings/components/PackageManagement.vue

623 lines
19 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

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

<template>
<div class="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>
<el-option label="演示医院" 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.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">
<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 }">
<el-button
type="primary"
size="small"
icon="Edit"
circle
@click="handleEdit(row)"
title="编辑"
/>
<el-button
type="info"
size="small"
icon="View"
circle
@click="handleView(row)"
title="查看"
/>
<el-button
type="danger"
size="small"
icon="Delete"
circle
@click="handleDelete(row)"
title="删除"
/>
</template>
</el-table-column>
</el-table>
</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 { ref, reactive, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { getDicts } from '@/api/system/dict/data'
import { listDept } from '@/api/system/dept'
import { listCheckPackage, getCheckPackage, delCheckPackage } from '@/api/system/checkType'
import request from '@/utils/request'
// 定义emit事件
const emit = defineEmits(['switch-to-settings'])
// 查询参数
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([])
// 初始化数据
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)
}
}
// 加载列表数据
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('数据加载失败,请重试')
}
}
// 删除
function handleDelete(row) {
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)
ElMessage.error('删除失败: ' + (error.message || '未知错误'))
}
}).catch(() => {})
}
</script>
<style scoped>
.package-management {
padding: 24px;
background-color: #f5f7fa;
min-height: 100vh;
}
.filter-section {
background: white;
border-radius: 8px;
padding: 20px;
margin-bottom: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
.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);
}
.pagination-section {
display: flex;
justify-content: flex-end;
padding: 16px 20px;
background: white;
border-radius: 8px;
margin-top: 16px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.03);
}
/* 响应式设计 */
@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;
}
}
</style>