This commit is contained in:
wyuu 2025-10-13 17:28:33 +08:00
parent 2f56182ca9
commit a5639b87ad
12 changed files with 738 additions and 256 deletions

View File

@ -181,6 +181,7 @@
.vxe-table { .vxe-table {
border: 1px solid #96B8D9 !important; border: 1px solid #96B8D9 !important;
border-top: none !important;
/* 蓝色外边框 */ /* 蓝色外边框 */
border-radius: 8px; border-radius: 8px;
/* 可选:添加边框圆角 */ /* 可选:添加边框圆角 */

View File

@ -0,0 +1,330 @@
<template>
<div class="double-click-input-table" ref="componentRef">
<!-- 输入框 -->
<el-input ref="inputRef" v-model="inputValue" class="full-width-input" :placeholder="placeholder"
:disabled="disabled" @dblclick="handleDoubleClick" @change="handleInputBlur" />
<!-- 下拉表格面板 -->
<el-popover ref="popoverRef" v-model:visible="isPopoverVisible" :width="popoverWidth" trigger="click"
placement="bottom-start" :teleported="false" @hide="handlePopoverHide">
<!-- 搜索框 -->
<el-input ref="searchInputRef" v-model="searchKeyword" placeholder="搜索..." size="small" class="search-input"
@input="handleSearch" @keydown="handleSearchKeydown" />
<!-- 表格 -->
<el-table ref="tableRef" :data="filteredData" :height="tableHeight" highlight-current-row
:current-row-key="currentRowKey" @row-dblclick="handleRowDblClick" class="select-table">
<!-- 动态渲染列 -->
<el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label"
:width="column.width" :align="column.align || 'left'" />
</el-table>
</el-popover>
</div>
</template>
<script setup lang="ts">
import { ref, watch, nextTick, onMounted, defineProps, defineEmits, toRefs } from 'vue';
import { ElInput, ElPopover, ElTable, ElTableColumn } from 'element-plus';
// 定义列的类型
interface TableColumn {
prop: string;
label: string;
width?: number | string;
align?: 'left' | 'center' | 'right';
}
interface DataItem {
[key: string]: any; // 或者更严格的字段定义
}
const props = defineProps<{
modelValue: string | number | DataItem;
data: DataItem[];
columns: TableColumn[];
labelField: string;
valueField: string;
placeholder?: string;
disabled?: boolean;
popoverWidth?: number | string;
tableHeight?: number | string;
searchable?: boolean;
}>();
// 定义事件
const emits = defineEmits(['update:modelValue', 'change', 'select', 'visible-change']);
// 解构 props
const { modelValue, data, columns, labelField, valueField, searchable } = toRefs(props);
// 组件内部状态
const inputValue = ref('');
const isPopoverVisible = ref(false);
const searchKeyword = ref('');
const filteredData = ref<any[]>([]);
const currentRowKey = ref('');
const currentIndex = ref(-1);
const isInputFocused = ref(false);
// 引用
const inputRef = ref<InstanceType<typeof ElInput>>();
const popoverRef = ref<InstanceType<typeof ElPopover>>();
const tableRef = ref<InstanceType<typeof ElTable>>();
const searchInputRef = ref<InstanceType<typeof ElInput>>();
const componentRef = ref<HTMLElement | null>(null);
// 初始化
onMounted(() => {
filteredData.value = [...data.value];
syncInputValue();
// 监听数据变化
watch(data, (newVal) => {
filteredData.value = [...newVal];
handleSearch();
});
// 监听选中值变化
watch(modelValue, () => {
syncInputValue();
});
});
// 同步输入框值
const syncInputValue = () => {
if (modelValue.value && typeof modelValue.value === 'object') {
inputValue.value = modelValue.value[labelField.value] || '';
setCurrentRow(modelValue.value);
} else if (modelValue.value) {
// 当值不是对象时,查找对应的数据
const found = data.value.find((item: any) => item[valueField.value] === modelValue.value);
inputValue.value = found ? found[labelField.value] : '';
setCurrentRow(found);
} else {
inputValue.value = '';
currentRowKey.value = '';
currentIndex.value = -1;
}
};
// 设置当前选中行
const setCurrentRow = (row: any) => {
if (!row) return;
currentRowKey.value = row[valueField.value];
const index = filteredData.value.findIndex(item => item[valueField.value] === row[valueField.value]);
if (index !== -1) {
currentIndex.value = index;
}
};
// 双击输入框显示下拉面板
const handleDoubleClick = () => {
if (props.disabled) return;
isPopoverVisible.value = true;
emits('visible-change', true);
// 延迟聚焦搜索框,确保弹窗已渲染
nextTick(() => {
if (searchable.value && searchInputRef.value) {
searchInputRef.value.focus();
} else if (tableRef.value) {
// 如果不可搜索,聚焦表格
const tableEl = tableRef.value.$el.querySelector('.el-table__body-wrapper');
tableEl?.focus();
}
});
};
// 处理搜索
const handleSearch = () => {
const keyword = searchKeyword.value.toLowerCase().trim();
if (!keyword) {
filteredData.value = [...data.value];
currentIndex.value = -1;
return;
}
// 过滤数据,在所有列中搜索
filteredData.value = data.value.filter((item: any) => {
return columns.value.some(column => {
const value = item[column.prop];
return value !== null && value !== undefined &&
String(value).toLowerCase().includes(keyword);
});
});
// 重置当前索引
currentIndex.value = filteredData.value.length > 0 ? 0 : -1;
if (currentIndex.value !== -1) {
currentRowKey.value = filteredData.value[0][valueField.value];
scrollToCurrentRow();
} else {
currentRowKey.value = '';
}
};
// 表格行点击
const handleRowClick = (row: any) => {
currentRowKey.value = row[valueField.value];
currentIndex.value = filteredData.value.findIndex(item => item[valueField.value] === row[valueField.value]);
};
// 表格行双击
const handleRowDblClick = (row: any) => {
selectRow(row);
};
// 选择行
const selectRow = (row: any) => {
if (!row) return;
inputValue.value = row[labelField.value];
emits('update:modelValue', row[valueField.value]);
emits('change', row[valueField.value]);
emits('select', row);
isPopoverVisible.value = false;
emits('visible-change',);
// 聚焦输入框
nextTick(() => {
inputRef.value?.focus();
});
};
// 处理输入框失焦
const handleInputBlur = () => {
emits('visible-change');
};
// 处理输入框键盘事件
const handleInputKeydown = (e: any) => {
// 按向下箭头显示下拉面板
if (e.key === 'ArrowDown' && isInputFocused.value) {
e.preventDefault();
handleDoubleClick();
}
};
// 处理搜索框键盘事件
const handleSearchKeydown = (e: any) => {
// 上下箭头控制表格选择
if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {
e.preventDefault();
handleArrowKey(e.key);
}
// 回车键选择
else if (e.key === 'Enter') {
e.preventDefault();
if (currentIndex.value !== -1 && filteredData.value[currentIndex.value]) {
selectRow(filteredData.value[currentIndex.value]);
}
}
// ESC键关闭
else if (e.key === 'Escape') {
isPopoverVisible.value = false;
emits('visible-change', false);
inputRef.value?.focus();
}
};
// 处理箭头键
const handleArrowKey = (direction: 'ArrowUp' | 'ArrowDown') => {
if (filteredData.value.length === 0) return;
if (direction === 'ArrowUp') {
currentIndex.value = Math.max(0, currentIndex.value - 1);
} else {
currentIndex.value = Math.min(filteredData.value.length - 1, currentIndex.value + 1);
}
const currentRow = filteredData.value[currentIndex.value];
currentRowKey.value = currentRow[valueField.value];
scrollToCurrentRow();
};
// 滚动到当前行
const scrollToCurrentRow = () => {
nextTick(() => {
const table = tableRef.value;
if (!table) return;
const rowEl = table.$el.querySelector(`.el-table__row[current-row-key="${currentRowKey.value}"]`);
const scrollContainer = table.$el.querySelector('.el-table__body-wrapper');
if (rowEl && scrollContainer) {
// 计算滚动位置,使当前行居中
const containerHeight = scrollContainer.clientHeight;
const rowHeight = rowEl.offsetHeight;
const rowTop = rowEl.offsetTop;
const scrollTop = rowTop - containerHeight / 2 + rowHeight / 2;
scrollContainer.scrollTo({
top: scrollTop,
behavior: 'smooth'
});
}
});
};
// 处理弹窗隐藏
const handlePopoverHide = () => {
searchKeyword.value = '';
handleSearch(); // 重置搜索
emits('visible-change', false);
};
</script>
<style scoped lang="scss">
.full-width-input .el-input__inner {
width: 100%;
height: 100%;
background: transparent !important;
border: none !important;
}
.full-width-input {
:deep(.el-input__wrapper) {
// box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
box-shadow: none;
background: transparent !important;
padding: 0 !important;
outline: none;
&:hover {
box-shadow: none !important;
}
}
}
.double-click-input-table {
position: relative;
width: 100%;
}
.search-input {
margin-bottom: 8px;
width: 100%;
}
.select-table {
width: 100%;
}
::v-deep .el-table__body-wrapper {
tabindex: 0;
outline: none;
}
::v-deep .el-table__row.current-row {
background-color: #e6f7ff !important;
}
</style>

View File

@ -10,31 +10,32 @@
* tableData[Required]: 表格数据对象 * tableData[Required]: 表格数据对象
* value: 双向绑定数据的值(为空时data属性为表格当前行数据对象) * value: 双向绑定数据的值(为空时data属性为表格当前行数据对象)
* label: 选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey) * label: 选择框显示的文本内容(为空时分别查看value和objKey是否为空,若value和objKey都有值,则value优先级大于objKey)
* objKey: 对象key(绑定值的唯一标识,绑定值为对象时必填) * objKey: 对象key(绑定值的唯一标识,绑定值为对象时必填) 数据回显对应值
* placeholder: 选择框占位文本 * placeholder: 选择框占位文本
* size: 组件大小 * size: 组件大小
* border: 表格是否带有边框 * border: 表格是否带有边框
--> -->
<template> <template>
<div> <div class="select-table-container">
<el-select ref="selectTable" v-model="selectShowValue" :placeholder="props.placeholder" :size="props.size" <el-select ref="selectTable" v-model="selectShowValue" :placeholder="props.placeholder" :size="props.size"
:style="{ width: props.width }" @visible-change="visibleChange" @clear="clearHandle" clearable> :style="{ width: props.width }" @visible-change="visibleChange" @clear="clearHandle" clearable>
<template #empty> <template #empty>
<div class="select-table-dropdown"> <div class="select-table-dropdown">
<div style="text-align: left;"> <div style="text-align: left;">
<el-input v-model="searchKey" ref="searchInput" size="small" placeholder="快速搜索(支持简拼)" class="mb10" clearable <el-input v-model="searchKey" ref="searchInput" size="small" placeholder="快速搜索(支持简拼)" class="mb10" clearable
@clear="filterDataHandle" @input="filterDataHandle" /> @clear="filterDataHandle" @input="filterDataHandle" @keydown="handleInputKeydown" />
</div> </div>
<!-- 数据为空时显示空状态提示 --> <!-- 数据为空时显示空状态提示 -->
<el-empty v-if="filterData.length === 0" description="没有匹配的数据" :image-size="100" /> <el-empty v-if="filterData.length === 0" description="没有匹配的数据" :image-size="100" />
<div @keydown="handleKeydown" v-else tabindex="0" style="outline: none;">
<!-- 数据存在时显示表格 --> <!-- 数据存在时显示表格 -->
<el-table ref="tableRef" v-else :data="filterData" :highlight-current-row="props.isHighlight" <el-table ref="tableRef" :data="filterData" :highlight-current-row="props.isHighlight" style="width: 100%"
style="width: 100%" :border="props.border" @row-click="handleRowChange" max-height="350px"> :border="props.border" @row-click="handleRowChange" max-height="350px" :row-style="{ height: '25px' }">
<el-table-column v-for="field in props.fields" :prop="field.prop" :label="field.label" :width="field.width" <el-table-column v-for="field in props.fields" :prop="field.prop" :label="field.label"
:show-overflow-tooltip="field.showTooltip" align="center" /> :width="field.width" show-overflow-tooltip align="center" />
</el-table> </el-table>
</div> </div>
</div>
</template> </template>
</el-select> </el-select>
</div> </div>
@ -49,8 +50,7 @@ import { ElEmpty } from 'element-plus';
interface Field { interface Field {
prop: string; prop: string;
label: string; label: string;
width: number; width?: number;
showTooltip?: boolean;
enablePinyinSearch?: boolean; //是否参与拼音搜索 enablePinyinSearch?: boolean; //是否参与拼音搜索
} }
@ -90,7 +90,7 @@ const searchInput = ref();
const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]); const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
// 存储预处理后的带拼音数据 // 存储预处理后的带拼音数据
const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]); const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const currentIndex = ref(-1);
// 预处理数据:生成拼音信息 // 预处理数据:生成拼音信息
const processTableData = (data: object[]) => { const processTableData = (data: object[]) => {
return data.map((item: any) => { return data.map((item: any) => {
@ -117,8 +117,15 @@ const processTableData = (data: object[]) => {
const visibleChange = (val: any) => { const visibleChange = (val: any) => {
searchKey.value = ''; searchKey.value = '';
if (val) { if (val) {
nextTick(() => { nextTick(() => {
searchInput.value.focus(); searchInput.value.focus();
currentIndex.value = filterData.value.findIndex(item => item[props.value!] === props.data);
if (currentIndex.value >= 0) {
tableRef.value.setCurrentRow(filterData.value[currentIndex.value]);
tableRef.value.setScrollTop(currentIndex.value * 25);
}
}); });
} }
filterDataHandle() filterDataHandle()
@ -137,15 +144,84 @@ watch(() => props.tableData, (newVal) => {
handleDataEcho(); handleDataEcho();
}, { deep: true }); }, { deep: true });
// 数据回显处理
const handleDataEcho = () => { // 监听绑定值数据变化,重新处理
if (props.data === null || props.data === undefined) return; watch(() => props.data, (newVal) => {
// 查找匹配的行数据 // 数据回显
const row: any = props.tableData.find((item: any) => props.objKey && item[props.objKey] === props.data) handleDataEcho();
selectShowValue.value = row ? (props.label ? row[props.label] : (props.value ? row[props.value] : '')) : ''; }, { deep: true });
// 输入框键盘事件处理(专门处理上下键)
const handleInputKeydown = (e: KeyboardEvent) => {
// 仅处理上下方向键
if (e.key !== "ArrowUp" && e.key !== "ArrowDown") return;
// 阻止事件冒泡到输入框父元素
e.stopPropagation();
// 如果有筛选数据,转移焦点到表格容器
if (filterData.value.length > 0) {
// 筛选后让表格容器获得焦点
nextTick(() => {
// 通过组件内部的 DOM 结构关系查找当前实例的表格容器 避免找不到table焦点
const dropdown = searchInput.value.$el.closest('.select-table-dropdown');
const tableContainer = dropdown?.querySelector('div[tabindex="0"]') as HTMLElement;
if (tableContainer) {
tableContainer.focus();
}
// 手动触发一次表格容器的键盘事件
const event = new KeyboardEvent('keydown', { key: e.key });
tableContainer?.dispatchEvent(event);
});
}
}; };
// 监听table键盘事件(核心)
const handleKeydown = (e: any) => {
// console.log('e==>', e);
// 仅处理方向键和回车键
if (!['ArrowUp', 'ArrowDown', 'Enter'].includes(e.key)) return;
// 阻止默认行为(如页面滚动)
e.preventDefault();
// 根据方向键更新“当前选中行索引”
const maxIndex = filterData.value.length - 1; // 末行索引
if (e.key === "ArrowUp") {
// 按↑:索引减1,最小为0(首行)
currentIndex.value = Math.max(0, currentIndex.value - 1);
} else if (e.key === "ArrowDown") {
// 按↓:索引加1,最大为 maxIndex(末行)
currentIndex.value = Math.min(maxIndex, currentIndex.value + 1);
}
tableRef.value.setCurrentRow(filterData.value[currentIndex.value]);
// 滚动到当前行
tableRef.value.setScrollTop(currentIndex.value * 25);
// 回车确认
if (e.key === "Enter") {
const currentRow = filterData.value[currentIndex.value];
handleRowChange(currentRow);
}
}
// 数据回显处理
const handleDataEcho = () => {
if (props.data === null || props.data === undefined) {
selectShowValue.value = '';
emits('update:data', null);
// emits('getDataValue', null);
} else {
// 查找匹配的行数据
const row: any = props.tableData.find((item: any) => props.objKey && item[props.objKey] === props.data)
selectShowValue.value = row ? (props.label ? row[props.label] : (props.value ? row[props.value] : '')) : '';
}
};
// 搜索过滤逻辑 // 搜索过滤逻辑
const filterDataHandle = () => { const filterDataHandle = () => {
@ -155,6 +231,8 @@ const filterDataHandle = () => {
// 空搜索时显示全部预处理数据 // 空搜索时显示全部预处理数据
filterData.value = [...processedTableData.value]; filterData.value = [...processedTableData.value];
return; return;
} else {
currentIndex.value = 0
} }
filterData.value = processedTableData.value.filter((item: any) => { filterData.value = processedTableData.value.filter((item: any) => {
@ -180,11 +258,16 @@ const filterDataHandle = () => {
return matchLabel || matchValue || matchLabelPinyin || matchValuePinyin || matchOtherField; return matchLabel || matchValue || matchLabelPinyin || matchValuePinyin || matchOtherField;
}); });
if (filterData.value.length > 0) {
tableRef.value.setScrollTop(0)
tableRef.value.setCurrentRow(filterData.value[0]);
}
}; };
const handleRowChange = (val: any) => { const handleRowChange = (val: any) => {
setLabel(val);
emits("update:data", props.value ? val[props.value] : val); emits("update:data", props.value ? val[props.value] : val);
setLabel(val);
}; };
const setLabel = (val: any) => { const setLabel = (val: any) => {
@ -194,7 +277,8 @@ const setLabel = (val: any) => {
selectShowValue.value = val[props.value]; selectShowValue.value = val[props.value];
} }
if (props.objKey) { if (props.objKey) {
emits('getDataValue', val[props.objKey]); // 返回值objKey对应的对象数据
emits('getDataValue', val);
} }
selectTable.value.blur(); selectTable.value.blur();
}; };
@ -207,9 +291,12 @@ const clearHandle = () => {
</script> </script>
<style scoped> <style scoped>
.select-table-container {
width: 100%;
}
.select-table-dropdown { .select-table-dropdown {
padding: 8px; padding: 8px;
} }
:deep(.el-table .el-table__cell) { :deep(.el-table .el-table__cell) {

View File

@ -0,0 +1,101 @@
<template>
<div>
<el-dialog :title="dialogTitle" v-model="csjgVisible" width="300px" :close-on-click-modal="false" :draggable="true">
<el-input v-model="searchKey" :placeholder="placeholder" size="small" class="mb10" clearable
@clear="filterDictData" @input="filterDictData" />
<el-table :data="filterData" max-height="300px" border @row-dblclick="selectItem" highlight-current-row
:row-style="{ helght: '25px' }">
<el-table-column prop="qz" label="取值" align="center" />
<el-table-column prop="pinyin" label="简拼" align="center" />
</el-table>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref, watch, computed, reactive, toRaw } from 'vue';
// @ts-ignore
import { getFirstLetter } from '@/api/pinyin.js';
interface Props {
tableData: object[];
label?: string;
dialogTitle?: string;
placeholder?: string;
}
const props = withDefaults(defineProps<Props>(), {
label: undefined,
dialogTitle: '选择数据',
placeholder: '支持简拼搜索'
});
const csjgVisible = ref(false)
const searchKey = ref('')
const filterData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
const filterDictData = () => {
const key = searchKey.value.trim().toLowerCase();
if (!key) {
// 空搜索时显示全部预处理数据
filterData.value = [...processedTableData.value];
return;
}
filterData.value = processedTableData.value.filter((item: any) => {
// 1. 原文本匹配
const matchLabel = props.label && item[props.label]?.toString().toLowerCase().includes(key);
return matchLabel;
});
}
interface Emits {
(e: "selectItem", val: any): void;
}
const emits = defineEmits<Emits>();
const selectItem = (row: any) => {
emits('selectItem', row);
csjgVisible.value = false;
}
// 存储预处理后的带拼音数据
const processedTableData = ref<{ [key: string]: any; pinyinMap: Record<string, string> }[]>([]);
// 初始化数据
onMounted(() => {
processedTableData.value = processTableData(props.tableData);
filterData.value = [...processedTableData.value];
});
// 监听表格数据变化,重新处理
watch(() => props.tableData, (newVal) => {
processedTableData.value = processTableData(newVal);
filterData.value = [...processedTableData.value];
// 数据回显
}, { deep: true });
// 预处理数据:生成拼音信息
const processTableData = (data: object[]) => {
return data.map((item: any) => {
const pinyinMap: Record<string, string> = {};
// 处理label字段拼音
if (props.label) {
pinyinMap['pinyin'] = getFirstLetter(item[props.label]).toLowerCase();
return { ...item, ...pinyinMap };
}
});
};
const open = () => {
csjgVisible.value = true;
};
defineExpose({
open,
});
</script>
<style scoped lang="scss">
:deep(.el-table .el-table__cell) {
padding: 0;
}
</style>

View File

@ -80,7 +80,7 @@ const props = defineProps({
// 纵向滚动配置 // 纵向滚动配置
scrollYConfig: { scrollYConfig: {
type: Object, type: Object,
default: () => ({ enabled: true, rSize: 50, adaptive: true }) default: () => ({ enabled: true, rSize: 30, adaptive: true })
}, },
// 表格尺寸 // 表格尺寸
size: { size: {

View File

@ -12,7 +12,7 @@
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<el-form-item label="打印状态:" prop="dybz"> <el-form-item label="打印状态:" prop="dybz">
<el-select v-model="queryParams.dybz" placeholder="请选择" clearable> <el-select v-model="queryParams.dybz" placeholder="请选择" clearable @change="handleQuery">
<el-option label="已打印" value="1" /> <el-option label="已打印" value="1" />
<el-option label="未打印" value="0" /> <el-option label="未打印" value="0" />
</el-select> </el-select>
@ -20,16 +20,16 @@
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<el-form-item label="科室:" prop="ksdh"> <el-form-item label="科室:" prop="ksdh">
<el-select v-model="queryParams.ksdh" placeholder="请选择" filterable clearable> <SelectTable v-model:data="queryParams.ksdh" :fields="ksdhFields" :tableData="dictData.DP || []"
<el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" /> label="label" value="value" objKey="value" :border="true" placeholder="请选择科室"
</el-select> @getDataValue="handleQuery" />
</el-form-item> </el-form-item>
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<el-form-item label="病人类型:" prop="brlyname"> <el-form-item label="病人类型:" prop="brlyname">
<el-select v-model="queryParams.brlyname" placeholder="请选择" clearable> <SelectTable v-model:data="queryParams.brlyname" :fields="brlxFields" :tableData="dictData.PT || []"
<el-option v-for="item in dictData.PT" :key="item.value" :label="item.label" :value="item.label" /> label="label" value="label" objKey="label" :border="true" placeholder="请选择科室"
</el-select> @getDataValue="handleQuery" />
</el-form-item> </el-form-item>
</el-col> </el-col>
@ -68,7 +68,6 @@
</el-form-item> </el-form-item>
</el-col> </el-col>
</el-row> </el-row>
</el-form> </el-form>
</el-row> </el-row>
</div> </div>
@ -112,8 +111,8 @@
<!-- <el-button type="primary">知识库</el-button> --> <!-- <el-button type="primary">知识库</el-button> -->
</div> </div>
<CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig" :loading="loading" <CustomTable :data="rightTableData" :columns="rightColumns" :config="rightTableConfig" @row-click="xmrowHandle"
@row-click="xmrowHandle" ref="rightTableRef"> ref="rightTableRef">
<template #jgbz="{ row }"> <template #jgbz="{ row }">
{{ formatJgbz(row.jgbz) }} {{ formatJgbz(row.jgbz) }}
</template> </template>
@ -121,7 +120,7 @@
<div class="mt10" v-if="bottomTableData.length > 0"></div> <div class="mt10" v-if="bottomTableData.length > 0"></div>
<CustomTable v-if="bottomTableData.length > 0" :data="bottomTableData" :columns="bottomColumns" <CustomTable v-if="bottomTableData.length > 0" :data="bottomTableData" :columns="bottomColumns"
:config="bottomTableConfig" :loading="loading"> :config="bottomTableConfig">
</CustomTable> </CustomTable>
</el-col> </el-col>
</el-row> </el-row>
@ -152,6 +151,7 @@ import CustomTable from '@/components/elTable/index.vue'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { reportList, reportPrintPdf, reportResult, reportMedList, reportFsresult } from '@/api/checkCode/index' import { reportList, reportPrintPdf, reportResult, reportMedList, reportFsresult } from '@/api/checkCode/index'
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import SelectTable from '@/components/SelectTable/index.vue';
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { classCom } from '@/utils/classCom' import { classCom } from '@/utils/classCom'
import { HiprintPrinter } from '@/utils/hiprintPrinter'; import { HiprintPrinter } from '@/utils/hiprintPrinter';
@ -215,7 +215,6 @@ queryParams.ksdh = (route.query.deptcode as string) || '';
queryParams.userid = (route.query.userid as string) || ''; queryParams.userid = (route.query.userid as string) || '';
queryParams.brdh = (route.query.brdh as string) || ''; queryParams.brdh = (route.query.brdh as string) || '';
console.log('queryParams==>', queryParams);
const single = ref(true); const single = ref(true);
const multiple = ref(true); const multiple = ref(true);
@ -248,7 +247,7 @@ const resetQuery = () => {
getList() getList()
} }
const loading = false; const loading = ref(false);
// 勾选框数据 // 勾选框数据
const rows = ref<any[]>([]) const rows = ref<any[]>([])
// 数据源 // 数据源
@ -363,7 +362,7 @@ const alarmHandle = (val: boolean) => {
} else { } else {
alarmflag.value = null alarmflag.value = null
} }
getList()
} }
const jzbzHandle = (val: boolean) => { const jzbzHandle = (val: boolean) => {
if (val) { if (val) {
@ -373,6 +372,7 @@ const jzbzHandle = (val: boolean) => {
} else { } else {
jzbz.value = null jzbz.value = null
} }
getList()
} }
@ -392,6 +392,17 @@ const updateColumns = (arr: any[]) => {
columns.value = arr columns.value = arr
}; };
const ksdhFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', enablePinyinSearch: true },
])
const brlxFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', enablePinyinSearch: true },
])
// 右侧数据源 // 右侧数据源
const rightTableData = ref([]) const rightTableData = ref([])
@ -781,11 +792,18 @@ const getList = () => {
} else { } else {
return ElMessage.error('日期时间段不能为空'); return ElMessage.error('日期时间段不能为空');
} }
loading.value = true
reportList(data).then((res: any) => { reportList(data).then((res: any) => {
if (res.code == 0) { if (res.code == 0) {
loading.value = false
tableData.value = res.data tableData.value = res.data
if (res.data.length > 0) {
tableRefs.value.setCurrentRow(res.data[0]) tableRefs.value.setCurrentRow(res.data[0])
rowHandle(res.data[0]) rowHandle(res.data[0])
} else {
rightTableData.value = []
bottomTableData.value = []
}
} }
}) })
} }

View File

@ -1,37 +1,11 @@
<template> <template>
<div> <div>
<!-- <SelectTable v-model:data="query" :fields="fields" :tableData="tableData" label="name" objKey="id" :border="true"
:width="'300px'" placeholder="请选择指定项" @getDataValue="getDataValue" /> -->
</div> </div>
</template> </template>
<script setup> <script setup>
import { useFingerprint } from '@/utils/useFingerprint'; import { useFingerprint } from '@/utils/useFingerprint';
import SelectTable from '@/components/SelectTable/index.vue';
const query = ref(6);
const fields = [
{ prop: 'name', label: '名称', width: 150, enablePinyinSearch: true },
{ prop: 'value', label: '值', width: 150, enablePinyinSearch: true },
{ prop: 'description', label: '描述', width: 300, showTooltip: true }
];
const tableData = [
{ id: 1, name: '指纹', value: 'fingerprint', description: '唯一标识用户的指纹信息' },
{ id: 2, name: '浏览器', value: 'browser', description: '用户使用的浏览器信息' },
{ id: 3, name: '操作系统', value: 'os', description: '用户使用的操作系统信息' },
{ id: 4, name: 'IP地址', value: 'ip', description: '用户的IP地址信息' },
{ id: 5, name: '设备信息', value: 'device', description: '用户使用的设备信息' },
{ id: 6, name: '地理位置', value: 'location', description: '用户使用的地理位置信息' },
{ id: 7, name: '浏览器语言', value: 'language', description: '用户使用的浏览器语言' },
{ id: 8, name: '屏幕分辨率', value: 'screen', description: '用户使用的屏幕分辨率信息' },
{ id: 9, name: '时区', value: 'timezone', description: '用户使用的时区信息' },
];
const getDataValue = (key) => {
console.log('SelectTable组件绑定对应值:', key);
}
// 使用自定义hook获取指纹信息 // 使用自定义hook获取指纹信息
const { const {

View File

@ -15,11 +15,12 @@
</el-input> --> </el-input> -->
<!-- 字典选择弹窗 --> <!-- 字典选择弹窗 -->
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="30vw" @close="handleDialogClose"> <el-dialog v-model="dialogVisible" :title="dialogTitle" width="30vw" :close-on-click-modal="false" :draggable="true"
@close="handleDialogClose">
<el-input v-model="searchKey" placeholder="搜索(支持名称、编码、简拼)..." class="mb10" clearable @clear="filterDictData" <el-input v-model="searchKey" placeholder="搜索(支持名称、编码、简拼)..." class="mb10" clearable @clear="filterDictData"
@input="filterDictData" /> @input="filterDictData" />
<el-table :data="filteredDictData" height="300px" border @row-click="selectItem"> <el-table :data="filteredDictData" height="300px" border @row-dblclick="selectItem" highlight-current-row>
<el-table-column prop="value" label="编码" align="center" width="150" /> <el-table-column prop="value" label="编码" align="center" width="150" />
<el-table-column prop="label" label="名称" align="center" show-overflow-tooltip /> <el-table-column prop="label" label="名称" align="center" show-overflow-tooltip />
<el-table-column prop="pinyin" label="简拼" align="center" width="100" /> <!-- 显示简拼便于调试 --> <el-table-column prop="pinyin" label="简拼" align="center" width="100" /> <!-- 显示简拼便于调试 -->

View File

@ -11,10 +11,9 @@
</div> </div>
<el-form-item label="病人来源"> <el-form-item label="病人来源">
<el-col :span="14"> <el-col :span="14">
<el-select v-model="labPat.brly" placeholder="请选择" style="width: 100%;" filterable size="small" <SelectTable v-model:data="labPat.brly" :fields="brlyFields" :tableData="dictData.PT || []" label="label"
@change="handleFieldChange"> value="value" objKey="value" :border="true" placeholder="请选择" size="small"
<el-option v-for="item in dictData.PT" :key="item.value" :label="item.label" :value="item.value" /> @getDataValue="handleFieldChange" />
</el-select>
</el-col> </el-col>
<el-col :span="10"> <el-col :span="10">
<el-switch v-model="labPat.jzbz" active-text="急诊" active-color="#FD0101" active-value="1" inactive-value="0" <el-switch v-model="labPat.jzbz" active-text="急诊" active-color="#FD0101" active-value="1" inactive-value="0"
@ -50,20 +49,28 @@
</el-row> </el-row>
</el-form-item> </el-form-item>
<el-form-item label="送检科室"> <el-form-item label="送检科室">
<el-select v-model="labPat.ksdh" placeholder="请选择" style="width: 100%;" filterable @change="handleFieldChange" <!-- <el-select v-model="labPat.ksdh" placeholder="请选择" style="width: 100%;" filterable @change="handleFieldChange"
size="small"> size="small">
<el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in dictData.DP" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select> -->
<SelectTable v-model:data="labPat.ksdh" :fields="ksdhFields" :tableData="dictData.DP || []" label="label"
value="value" objKey="value" :border="true" placeholder="请选择" size="small"
@getDataValue="handleFieldChange" />
</el-form-item> </el-form-item>
<el-form-item label="床 号"> <el-form-item label="床 号">
<el-input v-model="labPat.ch" placeholder="请输入床号" @blur="handleFieldChange" @keyup.enter="handleFieldChange" <el-input v-model="labPat.ch" placeholder="请输入床号" @blur="handleFieldChange" @keyup.enter="handleFieldChange"
size="small" /> size="small" />
</el-form-item> </el-form-item>
<el-form-item label="样本类型"> <el-form-item label="样本类型">
<el-select v-model="labPat.yblx" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable <!-- <el-select v-model="labPat.yblx" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable
size="small"> size="small">
<el-option v-for="item in dictData.BT" :key="item.value" :label="item.label" :value="item.value" /> <el-option v-for="item in dictData.BT" :key="item.value" :label="item.label" :value="item.value" />
</el-select> </el-select> -->
<SelectTable v-model:data="labPat.ksdh" :fields="ksdhFields" :tableData="dictData.DP || []" label="label"
value="value" objKey="value" :border="true" placeholder="请选择" size="small"
@getDataValue="handleFieldChange" />
</el-form-item> </el-form-item>
<el-form-item label="申请医生"> <el-form-item label="申请医生">
<el-select v-model="labPat.sjys" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable <el-select v-model="labPat.sjys" placeholder="请选择" style="width: 100%;" @change="handleFieldChange" filterable
@ -127,9 +134,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, reactive, watch, toRaw, computed, onMounted } from 'vue'; import { ref, reactive, watch, toRaw, computed, onMounted } from 'vue';
import { changepatcolumn, updateLabPat } from "@/api/liswork/work/LisWork"; import { changepatcolumn, updateLabPat } from "@/api/liswork/work/LisWork";
//@ts-ignore import SelectTable from '@/components/SelectTable/index.vue';
import { comDict } from '@/utils/dict'
const loading = ref(true);
const props = defineProps({ const props = defineProps({
labPat: { labPat: {
type: Object, type: Object,
@ -153,6 +159,16 @@ watch(() => props.labPat, (newVal) => {
const emit = defineEmits(['update:labPat']); const emit = defineEmits(['update:labPat']);
const lastValue = ref(''); const lastValue = ref('');
const brlyFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', width: 120, enablePinyinSearch: true },
])
const ksdhFields = ref([
{ prop: 'value', label: '代号', width: 80, enablePinyinSearch: true },
{ prop: 'label', label: '名称', width: 150, enablePinyinSearch: true },
])
/** /**
* 通用字段变化处理(失焦、回车触发) * 通用字段变化处理(失焦、回车触发)
*/ */
@ -269,32 +285,6 @@ const resetMain = () => {
lastValue.value = JSON.stringify(resetData); lastValue.value = JSON.stringify(resetData);
}; };
// interface DictData {
// PT?: Array<any>;
// AU?: Array<any>;
// SX?: Array<any>;
// DP?: Array<any>;
// BT?: Array<any>;
// SRD?: Array<any>;
// [key: string]: any[] | undefined; // 添加索引签名以支持动态访问
// }
// // 字典数据存储
// const dictData = ref<DictData>({});
// onMounted(async () => {
// // 加载病人来源字典
// const dictRefs = await comDict('PT', 'AU', 'SX', 'DP', 'BT', 'SRD');
// // 从 ref 中获取实际数据
// dictData.value = {
// PT: toRaw(dictRefs.PT.value) || [],
// AU: toRaw(dictRefs.AU.value) || [],
// SX: toRaw(dictRefs.SX.value) || [],
// DP: toRaw(dictRefs.DP.value) || [],
// BT: toRaw(dictRefs.BT.value) || [],
// SRD: toRaw(dictRefs.SRD.value) || [],
// };
// });
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -23,6 +23,9 @@
<template #nldw="{ row }"> <template #nldw="{ row }">
{{ formatDict(row.nldw, 'AU') }} {{ formatDict(row.nldw, 'AU') }}
</template> </template>
<template #ksdh="{ row }">
{{ formatDict(row.ksdh, 'DP') }}
</template>
<template #autojgbz="{ row }"> <template #autojgbz="{ row }">
<el-checkbox :model-value="row.autojgbz === '1'" disabled /> <el-checkbox :model-value="row.autojgbz === '1'" disabled />
@ -66,26 +69,26 @@ const emits = defineEmits(['select'])
// 表格列配置 // 表格列配置
const tableColumns = ref([ const tableColumns = ref([
{ field: 'finish', title: '完成', width: 60, align: 'center', slotName: 'finish' }, { field: 'finish', title: '完成', width: 50, align: 'center', slotName: 'finish' },
{ field: 'jgbz', title: '审核', width: 60, align: 'center', slotName: 'jgbz' }, { field: 'jgbz', title: '审核', width: 50, align: 'center', slotName: 'jgbz' },
{ field: 'alarmflag', title: '报警', width: 60, align: 'center', slotName: 'alarmflag' }, { field: 'alarmflag', title: '报警', width: 50, align: 'center', slotName: 'alarmflag' },
{ field: 'brly', title: '类型', width: 60, align: 'center', slotName: 'brly' }, { field: 'brly', title: '类型', width: 50, align: 'center', slotName: 'brly' },
{ field: 'autojgbz', title: '自审', width: 60, align: 'center', slotName: 'autojgbz' }, { field: 'autojgbz', title: '自审', width: 50, align: 'center', slotName: 'autojgbz' },
{ field: 'ybh', title: '样本号', width: 100, align: 'center' }, { field: 'ybh', title: '样本号', width: 80, align: 'center' },
{ field: 'brxm', title: '病人姓名', width: 100, align: 'center' }, { field: 'brxm', title: '病人姓名', width: 80, align: 'center' },
{ field: 'brdh', title: '病历号', width: 120, align: 'center' }, { field: 'brdh', title: '病历号', width: 120, align: 'center' },
{ field: 'ch', title: '床号', width: 60, align: 'center' }, { field: 'ch', title: '床号', width: 50, align: 'center' },
{ field: 'brxb', title: '病人性别', width: 100, align: 'center', slotName: 'brxb' }, { field: 'brxb', title: '病人性别', width: 80, align: 'center', slotName: 'brxb' },
{ field: 'nl', title: '年', width: 60, align: 'center' }, { field: 'nl', title: '年', width: 50, align: 'center' },
{ field: 'nldw', title: '龄', width: 60, align: 'center', slotName: 'nldw' }, { field: 'nldw', title: '龄', width: 50, align: 'center', slotName: 'nldw' },
{ field: 'ksdh', title: '科室', width: 60, align: 'center' }, { field: 'ksdh', title: '科室', width: 100, align: 'center', slotName: 'ksdh' },
{ field: 'yblx', title: '标本', width: 60, align: 'center' }, { field: 'yblx', title: '标本', width: 50, align: 'center' },
{ field: 'dybz', title: '打印', width: 60, align: 'center', slotName: 'dybz' }, { field: 'dybz', title: '打印', width: 50, align: 'center', slotName: 'dybz' },
{ field: 'fslx', title: '发送', width: 60, align: 'center', slotName: 'fslx' }, { field: 'fslx', title: '发送', width: 50, align: 'center', slotName: 'fslx' },
{ field: 'shcs', title: '次数', width: 60, align: 'center' }, { field: 'shcs', title: '次数', width: 50, align: 'center' },
{ field: 'sqh', title: '申请号/条码', width: 150, align: 'center' }, { field: 'sqh', title: '申请号/条码', width: 150, align: 'center' },
{ field: 'jymd', title: '检验目的', width: 120, align: 'center' }, { field: 'jymd', title: '检验目的', width: 120, align: 'center' },
{ field: 'yhdh', title: '检验医生', width: 120, align: 'center', }, { field: 'yhdh', title: '检验医生', width: 80, align: 'center', },
{ field: 'yljg', title: '送检医院', width: 120, align: 'center', slotName: 'yljg' } { field: 'yljg', title: '送检医院', width: 120, align: 'center', slotName: 'yljg' }
]) ])

View File

@ -16,24 +16,22 @@
</div> </div>
</div> </div>
<el-table :data="tableData" border ref="tableRef" highlight-current-row class="custom-table-container" height="100%" <el-table :data="tableData" border ref="tableRef" highlight-current-row class="custom-table-container" height="100%"
@row-click="handleRowClick"> @row-click="handleRowClick" :cell-style="cellStyle">
<el-table-column type="index" prop="id" label="" width="45" :index="getRowIndex"></el-table-column> <el-table-column type="index" prop="id" label="" width="45" :index="getRowIndex"></el-table-column>
<!-- <el-table-column type="selection" width="40" align="center"></el-table-column> -->
<el-table-column prop="xmdh" label="项目" show-overflow-tooltip> <el-table-column prop="xmdh" label="项目" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="xmmc" label="名称" show-overflow-tooltip> <el-table-column prop="xmmc" label="名称" align="center" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="csjg" label="结果"> <el-table-column prop="csjg" label="结果" align="center">
<template #default="scope"> <template #default="scope">
<el-input v-if="labPat.jgbz == 0 || labPat.jgbz == null" v-model="scope.row.csjg" size="small" <el-input v-model="scope.row.csjg" v-if="labPat.jgbz == 0 || labPat.jgbz == null" size="small"
class="full-width-input" :disabled="readonly" @blur="handleCsjgEnter(scope.row)" class="full-width-input" @change="handleCsjgEnter(scope.row)" @dblclick="handleInputFocus(scope.row)" />
@keyup.enter="handleCsjgEnter(scope.row)"></el-input>
<span v-else>{{ scope.row.csjg }}</span> <span v-else>{{ scope.row.csjg }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="refs" label="参考值" show-overflow-tooltip> <el-table-column prop="refs" label="参考值" align="center" show-overflow-tooltip>
</el-table-column> </el-table-column>
<el-table-column prop="dw" label="单位" show-overflow-tooltip> <el-table-column prop="dw" label="单位" align="center" show-overflow-tooltip>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -46,6 +44,9 @@
<div> <div>
</div> </div>
</div> --> </div> -->
<!-- csjg弹窗 -->
<projectsJg ref="csjgRef" :tableData='csjgTableData' :dialog-title="'项目常用结果选择'" @selectItem="selectItem"
label="qz" />
<!-- 新增明细 --> <!-- 新增明细 -->
<ItemInput ref="itemDictRef" v-model="selectedItemValue" :label-value.sync="selectedItemLabel" <ItemInput ref="itemDictRef" v-model="selectedItemValue" :label-value.sync="selectedItemLabel"
@ -135,19 +136,23 @@
<script setup> <script setup>
import { ref, toRefs, watch, nextTick, onMounted } from "vue"; import { ref, toRefs, watch, nextTick, onMounted } from "vue";
import { check1, check2, uncheck2, unconfirmlog, checkuser, reglimit, queryXmInfo, deleteresult, changeresult, newresult, printsample, printListwork } from "@/api/liswork/work/LisWork"; import { check1, check2, uncheck2, unconfirmlog, checkuser, reglimit, queryXmInfo, deleteresult, changeresult, newresult, queryXmVal, printListwork } from "@/api/liswork/work/LisWork";
import { ElMessageBox, ElMessage } from "element-plus"; import { ElMessageBox, ElMessage } from "element-plus";
import ItemInput from "@/views/liswork/work/components/ItemInput.vue"; import ItemInput from "@/views/liswork/work/components/ItemInput.vue";
import projectsJg from "@/components/projectsjg/index.vue"
import { classCom } from '@/utils/classCom' import { classCom } from '@/utils/classCom'
// 组件属性定义 // 组件属性定义
const props = defineProps({ const props = defineProps({
tableData: { type: Array, default: () => [] }, tableData: { type: Array, default: () => [] },
tableKey: { type: Object, default: () => { } }, tableKey: {
readonly: { type: Boolean, default: false }, type: Object, default: () => { }
hasChanges: { type: Boolean, default: false }, },
labPat: { type: Object, required: true }, labPat: { type: Object, required: true },
}); });
// 解构props
const { tableData, tableKey, } = toRefs(props);
const selectedRows = ref([]);
watch(() => props.tableData, (newVal) => { watch(() => props.tableData, (newVal) => {
selectedRows.value = []; selectedRows.value = [];
@ -198,7 +203,7 @@ const rules = ref(
} }
); );
const csjgRef = ref();
const itemDictRef = ref(); // DictInput组件引用 const itemDictRef = ref(); // DictInput组件引用
const selectedItemValue = ref(""); // 选择的项目编码xmdh const selectedItemValue = ref(""); // 选择的项目编码xmdh
@ -289,6 +294,25 @@ const handleCheck = (checkType) => {
} }
}; };
const cellStyle = ({ row, column, rowIndex, columnIndex }) => {
if (column.label == "结果" && (row.alarm_flag ?? "").trim().length > 0) {
return { background: '#f00 !important', color: '#FFF' };
}
if (column.label == "结果" && row.jgbz == "H") {
return { background: '#ffc0c0 !important', color: '#606266' };
}
if (column.label == "结果" && row.jgbz == "L") {
return { background: '#8080ff !important', color: '#fff' };
}
if (column.label == "结果" && row.jgbz == "P") {
return { background: '#ffc0c0 !important', color: '#606266' };
}
if (column.label == "结果" && row.jgbz == "Q") {
return { background: '#ffff80 !important', color: '#606266' };
}
};
const shHandleCheck = () => { const shHandleCheck = () => {
check2({ ...props.tableKey, yhdh: 'admin' }).then(res => { check2({ ...props.tableKey, yhdh: 'admin' }).then(res => {
emits("changeStatus", props.labPat); emits("changeStatus", props.labPat);
@ -352,8 +376,6 @@ const unconfirmlogConfirm = async () => {
if (!valid) return false; if (!valid) return false;
} }
tableKey.value.problemId = 0
unconfirmlog({ ...tableKey.value, ...uncheckForm.value }).then(response => { unconfirmlog({ ...tableKey.value, ...uncheckForm.value }).then(response => {
if (response.code == "0") { if (response.code == "0") {
unCheckshow.value = false; unCheckshow.value = false;
@ -431,82 +453,41 @@ const emits = defineEmits([
'previewHandle' 'previewHandle'
]); ]);
// 解构props const csjgTableData = ref([]);
const { tableData, tableKey, readonly, hasChanges } = toRefs(props); // const dbRow = ref({});
const selectedRows = ref([]); const handleInputFocus = (row) => {
const filteredList = ref([]); // dbRow.value = row
const originalData = ref(JSON.parse(JSON.stringify(props.tableData))); queryXmVal({ yq: tableKey.value.yq, xmdh: row.xmdh }).then(res => {
const isEnterHandled = ref(false); if (res.code == 0) {
const originalCsjgMap = ref({}); csjgTableData.value = res.data
}
// 组件挂载时初始化缓存 nextTick(() => {
onMounted(() => { csjgRef.value.open()
updateOriginalCsjgMap(); })
}); })
/**
* 更新csjg原始值缓存
*/
const updateOriginalCsjgMap = () => {
const newMap = {};
props.tableData.forEach((row) => {
const uniqueKey = getRowUniqueKey(row);
let originalCsjg = row.csjg;
originalCsjg = originalCsjg == null ? "" : String(originalCsjg).trim();
newMap[uniqueKey] = originalCsjg;
});
originalCsjgMap.value = newMap;
}; };
/** const selectItem = (row) => {
* 获取行的唯一标识 // 查找修改行
*/ const index = tableData.value.findIndex(item => item.xmdh == row.xmdh)
const getRowUniqueKey = (row) => { tableData.value[index].csjg = row.qz
return row.xmdh; handleCsjgEnter(tableData.value[index])
}; };
/** /**
* csjg输入框回车触发保存 * csjg输入框回车触发保存
*/ */
const handleCsjgEnter = async (row) => { const handleCsjgEnter = async (row) => {
// 获取当前输入值
const currentCsjg = row.csjg ? String(row.csjg).trim() : "";
// 获取原始缓存值
const uniqueKey = getRowUniqueKey(row);
const originalCsjg = originalCsjgMap.value[uniqueKey] || "";
// 判断值是否改变
if (currentCsjg === originalCsjg) return;
// 值已改变,执行保存逻辑
await saveCsjgChange(row);
nextTick(() => {
const input = document.activeElement;
if (input.tagName === "INPUT") input.blur();
});
};
/**
* 核心:保存csjg修改(调用API + 失败回滚)
*/
const saveCsjgChange = async (row) => {
const requestParams = {
...row,
};
if (row.id) { if (row.id) {
newresult(requestParams).then(response => { newresult(row).then(response => {
ElMessage.success("结果保存成功"); ElMessage.success("结果保存成功");
emits('fetchLabResults'); emits('fetchLabResults');
}).catch(error => { }).catch(error => {
emits('fetchLabResults'); emits('fetchLabResults');
}); });
} else { } else {
changeresult(requestParams).then(response => { changeresult(row).then(response => {
ElMessage.success("结果保存成功"); ElMessage.success("结果保存成功");
emits('fetchLabResults'); emits('fetchLabResults');
}).catch(error => { }).catch(error => {
@ -514,6 +495,10 @@ const saveCsjgChange = async (row) => {
}); });
} }
nextTick(() => {
const input = document.activeElement;
if (input.tagName === "INPUT") input.blur();
});
}; };
/** /**
@ -549,8 +534,6 @@ const handleAdd = () => {
if (inputs.length > 0) { if (inputs.length > 0) {
inputs[inputs.length - 1].focus(); inputs[inputs.length - 1].focus();
} }
updateOriginalCsjgMap();
}); });
}; };
// 新增行核心方法:接收父组件传递的项目数据 // 新增行核心方法:接收父组件传递的项目数据
@ -576,13 +559,13 @@ const addRowFromDict = async (rowData) => {
const newRow = { ...rowData, csjg: "" }; const newRow = { ...rowData, csjg: "" };
// 4. 添加到表格数据 // 4. 添加到表格数据
tableData.value.unshift(newRow); tableData.value.push(newRow);
console.log("子组件新增行:", newRow); console.log("子组件新增行:", newRow);
// 4. 同步缓存 // // 4. 同步缓存
await nextTick(); // await nextTick();
updateOriginalCsjgMap(); // updateOriginalCsjgMap();
// 5. 自动聚焦 // 5. 自动聚焦
@ -590,10 +573,21 @@ const addRowFromDict = async (rowData) => {
selectedRows.value = [newRow]; selectedRows.value = [newRow];
tableRef.value.setCurrentRow(newRow); tableRef.value.setCurrentRow(newRow);
nextTick(() => { nextTick(() => {
tableRef.value.setScrollTop(30 * tableRef.value.length) tableRef.value.setScrollTop(23 * tableRef.value.length)
}) })
// const lastInput = document.querySelector(`.el-table__row:last-child .csjg-input .el-input__inner`); nextTick(() => {
// if (lastInput) lastInput.focus(); const tableBody = document.querySelector('.el-table__body-wrapper');
if (tableBody) {
tableBody.scrollTop = tableBody.scrollHeight;
}
// 聚焦到新行的输入框
const inputs = document.querySelectorAll('.full-width-input .el-input__inner');
if (inputs.length > 0) {
inputs[inputs.length - 1].focus();
}
});
}); });
return true; return true;
@ -601,16 +595,7 @@ const addRowFromDict = async (rowData) => {
/**
* 保存数据
*/
const handleSave = () => {
if (hasChanges.value) {
emits('save', props.tableData);
updateOriginalCsjgMap();
emits("update:hasChanges", false);
}
};
@ -683,14 +668,14 @@ const getRowIndex = (index) => {
onMounted(() => { onMounted(() => {
if (props.tableData.length > 0) { if (props.tableData.length > 0) {
updateOriginalCsjgMap(); // updateOriginalCsjgMap();
} }
}); });
watch( watch(
() => props.tableData, () => props.tableData,
() => { () => {
updateOriginalCsjgMap(); // updateOriginalCsjgMap();
} }
); );
@ -720,18 +705,23 @@ watch(
.full-width-input .el-input__inner { .full-width-input .el-input__inner {
width: 100%; width: 100%;
height: calc(var(--table-row-height) - 2px); height: 100%;
padding: 0 8px;
/* 增加内边距提升输入体验 */ background: transparent !important;
border: 0px solid #dcdfe6; border: none !important;
border-radius: 0;
box-sizing: border-box;
background-color: transparent;
} }
.full-width-input .el-input__inner:focus { .full-width-input {
border-color: #409eff; :deep(.el-input__wrapper) {
box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2); // box-shadow: 0 0 0 1px rgba(64, 158, 255, 0.2);
box-shadow: none;
background: transparent !important;
padding: 0 !important;
outline: none; outline: none;
&:hover {
box-shadow: none !important;
}
}
} }
</style> </style>

View File

@ -14,23 +14,14 @@
style="width:140px"></el-date-picker> style="width:140px"></el-date-picker>
</el-form-item> </el-form-item>
<el-form-item label="部门:" prop="instrGroup"> <el-form-item label="部门:" prop="instrGroup">
<!-- <el-select v-model="queryParams.instrGroup" placeholder="请选择" style="width:140px" filterable
@change="handleinstrGroupChange">
<el-option v-for="item in instrGroupOptions" :key="item.zddh" :label="item.zdmc" :value="item.zddh" />
</el-select> -->
<SelectTable v-model:data="queryParams.instrGroup" :fields="instrGroupFields" <SelectTable v-model:data="queryParams.instrGroup" :fields="instrGroupFields"
:tableData="instrGroupOptions" label="zdmc" objKey="zddh" :border="true" width="140px" :tableData="instrGroupOptions" label="zdmc" value="zddh" objKey="zddh" :border="true" width="140px"
placeholder="请选择部门" @getDataValue="handleinstrGroupChange" /> placeholder="请选择部门" @getDataValue="handleinstrGroupChange" />
</el-form-item> </el-form-item>
<el-form-item label="仪器:" prop="yq"> <el-form-item label="仪器:" prop="yq">
<!-- <el-select v-model="queryParams.yq" placeholder="请选择" style="width:140px" @change="yqHandleChange"
filterable>
<el-option v-for="item in instrOptions" :key="item.value" :label="item.label" :value="item.value" />
</el-select> -->
<SelectTable v-model:data="queryParams.yq" :fields="yqFields" :tableData="instrOptions" label="yqmc" <SelectTable v-model:data="queryParams.yq" :fields="yqFields" :tableData="instrOptions" label="yqmc"
objKey="yq" :border="true" width="140px" placeholder="请选择仪器" @getDataValue="yqHandleChange" /> value="yq" objKey="yq" :border="true" width="140px" placeholder="请选择仪器"
@getDataValue="yqHandleChange" />
</el-form-item> </el-form-item>
<el-form-item> <el-form-item>
<el-icon color="#3c80d9" size="20" style="margin-right: 10px;" class="cp"> <el-icon color="#3c80d9" size="20" style="margin-right: 10px;" class="cp">
@ -43,10 +34,10 @@
</el-form> </el-form>
<el-row :gutter="5"> <el-row :gutter="5">
<el-col :span="8"> <el-col :span="8">
<LabPat ref="labPatRef" v-model:labPat="labPat" :labPatKey="labPat" :dictData="dictData" /> <LabPat ref="labPatRef" v-model:labPat="labPat" :labPatKey="queryParams" :dictData="dictData" />
</el-col> </el-col>
<el-col :span="16"> <el-col :span="16">
<LabResult ref="labResultRef" v-model:labPat="labPat" :tableData="labResuts" :tableKey="labPat" <LabResult ref="labResultRef" v-model:labPat="labPat" :tableData="labResuts" :tableKey="queryParams"
@fetchLabResults="fetchLabResults" @changeStatus="changeStatus" @previewHandle="previewHandle" /> @fetchLabResults="fetchLabResults" @changeStatus="changeStatus" @previewHandle="previewHandle" />
</el-col> </el-col>
</el-row> </el-row>
@ -187,7 +178,7 @@ const labPat = ref<any>(
yhdh: '', yhdh: '',
hdys: '', hdys: '',
jgbz: '0', jgbz: '0',
yblx: '' yblx: '',
}, },
) )
const labPatList = ref<Array<{ ybh: string; jgbz: string }>>([]); const labPatList = ref<Array<{ ybh: string; jgbz: string }>>([]);
@ -204,17 +195,14 @@ const previewHandle = (url: string) => {
pdfUrl.value = `data:application/pdf;base64,${url}` pdfUrl.value = `data:application/pdf;base64,${url}`
previewShow.value = true previewShow.value = true
} }
const handleinstrGroupChange = (val: string) => { const handleinstrGroupChange = (val: any) => {
queryParams.value.instrGroup = val
queryParams.value.yq = '' queryParams.value.yq = ''
labResuts.value = [] // labResuts.value = []
labPat.value = {} // labPat.value = {}
getYqConfig() getYqConfig()
// fetchlabPatList()
} }
const yqHandleChange = (val: string) => { const yqHandleChange = (val: any) => {
queryParams.value.yq = val
labResuts.value = [] labResuts.value = []
labPat.value = {} labPat.value = {}
fetchlabPatList() fetchlabPatList()
@ -268,7 +256,6 @@ const labResuts = ref([])
//载入样本结果表单(中间labresult.vue组件) //载入样本结果表单(中间labresult.vue组件)
const fetchLabResults = async () => { const fetchLabResults = async () => {
queryLabResults({ jyrq: queryParams.value.jyrq, yq: queryParams.value.yq, ybh: queryParams.value.ybh }).then((response: any) => { queryLabResults({ jyrq: queryParams.value.jyrq, yq: queryParams.value.yq, ybh: queryParams.value.ybh }).then((response: any) => {
// console.log('RESULT:', response);
labResuts.value = response.data; labResuts.value = response.data;
}) })
} }