| | |
| | | border |
| | | style="width: 100%" |
| | | class="medical-table" |
| | | v-fit-columns |
| | | :key="tableKey" |
| | | @header-dragend="handleHeaderDragEnd" |
| | | v-loading="tableLoading" |
| | |
| | | <el-table-column |
| | | v-for="(col, index) in dynamicColumns" |
| | | :key="col.key" |
| | | :label="col.label" |
| | | :min-width="160" |
| | | :resizable="isEditing" |
| | | header-align="center" |
| | | align="center" |
| | | > |
| | | <!-- 自定义列头 --> |
| | | <template #header> |
| | | <div class="custom-column-header"> |
| | | <div class="header-content" @click.stop="handleEditColumn(index)"> |
| | | <span class="header-label">{{ |
| | | col.label.replace("\n", " ") |
| | | }}</span> |
| | | <el-tooltip |
| | | v-if="col.remark" |
| | | :content="col.remark" |
| | | placement="top" |
| | | > |
| | | <i class="el-icon-info header-remark-icon"></i> |
| | | </el-tooltip> |
| | | </div> |
| | | <div class="header-actions" v-if="isEditing"> |
| | | <el-tooltip content="编辑时间" placement="top"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-edit" |
| | | class="header-btn" |
| | | @click.stop="handleEditColumn(index)" |
| | | /> |
| | | </el-tooltip> |
| | | <el-tooltip content="删除此列" placement="top"> |
| | | <el-button |
| | | size="mini" |
| | | type="text" |
| | | icon="el-icon-delete" |
| | | class="header-btn delete-btn" |
| | | @click.stop="handleDeleteColumnByIndex(index)" |
| | | /> |
| | | </el-tooltip> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <template #default="scope"> |
| | | <div class="cell-content-wrapper"> |
| | | <template v-if="scope.row.type === 'select'"> |
| | |
| | | methods: { |
| | | // 从外部数据初始化组件 |
| | | initFromExternalData() { |
| | | console.log(this.internalData, "this.internalData"); |
| | | console.log(this.internalData, "this.internalData"); |
| | | |
| | | if (this.internalData.data && this.internalData.columns) { |
| | | this.tableData = this.internalData.data.map(item => ({ |
| | | ...item, |
| | | values: |
| | | item.values || new Array(this.internalData.columns.length).fill("") |
| | | })); |
| | | this.dynamicColumns = [...this.internalData.columns]; |
| | | } else { |
| | | this.initTableData(); |
| | | } |
| | | if (this.internalData.data && this.internalData.columns) { |
| | | // 获取最新的项目定义(含你刚改的白细胞 number 类型) |
| | | const latestItems = this.getMedicalItems(); |
| | | |
| | | // 初始化附件 |
| | | if (this.internalData.attachments) { |
| | | this.attachments = [...this.internalData.attachments]; |
| | | } |
| | | }, |
| | | this.tableData = this.internalData.data.map(item => { |
| | | // 找到对应的最新定义 |
| | | const latestItem = latestItems.find( |
| | | l => l.itemName === item.itemName |
| | | ); |
| | | |
| | | return { |
| | | ...item, // ✅ 先把老数据展开(含 values、旧 type 等) |
| | | ...latestItem, // ✅ 再用最新定义覆盖(强制更新 type、options、unit 等) |
| | | values: |
| | | item.values || |
| | | new Array(this.internalData.columns.length).fill("") |
| | | }; |
| | | }); |
| | | |
| | | this.dynamicColumns = [...this.internalData.columns]; |
| | | } else { |
| | | this.initTableData(); |
| | | } |
| | | |
| | | // 初始化附件 |
| | | if (this.internalData.attachments) { |
| | | this.attachments = [...this.internalData.attachments]; |
| | | } |
| | | }, |
| | | |
| | | // 初始化默认表格数据 |
| | | initTableData() { |
| | |
| | | }, |
| | | { |
| | | itemName: "白细胞", |
| | | type: "select", |
| | | type: "number", |
| | | required: true, |
| | | options: [ |
| | | { value: "-", label: "阴性(-)" }, |
| | | { value: "+", label: "阳性(+)" }, |
| | | { value: "++", label: "阳性(++)" }, |
| | | { value: "+++", label: "阳性(+++)" } |
| | | ], |
| | | reference: "正常为阴性(-)" |
| | | unit: "/μL", |
| | | reference: "0-385", |
| | | min: 0, |
| | | max: 385, |
| | | placeholder: "0-385" |
| | | }, |
| | | { |
| | | itemName: "红细胞", |
| | |
| | | }); |
| | | }, |
| | | |
| | | editColumn(index) { |
| | | handleEditColumn(index) { |
| | | if (!this.isEditing) return; |
| | | this.editingColumnIndex = index; |
| | | const column = this.dynamicColumns[index]; |
| | | this.columnForm = { |
| | | date: column.date, |
| | | time: column.time, |
| | | date: column.date || "", |
| | | time: column.time || "", |
| | | remark: column.remark || "" |
| | | }; |
| | | this.columnDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.$refs.columnFormU && this.$refs.columnFormU.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | confirmAddColumn() { |
| | |
| | | this.saveLoading = true; |
| | | |
| | | if (this.editingColumnIndex !== null) { |
| | | // 编辑现有列 |
| | | const column = this.dynamicColumns[this.editingColumnIndex]; |
| | | column.label = `${this.columnForm.date}\n${this.columnForm.time}`; |
| | | column.date = this.columnForm.date; |
| | |
| | | column.remark = this.columnForm.remark; |
| | | this.$message.success("时间点修改成功"); |
| | | } else { |
| | | // 新增列 |
| | | const newColumn = { |
| | | label: `${this.columnForm.date}\n${this.columnForm.time}`, |
| | | key: `time${Date.now()}`, |
| | |
| | | time: this.columnForm.time, |
| | | remark: this.columnForm.remark |
| | | }; |
| | | this.internalData.columns.push(newColumn); |
| | | |
| | | this.dynamicColumns.push(newColumn); |
| | | this.tableData.forEach(row => { |
| | |
| | | |
| | | handleDeleteColumn() { |
| | | if (this.editingColumnIndex !== null) { |
| | | this.$confirm("确定要删除这个时间点吗?", "提示", { |
| | | confirmButtonText: "确定", |
| | | this.$confirm( |
| | | `确定要删除「${this.dynamicColumns[ |
| | | this.editingColumnIndex |
| | | ].label.replace("\n", " ")}」这个时间点的所有数据吗?`, |
| | | "删除确认", |
| | | { |
| | | confirmButtonText: "确定删除", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | } |
| | | ) |
| | | .then(() => { |
| | | this.dynamicColumns.splice(this.editingColumnIndex, 1); |
| | | this.tableData.forEach(row => { |
| | | if (row.values && row.values.length > this.editingColumnIndex) { |
| | | row.values.splice(this.editingColumnIndex, 1); |
| | | } |
| | | }); |
| | | this.columnDialogVisible = false; |
| | | this.tableKey += 1; |
| | | this.saveData(); |
| | | this.$message.success("时间点删除成功"); |
| | | }) |
| | | .catch(() => { |
| | | // 取消删除,不做任何操作 |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | handleDeleteColumnByIndex(index) { |
| | | this.$confirm( |
| | | `确定要删除「${this.dynamicColumns[index].label.replace( |
| | | "\n", |
| | | " " |
| | | )}」这个时间点的所有数据吗?`, |
| | | "删除确认", |
| | | { |
| | | confirmButtonText: "确定删除", |
| | | cancelButtonText: "取消", |
| | | type: "warning" |
| | | }).then(() => { |
| | | this.dynamicColumns.splice(this.editingColumnIndex, 1); |
| | | } |
| | | ) |
| | | .then(() => { |
| | | this.dynamicColumns.splice(index, 1); |
| | | this.tableData.forEach(row => { |
| | | row.values.splice(this.editingColumnIndex, 1); |
| | | if (row.values && row.values.length > index) { |
| | | row.values.splice(index, 1); |
| | | } |
| | | }); |
| | | this.columnDialogVisible = false; |
| | | this.tableKey += 1; |
| | | this.saveData(); |
| | | this.$message.success("时间点删除成功"); |
| | | }) |
| | | .catch(() => { |
| | | // 取消删除,不做任何操作 |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | handleDialogClosed() { |
| | |
| | | overflow-x: auto; |
| | | } |
| | | |
| | | /* 自定义列头样式 */ |
| | | .custom-column-header { |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | gap: 4px; |
| | | padding: 4px 0; |
| | | } |
| | | |
| | | .header-content { |
| | | display: flex; |
| | | align-items: center; |
| | | gap: 4px; |
| | | cursor: pointer; |
| | | transition: color 0.2s; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .header-content:hover { |
| | | color: #409eff; |
| | | } |
| | | |
| | | .header-label { |
| | | font-size: 13px; |
| | | line-height: 1.4; |
| | | } |
| | | |
| | | .header-remark-icon { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | cursor: help; |
| | | } |
| | | |
| | | .header-actions { |
| | | display: flex; |
| | | gap: 4px; |
| | | opacity: 0; |
| | | transition: opacity 0.2s; |
| | | } |
| | | |
| | | .custom-column-header:hover .header-actions { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .header-btn { |
| | | padding: 2px 4px !important; |
| | | font-size: 14px !important; |
| | | } |
| | | |
| | | .header-btn.delete-btn { |
| | | color: #f56c6c !important; |
| | | } |
| | | |
| | | .header-btn.delete-btn:hover { |
| | | color: #c0392b !important; |
| | | } |
| | | |
| | | /* 编辑模式下始终显示操作按钮 */ |
| | | .is-editing .header-actions { |
| | | opacity: 1; |
| | | } |
| | | |
| | | .item-name-cell { |
| | | display: flex; |
| | | align-items: center; |
| | |
| | | align-items: center; |
| | | justify-content: center; |
| | | gap: 8px; |
| | | min-height: 32px; |
| | | min-height: 36px; |
| | | } |
| | | |
| | | .value-input { |
| | |
| | | .value-text { |
| | | font-weight: 500; |
| | | color: #303133; |
| | | max-width: 80px; |
| | | max-width: 90px; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | |
| | | flex-direction: column; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .custom-column-header { |
| | | padding: 2px 0; |
| | | } |
| | | |
| | | .header-actions { |
| | | opacity: 1; |
| | | } |
| | | } |
| | | </style> |