| | |
| | | <div class="medical-panel"> |
| | | <div class="panel-header"> |
| | | <h3>肝功能肾功能记录表</h3> |
| | | <el-button |
| | | v-if="isEditing" |
| | | type="primary" |
| | | size="small" |
| | | @click="addColumn" |
| | | icon="el-icon-plus" |
| | | > |
| | | 新增记录列 |
| | | </el-button> |
| | | <div class="panel-actions"> |
| | | <el-button |
| | | v-if="isEditing" |
| | | type="primary" |
| | | size="small" |
| | | @click="addColumn" |
| | | icon="el-icon-plus" |
| | | > |
| | | 新增记录列 |
| | | </el-button> |
| | | <el-tooltip content="刷新表格布局" placement="top"> |
| | | <el-button |
| | | size="small" |
| | | @click="forceTableLayout" |
| | | icon="el-icon-refresh" |
| | | > |
| | | 刷新布局 |
| | | </el-button> |
| | | </el-tooltip> |
| | | </div> |
| | | </div> |
| | | |
| | | <el-table |
| | |
| | | style="width: 100%" |
| | | class="medical-table" |
| | | :key="tableKey" |
| | | |
| | | @header-dragend="handleHeaderDragEnd" |
| | | > |
| | | <el-table-column |
| | |
| | | class-name="leave-alone" |
| | | > |
| | | <template #default="scope"> |
| | | <span :class="{ 'required-item': scope.row.required }"> |
| | | {{ scope.row.itemName }} |
| | | </span> |
| | | <div class="item-name-cell"> |
| | | <span :class="{ 'required-item': scope.row.required }"> |
| | | {{ scope.row.itemName }} |
| | | </span> |
| | | <el-tooltip |
| | | v-if="scope.row.reference" |
| | | :content="`参考范围: ${scope.row.reference}`" |
| | | placement="top" |
| | | > |
| | | <i class="el-icon-info reference-icon"></i> |
| | | </el-tooltip> |
| | | </div> |
| | | </template> |
| | | </el-table-column> |
| | | |
| | | <el-table-column |
| | | v-for="(col, index) in dynamicColumns" |
| | | :key="col.key" |
| | | :label="col.label" |
| | | :min-width="120" |
| | | :min-width="140" |
| | | :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"> |
| | | <el-input |
| | |
| | | |
| | | <!-- 列编辑对话框 --> |
| | | <el-dialog |
| | | title="编辑时间点" |
| | | :title="`${editingColumnIndex !== null ? '编辑' : '新增'}时间点`" |
| | | :visible.sync="columnDialogVisible" |
| | | width="400px" |
| | | width="450px" |
| | | @closed="handleDialogClosed" |
| | | > |
| | | <el-form :model="columnForm" label-width="80px" ref="columnForm"> |
| | | <el-form-item |
| | | label="日期" |
| | | prop="date" |
| | | :rules="[ |
| | | { required: true, message: '请选择日期', trigger: 'change' } |
| | | ]" |
| | | > |
| | | <el-form |
| | | :model="columnForm" |
| | | label-width="80px" |
| | | ref="columnForm" |
| | | :rules="columnRules" |
| | | > |
| | | <el-form-item label="日期" prop="date"> |
| | | <el-date-picker |
| | | v-model="columnForm.date" |
| | | type="date" |
| | | placeholder="选择日期" |
| | | value-format="yyyy-MM-dd" |
| | | style="width: 100%" |
| | | :disabled-date="disableFutureDates" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item |
| | | label="时间" |
| | | prop="time" |
| | | :rules="[ |
| | | { required: true, message: '请选择时间', trigger: 'change' } |
| | | ]" |
| | | > |
| | | <el-form-item label="时间" prop="time"> |
| | | <el-time-picker |
| | | v-model="columnForm.time" |
| | | placeholder="选择时间" |
| | |
| | | style="width: 100%" |
| | | /> |
| | | </el-form-item> |
| | | <el-form-item label="备注" prop="remark"> |
| | | <el-input |
| | | v-model="columnForm.remark" |
| | | type="textarea" |
| | | :rows="2" |
| | | placeholder="可选填写时间点备注信息" |
| | | maxlength="100" |
| | | show-word-limit |
| | | /> |
| | | </el-form-item> |
| | | </el-form> |
| | | <span slot="footer" class="dialog-footer"> |
| | | <el-button @click="columnDialogVisible = false">取消</el-button> |
| | | <el-button type="primary" @click="confirmAddColumn">确定</el-button> |
| | | <el-button |
| | | v-if="editingColumnIndex !== null" |
| | | type="danger" |
| | | @click="handleDeleteColumn" |
| | | > |
| | | 删除 |
| | | </el-button> |
| | | <el-button |
| | | type="primary" |
| | | @click="confirmAddColumn" |
| | | :loading="saveLoading" |
| | | > |
| | | 确定 |
| | | </el-button> |
| | | </span> |
| | | </el-dialog> |
| | | |
| | |
| | | columnDialogVisible: false, |
| | | columnForm: { |
| | | date: "", |
| | | time: "" |
| | | time: "", |
| | | remark: "" |
| | | }, |
| | | editingColumnIndex: null, |
| | | tableKey: 0, |
| | | tableLoading: false, |
| | | saveLoading: false, |
| | | internalData: {}, |
| | | columnRules: { |
| | | date: [{ required: true, message: "请选择日期", trigger: "change" }], |
| | | time: [{ required: true, message: "请选择时间", trigger: "change" }] |
| | | }, |
| | | |
| | | // 预览相关 |
| | | previewVisible: false, |
| | |
| | | this.forceTableLayout(); |
| | | }); |
| | | }, |
| | | deep: true |
| | | deep: true, |
| | | immediate: true |
| | | }, |
| | | // 监听附件数据变化 |
| | | attachments: { |
| | |
| | | label: `${new Date().toISOString().split("T")[0]}\n08:00`, |
| | | key: "time1", |
| | | date: new Date().toISOString().split("T")[0], |
| | | time: "08:00" |
| | | time: "08:00", |
| | | remark: "晨起检测" |
| | | } |
| | | ]; |
| | | } |
| | |
| | | }, |
| | | |
| | | addColumn() { |
| | | this.editingColumnIndex = null; |
| | | this.columnForm = { |
| | | date: new Date().toISOString().split("T")[0], |
| | | time: "08:00" |
| | | time: "08:00", |
| | | remark: "" |
| | | }; |
| | | this.columnDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.$refs.columnForm && this.$refs.columnForm.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | handleEditColumn(index) { |
| | | if (!this.isEditing) return; |
| | | this.editingColumnIndex = index; |
| | | const column = this.dynamicColumns[index]; |
| | | this.columnForm = { |
| | | date: column.date || '', |
| | | time: column.time || '', |
| | | remark: column.remark || '' |
| | | }; |
| | | this.columnDialogVisible = true; |
| | | this.$nextTick(() => { |
| | | this.$refs.columnForm && this.$refs.columnForm.clearValidate(); |
| | | }); |
| | | }, |
| | | |
| | | confirmAddColumn() { |
| | |
| | | return; |
| | | } |
| | | |
| | | const newIndex = this.dynamicColumns.length + 1; |
| | | const newColumn = { |
| | | label: `${this.columnForm.date}\n${this.columnForm.time}`, |
| | | key: `time${newIndex}`, |
| | | date: this.columnForm.date, |
| | | time: this.columnForm.time |
| | | }; |
| | | this.saveLoading = true; |
| | | |
| | | this.dynamicColumns.push(newColumn); |
| | | 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.time = this.columnForm.time; |
| | | column.remark = this.columnForm.remark; |
| | | this.$message.success("时间点修改成功"); |
| | | } else { |
| | | // 新增列 |
| | | const newIndex = this.dynamicColumns.length + 1; |
| | | const newColumn = { |
| | | label: `${this.columnForm.date}\n${this.columnForm.time}`, |
| | | key: `time${newIndex}_${Date.now()}`, |
| | | date: this.columnForm.date, |
| | | time: this.columnForm.time, |
| | | remark: this.columnForm.remark |
| | | }; |
| | | |
| | | // 为所有行新增一个空值 |
| | | this.tableData.forEach(row => { |
| | | if (!row.values) { |
| | | row.values = []; |
| | | } |
| | | row.values.push(""); |
| | | }); |
| | | this.dynamicColumns.push(newColumn); |
| | | |
| | | // 为所有行新增一个空值 |
| | | this.tableData.forEach(row => { |
| | | if (!row.values) { |
| | | row.values = []; |
| | | } |
| | | row.values.push(""); |
| | | }); |
| | | |
| | | this.$message.success("时间点添加成功"); |
| | | } |
| | | |
| | | this.columnDialogVisible = false; |
| | | this.$message.success("时间点添加成功"); |
| | | this.saveLoading = false; |
| | | this.tableKey += 1; |
| | | this.saveData(); |
| | | }); |
| | | }, |
| | | |
| | | handleDeleteColumn() { |
| | | if (this.editingColumnIndex !== null) { |
| | | 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(index, 1); |
| | | |
| | | // 同步删除每行对应的数据 |
| | | this.tableData.forEach(row => { |
| | | if (row.values && row.values.length > index) { |
| | | row.values.splice(index, 1); |
| | | } |
| | | }); |
| | | |
| | | this.tableKey += 1; |
| | | this.saveData(); |
| | | this.$message.success("时间点删除成功"); |
| | | }).catch(() => { |
| | | // 取消删除,不做任何操作 |
| | | }); |
| | | }, |
| | | |
| | | handleDialogClosed() { |
| | | this.columnForm = { date: "", time: "" }; |
| | | this.editingColumnIndex = null; |
| | | this.columnForm = { date: "", time: "", remark: "" }; |
| | | this.$refs.columnForm && this.$refs.columnForm.clearValidate(); |
| | | }, |
| | | |
| | | disableFutureDates(time) { |
| | | return time.getTime() > Date.now(); |
| | | }, |
| | | |
| | | handleValueChange(row, columnIndex) { |
| | |
| | | font-weight: 600; |
| | | } |
| | | |
| | | .panel-actions { |
| | | display: flex; |
| | | gap: 12px; |
| | | align-items: center; |
| | | } |
| | | |
| | | .medical-table { |
| | | margin-bottom: 30px; |
| | | min-width: 100%; |
| | |
| | | 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; |
| | | justify-content: space-between; |
| | | } |
| | | |
| | | .reference-icon { |
| | | color: #909399; |
| | | font-size: 14px; |
| | | cursor: help; |
| | | margin-left: 8px; |
| | | } |
| | | |
| | | .cell-content { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | min-height: 32px; |
| | | min-height: 38px; |
| | | gap: 8px; |
| | | } |
| | | |
| | | .value-input { |
| | | flex: 1; |
| | | margin-right: 8px; |
| | | min-width: 60px; |
| | | } |
| | | |
| | | .value-display { |
| | | font-weight: 500; |
| | | color: #303133; |
| | | max-width: 70px; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | } |
| | | |
| | | .unit { |
| | | color: #909399; |
| | | font-size: 12px; |
| | | margin-left: 4px; |
| | | font-size: 11px; |
| | | margin-left: 2px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .reference-range { |
| | | color: #67c23a; |
| | | font-size: 12px; |
| | | font-size: 11px; |
| | | font-style: italic; |
| | | margin-left: 8px; |
| | | margin-left: 4px; |
| | | flex-shrink: 0; |
| | | } |
| | | |
| | | .required-item::before { |
| | |
| | | .panel-header { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 12px; |
| | | } |
| | | |
| | | .panel-header h3 { |
| | | margin-bottom: 10px; |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | .panel-actions { |
| | | width: 100%; |
| | | justify-content: flex-end; |
| | | } |
| | | |
| | | .cell-content { |
| | | flex-direction: column; |
| | | align-items: flex-start; |
| | | gap: 4px; |
| | | } |
| | | |
| | | .reference-range { |
| | | margin-left: 0; |
| | | margin-top: 4px; |
| | | margin-top: 2px; |
| | | } |
| | | |
| | | .attachment-section { |
| | | padding: 10px; |
| | | } |
| | | |
| | | .custom-column-header { |
| | | padding: 2px 0; |
| | | } |
| | | |
| | | .header-actions { |
| | | opacity: 1; |
| | | } |
| | | } |
| | | </style> |