|
|
<template> <div id="changeUser-container" :key="refreshKey"> <!-- 顶部导航 --> <div class="header"> <div class="header-left"> <img src="@/assets/Index/left.svg" alt="返回" @click.stop="goBack" /> <span class="title">患者信息</span> <div class="divider"></div> <span class="tube-type">试管类型({{ tubeType }})</span> </div> </div>
<!-- 主要内容区域 --> <div class="content"> <!-- 上半部分样本 --> <div class="sample-section" v-for="box in boxes" :key="box.id"> <!-- 左侧标题栏 --> <div class="section-labels"> <div class="label-column"> <div class="label-item"> <span class="label">序号</span> </div> <div class="label-item"> <span class="label">试管信息</span> </div> <div class="label-item"> <span class="label">样本条形码</span> </div> <div class="label-item"> <span class="label">用户ID</span> </div> </div> </div>
<!-- 右侧样本展示 --> <div class="samples-grid"> <div v-for="item in box.samples" :key="item.tubeIndex" class="sample-item" :class="{ 'selected': selectedSampleId === item.tubeIndex }" @click="selectSample(item.tubeIndex)"> <div class="sample-content"> <!-- 序号 --> <div class="item-index">{{ item.tubeIndex + 1 }}</div>
<!-- 试管圆圈 --> <div class="sample-circle" :style="generateSampleBackground(item.projId!.filter(proj => proj !== null) as ReactionPlate[])"> <span class="blood-type">{{ item.bloodType }}</span> </div>
<!-- 输入框组 --> <div class="inputs"> <input class="input-field" v-model="item.sampleBarcode" placeholder="条形码" @focus="showKeyboard('barcode', item)" readonly /> <input class="input-field" v-model="item.userid" placeholder="用户ID" @focus="showKeyboard('userid', item)" readonly /> </div> </div> </div> </div> </div> </div>
<!-- 底部按钮 --> <div class="footer"> <button class="btn cancel" @click="goBack">取消</button> <button class="btn confirm" @click="confirmChange">确定</button> </div> <!-- 键盘组件 --> <transition name="slide-up"> <div class="keyboard-container"> <SimpleKeyboard v-if="keyboardVisible" :input="currentInputValue" @onChange="handleKeyboardInput" @onKeyPress="handleKeyPress" /> </div> </transition> </div> </template>
<script setup lang="ts"> import { onMounted, ref, watchEffect, onUnmounted, onActivated } from 'vue' import { useRouter } from 'vue-router' import { useTestTubeStore, useConsumablesStore, useSettingTestTubeStore } from '../../../store' import SimpleKeyboard from '../../../components/SimpleKeyboard.vue' import type { DataItem, ReactionPlate, TubeRack, handleTube, } from '../../../types/Index' import { getBloodTypeLabel, processTubeSettings, generateSampleBackground, } from '../Utils' import { updateTubeConfig } from '../../../services' import { ElMessage } from 'element-plus' import { ConsumableGroupBase } from '../../../websocket/socket' const testTubeStore = useTestTubeStore() const consumableStore = useConsumablesStore() const router = useRouter() const tubeInfo = ref<DataItem>({} as DataItem) //试管架信息
const processedTubeInfo = ref<handleTube>({} as handleTube) //经过清洗的试管架信息
const tubeSettings = ref<TubeRack[]>([]) //获取到的试管信息
const tubeType = ref<string>(testTubeStore.type || '自动') //导航栏的试管类型
const plates = ref<ConsumableGroupBase[]>(consumableStore.plates) //反应板信息
const selectedSampleId = ref<number | null>(null) //选中的试管
const keyboardVisible = ref(false) const currentInputValue = ref('') const currentInput = ref<{ type: 'barcode' | 'userid', item: any }>({ type: 'barcode', item: null }) const settingTestTubeStore = useSettingTestTubeStore() const refreshKey = ref(0) const modifiedTubes = ref(new Map<number, any>()) onMounted(() => { tubeInfo.value = testTubeStore.tubeInfo console.log("🚀 ~ onMounted ~ tubeInfo.value:", tubeInfo.value) tubeSettings.value = processTubeSettings( tubeInfo.value.tubeSettings, plates.value, getBloodTypeLabel, ) processedTubeInfo.value = { ...tubeInfo.value, tubeSettings: tubeSettings.value, } }) onActivated(() => { tubeInfo.value = testTubeStore.tubeInfo console.log("🚀 ~ onActivated ~ tubeInfo.value:", tubeInfo.value) tubeSettings.value = processTubeSettings( tubeInfo.value.tubeSettings, plates.value, getBloodTypeLabel, ) processedTubeInfo.value = { ...tubeInfo.value, tubeSettings: tubeSettings.value, } }) //返回试管页面
const goBack = () => { router.push('/index/regular/test-tube') } // 选择样本的函数
const selectSample = (id: number) => { if (selectedSampleId.value === id) { selectedSampleId.value = null } else { selectedSampleId.value = id } } const boxes = ref([ { id: 1, samples: [] as TubeRack[], }, { id: 2, samples: [] as TubeRack[], }, ]) watchEffect(() => { boxes.value.forEach((item) => { if (item.id === 1) { item.samples = tubeSettings.value.slice(0, 5) } else { item.samples = tubeSettings.value.slice(5, 10) } }) }) //确认事件
const confirmChange = async () => { const modifiedSettings = Array.from(modifiedTubes.value.values())
if (modifiedSettings.length > 0) { try { const updatePromises = modifiedSettings.map(setting => updateTubeConfig({ uuid: processedTubeInfo.value.uuid, setting }) ) const results = await Promise.all(updatePromises)
const allSuccess = results.every(res => res && res.success)
if (allSuccess) { ElMessage.success('所有修改已保存') goBack() } else { ElMessage.error('部分修改保存失败') } } catch (error) { console.error('保存修改失败:', error) ElMessage.error('保存失败') } } else { ElMessage.warning('没有需要保存的修改') } }
// 显示键盘
const showKeyboard = (type: 'barcode' | 'userid', item: any) => { keyboardVisible.value = true currentInput.value = { type, item } currentInputValue.value = type === 'barcode' ? item.sampleBarcode : item.userid }
// 处理键盘输入
const handleKeyboardInput = (input: string) => { if (!currentInput.value.item) return
if (currentInput.value.type === 'barcode') { currentInput.value.item.sampleBarcode = input } else { currentInput.value.item.userid = input }
currentInputValue.value = input
modifiedTubes.value.set(currentInput.value.item.tubeIndex, { tubeIndex: currentInput.value.item.tubeIndex, userid: currentInput.value.item.userid, sampleBarcode: currentInput.value.item.sampleBarcode, projId: currentInput.value.item.projId?.map((p: ReactionPlate) => p.projId) || [], bloodType: currentInput.value.item.bloodType, })
settingTestTubeStore.updateTubeSetting(tubeInfo.value.uuid, { tubeIndex: currentInput.value.item.tubeIndex, [currentInput.value.type === 'userid' ? 'userid' : 'sampleBarcode']: input }) }
// 处理键盘按键
const handleKeyPress = (button: string) => { if (button === '{enter}') { keyboardVisible.value = false currentInputValue.value = '' } }
// 在组件卸载时清理
onUnmounted(() => { modifiedTubes.value.clear() }) </script>
<style lang="less" scoped> #changeUser-container { width: 100%; height: 95vh; display: flex; flex-direction: column; position: relative; background-color: #f5f7fa; overflow: hidden;
.header { height: 80px; background-color: #fff; padding: 0 20px; display: flex; align-items: center;
.header-left { display: flex; align-items: center; gap: 12px;
img { width: 24px; height: 24px; cursor: pointer; }
.title { font-size: 28px; color: #303133; }
.tube-type { font-size: 24px; color: #606266; } } }
.content { padding: 16px; display: flex; flex-direction: column; gap: 20px; overflow: hidden;
.sample-section { background-color: #fff; border-radius: 8px; padding: 20px; display: flex; height: 380px;
.section-labels { width: 120px; display: flex; flex-direction: column; padding-top: 20px;
.label-column { .label-item { margin-bottom: 16px;
.label { font-size: 24px; color: #606266; } } } }
.samples-grid { flex: 1; display: grid; grid-template-columns: repeat(5, 1fr); gap: 12px;
.sample-item { background-color: #f5f7fa; border-radius: 4px; padding: 12px;
.sample-content { display: flex; flex-direction: column; align-items: center;
.item-index { font-size: 22px; color: #606266; margin-bottom: 8px; }
.sample-circle { width: 60px; height: 60px; border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 8px 0;
.blood-type { font-size: 20px; color: #fff; } }
.inputs { width: 100%; margin-top: 12px;
.input-field { width: 100%; height: 36px; border: 1px solid #dcdfe6; border-radius: 4px; margin-bottom: 8px; font-size: 20px; text-align: center; background-color: #fff;
&::placeholder { color: #c0c4cc; font-size: 18px; } } } } } } } }
.footer { height: 80px; padding: 10px 20px; background-color: #fff; display: flex; justify-content: center; gap: 16px;
.btn { width: 320px; height: 60px; border-radius: 30px; font-size: 24px; font-weight: normal;
&.cancel { background-color: #f5f7fa; color: #606266; border: 1px solid #dcdfe6; }
&.confirm { background-color: #409eff; color: #fff; } } }
.keyboard-container { position: absolute; bottom: 0; left: 0; width: 100%; height: 20vh; background-color: #fff; }
// 键盘动画
.slide-up-enter-active, .slide-up-leave-active { transition: transform 0.3s ease; }
.slide-up-enter-from, .slide-up-leave-to { transform: translateY(100%); } } </style>
|