|
|
<template>
|
|
|
<div v-loading="loading">
|
|
|
<div class="page-container">
|
|
|
<!-- 左侧:排班列表 -->
|
|
|
<div class="left-panel">
|
|
|
<div class="panel-header">
|
|
|
<span class="panel-title">排班</span>
|
|
|
</div>
|
|
|
<div class="device-list">
|
|
|
<div v-for="item in deviceList" :key="item.id"
|
|
|
class="device-item"
|
|
|
:class="{ active: selectedDevice?.id === item.id }"
|
|
|
@click="selectDevice(item)">
|
|
|
<div class="device-name">{{ item.device_name }}</div>
|
|
|
<div class="device-count">已绑定 {{ item.bind_count || 0 }} 项</div>
|
|
|
</div>
|
|
|
<div v-if="deviceList.length === 0" class="empty-tip">
|
|
|
暂无服务组
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 右侧:已绑定的检查项目 -->
|
|
|
<div class="right-panel">
|
|
|
<div class="panel-header">
|
|
|
<span class="panel-title">
|
|
|
{{ selectedDevice ? selectedDevice.device_name + ' - 已绑定的检查项目' : '请选择一个排班' }}
|
|
|
</span>
|
|
|
</div>
|
|
|
|
|
|
<div v-if="selectedDevice" class="right-content">
|
|
|
<div style="margin-bottom: 12px;">
|
|
|
<el-button type="primary" @click="openBindDialog">添加绑定</el-button>
|
|
|
</div>
|
|
|
|
|
|
<el-table :data="boundItems" style="width: 100%;" row-key="id" v-loading="boundLoading">
|
|
|
<el-table-column prop="item_code" label="项目编号" width="160" />
|
|
|
<el-table-column prop="item_name" label="检查项目名称" />
|
|
|
<el-table-column prop="item_class_name" label="医嘱类别" />
|
|
|
<el-table-column prop="" label="操作" width="100">
|
|
|
<template #default="scope">
|
|
|
<el-button type="danger" size="small" @click="removeBind(scope.row)">解绑</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
<div v-if="boundItems.length === 0 && !boundLoading" class="empty-tip" style="margin-top: 20px;">
|
|
|
暂无绑定的检查项目
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- 添加绑定弹窗 -->
|
|
|
<el-dialog v-model="showBindDialog" title="添加检查项目绑定" width="60%" @closed="closeBindDialog">
|
|
|
<div>
|
|
|
<div style="display: flex; gap: 12px; margin-bottom: 12px;">
|
|
|
<el-input v-model="bindSearchKeyword" placeholder="输入项目编号/名称搜索"
|
|
|
clearable style="flex: 1;" @input="onBindSearchInput" />
|
|
|
<el-button type="primary" @click="doBindSearch">搜索</el-button>
|
|
|
<el-button @click="refreshBindList">刷新</el-button>
|
|
|
</div>
|
|
|
<el-table :data="bindPageData" style="width: 100%;" row-key="id" v-loading="bindSearchLoading">
|
|
|
<el-table-column prop="item_code" label="项目编号" width="160" />
|
|
|
<el-table-column prop="item_name" label="检查项目名称" />
|
|
|
<el-table-column prop="item_class_name" label="医嘱类别" />
|
|
|
<el-table-column prop="" label="操作" width="100">
|
|
|
<template #default="scope">
|
|
|
<el-button type="primary" size="small" @click="addBindFromDialog(scope.row)"
|
|
|
:loading="bindItemLoading === scope.row.id">添加</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
<div style="display: flex; justify-content: space-between; align-items: center; margin-top: 12px;">
|
|
|
<span style="color: #909399; font-size: 13px;">共 {{ bindTotal }} 条</span>
|
|
|
<el-pagination v-model:current-page="bindCurrentPage" v-model:page-size="bindPageSize"
|
|
|
:page-sizes="[10]" layout="prev, pager, next" :total="bindTotal"
|
|
|
@current-change="onBindPageChange" />
|
|
|
</div>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
</template>
|
|
|
|
|
|
<script setup>
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
import {
|
|
|
GetDeptDeviceList,
|
|
|
GetDeviceBindItems,
|
|
|
GetUnboundCheckItems,
|
|
|
BindItemToDevice,
|
|
|
UnbindItemFromDevice
|
|
|
} from '@/api/api.js'
|
|
|
|
|
|
let loading = ref(false)
|
|
|
let deviceList = ref([])
|
|
|
let selectedDevice = ref(null)
|
|
|
let boundItems = ref([])
|
|
|
let boundLoading = ref(false)
|
|
|
let showBindDialog = ref(false)
|
|
|
let bindSearchKeyword = ref('')
|
|
|
let bindSearchResults = ref([])
|
|
|
let bindSearchLoading = ref(false)
|
|
|
let bindSearchTimer = null
|
|
|
let bindCurrentPage = ref(1)
|
|
|
let bindPageSize = ref(10)
|
|
|
let bindTotal = ref(0)
|
|
|
let bindItemLoading = ref(null)
|
|
|
|
|
|
// 分页数据(直接从后端返回的结果)
|
|
|
const bindPageData = computed(() => bindSearchResults.value)
|
|
|
|
|
|
// 获取设备列表
|
|
|
const getDeviceList = () => {
|
|
|
loading.value = true
|
|
|
GetDeptDeviceList().then(res => {
|
|
|
loading.value = false
|
|
|
if (res.status) {
|
|
|
deviceList.value = res.data
|
|
|
// 默认选中第一个
|
|
|
if (deviceList.value.length > 0) {
|
|
|
selectDevice(deviceList.value[0])
|
|
|
}
|
|
|
} else {
|
|
|
ElMessage.error(res.msg)
|
|
|
}
|
|
|
}).catch(() => {
|
|
|
loading.value = false
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const selectDevice = (device) => {
|
|
|
selectedDevice.value = device
|
|
|
getBoundItems(device.id)
|
|
|
}
|
|
|
|
|
|
const getBoundItems = (deviceId) => {
|
|
|
boundLoading.value = true
|
|
|
GetDeviceBindItems({ device_id: deviceId }).then(res => {
|
|
|
boundLoading.value = false
|
|
|
if (res.status) {
|
|
|
boundItems.value = res.data
|
|
|
} else {
|
|
|
ElMessage.error(res.msg)
|
|
|
}
|
|
|
}).catch(() => {
|
|
|
boundLoading.value = false
|
|
|
})
|
|
|
}
|
|
|
|
|
|
// 分页查询(后端分页,每页10条,已排除已绑定的项目)
|
|
|
const loadPage = () => {
|
|
|
bindSearchLoading.value = true
|
|
|
GetUnboundCheckItems({
|
|
|
device_id: selectedDevice.value.id,
|
|
|
keyword: bindSearchKeyword.value,
|
|
|
page: bindCurrentPage.value,
|
|
|
pageSize: bindPageSize.value
|
|
|
}).then(res => {
|
|
|
bindSearchLoading.value = false
|
|
|
if (res.status) {
|
|
|
bindSearchResults.value = res.data.list || []
|
|
|
bindTotal.value = res.data.count || 0
|
|
|
} else {
|
|
|
ElMessage.error(res.msg)
|
|
|
}
|
|
|
}).catch(() => {
|
|
|
bindSearchLoading.value = false
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const openBindDialog = () => {
|
|
|
showBindDialog.value = true
|
|
|
bindSearchKeyword.value = ''
|
|
|
bindCurrentPage.value = 1
|
|
|
loadPage()
|
|
|
}
|
|
|
|
|
|
// 防抖搜索
|
|
|
const onBindSearchInput = () => {
|
|
|
if (bindSearchTimer) clearTimeout(bindSearchTimer)
|
|
|
bindSearchTimer = setTimeout(() => {
|
|
|
bindCurrentPage.value = 1
|
|
|
loadPage()
|
|
|
}, 300)
|
|
|
}
|
|
|
|
|
|
const doBindSearch = () => {
|
|
|
bindCurrentPage.value = 1
|
|
|
loadPage()
|
|
|
}
|
|
|
|
|
|
const refreshBindList = () => {
|
|
|
loadPage()
|
|
|
}
|
|
|
|
|
|
const onBindPageChange = (page) => {
|
|
|
bindCurrentPage.value = page
|
|
|
loadPage()
|
|
|
}
|
|
|
|
|
|
const addBindFromDialog = (item) => {
|
|
|
bindItemLoading.value = item.id
|
|
|
BindItemToDevice({
|
|
|
item_id: item.id,
|
|
|
device_id: selectedDevice.value.id
|
|
|
}).then(res => {
|
|
|
bindItemLoading.value = null
|
|
|
if (res.status) {
|
|
|
ElMessage.success(`已绑定 ${item.item_name}`)
|
|
|
// 从当前页中移除
|
|
|
const idx = bindSearchResults.value.findIndex(i => i.id === item.id)
|
|
|
if (idx !== -1) {
|
|
|
bindSearchResults.value.splice(idx, 1)
|
|
|
}
|
|
|
bindTotal.value--
|
|
|
// 如果当前页没有项目了,回到前一页
|
|
|
if (bindSearchResults.value.length === 0 && bindCurrentPage.value > 1) {
|
|
|
bindCurrentPage.value--
|
|
|
loadPage()
|
|
|
}
|
|
|
// 刷新右侧列表
|
|
|
getBoundItems(selectedDevice.value.id)
|
|
|
} else {
|
|
|
ElMessage.error(res.msg)
|
|
|
}
|
|
|
}).catch(() => {
|
|
|
bindItemLoading.value = null
|
|
|
})
|
|
|
}
|
|
|
|
|
|
const closeBindDialog = () => {
|
|
|
bindSearchKeyword.value = ''
|
|
|
bindSearchResults.value = []
|
|
|
bindTotal.value = 0
|
|
|
bindCurrentPage.value = 1
|
|
|
}
|
|
|
|
|
|
const removeBind = (row) => {
|
|
|
ElMessageBox.confirm(`确定解绑 ${row.item_name} 吗?`, '提示', {
|
|
|
confirmButtonText: '确定',
|
|
|
cancelButtonText: '取消',
|
|
|
type: 'warning',
|
|
|
}).then(() => {
|
|
|
UnbindItemFromDevice({
|
|
|
item_id: row.id,
|
|
|
device_id: selectedDevice.value.id
|
|
|
}).then(res => {
|
|
|
if (res.status) {
|
|
|
ElMessage.success('解绑成功')
|
|
|
getBoundItems(selectedDevice.value.id)
|
|
|
} else {
|
|
|
ElMessage.error(res.msg)
|
|
|
}
|
|
|
})
|
|
|
}).catch(() => {})
|
|
|
}
|
|
|
|
|
|
onMounted(() => {
|
|
|
getDeviceList()
|
|
|
})
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.page-container {
|
|
|
display: flex;
|
|
|
height: calc(100vh - 120px);
|
|
|
gap: 16px;
|
|
|
}
|
|
|
|
|
|
.left-panel {
|
|
|
width: 280px;
|
|
|
min-width: 280px;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
border-radius: 8px;
|
|
|
background: #fff;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.panel-header {
|
|
|
padding: 14px 16px;
|
|
|
border-bottom: 1px solid #e4e7ed;
|
|
|
background: #f5f7fa;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: space-between;
|
|
|
}
|
|
|
|
|
|
.panel-title {
|
|
|
font-size: 15px;
|
|
|
font-weight: 600;
|
|
|
color: #303133;
|
|
|
}
|
|
|
|
|
|
.device-list {
|
|
|
flex: 1;
|
|
|
overflow-y: auto;
|
|
|
padding: 8px 0;
|
|
|
}
|
|
|
|
|
|
.device-item {
|
|
|
padding: 12px 16px;
|
|
|
cursor: pointer;
|
|
|
border-left: 3px solid transparent;
|
|
|
transition: all 0.2s;
|
|
|
}
|
|
|
|
|
|
.device-item:hover {
|
|
|
background: #f0f5ff;
|
|
|
}
|
|
|
|
|
|
.device-item.active {
|
|
|
background: #ecf5ff;
|
|
|
border-left-color: #409eff;
|
|
|
}
|
|
|
|
|
|
.device-name {
|
|
|
font-size: 14px;
|
|
|
color: #303133;
|
|
|
font-weight: 500;
|
|
|
}
|
|
|
|
|
|
.device-count {
|
|
|
font-size: 12px;
|
|
|
color: #909399;
|
|
|
margin-top: 4px;
|
|
|
}
|
|
|
|
|
|
.right-panel {
|
|
|
flex: 1;
|
|
|
border: 1px solid #e4e7ed;
|
|
|
border-radius: 8px;
|
|
|
background: #fff;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.right-content {
|
|
|
flex: 1;
|
|
|
padding: 16px;
|
|
|
overflow-y: auto;
|
|
|
}
|
|
|
|
|
|
.empty-tip {
|
|
|
text-align: center;
|
|
|
color: #c0c4cc;
|
|
|
font-size: 14px;
|
|
|
padding: 40px 0;
|
|
|
}
|
|
|
</style> |