Compare commits
No commits in common. "f69122ff6d68674edcc2d2baf96c8bfc9df8179c" and "5a3fb07f49a2680e2bf1a634e0b395e1617fd750" have entirely different histories.
f69122ff6d
...
5a3fb07f49
11
src/env.d.ts
vendored
11
src/env.d.ts
vendored
@ -1,14 +1,3 @@
|
||||
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
|
||||
declare global {
|
||||
namespace JSX {
|
||||
interface IntrinsicElements {
|
||||
[elemName: string]: HTMLAttributes
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
declare module '*.vue' {
|
||||
import type { DefineComponent } from 'vue'
|
||||
const component: DefineComponent<{}, {}, any>
|
||||
|
||||
@ -32,16 +32,6 @@ export default {
|
||||
console.log(progressEvent)
|
||||
//progressEvent.loaded 下载文件的当前大小
|
||||
//progressEvent.total 下载文件的总大小 如果后端没有返回 请让他加上
|
||||
let progressPercent = Math.round((progressEvent.loaded / progressEvent.total) * 100);
|
||||
let loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: `下载中,请稍候${progressPercent}%`,
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
loading.setText(`下载中,请稍候${progressPercent}%`)
|
||||
if (progressPercent == 100) {
|
||||
loading.close()
|
||||
}
|
||||
}
|
||||
}).then((res) => {
|
||||
const isBlob = blobValidate(res.data);
|
||||
|
||||
@ -63,7 +63,7 @@ function initSocket(onPrinterUpdate?: OnPrinterUpdate) {
|
||||
socket.on("connect_error", (err: Error) => {
|
||||
console.log('连接错误', err);
|
||||
ElMessageBox.alert(
|
||||
`连接失败!<br>请确保目标服务器已<a style="color: #1f79db" onclick="downExe()">下载安装</a> 并<a style="color: #1f79db" onclick="yxHiprint()"> 运行 </a> 打印服务!`,
|
||||
`连接失败!<br>请确保目标服务器已<a style="color: #1f79db" onclick="downExe()"> 下载 </a> 并运行 打印服务!`,
|
||||
"客户端未连接",
|
||||
{
|
||||
dangerouslyUseHTMLString: true,
|
||||
@ -79,17 +79,12 @@ function initSocket(onPrinterUpdate?: OnPrinterUpdate) {
|
||||
|
||||
const downExe = () => {
|
||||
// 下载逻辑实现
|
||||
// console.log('开始下载打印服务');
|
||||
Download.name('hiprint_win_x64-1.0.13.exe', false)
|
||||
};
|
||||
const yxHiprint = () => {
|
||||
// 唤起客户端
|
||||
window.open("hiprint://");
|
||||
console.log('开始下载打印服务');
|
||||
Download.name('hiprint_win_x64-1.0.13.exe')
|
||||
};
|
||||
|
||||
// 挂载到window,使其能被全局访问
|
||||
(window as any).downExe = downExe;
|
||||
(window as any).yxHiprint = yxHiprint;
|
||||
(window as any).downExe = downExe
|
||||
function silentPrintPdf(url: string, printOptions?: object) {
|
||||
const printStore = usePrintStore();
|
||||
if (!url) {
|
||||
|
||||
@ -93,9 +93,9 @@
|
||||
<div class="title">检验结果</div>
|
||||
<div class="mb10">
|
||||
<el-button type="primary" @click="openDB">对比历史</el-button>
|
||||
<el-button type="primary" @click="copyData('All')">复制全部数据</el-button>
|
||||
<el-button type="primary" @click="copyData('N')">复制异常数据</el-button>
|
||||
<el-button type="primary" @click="copyData('Y')">复制正常数据</el-button>
|
||||
<el-button type="primary">复制全部数据</el-button>
|
||||
<el-button type="primary">复制异常数据</el-button>
|
||||
<el-button type="primary">复制正常数据</el-button>
|
||||
<el-button type="primary">知识库</el-button>
|
||||
</div>
|
||||
|
||||
@ -351,6 +351,7 @@ const rightColumns = ref([
|
||||
{ prop: 'refs', label: '参考值', align: 'center', visible: true, key: 3 },
|
||||
{ prop: 'dw', label: '单位', align: 'center', visible: true, key: 4 },
|
||||
{ prop: 'jgbz', label: '结果标志', align: 'center', visible: true, key: 5 },
|
||||
|
||||
])
|
||||
// 表格配置
|
||||
const rightTableConfig = ref(
|
||||
@ -385,9 +386,7 @@ const periods = ref<string[]>([])
|
||||
|
||||
const compareTableData = ref([])
|
||||
|
||||
const aaaa = () => {
|
||||
|
||||
}
|
||||
// 历史数据对比
|
||||
const openDB = () => {
|
||||
// 00507620
|
||||
@ -420,49 +419,6 @@ const openDB = () => {
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
interface copyItem {
|
||||
xmdh: string;
|
||||
xmmc: string;
|
||||
csjg: string | number;
|
||||
refs: string | number;
|
||||
dw: string;
|
||||
jgbz: string;
|
||||
}
|
||||
// 复制函数
|
||||
const copyData = (type: string) => {
|
||||
let str = ''
|
||||
const getCommonRow = (item: copyItem) => `${item.xmdh}\t${item.xmmc}\t${item.csjg}\t${item.refs}\t${item.dw}\t${item.jgbz}\t\r\n`;
|
||||
rightTableData.value.forEach((v: any) => {
|
||||
if (type == 'All') {
|
||||
str = str + getCommonRow(v)
|
||||
str = str + v.xmmc + v.csjg + v.dw + + ',';
|
||||
}
|
||||
if (type == 'N' && v.jgbz != 'M' && v.jgbz != '') {
|
||||
str = str + getCommonRow(v)
|
||||
}
|
||||
if (type == 'Y' && v.jgbz == 'M' && v.jgbz != '') {
|
||||
str = str + getCommonRow(v)
|
||||
}
|
||||
})
|
||||
|
||||
var input = document.createElement('textarea');
|
||||
if (!str) return ElMessage.error('没有数据可复制');
|
||||
// 把文字放进input中,供复制
|
||||
input.value = str;
|
||||
document.body.appendChild(input);
|
||||
// 选中创建的input
|
||||
input.select();
|
||||
var copy_result = document.execCommand('copy');
|
||||
if (copy_result) {
|
||||
ElMessage.success('复制成功');
|
||||
} else {
|
||||
ElMessage.error('复制失败');
|
||||
}
|
||||
// 移除
|
||||
document.body.removeChild(input);
|
||||
}
|
||||
|
||||
// 数据重组
|
||||
const formatGroupedData = (data: any[]) => {
|
||||
if (!data || data.length === 0) return [];
|
||||
@ -536,6 +492,7 @@ const getEcharts = (data: any) => {
|
||||
axisLabel: {
|
||||
rotate: 30,
|
||||
textStyle: {
|
||||
|
||||
color: "#8D949B "
|
||||
}
|
||||
},
|
||||
|
||||
@ -1,296 +0,0 @@
|
||||
<template>
|
||||
<div class="editable-table-container">
|
||||
<el-table :data="tableData" border style="width: 100%" @cell-click="handleCellClick"
|
||||
:cell-class-name="cellClassName">
|
||||
<el-table-column prop="id" label="ID" width="80" align="center" />
|
||||
<el-table-column prop="name" label="姓名" width="120">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, scope.column)">
|
||||
<el-input v-model="scope.row.name" size="small" @blur="handleSave(scope.row, scope.column)"
|
||||
@keyup.enter="handleSave(scope.row, scope.column)" ref="editInput" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ scope.row.name }}</span>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
</el-table-column>
|
||||
<el-table-column prop="age" label="年龄" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, scope.column)">
|
||||
<el-input-number v-model="scope.row.age" :min="0" :max="150" size="small"
|
||||
@blur="handleSave(scope.row, scope.column)" @keyup.enter="handleSave(scope.row, scope.column)"
|
||||
ref="editInput" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ scope.row.age }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="email" label="邮箱">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, scope.column)">
|
||||
<el-input v-model="scope.row.email" size="small" @blur="handleSave(scope.row, scope.column)"
|
||||
@keyup.enter="handleSave(scope.row, scope.column)" ref="editInput" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ scope.row.email }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="department" label="部门" width="150">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, scope.column)">
|
||||
<el-select v-model="scope.row.department" size="small" @change="handleSave(scope.row, scope.column)"
|
||||
@blur="handleSave(scope.row, scope.column)" ref="editSelect" auto-focus>
|
||||
<el-option v-for="dept in departments" :key="dept.value" :label="dept.label" :value="dept.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ getDepartmentLabel(scope.row.department) }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="joinDate" label="入职日期" width="140">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, scope.column)">
|
||||
<el-date-picker v-model="scope.row.joinDate" type="date" format="YYYY-MM-DD" value-format="YYYY-MM-DD"
|
||||
size="small" @blur="handleSave(scope.row, scope.column)" @change="handleSave(scope.row, scope.column)"
|
||||
ref="editDate" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span>{{ scope.row.joinDate }}</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<div class="table-info">
|
||||
<p>点击任意单元格即可直接编辑该单元格内容</p>
|
||||
<p>编辑完成后可按Enter键或点击其他区域保存修改</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, nextTick } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
// 部门选项
|
||||
const departments = [
|
||||
{ label: '技术部', value: 'tech' },
|
||||
{ label: '市场部', value: 'marketing' },
|
||||
{ label: '销售部', value: 'sales' },
|
||||
{ label: '人力资源', value: 'hr' },
|
||||
{ label: '财务部', value: 'finance' }
|
||||
];
|
||||
|
||||
// 表格数据
|
||||
const tableData = reactive([
|
||||
{
|
||||
id: 1,
|
||||
name: '张三',
|
||||
age: 28,
|
||||
email: 'zhangsan@example.com',
|
||||
department: 'tech',
|
||||
joinDate: '2020-03-15',
|
||||
originalData: {} // 用于保存原始数据
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
name: '李四',
|
||||
age: 32,
|
||||
email: 'lisi@example.com',
|
||||
department: 'marketing',
|
||||
joinDate: '2019-07-22',
|
||||
originalData: {}
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
name: '王五',
|
||||
age: 45,
|
||||
email: 'wangwu@example.com',
|
||||
department: 'sales',
|
||||
joinDate: '2018-11-05',
|
||||
originalData: {}
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
name: '赵六',
|
||||
age: 23,
|
||||
email: 'zhaoliu@example.com',
|
||||
department: 'hr',
|
||||
joinDate: '2021-02-18',
|
||||
originalData: {}
|
||||
}
|
||||
]);
|
||||
|
||||
// 当前编辑的单元格信息
|
||||
const editingCell = ref({
|
||||
row: null,
|
||||
column: null
|
||||
});
|
||||
|
||||
// 判断单元格是否处于编辑状态
|
||||
const isEditing = (row, column) => {
|
||||
return editingCell.value.row === row && editingCell.value.column === column;
|
||||
};
|
||||
|
||||
// 获取部门显示名称
|
||||
const getDepartmentLabel = (value) => {
|
||||
const dept = departments.find(item => item.value === value);
|
||||
return dept ? dept.label : '';
|
||||
};
|
||||
|
||||
// 处理单元格点击事件 - 进入编辑状态
|
||||
const handleCellClick = (row, column) => {
|
||||
// ID列不可编辑
|
||||
if (column.property === 'id') return;
|
||||
|
||||
// 如果点击的是当前编辑的单元格,不做处理
|
||||
if (editingCell.value.row === row && editingCell.value.column === column) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 先保存之前编辑的单元格(如果有)
|
||||
if (editingCell.value.row && editingCell.value.column) {
|
||||
handleSave(editingCell.value.row, editingCell.value.column);
|
||||
}
|
||||
|
||||
// 保存原始值,用于取消编辑时恢复
|
||||
row.originalData[column.property] = JSON.parse(JSON.stringify(row[column.property]));
|
||||
|
||||
// 设置当前编辑的单元格
|
||||
editingCell.value = { row, column };
|
||||
|
||||
// 自动聚焦到输入框
|
||||
nextTick(() => {
|
||||
const inputElements = [
|
||||
document.querySelector('.el-input__inner'),
|
||||
document.querySelector('.el-input-number__input'),
|
||||
document.querySelector('.el-select'),
|
||||
document.querySelector('.el-date-editor input')
|
||||
];
|
||||
|
||||
const activeInput = inputElements.find(el => el);
|
||||
if (activeInput) {
|
||||
activeInput.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 保存编辑
|
||||
const handleSave = (row, column) => {
|
||||
// 如果不是编辑状态,直接返回
|
||||
if (!isEditing(row, column)) return;
|
||||
|
||||
const prop = column.property;
|
||||
let isValid = true;
|
||||
let errorMessage = '';
|
||||
|
||||
// 根据字段类型进行验证
|
||||
switch (prop) {
|
||||
case 'name':
|
||||
if (!row[prop].trim()) {
|
||||
isValid = false;
|
||||
errorMessage = '姓名不能为空';
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
if (row[prop] && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(row[prop])) {
|
||||
isValid = false;
|
||||
errorMessage = '请输入有效的邮箱地址';
|
||||
}
|
||||
break;
|
||||
case 'age':
|
||||
if (row[prop] === null || row[prop] <= 0 || row[prop] > 150) {
|
||||
isValid = false;
|
||||
errorMessage = '请输入有效的年龄(1-150)';
|
||||
}
|
||||
break;
|
||||
case 'department':
|
||||
if (!row[prop]) {
|
||||
isValid = false;
|
||||
errorMessage = '请选择部门';
|
||||
}
|
||||
break;
|
||||
case 'joinDate':
|
||||
if (!row[prop]) {
|
||||
isValid = false;
|
||||
errorMessage = '请选择入职日期';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 验证不通过,恢复原始值并提示
|
||||
if (!isValid) {
|
||||
row[prop] = row.originalData[prop];
|
||||
ElMessage.error(errorMessage);
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除编辑状态
|
||||
editingCell.value = { row: null, column: null };
|
||||
|
||||
// 实际应用中这里会调用API保存数据
|
||||
ElMessage.success('数据已更新');
|
||||
console.log(`保存单元格数据: 行ID=${row.id}, 字段=${prop}, 值=${row[prop]}`);
|
||||
};
|
||||
|
||||
// 单元格样式 - 用于突出显示可编辑单元格
|
||||
const cellClassName = ({ row, column }) => {
|
||||
// ID列不可编辑
|
||||
if (column.property === 'id') return '';
|
||||
|
||||
return isEditing(row, column)
|
||||
? 'cell-editing'
|
||||
: 'cell-editable';
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
console.log('点击编辑表格组件已加载');
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.editable-table-container {
|
||||
padding: 20px;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.table-info {
|
||||
margin-top: 15px;
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
::v-deep .cell-editable {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
::v-deep .cell-editable:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
::v-deep .cell-editing {
|
||||
background-color: #e6f7ff !important;
|
||||
}
|
||||
|
||||
::v-deep .el-input,
|
||||
::v-deep .el-input-number,
|
||||
::v-deep .el-select,
|
||||
::v-deep .el-date-editor {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* 移除输入框默认边框,与表格融合更自然 */
|
||||
::v-deep .cell-editing .el-input__inner,
|
||||
::v-deep .cell-editing .el-input-number__input,
|
||||
::v-deep .cell-editing .el-date-editor input {
|
||||
border-color: #409eff;
|
||||
box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
|
||||
}
|
||||
</style>
|
||||
@ -1,529 +0,0 @@
|
||||
<template>
|
||||
<div class="app-container">
|
||||
<el-table :data="tableData" border style="width: 100%" @cell-click="handleCellClick"
|
||||
:cell-class-name="cellClassName" :row-key="row => row.id" height="calc(100vh - 220px)" highlight-current-row
|
||||
ref="tableRef">
|
||||
<!-- 分单类别代号 -->
|
||||
<el-table-column prop="categoryCode" label="分单类别代号" width="120">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'categoryCode')">
|
||||
<el-input v-model="scope.row.categoryCode" size="small" @blur="handleSave(scope.row, 'categoryCode')"
|
||||
@keyup.enter="handleSave(scope.row, 'categoryCode')" ref="getInputRef(scope.row.id, 'categoryCode')"
|
||||
auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'categoryCode' })">
|
||||
{{ scope.row.categoryCode }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 类别名称 -->
|
||||
<el-table-column prop="categoryName" label="类别名称" width="180">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'categoryName')">
|
||||
<el-input v-model="scope.row.categoryName" size="small" @blur="handleSave(scope.row, 'categoryName')"
|
||||
@keyup.enter="handleSave(scope.row, 'categoryName')" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'categoryName' })">
|
||||
{{ scope.row.categoryName }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 类别简称 -->
|
||||
<el-table-column prop="shortName" label="类别简称" width="120">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'shortName')">
|
||||
<el-input v-model="scope.row.shortName" size="small" @blur="handleSave(scope.row, 'shortName')"
|
||||
@keyup.enter="handleSave(scope.row, 'shortName')" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'shortName' })">
|
||||
{{ scope.row.shortName }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 条码类别 -->
|
||||
<el-table-column prop="barcodeType" label="条码类别" width="130">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'barcodeType')">
|
||||
<el-select v-model="scope.row.barcodeType" size="small" @change="handleSave(scope.row, 'barcodeType')"
|
||||
@blur="handleSave(scope.row, 'barcodeType')" auto-focus>
|
||||
<el-option v-for="item in barcodeTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'barcodeType' })">
|
||||
{{ getOptionLabel(barcodeTypeOptions, scope.row.barcodeType) }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 标本类型 -->
|
||||
<el-table-column prop="specimenType" label="标本类型" width="140">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'specimenType')">
|
||||
<el-select v-model="scope.row.specimenType" size="small" @change="handleSave(scope.row, 'specimenType')"
|
||||
@blur="handleSave(scope.row, 'specimenType')" auto-focus>
|
||||
<el-option v-for="item in specimenTypeOptions" :key="item.value" :label="item.label"
|
||||
:value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'specimenType' })">
|
||||
{{ getOptionLabel(specimenTypeOptions, scope.row.specimenType) }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 标本采集说明 -->
|
||||
<el-table-column prop="collectionInstructions" label="标本采集说明" width="220">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'collectionInstructions')">
|
||||
<el-input v-model="scope.row.collectionInstructions" size="small" type="textarea" :rows="2"
|
||||
@blur="handleSave(scope.row, 'collectionInstructions')"
|
||||
@keyup.enter.ctrl="handleSave(scope.row, 'collectionInstructions')" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="multi-line-text"
|
||||
@click.stop="handleCellClick(scope.row, { property: 'collectionInstructions' })">
|
||||
{{ scope.row.collectionInstructions || '-' }}
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 报告领取规则 -->
|
||||
<el-table-column prop="reportCollectionRule" label="报告领取规则" width="160">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'reportCollectionRule')">
|
||||
<el-select v-model="scope.row.reportCollectionRule" size="small"
|
||||
@change="handleSave(scope.row, 'reportCollectionRule')"
|
||||
@blur="handleSave(scope.row, 'reportCollectionRule')" auto-focus>
|
||||
<el-option v-for="item in reportRuleOptions" :key="item.value" :label="item.label" :value="item.value" />
|
||||
</el-select>
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'reportCollectionRule' })">
|
||||
{{ getOptionLabel(reportRuleOptions, scope.row.reportCollectionRule) }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 打印份数 -->
|
||||
<el-table-column prop="printCopies" label="打印份数" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'printCopies')">
|
||||
<el-input-number v-model="scope.row.printCopies" :min="1" :max="10" size="small"
|
||||
@blur="handleSave(scope.row, 'printCopies')" @keyup.enter="handleSave(scope.row, 'printCopies')"
|
||||
auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'printCopies' })">
|
||||
{{ scope.row.printCopies }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 专业组(样本数) -->
|
||||
<el-table-column prop="deptSampleCount" label="专业组(样本数)" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'deptSampleCount')">
|
||||
<el-input-number v-model="scope.row.deptSampleCount" :min="1" size="small"
|
||||
@blur="handleSave(scope.row, 'deptSampleCount')" @keyup.enter="handleSave(scope.row, 'deptSampleCount')"
|
||||
auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'deptSampleCount' })">
|
||||
{{ scope.row.deptSampleCount }}
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 专业组(周转时间) -->
|
||||
<el-table-column prop="deptTurnaroundTime" label="专业组(周转时间)" width="160" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'deptTurnaroundTime')">
|
||||
<el-input-number v-model="scope.row.deptTurnaroundTime" :min="0" size="small" suffix="分钟"
|
||||
@blur="handleSave(scope.row, 'deptTurnaroundTime')"
|
||||
@keyup.enter="handleSave(scope.row, 'deptTurnaroundTime')" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<span @click.stop="handleCellClick(scope.row, { property: 'deptTurnaroundTime' })">
|
||||
{{ scope.row.deptTurnaroundTime }} 分钟
|
||||
</span>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 抗凝 -->
|
||||
<el-table-column prop="anticoagulant" label="抗凝" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.anticoagulant" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 血培 -->
|
||||
<el-table-column prop="bloodCulture" label="血培" width="100" align="center">
|
||||
<template #default="scope">
|
||||
<el-checkbox v-model="scope.row.bloodCulture" />
|
||||
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<!-- 标识颜色 -->
|
||||
<el-table-column prop="markerColor" label="标识颜色" width="140" align="center">
|
||||
<template #default="scope">
|
||||
<template v-if="isEditing(scope.row, 'markerColor')">
|
||||
<el-color-picker v-model="scope.row.markerColor" size="small" @change="handleSave(scope.row, 'markerColor')"
|
||||
@blur="handleSave(scope.row, 'markerColor')" auto-focus />
|
||||
</template>
|
||||
<template v-else>
|
||||
<div class="color-display" @click.stop="handleCellClick(scope.row, { property: 'markerColor' })">
|
||||
<span class="color-block" :style="{ backgroundColor: scope.row.markerColor }" />
|
||||
<span class="color-code">{{ scope.row.markerColor }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, nextTick } from 'vue';
|
||||
import { ElMessage, } from 'element-plus';
|
||||
|
||||
// 选项数据
|
||||
const barcodeTypeOptions = [
|
||||
{ label: '一维条码', value: '1d' },
|
||||
{ label: '二维码', value: '2d' },
|
||||
{ label: 'RFID', value: 'rfid' },
|
||||
{ label: '无', value: 'none' }
|
||||
];
|
||||
|
||||
const specimenTypeOptions = [
|
||||
{ label: '全血', value: 'whole_blood' },
|
||||
{ label: '血清', value: 'serum' },
|
||||
{ label: '血浆', value: 'plasma' },
|
||||
{ label: '尿液', value: 'urine' },
|
||||
{ label: '脑脊液', value: 'csf' },
|
||||
{ label: '其他', value: 'other' }
|
||||
];
|
||||
|
||||
const reportRuleOptions = [
|
||||
{ label: '自助打印', value: 'self_print' },
|
||||
{ label: '人工窗口', value: 'counter' },
|
||||
{ label: '科室配送', value: 'dept_delivery' },
|
||||
{ label: '线上查看', value: 'online' }
|
||||
];
|
||||
|
||||
// 表格数据
|
||||
const tableData = reactive([
|
||||
{
|
||||
id: 1,
|
||||
categoryCode: 'BH001',
|
||||
categoryName: '血常规检查',
|
||||
shortName: '血常规',
|
||||
barcodeType: '1d',
|
||||
specimenType: 'whole_blood',
|
||||
collectionInstructions: '空腹采血,采血量2ml,EDTA抗凝',
|
||||
reportCollectionRule: 'self_print',
|
||||
printCopies: 2,
|
||||
deptSampleCount: 1,
|
||||
deptTurnaroundTime: 30,
|
||||
anticoagulant: false,
|
||||
bloodCulture: false,
|
||||
markerColor: '#409eff',
|
||||
originalData: {}
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
categoryCode: 'BL002',
|
||||
categoryName: '生化全项检查',
|
||||
shortName: '生化全项',
|
||||
barcodeType: '2d',
|
||||
specimenType: 'serum',
|
||||
collectionInstructions: '空腹12小时以上采血,采血量5ml,分离血清',
|
||||
reportCollectionRule: 'counter',
|
||||
printCopies: 1,
|
||||
deptSampleCount: 1,
|
||||
deptTurnaroundTime: 120,
|
||||
anticoagulant: false,
|
||||
bloodCulture: false,
|
||||
markerColor: '#67c23a',
|
||||
originalData: {}
|
||||
}
|
||||
]);
|
||||
|
||||
// 表格引用
|
||||
const tableRef = ref(null);
|
||||
// 输入框引用
|
||||
const inputRefs = ref({});
|
||||
|
||||
// 当前编辑状态:{rowId: xxx, field: 'xxx'}
|
||||
const editingState = ref(null);
|
||||
|
||||
// 设置输入框引用
|
||||
const getInputRef = (rowId, field) => {
|
||||
return (el) => {
|
||||
if (el) {
|
||||
inputRefs.value[`${rowId}-${field}`] = el;
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
// 判断是否处于编辑状态
|
||||
const isEditing = (row, field) => {
|
||||
return editingState.value && editingState.value.rowId === row.id && editingState.value.field === field;
|
||||
};
|
||||
|
||||
// 获取选项标签
|
||||
const getOptionLabel = (options, value) => {
|
||||
const option = options.find(item => item.value === value);
|
||||
return option ? option.label : '';
|
||||
};
|
||||
|
||||
// 处理单元格点击 - 核心修复部分
|
||||
const handleCellClick = (row, column) => {
|
||||
// 忽略操作列
|
||||
if (column.label === '操作') return;
|
||||
|
||||
const field = column.property;
|
||||
|
||||
// 如果点击的是当前编辑的单元格,不重复处理
|
||||
if (isEditing(row, field)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 保存之前编辑的内容
|
||||
if (editingState.value) {
|
||||
const prevRow = tableData.find(r => r.id === editingState.value.rowId);
|
||||
if (prevRow) {
|
||||
handleSave(prevRow, editingState.value.field);
|
||||
}
|
||||
}
|
||||
|
||||
// 保存原始值用于恢复
|
||||
row.originalData[field] = JSON.parse(JSON.stringify(row[field]));
|
||||
|
||||
// 设置当前编辑状态
|
||||
editingState.value = {
|
||||
rowId: row.id,
|
||||
field: field
|
||||
};
|
||||
|
||||
// 强制刷新UI后聚焦
|
||||
nextTick(() => {
|
||||
const inputKey = `${row.id}-${field}`;
|
||||
const inputElement = inputRefs.value[inputKey]?.$el?.querySelector('input') ||
|
||||
inputRefs.value[inputKey]?.$el;
|
||||
|
||||
if (inputElement) {
|
||||
inputElement.focus();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 保存编辑
|
||||
const handleSave = (row, field) => {
|
||||
// 非编辑状态不处理
|
||||
if (!isEditing(row, field)) return;
|
||||
|
||||
let isValid = true;
|
||||
let errorMessage = '';
|
||||
|
||||
// 字段验证
|
||||
switch (field) {
|
||||
case 'categoryCode':
|
||||
if (!row[field]?.trim()) {
|
||||
isValid = false;
|
||||
errorMessage = '分单类别代号不能为空';
|
||||
}
|
||||
break;
|
||||
case 'categoryName':
|
||||
if (!row[field]?.trim()) {
|
||||
isValid = false;
|
||||
errorMessage = '类别名称不能为空';
|
||||
}
|
||||
break;
|
||||
case 'shortName':
|
||||
if (!row[field]?.trim()) {
|
||||
isValid = false;
|
||||
errorMessage = '类别简称不能为空';
|
||||
}
|
||||
break;
|
||||
case 'printCopies':
|
||||
case 'deptSampleCount':
|
||||
if (row[field] < 1) {
|
||||
isValid = false;
|
||||
errorMessage = '数值必须大于0';
|
||||
}
|
||||
break;
|
||||
case 'deptTurnaroundTime':
|
||||
if (row[field] < 0) {
|
||||
isValid = false;
|
||||
errorMessage = '数值不能为负数';
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// 验证失败处理
|
||||
if (!isValid) {
|
||||
row[field] = row.originalData[field];
|
||||
ElMessage.error(errorMessage);
|
||||
// 保持编辑状态以便修正
|
||||
return;
|
||||
}
|
||||
|
||||
// 清除编辑状态
|
||||
editingState.value = null;
|
||||
ElMessage.success('数据已更新');
|
||||
};
|
||||
|
||||
// 单元格样式
|
||||
const cellClassName = ({ row, column }) => {
|
||||
|
||||
return isEditing(row, column.property)
|
||||
? 'cell-editing'
|
||||
: 'cell-editable';
|
||||
};
|
||||
|
||||
// 新增项
|
||||
const addNewItem = () => {
|
||||
const newId = Math.max(...tableData.map(item => item.id), 0) + 1;
|
||||
tableData.unshift({
|
||||
id: newId,
|
||||
categoryCode: `NEW${newId}`,
|
||||
categoryName: '',
|
||||
shortName: '',
|
||||
barcodeType: '1d',
|
||||
specimenType: 'other',
|
||||
collectionInstructions: '',
|
||||
reportCollectionRule: 'self_print',
|
||||
printCopies: 1,
|
||||
deptSampleCount: 1,
|
||||
deptTurnaroundTime: 60,
|
||||
anticoagulant: 'N',
|
||||
bloodCulture: 'N',
|
||||
markerColor: '#909399',
|
||||
originalData: {}
|
||||
});
|
||||
ElMessage.info('已添加新类别,请完善信息');
|
||||
};
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 保持与之前相同的样式 */
|
||||
.editable-table-wrapper {
|
||||
padding: 20px;
|
||||
max-width: 1800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 10px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.table-header h3 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.table-desc {
|
||||
margin-top: 15px;
|
||||
padding: 10px 15px;
|
||||
background-color: #f5f7fa;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* 单元格样式优化 */
|
||||
::v-deep .cell-editable {
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
::v-deep .cell-editable:hover {
|
||||
background-color: #f0f7ff !important;
|
||||
}
|
||||
|
||||
::v-deep .cell-editing {
|
||||
background-color: #e6f7ff !important;
|
||||
}
|
||||
|
||||
::v-deep .el-table .el-table__cell {
|
||||
padding: 6px 0;
|
||||
}
|
||||
|
||||
/* 输入控件样式 */
|
||||
::v-deep .el-input,
|
||||
::v-deep .el-input-number,
|
||||
::v-deep .el-select,
|
||||
::v-deep .el-color-picker,
|
||||
::v-deep .el-textarea {
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
::v-deep .el-textarea__inner {
|
||||
min-height: 60px;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* 多行文本和颜色显示样式 */
|
||||
.multi-line-text {
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
line-height: 1.4;
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.color-display {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.color-block {
|
||||
display: inline-block;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
border-radius: 3px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.color-code {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
max-width: 80px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
</style>
|
||||
@ -32,8 +32,6 @@
|
||||
// "allowUnknownInTemplate": true // 允许模板中使用未知属性
|
||||
// }
|
||||
},
|
||||
"jsx": "preserve",
|
||||
"types": ["element-plus/global"],
|
||||
"include": ["src/**/*","src/**/*.ts", "src/**/*.vue","src/**/**/*.vue", "src/**/*.tsx", "src/**/*.d.ts", "auto-imports.d.ts"], // **表示任意目录,而 * 表示任意文件。这表明 src 目录中的所有文件都将被编译
|
||||
"exclude": ["node_modules", "dist"] ,// 指示不需要编译的文件目录
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user