This commit is contained in:
Your Name
2026-05-06 12:19:46 +08:00
parent dddc4d202a
commit 64ab3197ec
3 changed files with 276 additions and 68 deletions
+113 -17
View File
@@ -57,6 +57,15 @@
class="pending-assign-month" class="pending-assign-month"
@change="onPendingAssignOrderMonthChange" @change="onPendingAssignOrderMonthChange"
/> />
<el-input
v-if="formData.pending_assign === '1'"
v-model="formData.pending_assign_keyword"
placeholder="搜身份证/诊单ID/患者号/备注…"
clearable
class="pending-assign-search"
@keyup.enter="doSearch"
@clear="doSearch"
/>
</span> </span>
</div> </div>
<div class="search-row"> <div class="search-row">
@@ -132,6 +141,7 @@
<div class="table-wrap px-4"> <div class="table-wrap px-4">
<el-table <el-table
:data="pager.lists" :data="pager.lists"
row-key="id"
size="default" size="default"
v-loading="pager.loading" v-loading="pager.loading"
@selection-change="handleSelectionChange" @selection-change="handleSelectionChange"
@@ -425,6 +435,7 @@
v-model="assignDialogVisible" v-model="assignDialogVisible"
:title="assignMode === 'batch' ? '批量指派医助' : '指派医助'" :title="assignMode === 'batch' ? '批量指派医助' : '指派医助'"
width="500px" width="500px"
@closed="resetAssignDialog"
> >
<el-form :model="assignForm" label-width="100px"> <el-form :model="assignForm" label-width="100px">
<el-form-item label="选择医助" required> <el-form-item label="选择医助" required>
@@ -434,7 +445,6 @@
class="w-full" class="w-full"
filterable filterable
clearable clearable
value-key="id"
> >
<el-option <el-option
v-for="item in assistantOptions" v-for="item in assistantOptions"
@@ -445,7 +455,7 @@
</el-select> </el-select>
</el-form-item> </el-form-item>
<el-form-item v-if="assignMode === 'batch'" label="已选择"> <el-form-item v-if="assignMode === 'batch'" label="已选择">
<div class="text-gray-500">{{ selectedIds.length }} 条诊单记录</div> <div class="text-gray-500">{{ batchAssignIds.length }} 条诊单记录</div>
</el-form-item> </el-form-item>
<el-form-item v-else label="诊单信息"> <el-form-item v-else label="诊单信息">
<div class="text-gray-500"> <div class="text-gray-500">
@@ -721,7 +731,9 @@ const formData = reactive({
/** 顶部「已完成」Tab:至少有一条挂号记录为已完成 status=3 */ /** 顶部「已完成」Tab:至少有一条挂号记录为已完成 status=3 */
completed_appointment: '' as '' | '1', completed_appointment: '' as '' | '1',
/** 待分配医助:按业务订单创建月份筛选 YYYY-MM,空表示不限月份 */ /** 待分配医助:按业务订单创建月份筛选 YYYY-MM,空表示不限月份 */
pending_assign_order_month: '' as string pending_assign_order_month: '' as string,
/** 待分配医助 Tab:多字段检索(后端扩展至身份证/诊单ID/备注等;不额外套数据权限) */
pending_assign_keyword: '' as string
}) })
const activeTab = ref('all') const activeTab = ref('all')
@@ -756,7 +768,65 @@ const setConfirmed = (v: string) => {
fetchDateCounts() fetchDateCounts()
} }
/** 待分配 + 宽搜时:请求只吃分页与关键词,不带其它 Tab/日期/筛选(与 buildTcmDiagnosisListRequestPayload 一致) */
function isPendingAssignWideKeywordParams(req: Record<string, unknown>): boolean {
const pa = req.pending_assign === '1' || req.pending_assign === 1
if (!pa) {
return false
}
const k1 = String(req.pending_assign_keyword ?? '').trim()
const k2 = String(req.keyword ?? '').trim()
return k1 !== '' || k2 !== ''
}
/** 待分配宽搜:仅传分页 + pending_assign + 关键词,丢弃其余查询参数 */
function buildTcmDiagnosisListRequestPayload(req: Record<string, unknown>): Record<string, unknown> {
if (!isPendingAssignWideKeywordParams(req)) {
return req
}
const k1 = String(req.pending_assign_keyword ?? '').trim()
const k2 = String(req.keyword ?? '').trim()
const kw = k1 !== '' ? k1 : k2
return {
page_no: req.page_no,
page_size: req.page_size,
pending_assign: 1,
pending_assign_keyword: kw
}
}
const fetchTcmDiagnosisListsForPaging = (req: Record<string, unknown>) =>
tcmDiagnosisLists(buildTcmDiagnosisListRequestPayload(req) as any)
/** 宽搜提交前清理界面上的其它检索,避免 Chip 仍显示「已选」但与实际请求不一致 */
function clearSecondaryFiltersWhenPendingAssignWideSearch() {
if (formData.pending_assign !== '1') {
return
}
const kw1 = (formData.pending_assign_keyword ?? '').trim()
const kw2 = (formData.keyword ?? '').trim()
if (kw1 === '' && kw2 === '') {
return
}
formData.appointment_date = ''
formData.pending_booking = ''
formData.completed_appointment = ''
formData.diagnosis_confirmed = ''
formData.has_appointment = ''
formData.diagnosis_type = ''
formData.syndrome_type = ''
formData.assistant_id = ''
formData.start_time = ''
formData.end_time = ''
formData.pending_assign_order_month = ''
activeTab.value = 'all'
if (kw1 !== '') {
formData.keyword = ''
}
}
const doSearch = () => { const doSearch = () => {
clearSecondaryFiltersWhenPendingAssignWideSearch()
pager.page = 1 pager.page = 1
getLists() getLists()
fetchDateCounts() fetchDateCounts()
@@ -780,6 +850,7 @@ const pendingAssignCount = ref<number | undefined>(undefined)
const handleDateTabClick = async (value: string) => { const handleDateTabClick = async (value: string) => {
formData.pending_assign = '' formData.pending_assign = ''
formData.pending_assign_order_month = '' formData.pending_assign_order_month = ''
formData.pending_assign_keyword = ''
formData.pending_booking = '' formData.pending_booking = ''
formData.completed_appointment = '' formData.completed_appointment = ''
formData.has_appointment = '' formData.has_appointment = ''
@@ -793,6 +864,7 @@ const handleDateTabClick = async (value: string) => {
const handlePendingBookingTabClick = async () => { const handlePendingBookingTabClick = async () => {
formData.pending_assign = '' formData.pending_assign = ''
formData.pending_assign_order_month = '' formData.pending_assign_order_month = ''
formData.pending_assign_keyword = ''
formData.pending_booking = '1' formData.pending_booking = '1'
formData.completed_appointment = '' formData.completed_appointment = ''
formData.appointment_date = '' formData.appointment_date = ''
@@ -806,6 +878,7 @@ const handlePendingBookingTabClick = async () => {
const handleCompletedVisitTabClick = async () => { const handleCompletedVisitTabClick = async () => {
formData.pending_assign = '' formData.pending_assign = ''
formData.pending_assign_order_month = '' formData.pending_assign_order_month = ''
formData.pending_assign_keyword = ''
formData.pending_booking = '' formData.pending_booking = ''
formData.completed_appointment = '1' formData.completed_appointment = '1'
formData.has_appointment = '' formData.has_appointment = ''
@@ -850,14 +923,16 @@ const fetchDateCounts = async () => {
tcmDiagnosisLists({ page_no: 1, page_size: 1 }), tcmDiagnosisLists({ page_no: 1, page_size: 1 }),
tcmDiagnosisLists({ has_appointment: 0, page_no: 1, page_size: 1 }), tcmDiagnosisLists({ has_appointment: 0, page_no: 1, page_size: 1 }),
tcmDiagnosisLists({ completed_appointment: 1, page_no: 1, page_size: 1 }), tcmDiagnosisLists({ completed_appointment: 1, page_no: 1, page_size: 1 }),
tcmDiagnosisLists({ tcmDiagnosisLists(
pending_assign: 1, buildTcmDiagnosisListRequestPayload({
page_no: 1, pending_assign: 1,
page_size: 1, page_no: 1,
...(formData.pending_assign === '1' && formData.pending_assign_order_month page_size: 1,
? { pending_assign_order_month: formData.pending_assign_order_month } pending_assign_order_month: formData.pending_assign_order_month,
: {}) pending_assign_keyword: formData.pending_assign_keyword,
}) keyword: formData.keyword
}) as any
)
]) ])
dateCounts.value = { dateCounts.value = {
[yesterdayStr.value]: yesterday?.count ?? 0, [yesterdayStr.value]: yesterday?.count ?? 0,
@@ -991,7 +1066,7 @@ const getAssistantName = (assistantId: number | string) => {
} }
const { pager, getLists, resetParams, resetPage } = usePaging({ const { pager, getLists, resetParams, resetPage } = usePaging({
fetchFun: tcmDiagnosisLists, fetchFun: fetchTcmDiagnosisListsForPaging,
params: formData params: formData
}) })
@@ -1010,6 +1085,7 @@ const handleReset = () => {
formData.has_appointment = '' formData.has_appointment = ''
formData.pending_assign = '' formData.pending_assign = ''
formData.pending_assign_order_month = '' formData.pending_assign_order_month = ''
formData.pending_assign_keyword = ''
formData.pending_booking = '' formData.pending_booking = ''
formData.completed_appointment = '' formData.completed_appointment = ''
pager.page = 1 pager.page = 1
@@ -1238,6 +1314,8 @@ const lastQRCodeType = ref<'video' | 'confirm'>('confirm')
// 批量指派相关 // 批量指派相关
const selectedIds = ref<number[]>([]) const selectedIds = ref<number[]>([])
/** 批量指派打开弹窗时的诊单 id 快照(弹窗内选医助可能触发表格重绘导致勾选丢失,提交以快照为准) */
const batchAssignIds = ref<number[]>([])
const assignDialogVisible = ref(false) const assignDialogVisible = ref(false)
const assignLoading = ref(false) const assignLoading = ref(false)
const assignMode = ref<'batch' | 'single'>('batch') const assignMode = ref<'batch' | 'single'>('batch')
@@ -1246,6 +1324,11 @@ const assignForm = ref({
assistant_id: null as number | null assistant_id: null as number | null
}) })
function resetAssignDialog() {
batchAssignIds.value = []
assignForm.value.assistant_id = null
}
const handleSelectionChange = (selection: any[]) => { const handleSelectionChange = (selection: any[]) => {
selectedIds.value = selection.map(item => item.id) selectedIds.value = selection.map(item => item.id)
} }
@@ -1267,6 +1350,7 @@ const canBatchCancelAssign = computed(() => {
const handleSingleAssign = (row: any) => { const handleSingleAssign = (row: any) => {
assignMode.value = 'single' assignMode.value = 'single'
currentRow.value = row currentRow.value = row
batchAssignIds.value = []
// 确保类型一致,转换为数字 // 确保类型一致,转换为数字
assignForm.value.assistant_id = row.assistant_id ? Number(row.assistant_id) : null assignForm.value.assistant_id = row.assistant_id ? Number(row.assistant_id) : null
assignDialogVisible.value = true assignDialogVisible.value = true
@@ -1281,6 +1365,7 @@ const handleBatchAssign = () => {
assignMode.value = 'batch' assignMode.value = 'batch'
currentRow.value = null currentRow.value = null
assignForm.value.assistant_id = null assignForm.value.assistant_id = null
batchAssignIds.value = [...selectedIds.value]
assignDialogVisible.value = true assignDialogVisible.value = true
} }
@@ -1299,15 +1384,21 @@ const handleConfirmAssign = async () => {
assistant_id: assignForm.value.assistant_id assistant_id: assignForm.value.assistant_id
}) })
} else { } else {
// 批量指派 // 批量指派:使用打开弹窗时的 id 快照(避免弹窗内操作导致表格勾选被清空后提交为空)
const promises = selectedIds.value.map(id => const ids = batchAssignIds.value
tcmDiagnosisAssign({ if (!ids.length) {
id, feedback.msgWarning('没有可指派的诊单,请关闭后重新勾选')
assistant_id: assignForm.value.assistant_id return
}
const promises = ids.map((id) =>
tcmDiagnosisAssign({
id,
assistant_id: assignForm.value.assistant_id
}) })
) )
await Promise.all(promises) await Promise.all(promises)
selectedIds.value = [] selectedIds.value = []
batchAssignIds.value = []
} }
assignDialogVisible.value = false assignDialogVisible.value = false
@@ -1994,6 +2085,11 @@ onUnmounted(() => {
.pending-assign-month { .pending-assign-month {
width: 128px; width: 128px;
} }
.pending-assign-search {
width: 220px;
max-width: 42vw;
}
} }
.search-row { .search-row {
File diff suppressed because one or more lines are too long
+162 -50
View File
@@ -64,36 +64,35 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
// 待分配医助筛选:assistant_id 为空或 0(放在最前面判断,便于跳过医助角色的自我过滤) // 待分配医助筛选:assistant_id 为空或 0(放在最前面判断,便于跳过医助角色的自我过滤)
$pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1'; $pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1';
$pendingWideSearch = $this->isPendingAssignWideKeywordSearchActive();
// 仅「待分配列表」收窄到未分配医助;宽搜不套此池,与「全部」一致可命中已分配医助的诊单
$narrowPendingAssignPool = $pendingAssign && !$pendingWideSearch;
// 构建查询 // 构建查询(待分配+关键词检索时不带诊单启用/禁用 status 条件,并弱化挂号/确认等 Tab 残留参数)
$query = Diagnosis::where($this->searchWhere); $baseWhere = $pendingWideSearch
? $this->searchWhereWithoutDiagnosisStatus($this->searchWhere)
: $this->searchWhere;
$query = Diagnosis::where($baseWhere);
// 如果是医助角色(role_id=2),只显示自己添加的患者; // 如果是医助角色(role_id=2),只显示自己关联的诊单;待分配列表(未宽搜)放开以便看到全体未分配记录
// 但当前在查"待分配医助"时应放开,否则 NULL 记录永远不会匹配 assistant_id=adminId if (in_array(2, $roleIds) && (!$pendingAssign || $pendingWideSearch)) {
if (in_array(2, $roleIds) && !$pendingAssign) {
$query->where('assistant_id', $this->adminId); $query->where('assistant_id', $this->adminId);
} }
if (!$pendingAssign) { if (!$pendingAssign || $pendingWideSearch) {
$this->applyDataScopeByOwner($query, 'assistant_id'); $this->applyDataScopeByOwner($query, 'assistant_id');
} }
// 关键字搜索:支持患者姓名手机号(模糊匹配) // 关键字:待分配 Tab 用多字段宽搜;其它 Tab 仍为姓名/手机。窄「待分配列表」不套数据权限;宽搜与「全部」一致会套数据权限。
if (isset($this->params['keyword']) && trim((string) $this->params['keyword']) !== '') { $this->applyDiagnosisListKeywordFilter($query, $pendingAssign);
$keyword = trim((string) $this->params['keyword']);
$query->where(function ($q) use ($keyword) {
$q->whereLike('patient_name', '%' . $keyword . '%')
->whereOr('phone', 'like', '%' . $keyword . '%');
});
}
// 待分配医助:assistant_id 为 NULL 或 0使用 whereRaw 避免闭包 whereOr 歧义 // 待分配医助列表assistant_id 为 NULL 或 0宽搜不加此条件
if ($pendingAssign) { if ($narrowPendingAssignPool) {
$query->whereRaw('(assistant_id IS NULL OR assistant_id = 0)'); $query->whereRaw('(assistant_id IS NULL OR assistant_id = 0)');
} }
// 是否确认诊单:1=已确认 0=未确认 // 是否确认诊单:1=已确认 0=未确认(待分配+关键词检索时不限制确认态,避免搜不到)
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') { if (!$pendingWideSearch && isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed']; $confirmed = (int)$this->params['diagnosis_confirmed'];
$dvrTbl = (new DiagnosisViewRecord())->getTable(); $dvrTbl = (new DiagnosisViewRecord())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
@@ -105,16 +104,16 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
} }
} }
// 挂号日期筛选:当天/明天/后天(1=已预约 3=已完成 4=已过号,需展示以便取消等操作 // 挂号日期筛选:当天/明天/后天…(待分配+关键词检索时不限制日期
if (!empty($this->params['appointment_date'])) { if (!$pendingWideSearch && !empty($this->params['appointment_date'])) {
$aptDate = addslashes($this->params['appointment_date']); $aptDate = addslashes($this->params['appointment_date']);
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.appointment_date = '{$aptDate}' AND apt.status IN (1,3,4)"); $query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.appointment_date = '{$aptDate}' AND apt.status IN (1,3,4)");
} }
// 挂号状态:1=已挂号(含已预约/已完成/已过号)0=未挂号 // 挂号状态:1=已挂号 0=未挂号(待分配+关键词检索时不限制)
if (isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') { if (!$pendingWideSearch && isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') {
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
if ((int)$this->params['has_appointment'] === 1) { if ((int)$this->params['has_appointment'] === 1) {
@@ -124,8 +123,8 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
} }
} }
// 至少有一条挂号为「已完成」(status=3),用于列表顶部「已完成」Tab // 至少有一条挂号为「已完成」(status=3)(待分配+关键词检索时不限制)
if (isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1') { if (!$pendingWideSearch && isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1') {
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.status = 3"); $query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.status = 3");
@@ -133,15 +132,15 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$this->applyPendingAssignBusinessOrderMonthFilter($query); $this->applyPendingAssignBusinessOrderMonthFilter($query);
// 仅已开方(有处方)的诊单:随访列表传 only_has_prescription=1;问诊等页面不传则不过滤 // 仅已开方(待分配+关键词检索时不限制)
if (isset($this->params['only_has_prescription']) && (string) $this->params['only_has_prescription'] === '1') { if (!$pendingWideSearch && isset($this->params['only_has_prescription']) && (string) $this->params['only_has_prescription'] === '1') {
$rxTbl = (new Prescription())->getTable(); $rxTbl = (new Prescription())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$rxTbl} rx WHERE rx.diagnosis_id = {$diagTbl}.id AND rx.delete_time IS NULL"); $query->whereExists("SELECT 1 FROM {$rxTbl} rx WHERE rx.diagnosis_id = {$diagTbl}.id AND rx.delete_time IS NULL");
} }
// 医助所属部门(随访等页面传 assistant_dept_id):选父级时包含全部子级部门下医助 // 医助所属部门(待分配+关键词检索时不按部门收窄)
if (isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) { if (!$pendingWideSearch && isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) {
$rootDeptId = (int) $this->params['assistant_dept_id']; $rootDeptId = (int) $this->params['assistant_dept_id'];
$deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId); $deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId);
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool { $deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
@@ -162,7 +161,7 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$minAptDateCond = ''; $minAptDateCond = '';
if (!empty($this->params['appointment_date'])) { if (!$pendingWideSearch && !empty($this->params['appointment_date'])) {
$sortAptDate = addslashes((string) $this->params['appointment_date']); $sortAptDate = addslashes((string) $this->params['appointment_date']);
$minAptDateCond = " AND apt.appointment_date = '{$sortAptDate}'"; $minAptDateCond = " AND apt.appointment_date = '{$sortAptDate}'";
} }
@@ -173,8 +172,8 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
// 获取最早的挂号时间(用于同状态内排序) // 获取最早的挂号时间(用于同状态内排序)
$minAptExpr = '(SELECT MIN(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status IN (1,3,4)' . $minAptDateCond . ')'; $minAptExpr = '(SELECT MIN(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status IN (1,3,4)' . $minAptDateCond . ')';
// 「已完成」Tab:按最近一条「已完成」(status=3) 挂号日期+时间降序,最新在最前;有 appointment_date 时仍只在该日内比 // 「已完成」Tab:按最近一条「已完成」(status=3) 挂号日期+时间降序
$isCompletedTab = isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1'; $isCompletedTab = !$pendingWideSearch && isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1';
if ($isCompletedTab) { if ($isCompletedTab) {
$maxCompletedAptExpr = '(SELECT MAX(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status = 3' . $minAptDateCond . ')'; $maxCompletedAptExpr = '(SELECT MAX(CONCAT(apt.appointment_date, \' \', IFNULL(NULLIF(TRIM(apt.appointment_time), \'\'), \'00:00:00\'))) FROM ' . $aptTbl . ' apt WHERE apt.patient_id = ' . $diagTbl . '.id AND apt.status = 3' . $minAptDateCond . ')';
$orderRaw = 'IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC"; $orderRaw = 'IFNULL(' . $maxCompletedAptExpr . ", '1970-01-01 00:00:00') DESC, {$diagTbl}.id DESC";
@@ -481,33 +480,29 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
$pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1'; $pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1';
// 构建查询 // 构建查询
$query = Diagnosis::where($this->searchWhere); $pendingWideSearch = $this->isPendingAssignWideKeywordSearchActive();
$narrowPendingAssignPool = $pendingAssign && !$pendingWideSearch;
$baseWhere = $pendingWideSearch
? $this->searchWhereWithoutDiagnosisStatus($this->searchWhere)
: $this->searchWhere;
$query = Diagnosis::where($baseWhere);
// 如果是医助角色(role_id=2),只显示自己添加的患者;查"待分配医助"时放开 if (in_array(2, $roleIds) && (!$pendingAssign || $pendingWideSearch)) {
if (in_array(2, $roleIds) && !$pendingAssign) {
$query->where('assistant_id', $this->adminId); $query->where('assistant_id', $this->adminId);
} }
if (!$pendingAssign) { if (!$pendingAssign || $pendingWideSearch) {
$this->applyDataScopeByOwner($query, 'assistant_id'); $this->applyDataScopeByOwner($query, 'assistant_id');
} }
// 关键字搜索:支持患者姓名或手机号(模糊匹配) $this->applyDiagnosisListKeywordFilter($query, $pendingAssign);
if (isset($this->params['keyword']) && trim((string) $this->params['keyword']) !== '') {
$keyword = trim((string) $this->params['keyword']);
$query->where(function ($q) use ($keyword) {
$q->whereLike('patient_name', '%' . $keyword . '%')
->whereOr('phone', 'like', '%' . $keyword . '%');
});
}
// 待分配医助:assistant_id 为 NULL 或 0 if ($narrowPendingAssignPool) {
if ($pendingAssign) {
$query->whereRaw('(assistant_id IS NULL OR assistant_id = 0)'); $query->whereRaw('(assistant_id IS NULL OR assistant_id = 0)');
} }
// 是否确认诊单 // 是否确认诊单
if (isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') { if (!$pendingWideSearch && isset($this->params['diagnosis_confirmed']) && $this->params['diagnosis_confirmed'] !== '') {
$confirmed = (int)$this->params['diagnosis_confirmed']; $confirmed = (int)$this->params['diagnosis_confirmed'];
$dvrTbl = (new DiagnosisViewRecord())->getTable(); $dvrTbl = (new DiagnosisViewRecord())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
@@ -520,7 +515,7 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
} }
// 挂号日期筛选 // 挂号日期筛选
if (!empty($this->params['appointment_date'])) { if (!$pendingWideSearch && !empty($this->params['appointment_date'])) {
$aptDate = addslashes($this->params['appointment_date']); $aptDate = addslashes($this->params['appointment_date']);
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
@@ -528,7 +523,7 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
} }
// 挂号状态 // 挂号状态
if (isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') { if (!$pendingWideSearch && isset($this->params['has_appointment']) && $this->params['has_appointment'] !== '') {
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
if ((int)$this->params['has_appointment'] === 1) { if ((int)$this->params['has_appointment'] === 1) {
@@ -539,23 +534,140 @@ class DiagnosisLists extends BaseAdminDataLists implements ListsSearchInterface
} }
// 至少有一条挂号为「已完成」(status=3) // 至少有一条挂号为「已完成」(status=3)
if (isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1') { if (!$pendingWideSearch && isset($this->params['completed_appointment']) && (string) $this->params['completed_appointment'] === '1') {
$aptTbl = (new Appointment())->getTable(); $aptTbl = (new Appointment())->getTable();
$diagTbl = (new Diagnosis())->getTable(); $diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.status = 3"); $query->whereExists("SELECT 1 FROM {$aptTbl} apt WHERE apt.patient_id = {$diagTbl}.id AND apt.status = 3");
} }
// 仅已开方(待分配+关键词检索时不限制)
if (!$pendingWideSearch && isset($this->params['only_has_prescription']) && (string) $this->params['only_has_prescription'] === '1') {
$rxTbl = (new Prescription())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$rxTbl} rx WHERE rx.diagnosis_id = {$diagTbl}.id AND rx.delete_time IS NULL");
}
// 医助所属部门(待分配+关键词检索时不按部门收窄)
if (!$pendingWideSearch && isset($this->params['assistant_dept_id']) && $this->params['assistant_dept_id'] !== '' && (int) $this->params['assistant_dept_id'] > 0) {
$rootDeptId = (int) $this->params['assistant_dept_id'];
$deptIds = DeptLogic::getSelfAndDescendantIds($rootDeptId);
$deptIds = array_values(array_filter(array_map('intval', $deptIds), static function (int $id): bool {
return $id > 0;
}));
if ($deptIds === []) {
$query->whereRaw('0 = 1');
} else {
$inList = implode(',', $deptIds);
$adTbl = (new AdminDept())->getTable();
$diagTbl = (new Diagnosis())->getTable();
$query->whereExists("SELECT 1 FROM {$adTbl} ad WHERE ad.admin_id = {$diagTbl}.assistant_id AND ad.dept_id IN ({$inList})");
}
}
$this->applyPendingAssignBusinessOrderMonthFilter($query); $this->applyPendingAssignBusinessOrderMonthFilter($query);
return $query->count(); return $query->count();
} }
/**
* 待分配 Tab 且有关键词时:放宽列表条件(与 `applyDiagnosisListKeywordFilter` 判定一致)。
*/
private function isPendingAssignWideKeywordSearchActive(): bool
{
$pending = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1';
if (!$pending) {
return false;
}
$wideKw = trim((string) ($this->params['pending_assign_keyword'] ?? ''));
if ($wideKw === '') {
$wideKw = trim((string) ($this->params['keyword'] ?? ''));
}
return $wideKw !== '';
}
/**
* 从 `createWhere` 结果中移除诊单 `status`(启用/禁用),便于宽检索命中任意状态诊单。
*
* @param list<array{0:mixed,1?:mixed,2?:mixed}> $searchWhere
* @return list<array{0:mixed,1?:mixed,2?:mixed}>
*/
private function searchWhereWithoutDiagnosisStatus(array $searchWhere): array
{
$out = [];
foreach ($searchWhere as $cond) {
if (is_array($cond) && isset($cond[0]) && $cond[0] === 'status') {
continue;
}
$out[] = $cond;
}
return $out;
}
/**
* 列表关键字:待分配医助 Tab 为「多字段」检索(pending_assign_keyword 优先,否则回落 keyword);
* 其它 Tab 仍为姓名、手机。待分配 Tab 本身已不套 assistant_id 数据权限,检索可触达全部待分配诊单。
*/
private function applyDiagnosisListKeywordFilter($query, bool $pendingAssign): void
{
if ($pendingAssign) {
$wideKw = trim((string) ($this->params['pending_assign_keyword'] ?? ''));
if ($wideKw === '') {
$wideKw = trim((string) ($this->params['keyword'] ?? ''));
}
if ($wideKw !== '') {
$this->applyPendingAssignWideKeywordSearch($query, $wideKw);
}
return;
}
if (isset($this->params['keyword']) && trim((string) $this->params['keyword']) !== '') {
$keyword = trim((string) $this->params['keyword']);
$query->where(function ($q) use ($keyword) {
$q->whereLike('patient_name', '%' . $keyword . '%')
->whereOr('phone', 'like', '%' . $keyword . '%');
});
}
}
/**
* 待分配医助场景:多列模糊 + 纯数字时诊单 id / 患者号精确匹配。
*/
private function applyPendingAssignWideKeywordSearch($query, string $keyword): void
{
$kw = trim($keyword);
if ($kw === '') {
return;
}
$query->where(function ($q) use ($kw) {
$q->whereLike('patient_name', '%' . $kw . '%')
->whereOr('phone', 'like', '%' . $kw . '%')
->whereOr('id_card', 'like', '%' . $kw . '%')
->whereOr('remark', 'like', '%' . $kw . '%')
->whereOr('symptoms', 'like', '%' . $kw . '%')
->whereOr('current_medications', 'like', '%' . $kw . '%');
if (preg_match('/^\d+$/', $kw)) {
$n = (int) $kw;
if ($n > 0) {
$q->whereOr('id', '=', $n)
->whereOr('patient_id', '=', $n);
}
}
});
}
/** /**
* 「待分配医助」Tab:按月份筛选——诊单在指定自然月内需存在业务订单(order.patient_id = 诊单 id * 「待分配医助」Tab:按月份筛选——诊单在指定自然月内需存在业务订单(order.patient_id = 诊单 id
* create_time 兼容整型时间戳与 datetime 字符串 * create_time 兼容整型时间戳与 datetime 字符串
*/ */
private function applyPendingAssignBusinessOrderMonthFilter($query): void private function applyPendingAssignBusinessOrderMonthFilter($query): void
{ {
if ($this->isPendingAssignWideKeywordSearchActive()) {
return;
}
$pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1'; $pendingAssign = isset($this->params['pending_assign']) && (string) $this->params['pending_assign'] === '1';
if (!$pendingAssign || empty($this->params['pending_assign_order_month'])) { if (!$pendingAssign || empty($this->params['pending_assign_order_month'])) {
return; return;