690
openhis-ui-vue3/src/views/appoinmentmanage/clinicRoom/index.vue
Executable file
690
openhis-ui-vue3/src/views/appoinmentmanage/clinicRoom/index.vue
Executable file
@@ -0,0 +1,690 @@
|
||||
<template>
|
||||
<div class="app-container" v-loading="loading">
|
||||
<!-- 筛选区 -->
|
||||
<el-form :model="queryParams" ref="queryRef" :inline="true" class="filter-form">
|
||||
<el-form-item label="卫生机构" prop="orgName">
|
||||
<el-select
|
||||
v-model="queryParams.orgName"
|
||||
placeholder="请选择"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
:popper-append-to-body="false"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in tenantOptions"
|
||||
:key="item.id"
|
||||
:label="item.tenantName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="诊室名称" prop="roomName">
|
||||
<el-input
|
||||
v-model="queryParams.roomName"
|
||||
placeholder="请输入诊室名称"
|
||||
clearable
|
||||
style="width: 200px"
|
||||
maxlength="50"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||
<el-button type="primary" icon="Plus" @click="handleAdd">新增</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 表格区 -->
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="clinicRoomList"
|
||||
border
|
||||
style="width: 100%"
|
||||
class="clinic-room-table"
|
||||
>
|
||||
<el-table-column prop="id" label="ID" width="180" align="center" />
|
||||
<el-table-column label="卫生机构" width="200" align="center" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
<!-- 用==忽略类型匹配,string的"3"和number的3就能匹配上 -->
|
||||
{{ tenantOptions.find(item => item.id == scope.row.orgName)?.tenantName || scope.row.orgName || '未知机构' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="roomName" label="诊室名称" width="160" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="department" label="科室名称" width="160" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="building" label="诊室楼号" width="120" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="floor" label="诊室楼层" width="90" align="center" />
|
||||
<el-table-column prop="roomNo" label="诊室房间号" width="120" align="center" />
|
||||
<el-table-column prop="isDisabled" label="停用" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.isDisabled ? 'danger' : 'success'">
|
||||
{{ scope.row.isDisabled ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remarks" label="备注" min-width="100" align="center" show-overflow-tooltip />
|
||||
<el-table-column prop="void" label="作废" width="90" align="center">
|
||||
<template #default="scope">
|
||||
<el-tag :type="scope.row.void ? 'danger' : 'success'">
|
||||
{{ scope.row.void ? '是' : '否' }}
|
||||
</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作人" width="120" align="center" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ scope.row.updateBy || scope.row.createBy || '系统默认' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="250" align="center" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
icon="EditPen"
|
||||
@click="handleEdit(scope.row)"
|
||||
v-hasPermi="['appoinment:clinicRoom:edit']"
|
||||
>编辑</el-button>
|
||||
<el-button
|
||||
type="info"
|
||||
link
|
||||
icon="View"
|
||||
@click="handleView(scope.row)"
|
||||
v-hasPermi="['appoinment:clinicRoom:query']"
|
||||
>查看</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
icon="Delete"
|
||||
@click="handleDelete(scope.row)"
|
||||
v-hasPermi="['appoinment:clinicRoom:remove']"
|
||||
>删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<!-- 分页区 -->
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNum"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<!-- 新增/编辑/查看弹窗 -->
|
||||
<el-dialog
|
||||
:title="dialogTitle"
|
||||
v-model="dialogVisible"
|
||||
width="500px"
|
||||
append-to-body
|
||||
:close-on-click-modal="false"
|
||||
destroy-on-close
|
||||
>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="100px"
|
||||
:disabled="dialogType === 'view'"
|
||||
>
|
||||
<el-form-item label="科室名称" prop="department">
|
||||
<el-select
|
||||
v-model="form.department"
|
||||
placeholder="请选择科室"
|
||||
style="width: 100%"
|
||||
filterable
|
||||
:disabled="dialogType === 'view'"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in departmentOptions"
|
||||
:key="item.deptId || item.id"
|
||||
:label="item.deptName || item.name"
|
||||
:value="item.deptName || item.name"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="卫生机构" prop="orgName">
|
||||
<el-select
|
||||
v-model="form.orgName"
|
||||
placeholder="请选择"
|
||||
style="width: 100%"
|
||||
:disabled="dialogType === 'view'"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in addEditTenantOptions"
|
||||
:key="item.id"
|
||||
:label="item.tenantName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="诊室名称" prop="roomName">
|
||||
<el-input
|
||||
v-model="form.roomName"
|
||||
placeholder="请输入诊室名称"
|
||||
maxlength="20"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="诊室楼号" prop="building">
|
||||
<el-input
|
||||
v-model="form.building"
|
||||
placeholder="请输入诊室楼号"
|
||||
maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="诊室楼层" prop="floor">
|
||||
<el-input
|
||||
v-model="form.floor"
|
||||
placeholder="请输入诊室楼层"
|
||||
maxlength="10"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="诊室房间号" prop="roomNo">
|
||||
<el-input
|
||||
v-model="form.roomNo"
|
||||
placeholder="请输入诊室房间号"
|
||||
maxlength="50"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="停用状态" prop="isDisabled">
|
||||
<el-radio-group v-model="form.isDisabled">
|
||||
<el-radio :label="false">启用</el-radio>
|
||||
<el-radio :label="true">停用</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remarks">
|
||||
<el-input
|
||||
v-model="form.remarks"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
placeholder="请输入备注"
|
||||
maxlength="500"
|
||||
show-word-limit
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="作废" prop="void">
|
||||
<el-radio-group v-model="form.void">
|
||||
<el-radio :label="false">否</el-radio>
|
||||
<el-radio :label="true">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button v-if="dialogType !== 'view'" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="cancel">取 消</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ClinicRoom">
|
||||
import {getCurrentInstance, onMounted, reactive, ref} from 'vue'
|
||||
import {ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {
|
||||
addClinicRoom,
|
||||
deleteClinicRoom,
|
||||
getClinicRoomDetail,
|
||||
getClinicRoomList,
|
||||
getDepartmentList,
|
||||
getTenantList,
|
||||
updateClinicRoom
|
||||
} from '@/api/appoinmentmanage/clinicRoom'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
// 加载状态
|
||||
const loading = ref(false)
|
||||
// 表格数据
|
||||
const clinicRoomList = ref([])
|
||||
// 总数
|
||||
const total = ref(0)
|
||||
// 查询参数
|
||||
const queryParams = reactive({
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
roomName: '',
|
||||
orgName: ''
|
||||
})
|
||||
// 科室选项
|
||||
const departmentOptions = ref([])
|
||||
// 租户选项
|
||||
const tenantOptions = ref([]) // 用于查询表单
|
||||
const rawTenantOptions = ref([]) // 存储从API获取的所有租户选项
|
||||
const addEditTenantOptions = ref([]) // 用于新增/编辑弹窗的租户选项
|
||||
// 弹窗相关
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref('') // add/edit/view
|
||||
const dialogTitle = ref('')
|
||||
const formRef = ref()
|
||||
// 表单数据
|
||||
const form = reactive({
|
||||
id: null,
|
||||
roomName: '',
|
||||
department: '',
|
||||
building: '',
|
||||
floor: '',
|
||||
roomNo: '',
|
||||
isDisabled: false,
|
||||
remarks: '',
|
||||
void: false,
|
||||
orgName: ''
|
||||
})
|
||||
// 表单验证规则
|
||||
const rules = {
|
||||
roomName: [
|
||||
{ required: true, message: '诊室名称不能为空', trigger: 'blur' },
|
||||
{ max: 20, message: '诊室名称长度不能超过20个字符', trigger: 'blur' }
|
||||
],
|
||||
department: [
|
||||
{ required: true, message: '科室名称不能为空', trigger: 'change' }
|
||||
],
|
||||
orgName: [
|
||||
{ required: true, message: '卫生机构不能为空', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
|
||||
// 获取诊室列表
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getClinicRoomList(queryParams)
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
// 确保数据格式正确
|
||||
const records = response.data?.records || response.data || []
|
||||
clinicRoomList.value = records
|
||||
total.value = response.data?.total || records.length || 0
|
||||
// console.log('获取诊室列表成功,记录数:', records.length)
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取诊室列表失败')
|
||||
clinicRoomList.value = []
|
||||
total.value = 0
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取诊室列表失败:', error)
|
||||
ElMessage.error('获取诊室列表失败')
|
||||
clinicRoomList.value = []
|
||||
total.value = 0
|
||||
})
|
||||
.finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 查询按钮操作
|
||||
function handleQuery() {
|
||||
queryParams.pageNum = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
// 重置按钮操作
|
||||
function resetQuery() {
|
||||
proxy.resetForm('queryRef')
|
||||
queryParams.roomName = null
|
||||
queryParams.orgName = null
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
// 新增按钮操作
|
||||
async function handleAdd() {
|
||||
// 首先清空表单
|
||||
resetForm()
|
||||
|
||||
// 过滤租户选项,只保留 status 为 '0' 的卫生机构用于新增
|
||||
addEditTenantOptions.value = rawTenantOptions.value.filter(item => item.status === '0');
|
||||
|
||||
// 设置对话框状态
|
||||
dialogType.value = 'add'
|
||||
dialogTitle.value = '新增诊室'
|
||||
dialogVisible.value = true
|
||||
|
||||
// 然后获取当前用户信息并设置默认卫生机构
|
||||
try {
|
||||
const userModule = await import('@/store/modules/user')
|
||||
const useUserStore = userModule.default
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 从用户store获取租户ID和租户名称
|
||||
const tenantId = userStore?.tenantId
|
||||
const tenantName = userStore?.tenantName
|
||||
|
||||
// 检查租户ID是否存在,如果不存在则尝试从租户名称匹配
|
||||
let matchedTenantId = null
|
||||
if (tenantId) {
|
||||
// 优先使用租户ID进行匹配
|
||||
matchedTenantId = tenantId
|
||||
} else if (tenantName && tenantOptions.value && tenantOptions.value.length > 0) {
|
||||
// 如果没有租户ID但有租户名称,则尝试通过名称匹配
|
||||
const matchedTenant = tenantOptions.value.find(tenant =>
|
||||
tenant.tenantName === tenantName
|
||||
)
|
||||
if (matchedTenant) {
|
||||
matchedTenantId = matchedTenant.id
|
||||
}
|
||||
}
|
||||
|
||||
// 检查匹配到的租户是否在租户选项列表中
|
||||
if (matchedTenantId && tenantOptions.value && tenantOptions.value.length > 0) {
|
||||
const userTenantExists = tenantOptions.value.some(tenant =>
|
||||
tenant.id === matchedTenantId ||
|
||||
Number(tenant.id) === Number(matchedTenantId)
|
||||
)
|
||||
|
||||
console.log('用户租户是否存在:', userTenantExists)
|
||||
|
||||
if (userTenantExists) {
|
||||
// 确保数据类型一致,将租户ID转换为数字类型
|
||||
form.orgName = Number(matchedTenantId)
|
||||
console.log('已设置默认卫生机构ID:', form.orgName)
|
||||
|
||||
// 查找并显示卫生机构名称
|
||||
const tenantInfo = tenantOptions.value.find(tenant =>
|
||||
tenant.id === matchedTenantId ||
|
||||
Number(tenant.id) === Number(matchedTenantId)
|
||||
)
|
||||
if (tenantInfo) {
|
||||
console.log('已设置默认卫生机构名称:', tenantInfo.tenantName)
|
||||
}
|
||||
} else {
|
||||
console.log('当前用户租户不在租户选项列表中')
|
||||
}
|
||||
} else {
|
||||
console.log('用户租户ID和租户名称都为空,或租户选项列表为空')
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('无法获取当前用户信息:', error.message)
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 确保 form.orgName 是数字类型以便与下拉框选项匹配
|
||||
if (form.orgName && typeof form.orgName === 'string') {
|
||||
const orgNameAsNumber = Number(form.orgName)
|
||||
if (!isNaN(orgNameAsNumber)) {
|
||||
form.orgName = orgNameAsNumber
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 编辑按钮操作
|
||||
function handleEdit(row) {
|
||||
resetForm()
|
||||
const id = row.id
|
||||
// 编辑时不需要过滤租户状态,显示所有租户
|
||||
addEditTenantOptions.value = rawTenantOptions.value;
|
||||
getClinicRoomDetail(id)
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
const clinicRoomData = response.data || row
|
||||
|
||||
// 处理 orgName 数据类型,确保与下拉框选项值类型一致
|
||||
if (clinicRoomData.orgName) {
|
||||
const orgNameAsNumber = Number(clinicRoomData.orgName) // 转换为数字类型
|
||||
// 确保转换成功且不是一个 NaN 值
|
||||
if (!isNaN(orgNameAsNumber)) {
|
||||
clinicRoomData.orgName = orgNameAsNumber
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(form, clinicRoomData)
|
||||
dialogType.value = 'edit'
|
||||
dialogTitle.value = '编辑诊室'
|
||||
dialogVisible.value = true
|
||||
|
||||
// 为编辑模式设置基础验证规则
|
||||
rules.roomName = [
|
||||
{ required: true, message: '诊室名称不能为空', trigger: 'blur' },
|
||||
{ max: 20, message: '诊室名称长度不能超过20个字符', trigger: 'blur' }
|
||||
]
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取诊室详情失败')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取诊室详情失败:', error)
|
||||
ElMessage.error('获取诊室详情失败')
|
||||
})
|
||||
}
|
||||
|
||||
// 查看按钮操作
|
||||
function handleView(row) {
|
||||
resetForm()
|
||||
const id = row.id
|
||||
// 查看时不需要过滤租户状态,显示所有租户
|
||||
addEditTenantOptions.value = rawTenantOptions.value;
|
||||
getClinicRoomDetail(id)
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
const clinicRoomData = response.data || row
|
||||
|
||||
// 处理 orgName 数据类型,确保与下拉框选项值类型一致
|
||||
if (clinicRoomData.orgName) {
|
||||
const orgNameAsNumber = Number(clinicRoomData.orgName) // 转换为数字类型
|
||||
// 确保转换成功且不是一个 NaN 值
|
||||
if (!isNaN(orgNameAsNumber)) {
|
||||
clinicRoomData.orgName = orgNameAsNumber
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(form, clinicRoomData)
|
||||
dialogType.value = 'view'
|
||||
dialogTitle.value = '查看诊室详情'
|
||||
dialogVisible.value = true
|
||||
|
||||
// 查看模式不需要验证规则,因为表单是禁用的
|
||||
} else {
|
||||
ElMessage.error(response.msg || '获取诊室详情失败')
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取诊室详情失败:', error)
|
||||
ElMessage.error('获取诊室详情失败')
|
||||
})
|
||||
}
|
||||
|
||||
// 删除按钮操作
|
||||
function handleDelete(row) {
|
||||
const id = row.id || row.ids
|
||||
ElMessageBox.confirm('是否确认删除诊室ID为"' + id + '"的数据项?')
|
||||
.then(() => {
|
||||
return deleteClinicRoom(id)
|
||||
})
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('删除成功')
|
||||
getList()
|
||||
} else {
|
||||
ElMessage.error(response.msg || '删除失败')
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
function submitForm() {
|
||||
formRef.value.validate(valid => {
|
||||
if (valid) {
|
||||
if (dialogType.value === 'edit') {
|
||||
updateClinicRoom(form)
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('修改成功')
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
} else {
|
||||
// 特别处理唯一性验证错误
|
||||
if (response.msg && response.msg.includes('当前卫生机构下已存在该诊室名称')) {
|
||||
ElMessage.error('当前卫生机构下已存在该诊室名称,请更换诊室名称')
|
||||
} else {
|
||||
ElMessage.error(response.msg || '修改失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('修改失败:', error)
|
||||
ElMessage.error('修改失败')
|
||||
})
|
||||
} else {
|
||||
addClinicRoom(form)
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
ElMessage.success('新增成功')
|
||||
dialogVisible.value = false
|
||||
getList()
|
||||
} else {
|
||||
// 特别处理唯一性验证错误
|
||||
if (response.msg && response.msg.includes('当前卫生机构下已存在该诊室名称')) {
|
||||
ElMessage.error('当前卫生机构下已存在该诊室名称,请更换诊室名称')
|
||||
} else {
|
||||
ElMessage.error(response.msg || '新增失败')
|
||||
}
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('新增失败:', error)
|
||||
ElMessage.error('新增失败')
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 取消按钮
|
||||
function cancel() {
|
||||
dialogVisible.value = false
|
||||
resetForm()
|
||||
}
|
||||
|
||||
// 重置表单
|
||||
function resetForm() {
|
||||
form.id = null
|
||||
form.roomName = ''
|
||||
form.department = ''
|
||||
form.building = ''
|
||||
form.floor = ''
|
||||
form.roomNo = ''
|
||||
form.isDisabled = false
|
||||
form.remarks = ''
|
||||
form.void = false
|
||||
form.orgName = ''
|
||||
proxy.resetForm('formRef')
|
||||
}
|
||||
|
||||
// 获取科室列表
|
||||
function getDepartmentOptions() {
|
||||
getDepartmentList()
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
// 处理不同的响应格式
|
||||
if (Array.isArray(response.data)) {
|
||||
departmentOptions.value = response.data
|
||||
} else if (response.data?.data && Array.isArray(response.data.data)) {
|
||||
departmentOptions.value = response.data.data
|
||||
} else if (response.data?.records && Array.isArray(response.data.records)) {
|
||||
departmentOptions.value = response.data.records
|
||||
} else {
|
||||
departmentOptions.value = []
|
||||
}
|
||||
} else {
|
||||
console.warn('获取科室列表失败:', response.msg)
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取科室列表失败:', error)
|
||||
})
|
||||
}
|
||||
|
||||
// 获取租户列表
|
||||
function getTenantOptions() {
|
||||
return getTenantList()
|
||||
.then(response => {
|
||||
if (response.code === 200) {
|
||||
// 处理不同的响应格式
|
||||
let tenantList = [];
|
||||
if (response.data?.records && Array.isArray(response.data.records)) {
|
||||
tenantList = response.data.records;
|
||||
} else if (Array.isArray(response.data)) {
|
||||
tenantList = response.data;
|
||||
} else {
|
||||
tenantList = [];
|
||||
}
|
||||
|
||||
rawTenantOptions.value = tenantList; // 存储所有租户
|
||||
tenantOptions.value = tenantList; // 查询表单使用所有租户
|
||||
} else {
|
||||
console.warn('获取租户列表失败:', response.msg)
|
||||
rawTenantOptions.value = []
|
||||
tenantOptions.value = []
|
||||
}
|
||||
return response; // 返回响应以支持链式调用
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('获取租户列表失败:', error)
|
||||
rawTenantOptions.value = []
|
||||
tenantOptions.value = []
|
||||
return Promise.reject(error); // 返回拒绝的Promise
|
||||
})
|
||||
}
|
||||
|
||||
// 页面加载时初始化
|
||||
onMounted(() => {
|
||||
getDepartmentOptions()
|
||||
getTenantOptions()
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.app-container {
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.filter-form {
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.clinic-room-table {
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
:deep(.el-table) {
|
||||
.el-table__header-wrapper th {
|
||||
background-color: #fafafa;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.el-button--link) {
|
||||
padding: 0;
|
||||
margin: 0 5px;
|
||||
}
|
||||
|
||||
// 响应式布局
|
||||
@media (max-width: 768px) {
|
||||
.filter-form {
|
||||
:deep(.el-form-item) {
|
||||
display: block;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
:deep(.el-form-item__content) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.clinic-room-table {
|
||||
overflow-x: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
124
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/api.js
Executable file
124
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/api.js
Executable file
@@ -0,0 +1,124 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 添加医生排班
|
||||
* @param {Object} data - 排班数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function addDoctorSchedule(data) {
|
||||
return request({
|
||||
url: '/doctor-schedule/add',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加医生排班(带具体日期)
|
||||
* @param {Object} data - 排班数据
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function addDoctorScheduleWithDate(data) {
|
||||
return request({
|
||||
url: '/doctor-schedule/add-with-date',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新医生排班
|
||||
* @param {Object} data - 排班数据 (必须包含ID)
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function updateDoctorSchedule(data) {
|
||||
return request({
|
||||
url: '/doctor-schedule/update',
|
||||
method: 'put',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除医生排班
|
||||
* @param {String|Number} id - 排班记录ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function deleteDoctorSchedule(id) {
|
||||
return request({
|
||||
url: '/doctor-schedule/delete/' + id,
|
||||
method: 'delete'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量保存医生排班
|
||||
* @param {Array} data - 排班数据数组
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function batchSaveDoctorSchedule(data) {
|
||||
return request({
|
||||
url: '/doctor-schedule/batch-save',
|
||||
method: 'post',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取挂号科室列表
|
||||
* @param {Object} params - 查询参数
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getRegisterOrganizations(params) {
|
||||
return request({
|
||||
url: '/base-data-manage/organization/register-organizations',
|
||||
method: 'get',
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取医生排班列表
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getDoctorScheduleList() {
|
||||
return request({
|
||||
url: '/doctor-schedule/list',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据科室ID获取医生排班列表
|
||||
* @param {Number} deptId - 科室ID
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getDoctorScheduleListByDeptId(deptId) {
|
||||
return request({
|
||||
url: `/doctor-schedule/list-by-dept/${deptId}`,
|
||||
method: 'get',
|
||||
params: {
|
||||
_t: new Date().getTime() // 添加时间戳,防止GET请求缓存
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据科室ID和日期范围获取医生排班列表
|
||||
* @param {Number} deptId - 科室ID
|
||||
* @param {String} startDate - 开始日期
|
||||
* @param {String} endDate - 结束日期
|
||||
* @returns {Promise}
|
||||
*/
|
||||
export function getDoctorScheduleListByDeptIdAndDateRange(deptId, startDate, endDate) {
|
||||
return request({
|
||||
url: `/doctor-schedule/list-by-dept-and-date`,
|
||||
method: 'get',
|
||||
params: {
|
||||
deptId,
|
||||
startDate,
|
||||
endDate,
|
||||
_t: new Date().getTime() // 添加时间戳,防止GET请求缓存
|
||||
}
|
||||
})
|
||||
}
|
||||
894
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/doctorschedule/index.vue
Executable file
894
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/doctorschedule/index.vue
Executable file
@@ -0,0 +1,894 @@
|
||||
<template>
|
||||
<div class="doctorschedule-container">
|
||||
<div class="doctorschedule-header">
|
||||
<h2 class="doctorschedule-title">医生排班</h2>
|
||||
</div>
|
||||
|
||||
<div class="doctorschedule-content">
|
||||
<!-- 筛选条件 -->
|
||||
<div class="filter-condition">
|
||||
<span class="filter-label">卫生机构</span>
|
||||
<el-select v-model="filterParams.orgName" class="filter-select">
|
||||
<el-option label="演示医院" value="演示医院"></el-option>
|
||||
</el-select>
|
||||
|
||||
<span class="filter-label">科室名称</span>
|
||||
<el-select v-model="filterParams.deptName" class="filter-select">
|
||||
<el-option label="测试内科" value="测试内科"></el-option>
|
||||
</el-select>
|
||||
|
||||
<span class="filter-label">开始日期</span>
|
||||
<el-date-picker
|
||||
v-model="filterParams.startDate"
|
||||
type="date"
|
||||
placeholder="选择日期"
|
||||
class="filter-date-picker"
|
||||
/>
|
||||
|
||||
<span class="filter-label">排班类型</span>
|
||||
<div class="radio-group">
|
||||
<el-radio v-model="filterParams.appointmentType" label="普通" @change="handleAppointmentTypeChange">普通</el-radio>
|
||||
<el-radio v-model="filterParams.appointmentType" label="专家" @change="handleAppointmentTypeChange">专家</el-radio>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 排班表格 -->
|
||||
<div class="schedule-table-container">
|
||||
<!-- 按日期分组显示排班 -->
|
||||
<div v-for="(dateGroup, index) in groupedSchedule" :key="index" class="daily-schedule">
|
||||
<div class="daily-header">
|
||||
<span class="date-text">{{ dateGroup.date }}</span>
|
||||
<span class="weekday-text">{{ dateGroup.weekday }}</span>
|
||||
</div>
|
||||
<el-table :data="dateGroup.items" border style="width: 100%" class="schedule-table">
|
||||
<el-table-column prop="timeSlot" label="时段" width="100"></el-table-column>
|
||||
<el-table-column prop="doctorName" :label="filterParams.appointmentType === '专家' ? '专家' : '医生'" width="150">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.doctorName"
|
||||
placeholder="请选"
|
||||
class="inline-select"
|
||||
:disabled="!isEditMode"
|
||||
>
|
||||
<el-option
|
||||
v-for="doctor in getDoctorOptions(filterParams.appointmentType)"
|
||||
:key="doctor.value"
|
||||
:label="doctor.label"
|
||||
:value="doctor.value"
|
||||
></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="room" label="诊室" width="100">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.room"
|
||||
placeholder="请选"
|
||||
class="inline-select"
|
||||
:disabled="!isEditMode"
|
||||
>
|
||||
<el-option label="诊室1" value="诊室1"></el-option>
|
||||
<el-option label="诊室2" value="诊室2"></el-option>
|
||||
<el-option label="诊室3" value="诊室3"></el-option>
|
||||
<el-option label="诊室4" value="诊室4"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="startTime" label="开始时间" width="120">
|
||||
<template #default="scope">
|
||||
<el-time-picker
|
||||
v-model="scope.row.startTime"
|
||||
type="time"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm"
|
||||
placeholder="选择开始时间"
|
||||
:disabled="!isEditMode"
|
||||
class="time-picker"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="endTime" label="结束时间" width="120">
|
||||
<template #default="scope">
|
||||
<el-time-picker
|
||||
v-model="scope.row.endTime"
|
||||
type="time"
|
||||
format="HH:mm"
|
||||
value-format="HH:mm"
|
||||
placeholder="选择结束时间"
|
||||
:disabled="!isEditMode"
|
||||
class="time-picker"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="maxNumber" label="限号数量" width="80">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.maxNumber"
|
||||
type="number"
|
||||
:disabled="!isEditMode"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="record" label="号源记录" width="80">
|
||||
<template #default="scope">
|
||||
<el-icon
|
||||
@click="handleViewRecord(scope.row)"
|
||||
class="record-icon"
|
||||
title="查看号源记录"
|
||||
>
|
||||
<View />
|
||||
</el-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="appointmentItem" label="挂号项目" width="120">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.appointmentItem"
|
||||
placeholder="请选"
|
||||
class="inline-select"
|
||||
:disabled="!isEditMode"
|
||||
@change="handleAppointmentItemChange(scope.row)"
|
||||
>
|
||||
<el-option label="挂号费 50" value="挂号费 50"></el-option>
|
||||
<el-option label="一般诊疗费 10" value="一般诊疗费 10"></el-option>
|
||||
<el-option label="主任医师 27" value="主任医师 27"></el-option>
|
||||
<el-option label="副主任 15" value="副主任 15"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="registrationFee" label="挂号费(元)" width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.registrationFee || '0' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="clinicItem" label="诊查项目" width="150">
|
||||
<template #default="scope">
|
||||
<el-select
|
||||
v-model="scope.row.clinicItem"
|
||||
placeholder="请选择诊查项目"
|
||||
class="inline-select"
|
||||
:disabled="!isEditMode"
|
||||
@change="handleClinicItemChange(scope.row)"
|
||||
>
|
||||
<el-option label="常规诊查" value="常规诊查"></el-option>
|
||||
<el-option label="专科诊查" value="专科诊查"></el-option>
|
||||
<el-option label="特殊诊查" value="特殊诊查"></el-option>
|
||||
<el-option label="专家诊查" value="专家诊查"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="treatmentFee" label="诊疗费(元)" width="100">
|
||||
<template #default="scope">
|
||||
<span>{{ scope.row.treatmentFee || '0' }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="online" label="线上" width="60">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.online" :disabled="!isEditMode"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="stopClinic" label="停诊" width="60">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.stopClinic" :disabled="!isEditMode"></el-checkbox>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="stopReason" label="停诊原因" width="150">
|
||||
<template #default="scope">
|
||||
<el-input
|
||||
v-model="scope.row.stopReason"
|
||||
:placeholder="scope.row.stopClinic ? '请输入停诊原因' : ''"
|
||||
class="inline-input"
|
||||
:disabled="!isEditMode || !scope.row.stopClinic"
|
||||
></el-input>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
size="small"
|
||||
@click="handleAddSchedule(scope.row)"
|
||||
:disabled="!isEditMode"
|
||||
>
|
||||
添加
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
size="small"
|
||||
@click="handleDeleteSchedule(scope.row)"
|
||||
:disabled="!isEditMode"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 底部操作按钮 -->
|
||||
<div class="bottom-buttons">
|
||||
<el-button type="primary" @click="handleSave" :disabled="!isEditMode">确定</el-button>
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 号源记录对话框 -->
|
||||
<el-dialog
|
||||
v-model="recordDialogVisible"
|
||||
title="号源记录"
|
||||
width="30%"
|
||||
:close-on-click-modal="true"
|
||||
>
|
||||
<div class="appointment-records">
|
||||
<div class="record-item" v-for="record in appointmentRecords" :key="record.index">
|
||||
<span class="record-time">{{ record.time }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="recordDialogVisible = false">确定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="DoctorSchedule">
|
||||
import {computed, onMounted, ref, watch} from 'vue'
|
||||
import {useRoute, useRouter} from 'vue-router'
|
||||
import {ElDialog, ElMessage, ElMessageBox} from 'element-plus'
|
||||
import {View} from '@element-plus/icons-vue'
|
||||
import {addDoctorSchedule, addDoctorScheduleWithDate, deleteDoctorSchedule} from '../api'
|
||||
|
||||
// 路由和导航
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
// 筛选参数
|
||||
const filterParams = ref({
|
||||
orgName: '演示医院',
|
||||
deptName: '测试内科',
|
||||
startDate: new Date('2025-12-01'),
|
||||
appointmentType: '普通'
|
||||
})
|
||||
|
||||
// 医生列表数据
|
||||
const doctorOptions = ref({
|
||||
'普通': [
|
||||
{ label: '张医生', value: '张医生' },
|
||||
{ label: '李医生', value: '李医生' },
|
||||
{ label: '王医生', value: '王医生' },
|
||||
{ label: '赵医生', value: '赵医生' }
|
||||
],
|
||||
'专家': [
|
||||
{ label: '请选择专家', value: '' },
|
||||
{ label: '王教授', value: '王教授' },
|
||||
{ label: '李主任', value: '李主任' },
|
||||
{ label: '赵教授', value: '赵教授' },
|
||||
{ label: '张主任', value: '张主任' }
|
||||
]
|
||||
})
|
||||
|
||||
// 根据排班类型获取医生选项
|
||||
const getDoctorOptions = (appointmentType) => {
|
||||
return doctorOptions.value[appointmentType] || doctorOptions.value['普通']
|
||||
}
|
||||
|
||||
// 编辑模式控制
|
||||
const isEditMode = computed(() => {
|
||||
return route.query.mode === 'edit'
|
||||
})
|
||||
|
||||
// 排班列表数据
|
||||
const scheduleList = ref([])
|
||||
|
||||
// 当前科室信息
|
||||
const currentDept = ref(null)
|
||||
|
||||
// 按日期分组的排班数据
|
||||
const groupedSchedule = computed(() => {
|
||||
// 按日期分组
|
||||
const groups = {}
|
||||
|
||||
scheduleList.value.forEach(item => {
|
||||
if (!groups[item.date]) {
|
||||
groups[item.date] = {
|
||||
date: item.date,
|
||||
weekday: item.weekday,
|
||||
items: []
|
||||
}
|
||||
}
|
||||
groups[item.date].items.push(item)
|
||||
})
|
||||
|
||||
// 转换为数组
|
||||
return Object.values(groups)
|
||||
})
|
||||
|
||||
// 生成一周排班数据
|
||||
const generateWeekSchedule = (startDate) => {
|
||||
const days = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
|
||||
const timeSlots = [
|
||||
{ label: '上午', startTime: '08:00', endTime: '12:00' },
|
||||
{ label: '下午', startTime: '14:30', endTime: '18:00' }
|
||||
]
|
||||
|
||||
const schedule = []
|
||||
|
||||
// 生成一周7天的数据
|
||||
for (let i = 0; i < 7; i++) {
|
||||
const currentDate = new Date(startDate)
|
||||
currentDate.setDate(startDate.getDate() + i)
|
||||
|
||||
const dateStr = currentDate.toISOString().split('T')[0]
|
||||
const weekday = days[currentDate.getDay()]
|
||||
|
||||
// 每个时间段生成一条记录
|
||||
timeSlots.forEach(slot => {
|
||||
schedule.push({
|
||||
id: `${dateStr}-${slot.label}`,
|
||||
date: dateStr,
|
||||
weekday: weekday,
|
||||
timeSlot: slot.label,
|
||||
startTime: slot.startTime,
|
||||
endTime: slot.endTime,
|
||||
doctorName: '',
|
||||
room: '',
|
||||
maxNumber: '',
|
||||
appointmentItem: '',
|
||||
registrationFee: 0,
|
||||
clinicItem: '',
|
||||
treatmentFee: 0,
|
||||
online: true,
|
||||
stopClinic: false,
|
||||
stopReason: ''
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return schedule
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
// 从路由参数获取科室ID
|
||||
const deptId = route.query.deptId
|
||||
console.log('科室ID:', deptId)
|
||||
|
||||
// 设置当前科室信息
|
||||
if (deptId) {
|
||||
currentDept.value = { id: deptId }
|
||||
}
|
||||
|
||||
// 生成排班数据
|
||||
scheduleList.value = generateWeekSchedule(filterParams.value.startDate)
|
||||
}
|
||||
|
||||
// 排班类型变化处理
|
||||
const handleAppointmentTypeChange = () => {
|
||||
// 当排班类型改变时,清空所有排班记录的医生选择
|
||||
scheduleList.value.forEach(item => {
|
||||
// 如果当前选择的医生不在新类型对应的医生列表中,则清空
|
||||
const currentDoctors = getDoctorOptions(filterParams.value.appointmentType)
|
||||
const doctorExists = currentDoctors.some(doctor => doctor.value === item.doctorName)
|
||||
if (!doctorExists) {
|
||||
item.doctorName = ''
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 挂号项目变化处理
|
||||
const handleAppointmentItemChange = (row) => {
|
||||
if (row.appointmentItem) {
|
||||
// 从挂号项目中提取费用数字,例如 "挂号费 50" -> 50
|
||||
const feeMatch = row.appointmentItem.match(/(\d+)/)
|
||||
if (feeMatch) {
|
||||
row.registrationFee = parseInt(feeMatch[1])
|
||||
} else {
|
||||
row.registrationFee = 0
|
||||
}
|
||||
} else {
|
||||
row.registrationFee = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 诊查项目变化处理
|
||||
const handleClinicItemChange = (row) => {
|
||||
// 诊查项目费用映射表
|
||||
const clinicFeeMap = {
|
||||
'常规诊查': 10,
|
||||
'专科诊查': 20,
|
||||
'特殊诊查': 30,
|
||||
'专家诊查': 50
|
||||
}
|
||||
|
||||
if (row.clinicItem && clinicFeeMap[row.clinicItem]) {
|
||||
row.treatmentFee = clinicFeeMap[row.clinicItem]
|
||||
} else {
|
||||
row.treatmentFee = 0
|
||||
}
|
||||
}
|
||||
|
||||
// 添加排班
|
||||
const handleAddSchedule = (row) => {
|
||||
// 创建新的排班记录,基于当前行的日期和时段
|
||||
const newSchedule = {
|
||||
id: `new-${Date.now()}-${Math.random()}`,
|
||||
date: row.date,
|
||||
weekday: row.weekday,
|
||||
timeSlot: row.timeSlot,
|
||||
startTime: row.startTime || '08:00',
|
||||
endTime: row.endTime || '12:00',
|
||||
doctorName: '',
|
||||
room: '',
|
||||
maxNumber: '',
|
||||
appointmentItem: '',
|
||||
registrationFee: '',
|
||||
clinicItem: '',
|
||||
treatmentFee: '',
|
||||
online: true,
|
||||
stopClinic: false,
|
||||
stopReason: '',
|
||||
isNew: true // 标记为新添加的记录
|
||||
}
|
||||
|
||||
// 找到当前行在列表中的位置,在其后插入新记录
|
||||
const currentIndex = scheduleList.value.findIndex(item => item.id === row.id)
|
||||
if (currentIndex !== -1) {
|
||||
scheduleList.value.splice(currentIndex + 1, 0, newSchedule)
|
||||
ElMessage.success('添加排班成功')
|
||||
} else {
|
||||
// 如果找不到,添加到对应日期组的末尾
|
||||
scheduleList.value.push(newSchedule)
|
||||
ElMessage.success('添加排班成功')
|
||||
}
|
||||
}
|
||||
|
||||
// 删除排班
|
||||
const handleDeleteSchedule = (row) => {
|
||||
ElMessageBox.confirm('确定要删除这条排班记录吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
// 如果是已保存的记录(有后端ID),调用删除接口
|
||||
if (row.backendId && !row.isNew) {
|
||||
deleteDoctorSchedule(row.backendId).then(res => {
|
||||
if (res.code === 200) {
|
||||
// 从列表中移除
|
||||
const index = scheduleList.value.findIndex(item => item.id === row.id)
|
||||
if (index !== -1) {
|
||||
scheduleList.value.splice(index, 1)
|
||||
}
|
||||
ElMessage.success('删除成功')
|
||||
} else {
|
||||
ElMessage.error(res.msg || '删除失败')
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('删除排班失败:', error)
|
||||
ElMessage.error('删除失败,请稍后重试')
|
||||
})
|
||||
} else {
|
||||
// 如果是新添加的记录(未保存),直接从列表中移除
|
||||
const index = scheduleList.value.findIndex(item => item.id === row.id)
|
||||
if (index !== -1) {
|
||||
scheduleList.value.splice(index, 1)
|
||||
ElMessage.success('删除成功')
|
||||
}
|
||||
}
|
||||
}).catch(() => {
|
||||
// 用户取消删除
|
||||
})
|
||||
}
|
||||
|
||||
// 号源记录对话框相关
|
||||
const recordDialogVisible = ref(false)
|
||||
const currentRow = ref(null)
|
||||
const appointmentRecords = ref([])
|
||||
|
||||
// 计算号源记录
|
||||
const calculateAppointmentRecords = (row) => {
|
||||
const { startTime, endTime, maxNumber } = row
|
||||
|
||||
// 将时间转换为分钟数
|
||||
const [startHour, startMinute] = startTime.split(':').map(Number)
|
||||
const [endHour, endMinute] = endTime.split(':').map(Number)
|
||||
|
||||
const startTotalMinutes = startHour * 60 + startMinute
|
||||
const endTotalMinutes = endHour * 60 + endMinute
|
||||
|
||||
// 计算总时长和间隔
|
||||
const totalDuration = endTotalMinutes - startTotalMinutes
|
||||
const interval = Math.floor(totalDuration / maxNumber)
|
||||
|
||||
// 生成号源记录
|
||||
const records = []
|
||||
for (let i = 0; i < maxNumber; i++) {
|
||||
const minutes = startTotalMinutes + i * interval
|
||||
const hour = Math.floor(minutes / 60).toString().padStart(2, '0')
|
||||
const minute = (minutes % 60).toString().padStart(2, '0')
|
||||
records.push({
|
||||
index: i + 1,
|
||||
time: `${hour}:${minute}`
|
||||
})
|
||||
}
|
||||
|
||||
return records
|
||||
}
|
||||
|
||||
// 查看号源记录
|
||||
const handleViewRecord = (row) => {
|
||||
// 验证开始时间、结束时间和限号数量
|
||||
if (!row.startTime || !row.endTime || !row.maxNumber) {
|
||||
ElMessageBox.confirm('请先设置开始时间、结束时间和限号数量', '', {
|
||||
confirmButtonText: '确定',
|
||||
type: 'warning',
|
||||
showCancelButton: false
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 计算号源记录
|
||||
currentRow.value = row
|
||||
appointmentRecords.value = calculateAppointmentRecords(row)
|
||||
recordDialogVisible.value = true
|
||||
}
|
||||
|
||||
// 保存排班
|
||||
const handleSave = async () => {
|
||||
try {
|
||||
// 验证必填字段
|
||||
const invalidSchedules = scheduleList.value.filter(item => {
|
||||
// 检查是否有必填字段为空
|
||||
return !item.doctorName || !item.room || !item.startTime || !item.endTime || !item.maxNumber
|
||||
})
|
||||
|
||||
if (invalidSchedules.length > 0) {
|
||||
ElMessage.warning('请完善所有排班记录的必填信息(医生、诊室、开始时间、结束时间、限号数量)')
|
||||
return
|
||||
}
|
||||
|
||||
// 转换数据格式以匹配后端实体,并建立映射关系
|
||||
const schedulesToSave = scheduleList.value.map((item, index) => {
|
||||
// 解析挂号项目和诊查项目,提取费用
|
||||
let registrationFee = 0
|
||||
let diagnosisFee = 0
|
||||
|
||||
if (item.appointmentItem) {
|
||||
const feeMatch = item.appointmentItem.match(/(\d+)/)
|
||||
if (feeMatch) {
|
||||
registrationFee = parseInt(feeMatch[1])
|
||||
}
|
||||
}
|
||||
|
||||
if (item.clinicItem) {
|
||||
// 这里可以根据诊查项目设置诊疗费,暂时设为0或从其他字段获取
|
||||
diagnosisFee = item.treatmentFee ? parseInt(item.treatmentFee) : 0
|
||||
}
|
||||
|
||||
return {
|
||||
localIndex: index, // 保存本地索引,用于更新
|
||||
localId: item.id, // 保存本地ID
|
||||
scheduleData: {
|
||||
id: item.backendId || null, // 如果是新记录,id为null
|
||||
weekday: item.weekday,
|
||||
timePeriod: item.timeSlot,
|
||||
doctor: item.doctorName,
|
||||
clinic: item.room,
|
||||
startTime: item.startTime,
|
||||
endTime: item.endTime,
|
||||
limitNumber: parseInt(item.maxNumber) || 0,
|
||||
registerItem: item.appointmentItem || '',
|
||||
registerFee: registrationFee,
|
||||
diagnosisItem: item.clinicItem || '',
|
||||
diagnosisFee: diagnosisFee,
|
||||
isOnline: item.online || false,
|
||||
isStopped: item.stopClinic || false,
|
||||
stopReason: item.stopClinic ? (item.stopReason || '') : '',
|
||||
deptId: currentDept.value?.id || route.query.deptId || null,
|
||||
scheduledDate: item.date // 添加具体日期字段
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 只保存新添加的记录(isNew: true)或没有backendId的记录
|
||||
const newSchedulesToSave = schedulesToSave.filter(({ localIndex }) => {
|
||||
const item = scheduleList.value[localIndex]
|
||||
return item.isNew || !item.backendId // 只保存新记录或没有backendId的记录
|
||||
})
|
||||
|
||||
if (newSchedulesToSave.length === 0) {
|
||||
ElMessage.info('没有需要保存的新排班记录')
|
||||
// 返回上一页
|
||||
router.back()
|
||||
return
|
||||
}
|
||||
|
||||
// 批量保存排班
|
||||
let successCount = 0
|
||||
let failCount = 0
|
||||
const errors = []
|
||||
|
||||
for (const { localIndex, localId, scheduleData } of newSchedulesToSave) {
|
||||
try {
|
||||
// 确保新记录的id为null
|
||||
scheduleData.id = null
|
||||
|
||||
// 使用带日期的API,日期信息已包含在scheduleData中
|
||||
const res = await addDoctorScheduleWithDate(scheduleData)
|
||||
if (res.code === 200 && res.data) {
|
||||
successCount++
|
||||
// 更新本地记录的backendId
|
||||
const localItem = scheduleList.value[localIndex]
|
||||
if (localItem) {
|
||||
// 从响应中获取保存后的ID
|
||||
if (res.data && typeof res.data === 'object' && res.data.id) {
|
||||
localItem.backendId = res.data.id
|
||||
localItem.isNew = false
|
||||
} else if (res.data && typeof res.data === 'number') {
|
||||
localItem.backendId = res.data
|
||||
localItem.isNew = false
|
||||
} else if (res.data === true) {
|
||||
// 如果后端返回的是boolean true,标记为已保存
|
||||
localItem.isNew = false
|
||||
localItem.saved = true
|
||||
} else {
|
||||
// 其他情况,标记为已保存
|
||||
localItem.isNew = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
failCount++
|
||||
const errorMsg = res.msg || '保存失败'
|
||||
errors.push(errorMsg)
|
||||
console.error('保存排班失败:', errorMsg, res)
|
||||
}
|
||||
} catch (error) {
|
||||
failCount++
|
||||
const errorMsg = error.message || '保存异常'
|
||||
errors.push(errorMsg)
|
||||
console.error('保存排班异常:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 如果有错误,显示详细错误信息
|
||||
if (errors.length > 0) {
|
||||
console.error('保存错误详情:', errors)
|
||||
}
|
||||
|
||||
if (failCount === 0) {
|
||||
ElMessage.success(`排班保存成功,共保存 ${successCount} 条记录`)
|
||||
// 返回上一页
|
||||
router.back()
|
||||
} else {
|
||||
ElMessage.warning(`部分排班保存失败,成功 ${successCount} 条,失败 ${failCount} 条`)
|
||||
if (errors.length > 0) {
|
||||
console.error('错误详情:', errors.join('; '))
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('保存排班失败:', error)
|
||||
ElMessage.error('保存失败,请稍后重试')
|
||||
}
|
||||
}
|
||||
|
||||
// 取消操作
|
||||
const handleCancel = () => {
|
||||
// 返回上一页
|
||||
router.back()
|
||||
}
|
||||
|
||||
// 页面加载时初始化数据
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
|
||||
// 监听路由参数变化,重新初始化数据
|
||||
watch(
|
||||
() => [route.query.deptId, route.query.mode],
|
||||
() => {
|
||||
initData()
|
||||
},
|
||||
{ deep: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.doctorschedule-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 20px;
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.doctorschedule-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.doctorschedule-title {
|
||||
font-size: 20px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.doctorschedule-content {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.filter-condition {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.filter-label {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.filter-select {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.filter-date-picker {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.radio-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.schedule-table-container {
|
||||
margin-bottom: 20px;
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.daily-schedule {
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daily-header {
|
||||
background-color: #f5f7fa;
|
||||
padding: 10px 15px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.date-text {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.weekday-text {
|
||||
color: #606266;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.schedule-table {
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
|
||||
:deep(.el-table__header-wrapper) {
|
||||
width: 100% !important;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
:deep(.el-table__body-wrapper) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.el-table__header-wrapper th.el-table__cell),
|
||||
:deep(.el-table__body-wrapper td.el-table__cell) {
|
||||
text-align: center;
|
||||
padding: 8px 0;
|
||||
}
|
||||
|
||||
/* 确保表格容器填满 */
|
||||
:deep(.el-table__inner-wrapper) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 确保表格本身填满 */
|
||||
:deep(.el-table__body) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 确保表格列正确分配宽度 */
|
||||
:deep(.el-table__header) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
:deep(.el-table__header tr),
|
||||
:deep(.el-table__body tr) {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
/* 确保表格容器的最小宽度与内容匹配 */
|
||||
:deep(.el-table) {
|
||||
width: 100% !important;
|
||||
min-width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.inline-select {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.inline-input {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.time-picker {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.record-icon {
|
||||
font-size: 18px;
|
||||
color: #409eff;
|
||||
cursor: pointer;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
|
||||
.record-icon:hover {
|
||||
color: #66b1ff;
|
||||
}
|
||||
|
||||
.bottom-buttons {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
/* 号源记录对话框样式 */
|
||||
.appointment-records {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.record-item {
|
||||
padding: 8px 0;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.record-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.record-time {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.dialog-footer {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* 隐藏数字输入框的增减按钮 */
|
||||
:deep(.el-input__inner[type="number"]) {
|
||||
appearance: textfield;
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
:deep(.el-input__inner[type="number"])::-webkit-outer-spin-button,
|
||||
:deep(.el-input__inner[type="number"])::-webkit-inner-spin-button {
|
||||
appearance: none;
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
2178
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue
Executable file
2178
openhis-ui-vue3/src/views/appoinmentmanage/deptManage/index.vue
Executable file
File diff suppressed because it is too large
Load Diff
1050
openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue
Executable file
1050
openhis-ui-vue3/src/views/appoinmentmanage/deptappthoursManage/index.vue
Executable file
File diff suppressed because it is too large
Load Diff
1960
openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue
Executable file
1960
openhis-ui-vue3/src/views/appoinmentmanage/outpatientAppointment/index.vue
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user