右键菜单功能

This commit is contained in:
wyuu 2025-11-03 18:07:51 +08:00
parent afd7aa5f7c
commit b272e4c60f
8 changed files with 311 additions and 113 deletions

View File

@ -338,5 +338,21 @@ export function setredoSample(query?: Object) {
params: query
})
}
// 锁定报告
export function lockSample(query?: Object) {
return request({
url: '/lisworkoper/locksample',
method: 'get',
params: query
})
}
// 解锁报告
export function unLockSample(query?: Object) {
return request({
url: '/lisworkoper/unlocksample',
method: 'get',
params: query
})
}

View File

@ -31,9 +31,9 @@ import emitter from '@/utils/mitt';
// 组件Props
const props = withDefaults(defineProps<{
items: ContextMenuItem[];
autoClose?: boolean;
menuWidth?: number | string;
items: ContextMenuItem[]; // 菜单项
autoClose?: boolean; // 是否自动关闭菜单
menuWidth?: number | string; // 菜单宽度
}>(), {
autoClose: true,
menuWidth: 180,
@ -51,14 +51,29 @@ const visible = ref(false);
const top = ref(0);
const left = ref(0);
const handleContextMenu = (e: MouseEvent) => {
// 打开当前菜单关闭其他菜单
const handleContextMenu = async (e: MouseEvent) => {
emitter.emit('closeAllContextMenus');
top.value = Math.min(e.clientY, window.innerHeight - 200);
left.value = Math.min(e.clientX, window.innerWidth - 200);
// 先计算初始位置
left.value = Math.min(e.clientX, window.innerWidth - (typeof props.menuWidth === 'number' ? props.menuWidth : 180));
top.value = e.clientY;
visible.value = true;
emit('open');
// 等待菜单渲染完成后获取实际高度
await nextTick();
const menuEl = document.querySelector('.hospital-menu') as HTMLElement;
if (menuEl) {
const menuHeight = menuEl.offsetHeight;
const maxTop = window.innerHeight - menuHeight;
// 确保菜单不会超出底部可视区域
if (top.value > maxTop) {
top.value = maxTop;
// 如果向上调整后仍超出顶部,则取最小0值
if (top.value < 0) top.value = 0;
}
}
};
// 处理菜单项点击
@ -86,12 +101,14 @@ const handleClickOutside = (e: MouseEvent) => {
// 键盘事件处理
const handleKeyDown = (e: KeyboardEvent) => {
if (!visible.value) return;
// console.log('e==>', e);
if (e.key === 'Escape') {
hideMenu();
}
if (e.key === 'e') {
console.log('e==>', e);
// 统一转换小写
const row: ContextMenuItem | undefined = props.items.find(item => item.shortcut?.toLowerCase() === e.key?.toLowerCase());
if (row && !row.disabled) {
handleItemClick(row)
}
};

View File

@ -331,8 +331,8 @@ const setCurrentRow = (row: Object) => {
tableRef.value?.setCurrentRow(row);
};
const setScrollTop = (top: number) => {
tableRef.value?.setScrollTop(top);
const setScrollTo = (top: number) => {
tableRef.value?.scrollTo(top);
};
const clearSort = () => {
@ -353,7 +353,7 @@ defineExpose({
toggleRowSelection,
toggleAllSelection,
setCurrentRow,
setScrollTop,
setScrollTo,
clearSort,
doLayout,
sort,

View File

@ -62,49 +62,8 @@
<!-- 医生校验 -->
<el-dialog v-model="visible" title="请输入审核人员密码..." :width="500" :close-on-click-modal="false" :draggable="true">
<el-form ref="formRef" :model="form" label-width="100px">
<!-- 用户代号 -->
<el-form-item label="用户代号:">
<el-input v-model="form.yhdh" disabled placeholder="用户代号" />
</el-form-item>
<!-- 用户姓名 -->
<el-form-item label="用户姓名:">
<el-input v-model="form.userName" disabled placeholder="用户姓名" />
</el-form-item>
<!-- 密码输入 -->
<el-form-item label="密码:" prop="mm" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
<el-input v-model="form.mm" type="password" placeholder="请输入密码" />
</el-form-item>
<!-- 操作选项 -->
<el-form-item label="">
<el-radio-group v-model="form.type" @change="handleRadioChange">
<el-radio :label="1">仅修改当前标本</el-radio>
<el-radio :label="2">修改当天所有未审核并且无核对医生的标本</el-radio>
<el-radio :label="3"><span style="color: #f00;">修改当天所有未审核标本(将覆盖原有的核对医生)</span></el-radio>
<el-radio :label="4">
<el-row :gutter="5">
<el-col :span="8">指定样本号区间</el-col>
<template v-if="form.type == 4">
<el-col :span="8"> <el-input v-model="form.ybh1" size="small" /></el-col>
<el-col :span="8"><el-input v-model="form.ybh2" size="small" /></el-col>
</template>
</el-row>
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer ">
<el-button type="primary" @click="handleConfirm"> 确定 </el-button>
<el-button @click="handleCancel"> 取消 </el-button>
</div>
</template>
</el-dialog>
<YhVerification ref="formRef" :form-value="form" :labPatKey="labPatKey" @confirm="handleConfirm"
@cancel="handleCancel" />
</div>
</template>
@ -112,11 +71,12 @@
import { ref, reactive, watch, toRaw, computed, onMounted, nextTick } from 'vue';
import { changepatcolumn, updateLabPat, checkYsUser } from "@/api/liswork/work/LisWork";
import SelectTable from '@/components/SelectTable/index.vue';
// @ts-ignore
import { listUser } from '@/api/system/user.js'
import YhVerification from './yhVerification.vue';
import { PropType } from 'vue';
import emitter from '@/utils/mitt';
import { classCom } from '@/utils/classCom';
import { use } from 'vxe-pc-ui';
const aotuSize = classCom.useAutoSize();
interface LabSysItem {
kj: string;
@ -142,6 +102,10 @@ const props = defineProps({
type: Object,
default: () => { }
},
userList: {
type: Array as PropType<object[]>,
default: () => []
}
});
const lastJgbz = ref();
@ -170,8 +134,6 @@ const ysFields = ref([
{ prop: 'nickName', label: '用户姓名', width: 150, enablePinyinSearch: true },
])
const visible = ref(false);
// 表单数据
const form = ref({
yhdh: '',
userName: '',
@ -194,25 +156,12 @@ const formRef = ref();
// 处理确定按钮点击
const handleConfirm = async () => {
if (!formRef.value) return;
// 表单验证
const valid = await formRef.value.validate();
if (valid) {
checkYsUser({ ...form.value, ...props.labPatKey }).then((res: any) => {
if (res.code == 0) {
visible.value = false
form.value.mm = ''
handleFieldChange()
}
})
}
form.value.mm = ''
handleFieldChange()
};
// 处理取消按钮点击
const handleCancel = () => {
formRef.value?.resetFields();
visible.value = false
if (form.value.zdmc = 'yhdh') {
props.labPat.yhdh = JSON.parse(lastValue.value).yhdh
}
@ -222,13 +171,6 @@ const handleCancel = () => {
};
const handleRadioChange = () => {
if (form.value.type == 4) {
form.value.ybh1 = ''
form.value.ybh2 = ''
}
}
// 医生校验
const getJyysData = (data: any) => {
if (data.userName == JSON.parse(lastValue.value).yhdh) return
@ -236,7 +178,7 @@ const getJyysData = (data: any) => {
form.value.userName = data.nickName;
form.value.zdmc = 'yhdh';
nextTick(() => {
visible.value = true
formRef.value?.open()
})
}
const getHdysData = (data: any) => {
@ -245,7 +187,7 @@ const getHdysData = (data: any) => {
form.value.userName = data.nickName;
form.value.zdmc = 'hdys';
nextTick(() => {
visible.value = true
formRef.value?.open()
})
}
@ -378,13 +320,7 @@ const getColor = (type: string) => {
return 'default';
}
}
const userList = ref([]);
onMounted(() => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res: any) => {
userList.value = res.rows;
});
// 监听保存病人结果
emitter.on('saveResult', () => {
saveLabPat(null)

View File

@ -153,14 +153,53 @@
</div>
</el-dialog>
<!-- 合并当前病人其他结果 -->
<el-dialog v-model="jgShow" title="绑定其他结果" width="40vw" :close-on-click-modal="false" :draggable="true">
<el-row>
<el-col :span="8">
<el-radio-group v-model="radio2">
<el-radio value="1">5天内</el-radio>
<el-radio value="2">10天内</el-radio>
<el-radio value="2">20天内</el-radio>
<el-radio value="2">30天内</el-radio>
</el-radio-group>
</el-col>
<el-col :span="4">
<el-radio-group v-model="radio2">
<el-radio value="1">复制</el-radio>
<el-radio value="2">移动</el-radio>
</el-radio-group>
</el-col>
<el-col :span="4">
<el-radio-group v-model="radio2">
<el-radio value="1">覆盖</el-radio>
<el-radio value="2">不覆盖</el-radio>
</el-radio-group>
</el-col>
<el-col :span="8">
<div> <el-checkbox>只显示同一申请单号</el-checkbox> </div>
<div>
<el-button type="primary">确定</el-button>
<el-button>取消</el-button>
</div>
</el-col>
</el-row>
</el-dialog>
<YhVerification ref="formRef" title="请输入锁定人员账号密码" :form-value="formData" @confirm="handleConfirm"
@cancel="handleCancel" :labelShow="false" />
</div>
</template>
<script setup>
import { ref, toRefs, watch, nextTick, onMounted, computed } from "vue";
import YhVerification from "./yhVerification.vue";
import {
check1, check2, uncheck2, unconfirmlog, checkuser, reglimit, queryXmInfo, deleteresult, changeresult,
newresult, queryXmVal, printListwork, getresultchangelog, setinputmdl, saveResult
check1, check2, uncheck2, unconfirmlog, checkuser, reglimit, deleteresult, changeresult,
newresult, queryXmVal, printListwork, getresultchangelog, setinputmdl, saveResult, lockSample, unLockSample
} from "@/api/liswork/work/LisWork";
import { ElMessageBox, ElMessage } from "element-plus";
import ProjectDetails from "@/components/projectDetails/index.vue";
@ -182,14 +221,19 @@ const props = defineProps({
type: Object, default: () => { }
},
labPat: { type: Object, required: true },
userList: { type: Array, default: () => [] },
});
// 解构props
const { tableData, tableKey, } = toRefs(props);
const { tableData, tableKey, labPat, userList } = toRefs(props);
const selectedRows = ref([]);
const lastjyrq = ref('');
const mbStore = useCommonStore();
const aotuSize = classCom.useAutoSize();
const radio2 = ref('1');
const jgShow = ref(false);
watch(() => props.tableData, (newVal) => {
selectedRows.value = [];
@ -254,7 +298,7 @@ const contextMenuItems = computed(() => [
{ label: '锁定报告', action: 'Lock', shortcut: 'L', },
{ label: '解除报告锁定', action: 'lift', shortcut: 'W', },
{ type: 'divider' },
{ label: '删除记录', action: 'delete', danger: true, shortcut: 'd', }
// { label: '删除记录', action: 'delete', danger: true, shortcut: 'd', },
]);
// 处理菜单选择
@ -267,13 +311,14 @@ const handleMenuSelect = (item) => {
previewHandle() //PDF报告预览
break;
case 'merge':
alert(`合并病人当前其他结果`);
jgShow.value = true;
// alert(`合并病人当前其他结果`);
break;
case 'Lock':
alert(`锁定报告`);
lockHandle();
break;
case 'lift':
alert(`解除报告锁定`);
unlockHandle()
break;
case 'delete':
if (confirm(`确定要删除患者的记录吗?`)) {
@ -282,6 +327,56 @@ const handleMenuSelect = (item) => {
break;
}
};
const formRef = ref();
const formData = ref({
yhdh: '',
userName: '',
mm: '',
})
const lockType = ref(1)
// 锁定报告
const lockHandle = () => {
lockType.value = 1
formRef.value.open()
}
// 解除锁定报告
const unlockHandle = () => {
lockType.value = 2
formRef.value.open()
}
// 锁定样本
const handleConfirm = (data) => {
const obj = {
yhdh: data.yhdh,
...tableKey.value
}
if (lockType.value == 1) {
lockSample(obj).then((res) => {
if (res.code == 0) {
formRef.value?.cancel()
ElMessage.success(res.msg)
}
});
} else {
unLockSample(obj).then((res) => {
if (res.code == 0) {
formRef.value?.cancel()
ElMessage.success(res.msg)
}
});
}
}
const handleCancel = () => {
formData.value = {
yhdh: '',
userName: '',
mm: '',
}
}
const columns = ref([
{ label: 'No', prop: 'index', width: 30, align: 'center', visible: true, slot: "index" },
{ label: "复", prop: "redo_flag", width: 30, align: 'center', visible: true, },
@ -418,7 +513,7 @@ const handleCheck = (checkType) => {
// yhdh = checkuserid.value
switch (checkType) {
case 1: // 初审
check1({ ...tableKey.value, yhdh: 'admin' }).then(res => {
check1({ ...tableKey.value, yhdh: props.labPat.yhdh }).then(res => {
emits("changeStatus", props.labPat);
})
break;
@ -439,7 +534,7 @@ const handleCheck = (checkType) => {
const shHandleCheck = () => {
check2({ ...props.tableKey, yhdh: 'admin' }).then(res => {
check2({ ...props.tableKey, yhdh: props.labPat.yhdh }).then(res => {
emits("changeStatus", props.labPat);
if (res.code == "4" && res.problemId === 4) {
tableKey.value.problemId = res.problemId;
@ -481,7 +576,7 @@ const handleWarningSuccess = async () => {
};
const unShHandle = () => {
uncheck2({ ...tableKey.value, yhdh: 'admin' }).then(res => {
uncheck2({ ...tableKey.value, yhdh: props.labPat.yhdh }).then(res => {
if (res.code == "4") {
tableKey.value.problemId = res.problemId;
reasonText.value = res.msg;
@ -582,7 +677,7 @@ const addRowFromDict = async (rowData) => {
nextTick(() => {
selectedRows.value = [newRow];
tableRef.value.setCurrentRow(newRow);
tableRef.value.setScrollTop(23 * tableData.value.length)
tableRef.value.setScrollTo(tableData.value.length - 1)
// 聚焦到新行的输入框
const inputs = document.querySelectorAll('.full-width-input .el-input__inner');
if (inputs.length > 0) {
@ -634,7 +729,7 @@ const handleCsjgEnter = async (row) => {
}
})
} else {
if (row.id) {
if (row && row.id) {
emitter.emit('saveResult');
newresult(row).then(response => {
ElMessage.success("结果保存成功");
@ -764,13 +859,8 @@ const previewHandle = () => {
const getRowIndex = (index) => {
return index + 1;
};
const userList = ref([]);
onMounted(() => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res) => {
userList.value = res.rows;
});
onMounted(() => {
// 获取模板项目
emitter.on('dbMbHandle', (info) => {
// console.log('data==>', info);

View File

@ -0,0 +1,133 @@
<template>
<div>
<el-dialog v-model="visible" :title="title" :width="500" :close-on-click-modal="false" :draggable="true">
<el-form ref="formRef" :model="formValue" label-width="100px">
<!-- 用户代号 -->
<el-form-item label="用户代号:">
<el-input v-model="formValue.yhdh" :disabled="labelShow" placeholder="用户代号" />
</el-form-item>
<!-- 用户姓名 -->
<el-form-item label="用户姓名:">
<el-input v-model="formValue.userName" :disabled="labelShow" placeholder="用户姓名" />
</el-form-item>
<!-- 密码输入 -->
<el-form-item label="密码:" prop="mm" :rules="[{ required: true, message: '请输入密码', trigger: 'blur' }]">
<el-input ref="passwordRef" v-model="formValue.mm" type="password" placeholder="请输入密码"
@keyup.enter="handleConfirm" />
</el-form-item>
<!-- 操作选项 -->
<el-form-item label="" v-if="labelShow">
<el-radio-group v-model="formValue.type" @change="handleRadioChange">
<el-radio :label="1">仅修改当前标本</el-radio>
<el-radio :label="2">修改当天所有未审核并且无核对医生的标本</el-radio>
<el-radio :label="3"><span style="color: #f00;">修改当天所有未审核标本(将覆盖原有的核对医生)</span></el-radio>
<el-radio :label="4">
<el-row :gutter="5">
<el-col :span="8">指定样本号区间</el-col>
<template v-if="formValue.type == 4">
<el-col :span="8"> <el-input v-model="formValue.ybh1" size="small" /></el-col>
<el-col :span="8"><el-input v-model="formValue.ybh2" size="small" /></el-col>
</template>
</el-row>
</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer ">
<el-button type="primary" @click="handleConfirm"> 确定 </el-button>
<el-button @click="cancel"> 取消 </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { checkYsUser } from '@/api/liswork/work/LisWork'
const props = defineProps({
formValue: {
type: Object,
default: {
yhdh: '',
userName: '',
mm: '',
type: 1,
zdmc: '',
ybh1: '',
ybh2: '',
}
},
labPatKey: {
type: Object,
default: () => { }
},
labelShow: {
type: Boolean,
default: true
},
title: {
type: String,
default: '请输入审核人员密码...'
}
});
const visible = ref(false);
// 表单数据
const formRef = ref();
const passwordRef = ref();
const open = () => {
visible.value = true
nextTick(() => {
// formRef.value.clearValidate('mm');
// passwordRef.value.focus();
});
}
const emit = defineEmits(["confirm", "cancel",]);
// 处理确定按钮点击
const handleConfirm = async () => {
if (!formRef.value) return;
// 表单验证
const valid = await formRef.value.validate();
if (valid) {
checkYsUser({ ...props.formValue, ...props.labPatKey }).then((res: any) => {
if (res.code == 0) {
emit('confirm', props.formValue)
}
})
}
};
// 处理取消按钮点击
const cancel = () => {
formRef.value?.resetFields();
visible.value = false
emit('cancel',)
};
const handleRadioChange = () => {
if (props.formValue.type == 4) {
props.formValue.ybh1 = ''
props.formValue.ybh2 = ''
}
}
defineExpose({
open,
cancel
})
</script>
<style scoped></style>

View File

@ -48,12 +48,12 @@
<el-row :gutter="5">
<el-col :span="8">
<LabPat ref="labPatRef" v-model:labPat="labPat" :labPatKey="queryParams" :labSysList="labInputcolList"
@changeStatus="changeStatus" />
:userList="userList" @changeStatus="changeStatus" />
</el-col>
<el-col :span="16">
<LabResult ref="labResultRef" v-model:labPat="labPat" :tableData="labResuts" :tableKey="queryParams"
:resultSysList="resultConfig" @fetchLabResults="fetchLabResults" @changeStatus="changeStatus"
@previewHandle="previewHandle" />
:userList="userList" :resultSysList="resultConfig" @fetchLabResults="fetchLabResults"
@changeStatus="changeStatus" @previewHandle="previewHandle" />
</el-col>
</el-row>
</div>
@ -159,7 +159,8 @@ import { getNextNumber, getPreviousNumber } from "@/utils/getNextNumber";
import { useCommonStore } from '@/store/modules/commonStore';
import { classCom } from '@/utils/classCom';
import PatientQueryForm from '@/components/selectSearch/index.vue'
// @ts-ignore
import { listUser } from '@/api/system/user.js'
const aotuSize = classCom.useAutoSize();
@ -611,7 +612,12 @@ const getSysList = () => {
}
getSysList()
const userList = ref([]);
onMounted(async () => {
const data = { status: 0, del_flag: 0, pageSize: 1000, pageNum: 1 }
listUser(data).then((res: any) => {
userList.value = res.rows;
});
// 加载病人来源字典
const dictRefs = await comDict('PT', 'AU', 'SX', 'DP', 'BT', 'SRD', 'HOS', 'ST');
// 从 ref 中获取实际数据

View File

@ -101,7 +101,7 @@ const addRowFromDict = async (rowData: any) => {
// 5. 自动聚焦
nextTick(() => {
tableRef.value.setCurrentRow(newRow);
tableRef.value.setScrollTop(23 * tableData.value.length)
tableRef.value.setScrollTo(tableData.value.length - 1)
// 聚焦到新行的输入框
const inputs = document.querySelectorAll('.full-width-input .el-input__inner') as NodeListOf<HTMLInputElement>;
if (inputs.length > 0) {