Browse Source

完成试管架基础布局

feature/history-20250108
zhangjiming 7 months ago
parent
commit
15768147d6
  1. 2
      package.json
  2. 262
      src/pages/Index/Regular/TestTube.vue
  3. 1
      src/pages/Index/components/Consumables/BallGrid.vue
  4. 165
      src/pages/Index/components/TestTube/ProjectSetting.vue
  5. 466
      src/pages/Index/components/TestTube/TestTubeRack.vue
  6. 33
      src/pages/Index/components/TestTube/Tube.vue
  7. 2
      src/pages/Index/components/TestTube/index.ts
  8. 1
      src/pages/Index/components/index.ts
  9. 22
      src/services/Index/Test-tube/test-tube.ts
  10. 2
      src/types/Index/TestTube.ts

2
package.json

@ -21,6 +21,7 @@
"nanoid": "^5.0.7",
"pinia": "^2.2.4",
"pinia-plugin-persistedstate": "^4.1.2",
"ramda": "^0.30.1",
"sass": "^1.79.5",
"simple-keyboard": "^3.8.17",
"simple-keyboard-layouts": "^3.4.41",
@ -30,6 +31,7 @@
"devDependencies": {
"@types/mockjs": "^1.0.10",
"@types/node": "^22.7.5",
"@types/ramda": "^0.30.2",
"@types/vue": "^2.0.0",
"@typescript-eslint/eslint-plugin": "^8.8.1",
"@typescript-eslint/parser": "^8.8.1",

262
src/pages/Index/Regular/TestTube.vue

@ -2,92 +2,139 @@
<div id="configuration-container" v-loading="loading">
<!-- 渲染试管架列表 -->
<div class="tube-rack-list">
<div v-for="(tubeRack, index) in tubeRacks" :key="`${tubeRack.uuid || index}-${componentRefreshKey}`"
class="tube-rack-container">
<TestTubeRack ref="tubeRackComponentInstance" :tubeRack="tubeRack" :plates="plates" :key="refreshKey"
@updateActivate="handleActivateChange" @updateSelectedSamples="handleUpdateSelectedSamples"
@deleteTubeRack="deleteHandle" @changeUser="handleChangeUser"
@clearProjectSelection="clearProjectSelection" />
<div
v-for="(tubeRack, index) in tubeRacks"
:key="tubeRack.uuid"
class="tube-rack-container"
>
<TestTubeRack :tubeRack="tubeRack" :index="index" />
</div>
</div>
<!-- 添加试管架按钮 -->
<div class="add-tube" @click="addTubeRack">
<div class="icon">
<img src="@/assets/add.svg" alt="Add Icon" />
<!-- 添加试管架按钮 -->
<div class="add-tube" @click="addTubeRack">
<div class="icon">
<img src="@/assets/add.svg" alt="Add Icon" />
</div>
<div class="text">添加试管架</div>
</div>
<div class="text">添加试管架</div>
</div>
<!-- 试管信息编辑 -->
<ProjectSelector :uuid="UUID" v-model:visible="showProjectSelector" :selectedSampleIds="selectedSampleIdsInParent"
@updateSample="handleSampleUpdate" @confirm="handleConfirmProjectSelector" @cancel="closeProjectSelector"
@clearSelection="clearProjectSelection" />
<div class="project-setting-panel">
<section class="project-select">
<h2 class="title">项目选择</h2>
<ul>
<li>
<div v-for="proj in projectsAvailable" :key="proj.projName">
{{ proj.projName }}
{{ proj.num }}
</div>
</li>
</ul>
</section>
<section class="blood-type">
<h2 class="title">血液类型</h2>
<div v-for="type in bloodTypes" :key="type.key">
{{ type.name }}
</div>
</section>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
<script setup>
import { ref, onMounted, nextTick, computed } from 'vue'
import * as R from 'ramda'
import { useRouter } from 'vue-router'
import TestTubeRack from '../Components/TestTube/TestTubeRack.vue'
import {
addTestTube,
getTestTube,
getProjectInfo,
deleteTube,
updateTubeConfig,
getBloodTypes,
} from '../../../services/Index/Test-tube/test-tube'
import type {
DataItem,
TubeSetting,
} from '../../../types/Index/index'
import { ConsumableGroupBase } from '../../../websocket/socket'
import ProjectSelector from '../Components/Consumables/ProjectSelector.vue'
import { useConsumablesStore, useTestTubeStore, useSettingTestTubeStore } from '../../../store'
// import type { DataItem, TubeSetting } from '../../../types/Index/index'
// import { ConsumableGroupBase } from '../../../websocket/socket'
import {
useConsumablesStore,
useTestTubeStore,
useSettingTestTubeStore,
} from '../../../store'
import { ElMessage } from 'element-plus'
const router = useRouter()
const testTubeStore = useTestTubeStore()
const settingTestTubeStore = useSettingTestTubeStore()
const consumables = useConsumablesStore()
const tubeRacks = ref<DataItem[]>([])
const tubeRacks = ref([]) //<DataItem[]>
const bloodTypes = ref([])
const loading = ref(false) //
const plates = ref<ConsumableGroupBase[]>(consumables.plates)
const refreshKey = ref(0)
const selectedUUID = ref<string>('')
const selectedUUID = ref('') //<string>
//
onMounted(() => {
getTubeData()
getProjectInfoData()
getBloodTypeData()
})
const projectsAvailable = computed(() => {
const group = R.groupBy((p) => p.projName, consumables.plates)
const pNames = R.keys(group)
const projArr = pNames.map((n) =>
R.reduce(
(acc, curr) => {
return { ...curr, num: acc.num + curr.num }
},
{ num: 0 },
group[n],
),
)
return projArr
})
//
const getProjectInfoData = async () => {
const res = await getProjectInfo()
const getBloodTypeData = async () => {
loading.value = true
const res = await getBloodTypes()
loading.value = false
if (res.success) {
bloodTypes.value = res.data
console.log(res.data)
} else {
ElMessage({
message: '加载血液类型失败',
type: 'error',
duration: 2000,
})
}
}
//
const getTubeData = async () => {
loading.value = true
const res = await getTestTube()
if (res.success) {
//
tubeRacks.value = res.data.map((rack: DataItem) => ({
...rack,
selectedSampleIds: [], //
}))
tubeRacks.value = res.data
console.log(tubeRacks.value)
} else {
ElMessage({
message: '加载试管架数据失败',
type: 'error',
duration: 2000,
})
}
loading.value = false
}
const selectedProject = ref<string | null>(null)
//
const clearProjectSelection = () => {
selectedProject.value = null //
}
const selectedProject = ref(null)
//
const handleChangeUser = async (uuid: string) => {
const handleChangeUser = async (uuid) => {
selectedUUID.value = uuid
const selectedTube = tubeRacks.value.find((t) => t.uuid === selectedUUID.value)
const selectedTube = tubeRacks.value.find(
(t) => t.uuid === selectedUUID.value,
)
if (!selectedTube) return
testTubeStore.setTubeInfo(selectedTube)
await updateTubeSettingsHandler()
@ -97,7 +144,7 @@ const handleChangeUser = async (uuid: string) => {
}
//
const deleteHandle = async (uuid: string) => {
const deleteHandle = async (uuid) => {
const res = await deleteTube(uuid)
if (res.success) {
ElMessage({
@ -114,58 +161,45 @@ const deleteHandle = async (uuid: string) => {
}
//
const addTubeRack = async () => {
loading.value = true //
const newTubeRack: DataItem = {
uuid: `uuid-${Date.now()}`,
active: false,
lock: false,
tubeSettings: Array.from({ length: 10 }, (_, index) => ({
tubeIndex: index,
userid: '',
sampleBarcode: '',
projId: [],
bloodType: 'WHOLE_BLOOD',
})),
selectedSampleIds: [],
}
loading.value = true
// tubeRacks
tubeRacks.value.push(newTubeRack)
//
const response = await addTestTube()
if (response && response.success) {
// tubeRacks
tubeRacks.value = [...tubeRacks.value]
tubeRacks.value = [...tubeRacks.value, response.data]
} else {
console.error('试管架添加失败')
ElMessage({
message: '试管架添加失败',
type: 'error',
})
}
loading.value = false //
loading.value = false
}
//
const handleActivateChange = (update: { uuid: string; active: boolean }) => {
const handleActivateChange = (update) => {
//: { uuid: string; active: boolean }
const rack = tubeRacks.value.find((t) => t.uuid === update.uuid)
if (rack) {
rack.active = update.active
}
}
const handleSampleUpdate = async ({
projectIds,
bloodType,
}: {
projectIds: number[]
bloodType: string
}): Promise<void> => {
projectIds, // number[]
bloodType, // string
}) => {
// Promise<void>
if (selectedSampleIdsInParent.value.length > 0) {
// tubeRacks
const updatedTubeRacks = JSON.parse(JSON.stringify(tubeRacks.value))
// tubeRack
const targetTubeRack = updatedTubeRacks.find(
(t: DataItem) => t.uuid === UUID.value,
// t: DataItem
(t) => t.uuid === UUID.value,
)
if (targetTubeRack) {
targetTubeRack.tubeSettings = targetTubeRack.tubeSettings.map(
(tube: TubeSetting) => {
//tube; TubeSetting
(tube) => {
if (selectedSampleIdsInParent.value.includes(tube.tubeIndex)) {
return {
...tube,
@ -180,12 +214,18 @@ const handleSampleUpdate = async ({
//
tubeRacks.value = []
await nextTick()
tubeRacks.value = updatedTubeRacks//
console.log("🚀 ~ handleSampleUpdate ~ updatedTubeRacks:", updatedTubeRacks)
const selectedTube = updatedTubeRacks.find((t: any) => t.uuid === UUID.value)
console.log("🚀 ~ handleSampleUpdate ~ selectedTube:", selectedTube)
tubeRacks.value = updatedTubeRacks //
console.log(
'🚀 ~ handleSampleUpdate ~ updatedTubeRacks:',
updatedTubeRacks,
)
const selectedTube = updatedTubeRacks.find((t) => t.uuid === UUID.value)
console.log('🚀 ~ handleSampleUpdate ~ selectedTube:', selectedTube)
testTubeStore.setTubeInfo(selectedTube)
console.log("🚀 ~ handleSampleUpdate ~ testTubeStore.$state.tubeInfo:", testTubeStore.$state.tubeInfo)
console.log(
'🚀 ~ handleSampleUpdate ~ testTubeStore.$state.tubeInfo:',
testTubeStore.$state.tubeInfo,
)
selectedProject.value = null //
await nextTick()
@ -203,69 +243,27 @@ const handleSampleUpdate = async ({
})
}
}
//
const UUID = ref<string>('')
// ID
const selectedSampleIdsInParent = ref<number[]>([])
// key
const componentRefreshKey = ref(0)
// handleConfirmProjectSelector
const handleConfirmProjectSelector = async () => {
await updateTubeSettingsHandler()
//
selectedSampleIdsInParent.value = []
//
const currentTubeRack = tubeRacks.value.find(rack => rack.uuid === UUID.value)
if (currentTubeRack) {
currentTubeRack.selectedSampleIds = []
}
//
showProjectSelector.value = false
UUID.value = ''
// ID
const selectedSampleIdsInParent = ref([]) //<number[]>
ElMessage({
message: '样本已确认,选中状态已清空',
type: 'success',
duration: 2000,
})
}
const tubeRackComponentInstance = ref()
const showProjectSelector = ref(false)
//
const closeProjectSelector = () => {
//
const currentTubeRack = tubeRacks.value.find(rack => rack.uuid === UUID.value)
if (currentTubeRack) {
currentTubeRack.selectedSampleIds = []
}
//
selectedSampleIdsInParent.value = []
showProjectSelector.value = false
UUID.value = ''
}
//
const handleUpdateSelectedSamples = ({ sampleIds, uuid }: { sampleIds: number[]; uuid: string }) => {
const handleUpdateSelectedSamples = ({
sampleIds, //number[]
uuid,
}) => {
//
const tubeRack = tubeRacks.value.find(tube => tube.uuid === uuid)
const tubeRack = tubeRacks.value.find((tube) => tube.uuid === uuid)
if (tubeRack) {
// sampleIds
if (sampleIds.length === 0) {
showProjectSelector.value = false
selectedSampleIdsInParent.value = []
tubeRack.selectedSampleIds = []
UUID.value = ''
} else {
//
tubeRack.selectedSampleIds = sampleIds
selectedSampleIdsInParent.value = sampleIds
UUID.value = uuid
showProjectSelector.value = true
}
}

1
src/pages/Index/components/Consumables/BallGrid.vue

@ -74,7 +74,6 @@ const ballStyle = (index) => {
const innerCircleStyle = (index) => {
const ball = props.data[index] || {}
const isActive = !!ball.isUse
console.log(ball.num)
return {
height: `${ball.num/25.0*100}%`,
backgroundColor: isActive ? ball.color : props.innerColor,

165
src/pages/Index/components/TestTube/ProjectSetting.vue

@ -1,165 +0,0 @@
<template>
<teleport to="body">
<div v-if="visible" class="overlay" @click="close">
<div class="dialog" @click.stop>
<div class="dialog-header">
<h3>请选择修改的试管类型</h3>
</div>
<!-- 标签选择 -->
<div class="tab-container">
<div v-for="(option, index) in options" :key="index" class="tab"
:class="{ active: selectedOption === option }" @click="selectOption(option)">
{{ option }}
</div>
</div>
<!-- 按钮区域 -->
<div class="button-container">
<button class="cancel-button" @click="cancel">取消</button>
<button class="confirm-button" @click="confirm">确认</button>
</div>
</div>
</div>
</teleport>
</template>
<script lang="ts" setup>
import { ref, watch } from 'vue'
import { useTestTubeStore } from '../../../../store'
const testTubeStore = useTestTubeStore()
const props = defineProps({
visible: Boolean,
selectedProject: String, //
})
watch(
() => props.selectedProject,
(newValue) => {
if (newValue === null) {
selectedOption.value = options.value[0] //
}
},
)
const emit = defineEmits(['update:visible', 'confirm', 'clearSelection'])
const options = ref(['自动', 'BT', 'Epp.0.5', 'Epp.1.5', 'mini', 'Ctip'])
const selectedOption = ref(options.value[0])
const selectOption = (option: string) => {
selectedOption.value = option
//pinia
testTubeStore.setTypeInfo(option)
}
const close = () => {
emit('update:visible', false)
}
const cancel = () => {
close()
}
const confirm = () => {
emit('confirm', selectedOption.value)
emit('clearSelection')
close()
}
</script>
<style lang="less" scoped>
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.dialog {
background-color: #fff;
width: 800px;
height: 400px;
padding: 40px;
border-radius: 10px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
display: flex;
flex-direction: column;
justify-content: space-around;
.dialog-header {
h3 {
margin: 0;
font-size: 32px;
font-weight: 600;
text-align: center;
}
}
.tab-container {
display: flex;
justify-content: space-around;
margin-top: 20px;
.tab {
width: 200px;
height: 100px;
line-height: 100px;
text-align: center;
background-color: #f5f5f5;
border-radius: 20px;
cursor: pointer;
font-size: 32px;
color: #478ffe;
transition: background 0.3s;
margin: 10px;
&.active {
background-color: #478ffe;
color: #fff;
}
&:nth-child(1) {
background-color: #fff;
color: #478ffe;
border: 2px solid #478ffe;
&.active {
background-color: #478ffe;
color: #fff;
}
}
}
}
.button-container {
display: flex;
justify-content: space-around;
margin-top: 30px;
.cancel-button,
.confirm-button {
width: 380px;
height: 100px;
border-radius: 20px;
font-size: 45px;
cursor: pointer;
border: none;
&.cancel-button {
background-color: #ebebeb;
color: #333;
}
&.confirm-button {
background-color: #478ffe;
color: #fff;
}
}
}
}
</style>

466
src/pages/Index/components/TestTube/TestTubeRack.vue

@ -1,442 +1,68 @@
<template>
<div id="tube-container">
<div class="tube-box">
<div class="tube-rack">
<!-- 状态面板 -->
<div class="status-panel" @click="toggleSetting">
<div class="title">{{ projectSetting }}</div>
<div class="status-icon">
<img src="@/assets/Vector.svg" alt="Status Icon" v-if="!isActivated" />
<img src="@/assets/Active-Vector.svg" alt="Status Icon" v-else />
</div>
<div class="status-text">
<span class="subtitle">{{ handleTip() }}</span>
</div>
</div>
<!-- 样本展示区域 -->
<div class="samples">
<el-popover v-for="(sample, index) in processedTubeSettings" :key="index" trigger="click"
placement="bottom-start" :ref="'popover-' + index" popper-class="custom-popover"
:popper-style="getPopoverStyle()">
<template #reference>
<div class="sample-item" :style="[
generateSampleBackground(sample.projId!.filter(proj => proj !== null) as ReactionPlate[]),
getActiveStyle(sample.tubeIndex),
]" @click="toggleSampleSelection(sample.tubeIndex)" @dblclick="showPopover(index)">
<div class="sample-number">{{ index + 1 }}</div>
<div class="sample-blood-type">{{ sample.bloodType }}</div>
</div>
</template>
<template #default>
<div class="item-detail">
<div v-for="(project, index) in sample.projId" :key="index">
<button :style="{
backgroundColor: project.color,
color: 'white',
border: 'none',
padding: '10px 20px',
borderRadius: '5px',
margin: '2px',
cursor: 'pointer',
}">
{{ project.projName }}
</button>
</div>
</div>
</template>
</el-popover>
</div>
</div>
<!-- 功能按钮区域 -->
<div class="function-buttons" v-if="isSetting">
<div class="active-button" @click="toggleActivate">
<div class="icon">
<img src="@/assets/outer-ring.svg" alt="" class="outer-ring" />
<img src="@/assets/inner-ring.svg" alt="" class="inner-ring" />
</div>
<div class="text">
{{ isActivated ? '取消激活' : '激活试管架' }}
</div>
</div>
<!-- 其他功能按钮可继续扩展 -->
<div class="update-button" @click="showSelector = true">
<div class="icon">
<img src="@/assets/update-tube-icon.svg" alt="" />
</div>
<div class="text">修改试管类型</div>
</div>
<div class="change-user-button" @click="turnChangeUser(tubeRack.uuid)">
<div class="icon">
<img src="@/assets/update-icon.svg" alt="" />
</div>
<div class="text">编辑患者信息</div>
</div>
<div class="del-button" @click="deleteTube(tubeRack.uuid)">
<div class="icon">
<img src="@/assets/del-icon.svg" alt="" />
</div>
<div class="text">删除试管架</div>
</div>
<div class="test-tube-rack">
<!--状态-->
<div class="tube-rack-state">
<div class="status-icon">
<img src="@/assets/Vector.svg" alt="Status Icon" v-if="tubeRack.state==='INACTIVE'" />
<img src="@/assets/Active-Vector.svg" alt="Status Icon" v-else />
</div>
</div>
<ProjectSetting v-model:visible="showSelector" @confirm="handleConfirm" />
<i class="split"></i>
<!--试管区-->
<section class="tube-list">
<div v-for="(tube, idx) in tubeRack.tubeSettings">
<Tube :tube="tube" :index="idx" />
</div>
</section>
</div>
<div class="test-tube-rack-op">
<div>修改试管架</div>
<div>编辑患者信息</div>
<div>删除试管架</div>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import ProjectSetting from './ProjectSetting.vue'
import { ReactionPlate, TubeRack, handleTube, DataItem } from '../../../../types/Index'
import {
getBloodTypeLabel,
processTubeSettings,
generateSampleBackground,
} from '../../Utils'
import { useTestTubeStore } from '../../../../store'
import { updateTubeActivationStatus } from '../../../../services/index'
import { ConsumableGroupBase } from '../../../../websocket/socket'
const testTubeStore = useTestTubeStore()
//
const props = defineProps<{
tubeRack: DataItem
plates: ConsumableGroupBase[]
}>()
onMounted(() => {
processedTubeSettings.value = processTubeSettings(
props.tubeRack.tubeSettings,
props.plates as ConsumableGroupBase[],
getBloodTypeLabel,
)
projectSetting.value = testTubeStore.getProjectSetting(props.tubeRack.uuid)
isActivated.value = props.tubeRack.active //
})
//
const turnChangeUser = (uuid: string) => {
emits('changeUser', uuid)
}
//
const deleteTube = (uuid: string) => {
emits('deleteTubeRack', uuid)
}
//
const getActiveStyle = (tubeIndex: number) => {
return props.tubeRack.selectedSampleIds!.includes(tubeIndex)
? selectedStyle
: {}
}
const processedTubeSettings = ref<TubeRack[]>([])
const showSelector = ref(false)
//
const emits = defineEmits<{
(e: 'updateActivate', update: { uuid: string; active: boolean }): void
(e: 'changeUser', uuid: string): void
(e: 'deleteTubeRack', uuid: string): void
(
e: 'updateSelectedSamples',
{ sampleIds, uuid }: { sampleIds: number[]; uuid: string },
): void
}>()
//
const selectedStyle = {
outline: '3px solid #4A90E2', // 使 outline
boxShadow: '0 0 10px rgba(74, 144, 226, 0.6)',
}
//
const toggleSampleSelection = (sampleId: number) => {
const currentSelected = [...(props.tubeRack.selectedSampleIds || [])]
const index = currentSelected.indexOf(sampleId)
if (index === -1) {
currentSelected.push(sampleId)
} else {
currentSelected.splice(index, 1)
}
//
emits('updateSelectedSamples', {
sampleIds: currentSelected,
uuid: props.tubeRack.uuid,
})
}
//
const isActivated = ref(props.tubeRack.active)
const isSetting = ref(false)
const projectSetting = ref(testTubeStore.getProjectSetting(props.tubeRack.uuid))
//
const toggleSetting = () => {
isSetting.value = !isSetting.value
}
//
const toggleActivate = async () => {
//
isActivated.value = !isActivated.value
// handleTube active
const updatedData: handleTube = {
uuid: props.tubeRack.uuid,
active: isActivated.value,
lock: props.tubeRack.lock,
tubeSettings: processedTubeSettings.value, // 使
}
// updateTubeInfo
const response = await updateTubeActivationStatus(updatedData)
if (response && response.success) {
emits('updateActivate', {
uuid: props.tubeRack.uuid,
active: isActivated.value,
})
} else {
console.error('Failed to update activation status')
//
isActivated.value = !isActivated.value
}
}
//
const handleTip = () => {
if (isSetting.value) return '正在配置'
return isActivated.value ? '已激活' : '未激活'
}
// click
const showPopover = (index: number) => {
const popover = 'popover-' + index
const popoverElement = document.querySelector(
`[aria-describedby="${popover}"]`,
)
// 使 HTMLElement click
if (popoverElement instanceof HTMLElement) {
popoverElement.click()
}
}
const handleConfirm = (selectedOption: string) => {
console.log("选中的数据有", selectedOption)
projectSetting.value = selectedOption
showSelector.value = false
// store
testTubeStore.setProjectSetting(props.tubeRack.uuid, selectedOption)
//
emits('updateSelectedSamples', {
sampleIds: [],
uuid: props.tubeRack.uuid,
})
}
const getPopoverStyle = () => ({
width: '100px',
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
})
//
const clearSelectedSamples = () => {
if (props.tubeRack.selectedSampleIds) {
props.tubeRack.selectedSampleIds = []
}
emits('updateSelectedSamples', {
sampleIds: [],
uuid: props.tubeRack.uuid
})
}
//
defineExpose({
clearSelectedSamples
})
<script setup>
import Tube from './Tube.vue'
const props = defineProps(['tubeRack', 'index'])
</script>
<style lang="less" scoped>
#tube-container {
<style scoped lang="less">
.test-tube-rack {
background-color: rgb(248, 248, 248);
height: 150px;
display: flex;
align-items: center;
width: 100%;
box-sizing: border-box;
flex-direction: column;
.tube-box {
display: flex;
align-items: center;
flex-direction: column;
width: 100%;
background-color: #fafafa;
border-radius: 10px;
.tube-rack {
display: flex;
align-items: center;
width: 100%;
background-color: #fafafa;
border-radius: 10px;
//
.status-panel {
width: 10%;
display: flex;
flex-direction: column;
position: relative;
padding: 20px 20px;
.title {
font-size: 32px;
font-weight: bold;
}
.status-icon {
img {
width: 80px;
height: 80px;
}
}
.status-text {
.subtitle {
font-size: 30px;
font-weight: 400;
}
}
&::after {
content: '';
display: block;
width: 2px;
height: 60%;
margin-top: 20px;
background-color: #000;
position: absolute;
right: 0;
}
}
.tube-rack-state {
width: 100px;
.status-icon img {
width: 56px;
}
}
//
.samples {
display: flex;
align-items: center;
background-color: #fafafa;
margin-left: 20px;
.item-detail {
width: 800px;
height: 500px;
}
.sample-item {
width: 65px;
height: 65px;
border-radius: 50%;
border: 2px solid black;
display: flex;
align-items: center;
justify-content: center;
margin-right: 10px;
position: relative;
flex-direction: column;
.sample-number {
position: absolute;
top: -28px;
font-size: 20px;
font-weight: bold;
}
.sample-blood-type {
color: black;
font-size: 20px;
font-weight: bold;
}
}
}
.split {
width: 1px;
height: 80px;
background-color: rgb(192,192,192);
margin-right: 10px;
}
//
.function-buttons {
.tube-list {
flex: 1 1 auto;
display: flex;
align-items: center;
background-color: #ebebeb;
justify-content: space-evenly;
width: 100%;
padding: 20px 0;
.active-button,
.update-button,
.change-user-button,
.del-button {
width: 200px;
height: 80px;
border-radius: 40px;
margin-right: 10px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border: none;
position: relative;
.outer-ring {
position: absolute;
width: 30px;
height: 30px;
z-index: 1;
top: 25px;
left: 25px;
}
.text {
font-size: 20px;
margin-left: 20px;
}
.inner-ring {
position: absolute;
width: 20px;
height: 20px;
z-index: 2;
top: 30px;
left: 30px;
}
}
.active-button,
.update-button,
.change-user-button {
background-color: #478ffe;
color: #fff;
}
.del-button {
background-color: #cd4143;
color: #fff;
}
justify-content: space-around;
}
.add-tube {
width: 100%;
height: 100px;
background-color: #f6f6f6;
}
.test-tube-rack-op {
height: 68px;
background-color: rgb(235,235,235);
display: flex;
justify-content: space-between;
align-items: center;
justify-content: center;
padding: 0 40px;
.icon {
width: 86px;
height: 86px;
}
.text {
font-size: 32px;
color: #73bc54;
font-weight: bold;
}
}
font-size: 22px;
font-weight: 600;
}
</style>

33
src/pages/Index/components/TestTube/Tube.vue

@ -0,0 +1,33 @@
<template>
<div class="tube-item">
<span class="order">{{ index + 1 }}</span>
<div class="tube-circle">
<span class="add-symbol">+</span>
</div>
<span class="user-id">111</span>
<!-- <div>{{ tube.bloodType }}</div> -->
</div>
</template>
<script setup>
const props = defineProps(['tube', 'index'])
</script>
<style scoped lang="less">
.tube-circle {
width: 72px;
height: 72px;
border-radius: 999px;
border: solid 1px gray;
background-color: #fff;
display: flex;
justify-content: center;
align-items: center;
.add-symbol {
color: black;
font-size: 20px;
}
}
</style>

2
src/pages/Index/components/TestTube/index.ts

@ -1,2 +0,0 @@
export { default as ProjectSetting } from './ProjectSetting.vue'
export { default as TestTubeRack } from './TestTubeRack.vue'

1
src/pages/Index/components/index.ts

@ -3,6 +3,5 @@
export * from './Consumables'
export * from './History'
export * from './Setting'
export * from './TestTube'
export * from './Setting'
export * from './Running'

22
src/services/Index/Test-tube/test-tube.ts

@ -1,5 +1,27 @@
import apiClient from '../../../utils/axios'
import type { TubeActivationStatus, TubeSetting } from '../../../types/Index'
/*
"data": [
{
"key": "WHOLE_BLOOD",
"name": "全血"
},
{
"key": "SERUM_OR_PLASMA",
"name": "血清或者血浆"
}
]
*/
export const getBloodTypes = async () => {
try {
const res = await apiClient.post(
'/api/v1/app/appTubeSettingMgr/getBloodTypes',
)
return res.data
} catch (error) {
console.log('获取已配置试管时出错:', error)
}
}
//获取已经配置的试管信息
export const getTestTube = async () => {
try {

2
src/types/Index/TestTube.ts

@ -22,7 +22,7 @@ export interface DataItem {
active: boolean
tubeSettings: TubeSetting[]
lock: boolean
selectedSampleIds?: Array<number>
// selectedSampleIds?: Array<number>
}
export interface handleTube {

Loading…
Cancel
Save