门诊采血模块更新

This commit is contained in:
jiangs 2025-05-20 16:34:38 +08:00 committed by tangw
parent 57dd3c04f8
commit 9081b17071
19 changed files with 1887 additions and 4280 deletions

31
eslint.config.js Normal file
View File

@ -0,0 +1,31 @@
import js from "@eslint/js";
import globals from "globals";
import pluginVue from "eslint-plugin-vue";
import { defineConfig } from "eslint/config";
export default defineConfig([
{ files: ["**/*.{js,mjs,cjs,vue}"], plugins: { js }, extends: ["js/recommended"] },
{ files: ["**/*.{js,mjs,cjs,vue}"], languageOptions: { globals: globals.browser } },
pluginVue.configs["flat/essential"],
{
plugins: {
'auto-import': {
extensions: ['.js', '.mjs', '.jsx', '.vue'],
},
},
},
{
rules: {
'auto-import/export': 'error',
},
},
{
settings: {
'auto-import': {
// 自动导入的模块配置,与 unplugin-auto-import 配置一致
imports: ['vue'],
},
},
},
]);

View File

@ -8,6 +8,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="icon" href="/favicon.ico">
<title>实验室检验系统</title>
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
<style>
html,

View File

@ -1,7 +1,7 @@
{
"name": "ruoyi",
"version": "3.8.7",
"description": "工具管理系统",
"name": "czlis",
"version": "8.0.0",
"description": "实验室管理系统",
"author": "工具",
"license": "MIT",
"type": "module",
@ -11,13 +11,9 @@
"build:stage": "vite build --mode staging",
"preview": "vite preview"
},
"repository": {
"type": "git",
"url": "https://gitee.com/y_project/RuoYi-Vue.git"
},
"dependencies": {
"@element-plus/icons-vue": "2.3.1",
"@vueup/vue-quill": "1.2.0",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "10.6.1",
"axios": "0.27.2",
"clipboard": "^2.0.11",
@ -25,24 +21,33 @@
"element-plus": "2.4.3",
"file-saver": "2.0.5",
"fuse.js": "6.6.2",
"jquery": "^3.7.1",
"js-beautify": "^1.14.11",
"js-cookie": "3.0.5",
"jsencrypt": "3.3.2",
"nprogress": "0.2.0",
"pinia": "2.1.7",
"pinyin-pro": "^3.26.0",
"select2": "^4.1.0-rc.0",
"vue": "3.4.0",
"vue-cropper": "1.1.1",
"vue-router": "4.2.5",
"vue3-select2-component": "^0.1.7",
"vuedraggable": "^4.1.0"
},
"devDependencies": {
"@eslint/js": "^9.27.0",
"@vitejs/plugin-vue": "5.0.1",
"@vue/compiler-sfc": "3.4.0",
"eslint": "^9.27.0",
"eslint-plugin-auto-import": "^0.1.1",
"eslint-plugin-vue": "^10.1.0",
"globals": "^16.1.0",
"sass": "1.69.5",
"unplugin-auto-import": "0.17.1",
"unplugin-vue-setup-extend-plus": "1.0.0",
"vite": "5.0.4",
"vite-plugin-compression": "0.5.1",
"vite-plugin-svg-icons": "2.0.1",
"unplugin-vue-setup-extend-plus": "1.0.0"
"vite-plugin-svg-icons": "2.0.1"
}
}

4247
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,6 @@ export function queryComDictListService(query) {
params: query
})
}
//增加字典数据
export function addComDictService(data) {
return request({
@ -16,4 +15,21 @@ export function addComDictService(data) {
method: 'post',
data
})
}
}
//修改字典数据
export function updateComDictService(data) {
return request({
url: '/comdict/update',
method: 'post',
data
})
}
// 根据字典类型查询字典数据信息
export function getComDicts(zdlb) {
return request({
url: '/comdict/getComDicts',
method: 'get',
params: zdlb
})
}

View File

@ -0,0 +1,18 @@
import request from '@/utils/request'
// 查询字典数据列表
export function querylabInstrList(query) {
return request({
url: '/instr/query',
method: 'get',
params: query
})
}
// 查询字典数据列表
export function querylabInstrListByLisgroup(query) {
return request({
url: '/instr/querybylisgroup',
method: 'get',
params: query
})
}

View File

@ -0,0 +1,59 @@
import request from '@/utils/request'
// 查询数据列表
export function queryLabPatList(query) {
return request({
url: '/liswork/samplelist',
method: 'get',
params: query
})
}
// 查询数据
export function queryLabPat(query) {
return request({
url: '/liswork/sampleinfo',
method: 'get',
params: query
})
}
// 查询服务器时间
export function getDate() {
return request({
url: '/liswork/getdate',
method: 'get',
})
}
//增加字典数据
export function addLabPatService(data) {
return request({
url: '/liswork/add',
method: 'post',
data
})
}
//修改字典数据
export function updateLabPatService(data) {
return request({
url: '/liswork/update',
method: 'post',
data
})
}
// 查询数据列表
export function queryLabResults(query) {
return request({
url: '/liswork/resultinfo',
method: 'get',
params: query
})
}
// 批量删除结果
export function deleteresult(data) {
return request({
url: '/liswork/deleteresult',
method: 'post',
data: data
})
}

43
src/api/pinyin.js Normal file
View File

@ -0,0 +1,43 @@
// pinyin.js - 拼音转换工具
// src/utils/pinyin-utils.js
import { pinyin } from 'pinyin-pro';
/**
* 获取文本的拼音首字母
* @param {string} text - 要转换的文本
* @returns {string} - 拼音首字母字符串
*/
export function getFirstLetter(text) {
if (!text) return '';
// 获取拼音(不带音调,数组形式)
const pinyinArray = pinyin(text, { toneType: "none", type: "array" });
// 提取每个拼音的首字母并连接
return pinyinArray.map(word => word.charAt(0)).join('');
}
/**
* 获取文本的拼音全拼
* @param {string} text - 要转换的文本
* @returns {string} - 拼音全拼字符串
*/
export function getFullPinyin(text) {
if (!text) return '';
// 获取拼音(不带音调,字符串形式)
return pinyin(text, { toneType: "none" }).replace(/\s+/g, '');
}
/**
* 处理选项数据,添加拼音和首字母属性
* @param {Array} options - 原始选项数组,每个元素包含id和text属性
* @returns {Array} - 处理后的选项数组,每个元素包含pinyin和firstLetters属性
*/
export function processOptions(options) {
return options.map(option => ({
...option,
pinyin: getFullPinyin(option.text),
firstLetters: getFirstLetter(option.text)
}));
}

View File

@ -7,6 +7,11 @@ import 'element-plus/dist/index.css'
import locale from 'element-plus/es/locale/lang/zh-cn'
import '@/assets/styles/index.scss' // global css
// 先导入 jQuery 并设置全局变量
import * as $ from 'jquery';
window.jQuery = $;
window.$ = $;
import App from './App'
import store from './store'
@ -24,7 +29,7 @@ import elementIcons from '@/components/SvgIcon/svgicon'
import './permission' // permission control
import { useDict } from '@/utils/dict'
import {useDict, comDict} from '@/utils/dict'
import { parseTime, resetForm, addDateRange, handleTree, selectDictLabel, selectDictLabels } from '@/utils/ruoyi'
// 分页组件
@ -47,6 +52,7 @@ import DictTag from '@/components/DictTag'
const app = createApp(App)
// 全局方法挂载
app.config.globalProperties.useDict = useDict
app.config.globalProperties.download = download
app.config.globalProperties.parseTime = parseTime
@ -66,12 +72,14 @@ app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
//VUE3标准方法注入全局方法
app.provide('comDict', comDict);
app.use(router)
app.use(store)
app.use(plugins)
app.use(elementIcons)
app.component('svg-icon', SvgIcon)
directive(app)
// 使用element-plus 并且设置全局的大小

View File

@ -1,6 +1,6 @@
import useDictStore from '@/store/modules/dict'
import { getDicts } from '@/api/system/dict/data'
import { getComDicts } from '@/api/liswork/dict/ComDict.js'
/**
* 获取字典数据
*/
@ -21,4 +21,45 @@ export function useDict(...args) {
})
return toRefs(res.value);
})()
}
/**
* 获取字典数据(改进版)
* 返回 Promise,确保所有字典数据加载完成后才 resolve
*/
export function comDict(...args) {
const res = ref({});
const promises = [];
args.forEach((dictType) => {
res.value[dictType] = [];
const dicts = useDictStore().getDict(dictType);
if (dicts) {
// 缓存存在,直接赋值
res.value[dictType] = dicts;
} else {
// 缓存不存在,发起请求并记录 Promise
const promise = getComDicts({ zdlb: dictType }).then(resp => {
const dictList = resp.data.map(p => ({
label: p.zdmc,
value: p.zddh,
elTagType: "",
elTagClass: ""
}));
res.value[dictType] = dictList;
useDictStore().setDict(dictType, dictList);
// console.log(`${dictType} 字典加载完成`, dictList);
return dictList;
});
promises.push(promise);
}
});
// 等待所有 Promise 完成后才返回
return Promise.all(promises).then(() => {
// console.log('所有请求的字典已加载完成', res.value);
return toRefs(res.value);
});
}

View File

@ -74,7 +74,8 @@ service.interceptors.request.use(config => {
// 响应拦截器
service.interceptors.response.use(res => {
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
const code = Number(res.data.code) || 200;
// 获取错误信息
const msg = errorCode[code] || res.data.msg || errorCode['default']
// 二进制数据则直接返回
@ -100,7 +101,7 @@ service.interceptors.response.use(res => {
} else if (code === 601) {
ElMessage({ message: msg, type: 'warning' })
return Promise.reject(new Error(msg))
} else if (code !== 200) {
} else if (code !== 200 && code !== 0) {
ElNotification.error({ title: msg })
return Promise.reject('error')
} else {

View File

@ -1,5 +1,7 @@
<script setup>
import {queryComDictListService} from "../../../api/liswork/dict/ComDict.js";
import { computed } from "vue";
import { getToken } from "@/utils/auth";
import {queryComDictListService,addComDictService,updateComDictService} from "../../../api/liswork/dict/ComDict.js";
const { proxy } = getCurrentInstance();
@ -26,13 +28,33 @@ const typeList = ref([])
const typeDetail = ref([])
const total = ref(0);
const zdlbmc = ref('');
const zdlbRef = ref('')
const open = ref(false);
const title = ref('');
// const zdlbmcComputed = computed(() => {
// return typeDetail.value.map(item => {
// return item.zddh+'('+item.zdmc+')';
// });
// });
/*** 用户导入参数 */
const upload = reactive({
// 是否显示弹出层(用户导入)
open: false,
// 弹出层标题(用户导入)
title: "",
// 是否禁用上传
isUploading: false,
// 是否更新已经存在的用户数据
updateSupport: 0,
// 设置上传的请求头部
headers: { Authorization: "Bearer " + getToken() },
// 上传的地址
url: import.meta.env.VITE_APP_BASE_API + "/comdict/importData"
});
function resetData(zdlb){
queryParams.value = {
pageNum: 1,
pageSize: 10,
pageSize: 15,
zdmc: undefined,
zdlb: zdlb,
zddh: undefined,
@ -76,6 +98,16 @@ function handleQuery(){
getList();
}
/** 表单重置 */
function reset() {
form.value = {
zdlb: '',
zdmc: '',
zddh: '',
bz: ''
};
proxy.resetForm("dictRef");
}
//重置
function resetQuery(){
queryParams.value = {
@ -88,7 +120,13 @@ function resetQuery(){
//新增
function handleAdd(){
if(queryParams.value.zdlb === undefined || queryParams.value.zdlb === ''){
proxy.$modal.msgWarning("请先选择字典大类");
return;
}
reset();
open.value = true;
title.value = '新增字典';
}
//修改
@ -100,11 +138,39 @@ function handleUpdate(){
function handleDelete(){
}
/** 导入按钮操作 */
function handleImport() {
upload.title = "字典导入";
upload.open = true;
};
/** 导出按钮操作 */
function handleExport() {
proxy.download("comdict/export", {
...queryParams.value,
},`user_${new Date().getTime()}.xlsx`);
};
/** 下载模板操作 */
function importTemplate() {
proxy.download("comdict/importTemplate", {
}, `user_template_${new Date().getTime()}.xlsx`);
};
/** 提交上传文件 */
function submitFileForm() {
proxy.$refs["uploadRef"].submit();
};
/**文件上传中处理 */
const handleFileUploadProgress = (event, file, fileList) => {
upload.isUploading = true;
};
/** 文件上传成功处理 */
const handleFileSuccess = (response, file, fileList) => {
upload.open = false;
upload.isUploading = false;
proxy.$refs["uploadRef"].handleRemove(file);
proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
handleQuery()
};
//导出
function handleExport(){
}
//多选框选中数据
function handleSelectionChange(){
@ -118,6 +184,38 @@ function handleQueryDetail(){
resetData()
}
//提交表单数据
function submitForm() {
proxy.$refs["dictRef"].validate(valid => {
if (valid) {
if (form.value.zdlb != undefined && form.value.zdlb != '') {
updateComDictService(form.value).then(response => {
proxy.$modal.msgSuccess("修改成功");
open.value = false;
getList();
}).catch(error => {
reset();
});
} else {
form.value.zdlb = queryParams.value.zdlb;
addComDictService(form.value).then(response => {
proxy.$modal.msgSuccess("新增成功");
open.value = false;
getList();
}).catch(error => {
form.value.zdlb = '';
});
}
}
});
}
function cancel() {
open.value = false;
reset();
}
getTypeList()
</script>
@ -130,12 +228,9 @@ getTypeList()
<el-input v-model="zdlbmc" clearable placeholder="请输入字典名称" prefix-icon="Search" style="margin-bottom: 20px" @keyup.enter="handleQueryDetail" @clear="handleQueryDetail" />
</div>
<div class="head-container">
<el-card class="scrollable-card">
<div class="card-content">
<p class="clickable" v-for="item in typeList" :key="item.zdlb" @click="refreshDetail(item.zddh)">{{ item.zdmc }}</p>
</div>
</el-card>
<div class="card-content">
<p class="clickable" v-for="item in typeList" :key="item.zdlb" @click="refreshDetail(item.zddh)">{{ item.zdmc }}</p>
</div>
</div>
</el-col>
<!-- 字典明细-->
@ -164,6 +259,9 @@ getTypeList()
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:dict:remove']">删除</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleImport" v-hasPermi="['system:dict:import']">导入</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="warning" plain icon="Download" @click="handleExport" v-hasPermi="['system:dict:export']">导出</el-button>
</el-col>
@ -188,12 +286,68 @@ getTypeList()
</el-col>
</el-row>
<!-- 添加或修改参数配置对话框 -->
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
<el-form ref="dictRef" :model="form" :rules="rules" label-width="80px">
<el-form-item label="字典类别" prop="zdlb">
<el-input v-model="form.zdlb" disabled></el-input>
</el-form-item>
<el-form-item label="字典代号" prop="zddh">
<el-input v-model="form.zddh" placeholder="请输入字典代号" />
</el-form-item>
<el-form-item label="字典名称" prop="zdmc">
<el-input v-model="form.zdmc" placeholder="请输入字典名称" />
</el-form-item>
<el-form-item label="备注" prop="bz">
<el-input v-model="form.bz" placeholder="请输入内容"></el-input>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</template>
</el-dialog>
<!-- 用户导入对话框 -->
<el-dialog :title="upload.title" v-model="upload.open" width="400px" append-to-body>
<el-upload
ref="uploadRef"
:limit="1"
accept=".xlsx, .xls"
:headers="upload.headers"
:action="upload.url + '?updateSupport=' + upload.updateSupport"
:disabled="upload.isUploading"
:on-progress="handleFileUploadProgress"
:on-success="handleFileSuccess"
:auto-upload="false"
drag
>
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
<template #tip>
<div class="el-upload__tip text-center">
<div class="el-upload__tip">
<el-checkbox v-model="upload.updateSupport" />是否覆盖已经存在的字典数据
</div>
<span>仅允许导入xls、xlsx格式文件。</span>
<el-link type="primary" :underline="false" style="font-size:12px;vertical-align: baseline;" @click="importTemplate">下载模板</el-link>
</div>
</template>
</el-upload>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitFileForm">确 定</el-button>
<el-button @click="upload.open = false">取 消</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<style scoped lang="scss">
.scrollable-card .card-content {
.card-content {
max-height: 450px; /* 设置最大高度 */
overflow-y: auto; /* 超出高度时显示垂直滚动条 */
padding: 16px; /* 保持与卡片默认一致的内边距 */
@ -206,4 +360,5 @@ getTypeList()
}
</style>

View File

@ -0,0 +1,276 @@
<template>
<div class="dict-input-wrapper">
<el-input
v-model="displayValue"
:placeholder="placeholder"
:clearable="clearable"
:disabled="isDictLoading || disabled"
@blur="handleBlur"
@clear="handleClear"
@dblclick="handleDblClick"
>
<template #prefix>
<el-icon v-if="isDictLoading" size="16"><Loading /></el-icon>
</template>
<template #suffix>
<el-icon @click="openDictSelector" size="16" class="select-icon">
<ArrowDown />
</el-icon>
</template>
</el-input>
<!-- 字典选择弹窗(Vue3 中使用 v-model 控制显示) -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="500px"
@close="handleDialogClose"
>
<el-input
v-model="searchKey"
placeholder="搜索字典项..."
class="mb-4"
clearable
@clear="filterDictData"
@input="filterDictData"
/>
<el-table
:data="filteredDictData"
height="300px"
border
@row-click="selectItem"
@row-dblclick="selectItem"
>
<el-table-column prop="value" label="编码" width="100" />
<el-table-column prop="label" label="名称" width="200" />
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button
size="small"
type="primary"
@click="selectItem(scope.row)"
>
选择
</el-button>
</template>
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script setup>
import { ref, computed, watch, onMounted } from 'vue';
import { Loading, ArrowDown } from '@element-plus/icons-vue';
// 组件参数(Vue3 语法)
const props = defineProps({
modelValue: { // 双向绑定的原始值
type: [String, Number],
default: ''
},
dictType: { // 字典类型
type: String,
required: true
},
placeholder: {
type: String,
default: '请输入或双击选择'
},
clearable: {
type: Boolean,
default: true
},
disabled: {
type: Boolean,
default: false
},
fetchDict: { // 获取字典的方法(需返回 Promise)
type: Function,
required: true
},
dialogTitle: {
type: String,
default: '选择字典项'
}
});
// 组件事件(Vue3 语法)
const emit = defineEmits([
'update:modelValue', // 同步原始值
'update:labelValue', // 同步标签值
'select' // 选中事件
]);
// 内部状态(Vue3 ref 响应式)
const tempValue = ref(''); // 临时输入值
const labelValue = ref(''); // 映射后的标签
const dictData = ref([]); // 完整字典数据
const filteredDictData = ref([]); // 过滤后的字典数据
const isDictLoading = ref(true); // 字典加载状态
const dialogVisible = ref(false); // 弹窗显示状态
const searchKey = ref(''); // 搜索关键词
// 显示值计算(Vue3 computed)
const displayValue = computed({
get() {
// 优先显示标签,无则显示原始值
return labelValue.value || tempValue.value || props.modelValue || '';
},
set(newValue) {
// 用户输入时更新临时值,并清除旧标签
tempValue.value = newValue;
if (labelValue.value && newValue !== labelValue.value) {
labelValue.value = '';
emit('update:labelValue', '');
}
}
});
// 加载字典数据
const loadDictData = async () => {
try {
isDictLoading.value = true;
// 调用父组件传入的字典获取方法
const data = await props.fetchDict(props.dictType);
dictData.value = data || [];
filteredDictData.value = [...dictData.value]; // 初始化过滤数据
} catch (error) {
console.error(`加载${props.dictType}字典失败:`, error);
dictData.value = [];
filteredDictData.value = [];
} finally {
isDictLoading.value = false;
}
};
// 过滤字典数据(搜索功能)
const filterDictData = () => {
if (!searchKey.value) {
filteredDictData.value = [...dictData.value];
return;
}
const key = searchKey.value.toLowerCase();
filteredDictData.value = dictData.value.filter(item =>
item.label.toLowerCase().includes(key) ||
String(item.value).toLowerCase().includes(key)
);
};
// 双击事件处理
const handleDblClick = () => {
if (!props.disabled && !isDictLoading.value) {
console.log('双击触发弹窗');
dialogVisible.value = true;
}
};
// 打开弹窗
const openDictSelector = () => {
if (!props.disabled && !isDictLoading.value) {
dialogVisible.value = true;
searchKey.value = ''; // 重置搜索
filterDictData(); // 重置过滤
}
};
// 选择字典项
const selectItem = (item) => {
// 同步原始值和标签值到父组件
emit('update:modelValue', item.value);
emit('update:labelValue', item.label);
emit('select', item); // 触发选中事件
// 更新内部状态
tempValue.value = item.value;
labelValue.value = item.label;
// 关闭弹窗
dialogVisible.value = false;
};
// 失焦时自动映射
const handleBlur = () => {
const value = tempValue.value.trim() || props.modelValue;
if (!value) {
emit('update:modelValue', '');
emit('update:labelValue', '');
labelValue.value = '';
tempValue.value = '';
return;
}
// 查找匹配的字典项
const matched = dictData.value.find(
item => String(item.value) === String(value)
);
if (matched) {
emit('update:labelValue', matched.label);
labelValue.value = matched.label;
} else {
emit('update:labelValue', value); // 无匹配时用原始值
labelValue.value = value;
}
};
// 清空输入
const handleClear = () => {
tempValue.value = '';
labelValue.value = '';
emit('update:modelValue', '');
emit('update:labelValue', '');
};
// 关闭弹窗
const handleDialogClose = () => {
dialogVisible.value = false;
};
// 监听父组件传入的初始值
watch(
() => props.modelValue,
(newVal) => {
if (newVal === tempValue.value) return; // 避免重复更新
tempValue.value = newVal || '';
// 初始值自动映射标签
if (newVal && !isDictLoading.value) {
const matched = dictData.value.find(
item => String(item.value) === String(newVal)
);
labelValue.value = matched?.label || newVal;
emit('update:labelValue', labelValue.value);
}
},
{ immediate: true } // 初始化时执行一次
);
// 组件挂载时加载字典
onMounted(async () => {
await loadDictData();
});
</script>
<style scoped>
.dict-input-wrapper {
position: relative;
}
.select-icon {
cursor: pointer;
color: #666;
margin-left: 5px;
transition: color 0.2s;
}
.select-icon:hover {
color: #409eff; /* 鼠标悬停变色 */
}
/* 修复输入框后缀图标与清除按钮的间距 */
:deep(.el-input__suffix) {
gap: 4px;
}
</style>

View File

@ -0,0 +1,102 @@
<template>
<div class="custom-select2">
<Select2
v-model="currentValue"
:options="processedOptions"
:settings="select2Settings"
@update:modelValue="handleChange"
:disabled="isDisabled"
/>
</div>
</template>
<script setup>
//import { ref, computed, watch, defineProps, defineEmits } from 'vue';
import Select2 from 'vue3-select2-component';
import 'select2/dist/css/select2.min.css';
import { processOptions } from '@/api/pinyin.js'; // 引入拼音处理工具
// 定义组件props
const props = defineProps({
options: {
type: Array,
default: () => []
},
value: {
type: [String, Number, Object],
default: null
},
placeholder: {
type: String,
default: '请选择'
},
disabled: {
type: Boolean,
default: false
},
width: {
type: String,
default: '200px'
}
});
// 定义组件emits
const emits = defineEmits(['update:modelValue', 'change']);
// 处理选项数据
const processedOptions = computed(() => {
return processOptions(props.options);
});
// 当前值
const currentValue = ref(props.value);
// Select2设置
const select2Settings = computed(() => ({
placeholder: props.placeholder,
allowClear: true,
width: props.width,
dropdownAutoWidth: false,
// 自定义搜索函数
matcher: (params, data) => {
if (!params.term || params.term.trim() === '') {
return data;
}
const term = params.term.toLowerCase();
// 检查文本、拼音或拼音首字母是否匹配
if (
data.text.toLowerCase().includes(term) ||
data.pinyin.toLowerCase().includes(term) ||
data.firstLetters.toLowerCase().includes(term)
) {
return data;
}
return null;
}
}));
// 跟踪禁用状态
const isDisabled = computed(() => props.disabled);
// 监听外部值变化
watch(() => props.value, (newVal) => {
currentValue.value = newVal;
});
// 处理值变更
const handleChange = (value) => {
currentValue.value = value;
emits('update:modelValue', value);
emits('change', value);
};
</script>
<style scoped>
.custom-select2 {
width: 100%;
}
</style>

View File

@ -0,0 +1,144 @@
<template>
<div class="compact-form">
<el-col :span="24" :xs="24">
<el-form :model="labPat" label-width="100px" class="compact-form" label-position="left">
<el-form-item label="病人来源" label-width="70px">
<el-col :span="16">
<dict-input v-model="labPat.brly" :dict-type="'PT'" placeholder="请输入病人来源" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.brlyLabel = label" dialog-title="选择病人来源"/>
</el-col>
<el-col :span="8">
<el-switch v-model="labPat.jzbz" active-text="急诊" active-color="#FD0101"></el-switch>
</el-col>
</el-form-item>
<el-form-item label="病 历 号" label-width="70px">
<el-input v-model="labPat.brdh" placeholder="请输入病历号"></el-input>
</el-form-item>
<el-form-item label="姓 名" label-width="70px">
<el-input v-model="labPat.brxm" placeholder="请输入姓名"></el-input>
</el-form-item>
<el-form-item label="性 别" label-width="70px">
<el-col :span="8">
<dict-input v-model="labPat.brxb" placeholder="性别" :dict-type="'SX'" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.brxbLabel = label"/>
</el-col>
<el-col :span="4"><div>年龄</div></el-col>
<el-col :span="6">
<el-input v-model="labPat.nl" placeholder="年龄"></el-input>
</el-col>
<el-col :span="6">
<dict-input v-model="labPat.nldw" placeholder="年月" :dict-type="'AU'" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.nldwLabel = label"/>
</el-col>
</el-form-item>
<el-form-item label="送检科室" label-width="70px">
<dict-input v-model="labPat.ksdh" placeholder="请输入科室" :dict-type="'DP'" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.ksdhLabel = label"/>
</el-form-item>
<el-form-item label="床 号" label-width="70px">
<el-input v-model="labPat.ch" placeholder="请输入床号"></el-input>
</el-form-item>
<el-form-item label="样本类型" label-width="70px">
<dict-input v-model="labPat.yblx" placeholder="请输入样本类型" :dict-type="'BT'" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.yblxLabel = label"/>
</el-form-item>
<el-form-item label="申请医生" label-width="70px">
<dict-input v-model="labPat.sjys" placeholder="请输入申请医生" :dict-type="'SRD'" :fetch-dict="fetchDict" @update:labelValue="(label) => labPat.sjysLabel = label"/>
</el-form-item>
<el-form-item label="上机时间" label-width="70px">
<el-date-picker v-model="labPat.sqsj" type="datetime" placeholder="上机时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"></el-date-picker>
</el-form-item>
<el-form-item label="采样时间" label-width="70px">
<el-date-picker v-model="labPat.cyrq" type="datetime" placeholder="采样时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"></el-date-picker>
</el-form-item>
<el-form-item label="报告时间" label-width="70px">
<el-date-picker v-model="labPat.dysj" type="datetime" placeholder="报告时间" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"></el-date-picker>
</el-form-item>
<el-form-item label="临床诊断" label-width="70px">
<el-input v-model="labPat.zd" placeholder="请输入临床诊断"></el-input>
</el-form-item>
<el-form-item label="备 注" label-width="70px">
<el-input v-model="labPat.bz" placeholder="请输入备注"></el-input>
</el-form-item>
<el-form-item label="检验医生" label-width="70px">
<el-input v-model="labPat.yhdh" placeholder="请输入检验医生"></el-input>
</el-form-item>
<el-form-item label="审核医生" label-width="70px">
<el-input v-model="labPat.hdys" placeholder="请输入审核医生"></el-input>
</el-form-item>
<el-form-item label="审核状态" >
<el-radio-group v-model="labPat.jgbz">
<el-radio :label="0">未审核</el-radio>
<el-radio :label="2">已审核</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<div class="compact-form">
<el-button type="primary" @click="saveMain">保存</el-button>
<el-button @click="resetMain">重置</el-button>
</div>
</el-col>
</div>
</template>
<script setup>
import DictInput from './DictInput.vue';
const props = defineProps({
labPat: {
type: Object,
required: true
},
//结果主键
labPatKey:{
type: Array,
default: () => []
}
});
const emit = defineEmits(['update:labPat']);
// 通过inject获取全局方法
const comDict = inject('comDict');
// 保存方法
const saveMain = () => {
// 提交时可获取原始值(brly)和标签(brlyLabel)
console.log('提交数据:', {
rawValue: props.labPat.brly,
label: props.labPat.brlyLabel,
...props.labPat
});
// 实际项目中调用接口提交数据
};
// 重置方法
const resetMain = () => {
emit('update:labPat', {
brly: '',
brlyLabel: '',
ksdh: '',
ksdhLabel: '',
sjys: '',
sjysLabel: '',
brxb: '',
brxbLabel: '',
nldw: '',
nldwLabel: '',
yblx: '',
yblxLabel: '',
// 保留其他字段的默认值
...props.labPat,
jzbz: props.labPat.jzbz || 0,
jgbz: props.labPat.jgbz || 0
});
}
// 字典获取方法(适配子组件)
const fetchDict = async (dictType) => {
const dictRefs = await comDict(dictType);
// 根据实际字典结构返回数组(需与子组件匹配)
return dictRefs[dictType]?.value || [];
};
</script>
<style scoped lang="scss">
.compact-form .el-form-item {
margin-bottom: 10px;
}
.compact-form .el-form-item__label {
padding-bottom: 5px;
}
</style>

View File

@ -0,0 +1,206 @@
<template>
<div class="table-container">
<el-input
v-model="searchText"
placeholder="输入条码号搜索"
class="mb-4"
@keyup.enter="search"
>
<template #append>
<el-button @click="search">搜索</el-button>
</template>
</el-input>
<el-table
:data="labPatList"
stripe
class="card-content"
:header-cell-style="headerCellStyle"
:cell-style="cellStyle"
@row-click="handleRowClick"
style="width: 100%"
border
>
<el-table-column prop="finish" label="完成" width="70">
<template #default="scope">
<span>{{ scope.row.finish === 1 ? '已完成' : '未完成' }}</span>
</template>
</el-table-column>
<el-table-column prop="jgbz" label="审核" width="25" align="center" header-align="center" >
<template #default="scope">
<el-checkbox :model-value="scope.row.jgbz === '2'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="alarmflag" label="报警" width="25">
<template #default="scope">
<el-checkbox :model-value="scope.row.alarmflag === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="brly" label="类型" width="50" show-overflow-tooltip></el-table-column>
<el-table-column prop="autojgbz" label="自审" width="25" >
<template #default="scope">
<el-checkbox :model-value="scope.row.autojgbz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="ybh" label="样本号" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="brxm" label="病人姓名" width="100" show-overflow-tooltip></el-table-column>
<el-table-column prop="brdh" label="病历号" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="ch" label="床号" width="80" show-overflow-tooltip ></el-table-column>
<el-table-column prop="brxb" label="病人性别" width="80" show-overflow-tooltip ></el-table-column>
<el-table-column prop="nl" label="年" width="40" show-overflow-tooltip ></el-table-column>
<el-table-column prop="nldw" label="龄" width="30" show-overflow-tooltip ></el-table-column>
<el-table-column prop="ksdh" label="科室" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="yblx" label="标本" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="dybz" label="打印" width="25" show-overflow-tooltip >
<template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="fslx" label="发送" width="25">
<template #default="scope">
<el-checkbox :model-value="scope.row.dybz === '1'" disabled>
</el-checkbox>
</template>
</el-table-column>
<el-table-column prop="shcs" label="次数" width="25" show-overflow-tooltip ></el-table-column>
<el-table-column prop="sqh" label="申请号/条码" width="100" show-overflow-tooltip></el-table-column>
<el-table-column prop="jymd" label="检验目的" width="100" show-overflow-tooltip></el-table-column>
<el-table-column prop="yhdh" label="检验医生" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="yljg" label="送检医院" width="80" show-overflow-tooltip></el-table-column>
<el-table-column prop="jyrq" label="检验日期" v-if="false"></el-table-column>
<el-table-column prop="yq" label="仪器" v-if="false"></el-table-column>
</el-table>
<el-pagination
class="mt-4"
background
layout="total, sizes, prev, pager, next, jumper"
:total="total"
:page-sizes="[5, 10, 15, 20]"
:page-size="pageSize"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div>
</template>
<script setup>
const props = defineProps({
labPatList: {
type: Array,
default: () => []
},
total: {
type: Number,
default: 0
}
})
const emits = defineEmits(['select', 'search', 'page-change'])
const searchText = ref('')
const pageSize = ref(15)
const currentPage = ref(1)
const search = () => {
emits('search', searchText.value, currentPage.value, pageSize.value)
}
const handleRowClick = (row) => {
emits('select', row)
}
const handleSizeChange = (val) => {
pageSize.value = val
emits('page-change', currentPage.value, pageSize.value)
}
const handleCurrentChange = (val) => {
currentPage.value = val
emits('page-change', currentPage.value, pageSize.value)
}
onMounted(() => {
emits('page-change', currentPage.value, pageSize.value)
})
// 设置列头颜色
const headerCellStyle = ({ row, column, rowIndex, columnIndex }) => {
return {
// backgroundColor: '#A6CAF0', // 浅蓝色
color: '#0050b3', // 文字颜色
fontWeight: 'bold' // 文字加粗
};
};
//设置内容格式
const cellStyle = ({ row, column, rowIndex, columnIndex }) => {
// console.log('检查单元格样式:', row, column); // 调试输出
// 使用 === 比较时注意值的类型(数字 vs 字符串)
if (column.property === 'finish' && row.status === '1') {
return {
backgroundColor: '#006400', // 使用驼峰式写法(可选)
fontWeight: 'bold',
color: 'white', // 添加文字颜色,确保可读性
height: '25px',
lineHeight: '25px'
};
}
if (column.property === 'jgbz' && row.status === '2') {
return {
backgroundColor: '#FF69B4',
fontWeight: 'bold',
height: '25px',
lineHeight: '25px'
};
}
if (column.property === 'alarmflag' && row.status === '1') {
return {
backgroundColor: '#FF0000',
fontWeight: 'bold',
color: 'white',
height: '25px',
lineHeight: '25px'
};
}
return {
height: '25px',
lineHeight: '25px',
padding: '0 0px'
}; // 默认不设置样式
};
</script>
<style scoped lang="scss">
.compact-form .el-form-item {
padding: 10px;
margin-bottom: 10px;
}
.compact-form .el-form-item__label {
padding-bottom: 5px;
}
.card-content {
max-height: 620px; /* 设置最大高度 */
overflow-y: auto; /* 超出高度时显示垂直滚动条 */
padding: 10px; /* 保持与卡片默认一致的内边距 */
}
.clickable:hover {
cursor: pointer; /* 鼠标悬停时显示手型 */
transition: all 0.3s; /* 平滑过渡效果 */
color: blue;
}
.card-content.el-table__row.current-row {
background-color: #e6f4ff !important;
color: #0050b3 !important; /* 文字颜色 */
}
.card-content.el-table__row.current-row:hover {
background-color: #d6e8ff !important;
}
</style>

View File

@ -0,0 +1,240 @@
<template>
<div class="compact-form">
<el-table
:data="tableData"
border
stripe
highlight-current-row
@selection-change="handleSelectionChange"
class="custom-table-container"
height="640px"
style="width: 100%; --table-row-height: 32px;"
:header-cell-style="{ backgroundColor: '#f5f7fa' }"
>
<el-table-column
type="index" prop="id" label="" width="45" :index="getRowIndex" >
</el-table-column>
<el-table-column type="selection" width="30"></el-table-column>
<el-table-column prop="jyrq" label="检验日期" v-if="false"></el-table-column>
<el-table-column prop="yq" label="仪器" v-if="false"></el-table-column>
<el-table-column prop="ybh" label="样本号" v-if="false"></el-table-column>
<el-table-column prop="xmdh" label="项目" width="60" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="xmmc" label="名称" width="100" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="csjg" label="结果" width="100" >
<template #default="scope">
<el-input v-model="scope.row.csjg" size="small" class="full-width-input"></el-input>
</template>
</el-table-column>
<el-table-column prop="od" label="OD" width="60" v-if="false">
<template #default="scope">
<el-input v-model="scope.row.od" size="small" class="full-width-input"></el-input>
</template>
</el-table-column>
<el-table-column prop="cutoff" label="SCO" width="60" v-if="false">
<template #default="scope">
<el-input v-model="scope.row.od" size="small" class="full-width-input"></el-input>
</template>
</el-table-column>
<el-table-column prop="refs" label="参考值" width="100" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="dw" label="单位" width="100" show-overflow-tooltip >
</el-table-column>
</el-table>
</div>
</template>
<script setup>
//import {ref, reactive, watch, toRefs, nextTick} from 'vue';
import {deleteresult, queryLabResults} from "@/api/liswork/work/LisWork.js";
import {delType} from "@/api/system/dict/type.js";
import {ElMessageBox} from "element-plus";
const { proxy } = getCurrentInstance();
const props = defineProps({
// 表格数据
tableData: {type: Array,default: () => []},
//主键
tableKey:{type: Array, default: () => []},
// 部门选项
// departments: {
// type: Array,
// default: () => []
// },
// 是否只读
readonly: {type: Boolean, default: false}
});
const { tableData, tableKey, readonly,undeleteflag,hasChanges } = toRefs(props);
const emits = defineEmits([
'add', // 新增行
'save', // 保存数据
'delete', // 删除行
'batch-delete', // 批量删除
'fetchLabResults' //刷新数据
]);
// 选中的行
const selectedRows = ref([]);
const filteredList = ref([]);
// 原始数据副本,用于检测变化
const originalData = ref(JSON.parse(JSON.stringify(props.tableData)));
// 处理选择变化
const handleSelectionChange = (val) => {
selectedRows.value = val;
if (val.length === 0) {
emits('delfalg', true);
}else{
emits('delfalg', false);
}
};
// 新增行
const handleAdd = () => {
// emits('add');
// 创建新行数据
const newRow = {
jyrq: tableKey.jyrq,
yq: tableKey.yq,
ybh: tableKey.ybh
};
// 添加到表格数据
tableData.value.push(newRow);
// 动画结束后移除标记
// 可选:滚动到表格底部并聚焦到第一个输入框
nextTick(() => {
const tableData = document.querySelector('.el-table__body-wrapper');
if (tableData) {
tableData.scrollTop = tableData.scrollHeight;
}
// 聚焦到新行的第一个输入框
const inputs = document.querySelectorAll('.el-input__inner');
if (inputs.length > 0) {
inputs[4].focus(); // 通常最后一个是操作列的按钮,所以取倒数第二个
}
})
};
// 保存数据
const handleSave = () => {
if (hasChanges.value) {
emits('save', props.tableData);
// 保存后更新原始数据
// originalData.value = JSON.parse(JSON.stringify(props.tableData));
hasChanges.value = false;
}
};
// 删除行
const handleDelete = (row) => {
emits('delete', row);
tableData.value = tableData.value.filter(item => item.id !== row.id);
};
// 批量删除(调用API)
const handleBatchDelete = async () => {
try {
if (selectedRows.length === 0) {
return;
}
// 确认删除
await ElMessageBox.confirm(
`确定要删除选中的 ${selectedRows.value.length} 条记录吗?`,
'提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
);
await deleteresult(selectedRows.value);
fetchLabResults();
} catch (error) {
// 错误已经在拦截器中处理,这里可以不做处理或添加额外逻辑
console.error('删除失败:', error);
}
}
// 重置表格的修改状态
const resetChange = () => {
// 原始数据副本,用于检测变化
originalData.value = JSON.parse(JSON.stringify(props.tableData));
hasChanges.value = false;
};
const fetchLabResults = () => {
emits('fetchLabResults');
}
// 监听数据变化
watch(() => props.tableData, (newVal, oldVal) => {
// 简单比较数据是否有变化
//hasChanges.value = JSON.stringify(newVal) !== JSON.stringify(originalData.value);
}, { deep: true });
// 获取行号(支持分页时显示正确行号)
const getRowIndex = (index) => {
// 如果有分页,可以根据当前页码计算真实行号
// return (currentPage - 1) * pageSize + index + 1;
// 无分页时直接返回索引+1
return index + 1;
};
// 暴露方法给父组件
defineExpose({
handleAdd,
handleDelete,
handleSave,
handleBatchDelete
});
</script>
<style scoped>
/* 移除单元格内边距 */
.custom-table-container /deep/ .el-table__cell {
padding: 0 !important;
}
/* 表格行高 */
.custom-table-container /deep/ .el-table__row {
height: var(--table-row-height) !important;
}
/* 输入框占满单元格 */
.full-width-input /deep/ .el-input__inner {
width: 100%;
height: calc(var(--table-row-height) - 2px); /* 减去上下边框 */
padding: 0 0px; /* 减小内边距 */
border: 0px solid #dcdfe6;
border-radius: 0;
box-sizing: border-box;
background-color: transparent; /* 可选:透明背景 */
}
/* 输入框聚焦效果 */
.full-width-input /deep/ .el-input__inner:focus {
border-color: #409eff;
box-shadow: 0 0 0 0px rgba(64, 158, 255, 0.2);
outline: none;
}
/* 新增行动画 */
.el-table__body-wrapper {
overflow: visible !important; /* 确保动画不被裁剪 */
}
.new-row-enter-active {
animation: fadeIn 0.5s ease;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
</style>

View File

@ -0,0 +1,505 @@
<template>
<div class="app-container">
<el-row :gutter="10" class="compact-form">
<el-col :span="1.5">
<el-button type="primary" plain icon="Plus" @click="handleAdd" v-hasPermi="['system:dict:add']">新增明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="danger" plain icon="Delete" :disabled="delResultFlag" @click="handleDelete" v-hasPermi="['system:dict:remove']">删除明细</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" :disabled="changeResultFlag" @click="handleUpdate" v-hasPermi="['system:dict:edit']">保存</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Edit" @click="handleCheck" v-hasPermi="['system:dict:import']">初报</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleCheck" v-hasPermi="['system:dict:import']">初审</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleUnCheck" v-hasPermi="['system:dict:export']">取消初审</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleCheck" v-hasPermi="['system:dict:import']">审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="info" plain icon="Upload" @click="handleUnCheck" v-hasPermi="['system:dict:export']">取消审核</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="handleUnCheck" v-hasPermi="['system:dict:export']">预览</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="primary" plain icon="Edit" @click="handleUnCheck" v-hasPermi="['system:dict:export']">打印</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Upload" @click="handleUnCheck" v-hasPermi="['system:dict:export']">上一个</el-button>
</el-col>
<el-col :span="1.5">
<el-button type="success" plain icon="Download" @click="handleUnCheck" v-hasPermi="['system:dict:export']">下一个</el-button>
</el-col>
</el-row>
<el-row :gutter="10" class="compact-form" >
<el-form :model="queryParams" ref="queryRef" :inline="true" :rules="rules" >
<el-form-item class="compact-form" label="" label-width="0px" prop="ybh" >
<el-col :span="1.5">样本编号</el-col>
<el-col :span="4.5">
<el-input v-model="queryParams.ybh" clearable placeholder="样本编号" prefix-icon="Search" @keyup.enter="handleQueryYbh" @clear="handleQueryYbh" />
</el-col>
<el-col :span="1.5"><div>检验日期</div></el-col>
<el-col :span="3.5">
<el-date-picker v-model="queryParams.jyrq" type="date" label-width="20px" placeholder="检验日期" value-format="YYYY-MM-DD" :clearable="false" @change="handleQuery" ></el-date-picker>
</el-col>
<el-col :span="1.5"><div>部门组别</div></el-col>
<el-col :span="3.5">
<Ddlb
v-model="queryParams.instrGroup"
:options="instrGroupOptions"
placeholder="请选择部门组别"
@change="handleinstrGroupChange"
:value="setInstrGroup"
label-width="40px"
/>
</el-col><el-col :span="1"><div>仪器</div></el-col>
<el-col :span="4">
<Ddlb
v-model="queryParams.yq"
:options="instrOptions"
placeholder="请选择仪器"
@change="handleYqChange"
:value="setInstr"
/>
</el-col>
</el-form-item>
</el-form>
</el-row>
<el-row :gutter="5">
<el-col :span="5" :xs="24">
<div class="compact-form">
<LabPatForm v-model:labPat="labPat" ref="labPatRef" @save="savelabPat" @reset="resetlabPat" />
</div>
</el-col>
<el-col :span="10" :xs="24">
<div class="compact-form">
<LabResultForm ref="labResultRef"
:tableData="labResuts"
:tableKey="queryParams"
@add="addLabResult"
@delete="deleteLabResults"
@edit="editLabResult"
@delfalg="setdelResultFlag"
@changefalg="setchangeResultFlag"
@fetchLabResults="fetchLabResults"
/>
</div>
</el-col>
<el-col :span="9" :xs="24">
<div class="compact-form">
<LabPatListForm
:labPatList="labPatList"
:labPatKey="queryParams"
:total="total"
@select="selectJob"
@search="searchJobs"
@page-change="pageChange"
/>
</div>
</el-col>
</el-row>
</div>
</template>
<script setup>
import {getDate,queryLabPatList,queryLabResults,updateLabPatService,queryLabPat} from "../../../api/liswork/work/LisWork.js";
import {querylabInstrList,querylabInstrListByLisgroup} from "../../../api/liswork/dict/LabInstr.js";
import {getComDicts, queryComDictListService} from "../../../api/liswork/dict/ComDict.js";
import LabPatForm from './components/labpat.vue'
import LabResultForm from './components/labresult.vue'
import LabPatListForm from './components/labpatlist.vue'
import Ddlb from './components/ddlb.vue'
const single = ref(true);
const delResultFlag = ref(true);
const changeResultFlag = ref(true);
const loading = ref(true);
const instrGroupOptions = ref([]);
const instrOptions = ref([]);
const labPatList = ref([])
const patdata = reactive({
form: {},
labPat: {
jyrq: '',
yq: '',
ybh: '',
brly:'',
jzbz:'',
brdh:'',
brxm:'',
brxb:'',
nl:null,
nldw:'1',
ksdh:'',
ch:'',
sjys:'',
sqsj:'',
cyrq:'',
dysj:'',
zd:'',
bz:'',
yhdh:'',
hdys:'',
jgbz:'0',
yblx:''
},
rules: {}
});
const { labPat } = toRefs(patdata);
const labResuts = ref([])
const total = ref(0)
const currentLabResults = ref([])
const searchText = ref('')
const pageSize = ref(10)
const currentPage = ref(1)
const setInstrGroup=ref('')
const setInstr=ref('')
const yq0=ref('')
const jyrq0=ref('')
const ybh0=ref('')
const querydata = reactive({
form: {},
queryParams: {
pageNum: 1,
pageSize: 10,
jyrq: formatDate(new Date()),
yq: '1',
ybh: '1',
days: 1,
instrGroup:'#'
},
rules: {
ybh: [],
jyrq: [],
instrGroup: [],
yq: [],
}
,
});
const { queryParams, form, rules } = toRefs(querydata);
//子组件labResult.vue
const labResultRef = ref(null);
//子组件labPatRef.vue
const labPatRef = ref(null);
// 页面载入时获取基本默认值参数信息,后面有扩展需要都放这里
const loadYqConfig = async () => {
// 使用 .value 获取响应式数据的值
jyrq0.value='2022-06-19';//formatDate(new Date());
yq0.value='COBAS';
ybh0.value='1';
querydata.queryParams.jyrq = jyrq0.value;
querydata.queryParams.yq = yq0.value;
querydata.queryParams.ybh = ybh0.value;
querydata.queryParams.instrGroup = 'A2';
}
// 加载部门下拉列表
function loadinstrGroupOptions(){
loading.value = true;
getComDicts({zdlb: 'GROUP'}).then(response => {
instrGroupOptions.value = response.data.map(item => ({
id: item.zddh,
text: item.zdmc
}));
loading.value = false;
setInstrGroup.value=querydata.queryParams.instrGroup;
});
loadYqConfig();
}
// 部门组别变化时更新仪器下拉框
const handleinstrGroupChange = (value) => {
queryParams.value.instrGroup = value
// 清空仪器选择并重新加载
// queryParams.value.yq = '';
instrOptions.value = [];
// 加载对应组别的仪器数据
loadinstrOptions();
}
// 仪器变化时的处理函数
const handleYqChange = (value) => {
if (value === null || value === '') {
queryParams.value.yq = yq0.value;
setInstr.value= yq0.value;
}else {
yq0.value=value;
queryParams.value.yq = value;
handleQuery();
}
}
// 加载仪器下拉列表
const loadinstrOptions = () => {
loading.value = true;
querylabInstrListByLisgroup({pageNum: 1, pageSize: 100,lisgroup: queryParams.value.instrGroup})
.then(response => {
if (response.rows) {
instrOptions.value = toRaw(response.rows).map(item => ({
id: item.yq.trimEnd(),
text: item.yqmc
}))
loading.value = false;
setInstr.value=querydata.queryParams.yq;
} else {
throw new Error('仪器响应数据为空');
}
})
.catch(error => {
console.error('仪器数据加载失败', error);
loading.value = false;
});
// console.error('aaa', queryParams.value);
//console.log('test22:', instrOptions.value);
}
//获取服务器时间
const serverDate = computed(() => {
return formatDate(new Date());
});
//切换仪器或检验日期后重置页码
function resetData(){
queryParams.value = {
pageNum: 1,
pageSize: 15,
}
searchText.value = ''
currentPage.value = 1
pageSize.value = 15
}
//载入样本列表(右边labpatlist.vue组件)
const fetchlabPatList = async (search = '', page = 1, size = 15) => {
loading.value = true;
queryParams.value.pageSize = size;
queryParams.value.pageNum = page;
queryLabPatList(queryParams.value).then(response => {
labPatList.value = response.rows;
total.value = response.total;
loading.value = false;
}) .catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//载入样本编辑表单(左边labpat.vue组件)
const fetchLabPat = async () => {
loading.value = true;
queryLabPat(queryParams.value).then(response => {
labPat.value = response.data;
loadingPatLabel();
loading.value = false;
}) .catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//载入样本结果表单(中间labresult.vue组件)
const fetchLabResults = async () => {
loading.value = true;
// console.log('test', queryParams.value);
queryLabResults({jyrq: queryParams.value.jyrq,yq: queryParams.value.yq,ybh: queryParams.value.ybh}).then(response => {
// console.log('RESULT:', response);
labResuts.value = response.data;
loading.value = false;
}) .catch(error => {
console.error('列表数据加载失败', error);
loading.value = false;
});
}
//保存样本表单
const savelabPat = async (labPat) => {
// 实际项目中这里应该是API调用
console.log('保存主表:', labPat)
if (!labPat.id) {
// 新增
labPat.id = Date.now()
labPatList.value.unshift({...labPat})
} else {
// 更新
const index = labPatList.value.findIndex(item => item.id === labPat.id)
if (index !== -1) {
labPatList.value.splice(index, 1, {...labPat})
}
}
alert('保存成功')
}
//重置结果表单
const resetlabPat = () => {
labResuts.value = []
currentLabResults.value = []
}
//新增结果列
const addLabResult = async (LabResult) => {
// 实际项目中这里应该是API调用
console.log('添加明细:', LabResult)
if (!LabResult.xh) {
LabResult.xh = currentLabResults.value.length + 1
}
const index = currentLabResults.value.findIndex(item => item.xh === LabResult.xh)
if (index !== -1) {
currentLabResults.value.splice(index, 1, {...LabResult})
} else {
currentLabResults.value.push({...LabResult})
}
alert('添加成功')
}
//删除结果列
const deleteLabResults = async (xhList) => {
// 实际项目中这里应该是API调用
console.log('删除明细:', xhList)
currentLabResults.value = currentLabResults.value.filter(item => !xhList.includes(item.xh))
alert('删除成功')
}
//修改结果列
const editLabResult = (LabResult) => {
console.log('编辑明细:', LabResult)
// 在表单中填充数据
// 这里不需要做任何事情,因为组件已经绑定了数据
}
//样本列表点击切换样本信息,同步载入左边样本信息表单和中间结果表单
const selectJob = async (job) => {
// 复制选中的job数据到当前编辑状态
// Object.assign(labPat.value, {...job})
queryParams.value.jyrq = job.jyrq;
queryParams.value.yq = job.yq;
queryParams.value.ybh = job.ybh;
checkQuery()
//await nextTick();
// 获取对应的明细数据
await fetchLabPat();
await fetchLabResults();
}
//样本列表检索时刷新列表信息
const searchJobs = async (text, page, size) => {
searchText.value = text
currentPage.value = page
pageSize.value = size
loadlabPatList()
}
//样本列表翻页时刷新列表信息
const pageChange = async (page, size) => {
currentPage.value = page
pageSize.value = size
loadlabPatList()
}
//刷新样本列表方法封装
const loadlabPatList = async () => {
await fetchlabPatList(searchText.value, currentPage.value, pageSize.value)
}
/** 查询服务器时间 */
function getdate() {
getDate().then(response => {
serverDate.value= formatDate(new Date(response.data));
})
}
//日期格式化
function formatDate(date) {
const year = date.getFullYear();
// 月份是从0开始的,所以要加1
const month = String(date.getMonth() + 1).padStart(2, '0');
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day} 00:00:00`;
}
const setdelResultFlag = (value) =>{
delResultFlag.value=value;
}
const setchangeResultFlag = (value) =>{
changeResultFlag.value=value;
}
//新增
const handleAdd = () => {
labResultRef.value?.handleAdd();
};
function handleUpdate(){
}
function handleDelete(){
labResultRef.value?.handleBatchDelete();
}
function loadingPatLabel(){
// labPatRef.value?.loadingLabel();
}
function handleCheck(){
}
function handleUnCheck(){
}
function handlePrint(){
}
function handleQueryDetail(){
}
//切换仪器或检验日期时载入
const handleQuery= async()=>{
if (checkQuery()===false) return false
// resetData();
await loadlabPatList();
//暂时默认打开1号样本,后面由参数控制
querydata.queryParams.ybh='1';
await handleQueryYbh();
}
//切换样本号时载入
const handleQueryYbh = async()=>{
if (checkQuery()===false) return false
await fetchLabPat();
await fetchLabResults();
}
//主键空置拦截
function checkQuery(){
if (querydata.queryParams.jyrq === ''|| querydata.queryParams.jyrq === null) {
querydata.queryParams.jyrq = jyrq0.value
return false;
}
if (querydata.queryParams.yq === ''|| querydata.queryParams.yq === null) {
querydata.queryParams.yq = yq0.value
return false;
}
if (querydata.queryParams.ybh === ''|| querydata.queryParams.ybh === null) {
querydata.queryParams.ybh = ybh0.value
return false;
}
jyrq0.value=querydata.queryParams.jyrq
yq0.value=querydata.queryParams.yq
ybh0.value=querydata.queryParams.ybh
}
//打开页面需要先初始化的内容都放这里
onMounted(() => {
loadYqConfig();
loadinstrGroupOptions(); // 页面加载时获取部门组别数据
loadinstrOptions();
loadlabPatList();
})
</script>
<style scoped lang="scss">
.compact-form .el-form-item {
padding: 5px;
margin-bottom: 5px;
}
.compact-form .el-form-item__label {
padding-bottom: 0px;
}
.card-content {
max-height: 450px; /* 设置最大高度 */
overflow-y: auto; /* 超出高度时显示垂直滚动条 */
padding: 5px; /* 保持与卡片默认一致的内边距 */
}
.clickable:hover {
cursor: pointer; /* 鼠标悬停时显示手型 */
transition: all 0.3s; /* 平滑过渡效果 */
color: blue;
}
</style>

View File

@ -62,4 +62,7 @@ export default defineConfig(({ mode, command }) => {
}
}
}
optimizeDeps: {
include: ['jquery', 'select2', 'vue3-select2-component']
}
})