Browse Source

调整目录名称,完成移液头区拖动操作,同步更换小缓冲液区显示组件

feature/history-20250108
zhangjiming 7 months ago
parent
commit
7447c92e98
  1. 4
      src/pages/Index/History.vue
  2. 2
      src/pages/Index/Index.vue
  3. 2
      src/pages/Index/Regular.vue
  4. 62
      src/pages/Index/Regular/Consumables.vue
  5. 4
      src/pages/Index/Regular/Running.vue
  6. 4
      src/pages/Index/Regular/TestTube.vue
  7. 2
      src/pages/Index/Settings/Users.vue
  8. 2
      src/pages/Index/TestTube/ChangeUser.vue
  9. 181
      src/pages/Index/components/Consumables/BallGrid2.vue
  10. 37
      src/pages/Index/components/Consumables/DragAreaEx.vue
  11. 3
      src/pages/Index/components/Consumables/InfoBar.vue
  12. 8
      src/pages/Index/components/Consumables/MainComponent.vue
  13. 50
      src/pages/Index/components/Consumables/MoveLiquidArea.vue
  14. 35
      src/pages/Index/components/Consumables/Plate.vue
  15. 113
      src/pages/Index/components/Consumables/SliderArea.vue
  16. 30
      src/pages/Index/components/Consumables/SliderAreaEx.vue
  17. 93
      src/pages/Index/components/Consumables/SpttingPlates.vue
  18. 2
      src/pages/Index/components/Running/SampleDisplay.vue
  19. 2
      src/pages/Index/components/TestTube/TestTubeRack.vue

4
src/pages/Index/History.vue

@ -132,14 +132,14 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue'
import dayjs from 'dayjs'
import { HistoryTable, HistoryWarn } from './components/index'
import { HistoryTable, HistoryWarn } from './Components/index'
import {
getHistoryInfo,
deleteHistoryInfo,
searchHistoryInfo,
printHistoryInfo,
} from '../../services/Index/index'
import HistoryMessage from './components/History/HistoryMessage.vue'
import HistoryMessage from './Components/History/HistoryMessage.vue'
import type { TableItem } from '../../types/Index'
import { ElMessage } from 'element-plus'
import WarnSvg from '@/assets/Index/History/warn.svg'

2
src/pages/Index/Index.vue

@ -82,7 +82,7 @@
<script setup lang="ts">
import { ref, onMounted, onBeforeUnmount } from 'vue';
import { Time, InitWarn, LoadingModal } from './components/Consumables';
import { Time, InitWarn, LoadingModal } from './Components/Consumables';
import { startWork, pauseWork, continueWork, stopWork, getInitState, initDevice, saveMountedCardInfo, openBuzzer, closeBuzzer } from '../../services/index';
import { CheckItem, User } from '../../types/Index';
import { useConsumablesStore } from '../../store';

2
src/pages/Index/Regular.vue

@ -12,7 +12,7 @@
</template>
<script setup lang="ts">
import TabBar from './components/Consumables/TabBar.vue'
import TabBar from './Components/Consumables/TabBar.vue'
import { createWebSocket } from '../../websocket/socket'
import { getServerInfo } from '../../utils/getServerInfo'
import { onMounted, onDeactivated } from 'vue';

62
src/pages/Index/Regular/Consumables.vue

@ -76,8 +76,8 @@
</div>
<div class="ball-area">
<MainComponent
v-for="item in bufferLittles"
:key="item"
v-for="(item, idx) in bufferLittles"
:key="idx"
class="ball-grid"
:projectName="item.projShortName"
:currentCount="item.num"
@ -85,7 +85,7 @@
:activatedBalls="item.num"
:columns="5"
gridWidth="240px"
gridHeight="235px"
gridHeight="240px"
:activeColor="item.color"
/>
</div>
@ -94,7 +94,7 @@
</template>
<script setup lang="ts">
import { MoveLiquidArea, SpttingPlates, MainComponent } from '../components'
import { MoveLiquidArea, SpttingPlates, MainComponent } from '../Components'
// import SliderAreaEx from '../components/Consumables/SliderAreaEx.vue';
// import DragAreaEx from '../components/Consumables/DragAreaEx.vue';
import { ref, onMounted, onActivated, onBeforeUnmount, watch } from 'vue'
@ -257,10 +257,14 @@ const handleSensorState = (data: SensorStateMessage['data']) => {
const handleConsumablesState = (data: ConsumablesStateMessage['data']) => {
if (isAlreadyLoad.value && isHandleScan.value) {
consumableStore.setConsumablesData(data)
moveLiquids.value = data.tips
plates.value = data.reactionPlateGroup as ReactionPlate[]
bufferLittles.value = data.littBottleGroup as BufferLittle[]
bufferBig.value = data.larBottleGroup as BottleGroup[]
if (!isDragging.value) {
moveLiquids.value = data.tips
plates.value = data.reactionPlateGroup as ReactionPlate[]
bufferLittles.value = data.littBottleGroup as BufferLittle[]
bufferBig.value = data.larBottleGroup as BottleGroup[]
} else {
console.log('正在拖动,不更新耗材')
}
} else {
return
}
@ -278,7 +282,11 @@ const updatePlatesAndBuffers = ({
if (type === 'Plate' && plates.value && plates.value[index]) {
plates.value[index].num = value
}
if (type === 'LittleBuf' && bufferLittles.value && bufferLittles.value[index]) {
if (
type === 'LittleBuf' &&
bufferLittles.value &&
bufferLittles.value[index]
) {
bufferLittles.value[index].num = value
}
if (type === 'BigBuf' && bufferBig.value && bufferBig.value[index]) {
@ -419,25 +427,34 @@ const handleIsUnload = () => {
]
bufferBig.value = []
}
const isDragging = ref(false)
const updateTipNum = async ({
index,
tipNum,
sync,
}: {
index: number
tipNum: number
sync: boolean
}) => {
//
tempTipNum.value[index] = tipNum
console.log('🚀 ~ updateTipNum ~ tempTipNum:', tempTipNum.value)
//
try {
if (deviceStore.status === 'IDLE') {
await updateTipsNum({ group: `TipG${index+1}`, num: tipNum })
if (deviceStore.status === 'IDLE') {
tempTipNum.value[index] = tipNum
//
if (sync) {
console.log(`🚀 ~ updateTipNum ~ order ${index + 1}, num ${tipNum}`, )
isDragging.value = false
try {
await updateTipsNum({ group: `TipG${index + 1}`, num: tipNum })
} catch (error) {
console.error('修改耗材数量失败:', error)
}
} else {
ElMessage.error('设备正在工作,无法修改数值')
isDragging.value = true
}
} catch (error) {
console.error('修改耗材数量失败:', error)
} else {
ElMessage.error('设备正在工作,无法修改数值')
}
}
</script>
@ -548,11 +565,12 @@ const updateTipNum = async ({
.ball-area {
display: flex;
justify-self: space-between;
flex-wrap: wrap;
column-gap: 20px;
row-gap: 12px;
padding: 0 24px ;
.ball-grid {
margin: 0 15px 5px 15px;
overflow: hidden;
}
}
}

4
src/pages/Index/Regular/Running.vue

@ -116,14 +116,14 @@
import { ref, onMounted, onUnmounted, watch, onActivated, onDeactivated } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useTestTubeStore, useConsumablesStore } from '../../../store'
import { getBloodTypeLabel, processTubeSettings } from '../utils'
import { getBloodTypeLabel, processTubeSettings } from '../Utils'
import {
SampleDisplay,
PlateDisplay,
LittleBufferDisplay,
BallGrid,
EmergencyResultDialog,
} from '../components'
} from '../Components'
import { wasteArea, getTubeRackState } from '../../../services/index'
import type { Subtank, TubeRackInfo } from '../../../types/Index'
import { getRunningList } from '../../../services/Index/running/running'

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

@ -27,7 +27,7 @@
<script setup lang="ts">
import { ref, onMounted, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import TestTubeRack from '../components/TestTube/TestTubeRack.vue'
import TestTubeRack from '../Components/TestTube/TestTubeRack.vue'
import {
addTestTube,
getTestTube,
@ -40,7 +40,7 @@ import type {
TubeSetting,
} from '../../../types/Index/index'
import { ConsumableGroupBase } from '../../../websocket/socket'
import ProjectSelector from '../components/Consumables/ProjectSelector.vue'
import ProjectSelector from '../Components/Consumables/ProjectSelector.vue'
import { useConsumablesStore, useTestTubeStore, useSettingTestTubeStore } from '../../../store'
import { ElMessage } from 'element-plus'
const router = useRouter()

2
src/pages/Index/Settings/Users.vue

@ -50,7 +50,7 @@
<script setup lang="ts">
import type { User } from '../../../types/Index'
import { DelWarn, DelMessage, AddUserModal, EnterPinModal } from '../components'
import { DelWarn, DelMessage, AddUserModal, EnterPinModal } from '../Components'
import { ref, onMounted, computed } from 'vue'
import {
getUserList,

2
src/pages/Index/TestTube/ChangeUser.vue

@ -89,7 +89,7 @@ import {
getBloodTypeLabel,
processTubeSettings,
generateSampleBackground,
} from '../utils'
} from '../Utils'
import { updateTubeConfig } from '../../../services'
import { ElMessage } from 'element-plus'
import { ConsumableGroupBase } from '../../../websocket/socket'

181
src/pages/Index/components/Consumables/BallGrid2.vue

@ -0,0 +1,181 @@
<template>
<div class="ball-area" :style="gridStyle">
<div class="ball-grid">
<div
v-for="(isActive, index) in ballStates"
:key="index"
:class="['ball', { active: isActive }]"
:style="ballStyle(index)"
>
<div class="inner-circle" :style="innerCircleStyle(index)"></div>
</div>
</div>
<!-- 拖动区 -->
<DragAreaEx
class="drag-area"
v-if="canUpdate"
:hTotal="columns"
:vTotal="total / columns"
:hVal="(total - activated) % columns"
:vVal="(total - activated) / columns"
@update:sliderValue="updateSliderVal"
@update:sliderEndValue="updateSliderEndVal"
/>
</div>
</template>
<script setup>
import { computed, ref, watch, onMounted, onBeforeUnmount } from 'vue'
import DragAreaEx from './DragAreaEx.vue'
import { useDeviceStore } from '../../../../store/index'
//
// Props
const props = defineProps({
order: {
type: Number,
default: 0,
},
total: {
type: Number,
required: true,
},
activated: {
type: Number,
default: 0,
},
width: {
type: String,
default: '300px',
},
height: {
type: String,
default: '300px',
},
columns: {
type: Number,
default: 10, //
},
activeColor: {
type: String,
default: '#4caf50', //
},
innerColor: {
type: String,
default: 'lightgray', //
},
canUpdate: {
type: Boolean,
default: false,
},
})
const { total, columns } = props
const deviceStore = useDeviceStore()
const isDragging = ref(false)
const ballStates = ref(Array.from({ length: props.total }, () => false)) //
//
const emit = defineEmits(['update:TipNum'])
const updateSliderVal = async (hVal, vVal) => {
if (deviceStore.status === 'IDLE') {
isDragging.value = true
const empty = parseInt(vVal) * columns + parseInt(hVal)
const activated = total - empty
for (let i = 0; i < total; i++) {
ballStates.value[i] = i < activated
}
emit('update:TipNum', activated, props.order, false)
} else {
ElMessage.error('设备正在工作,无法修改数值')
}
}
const updateSliderEndVal = async (hVal, vVal) => {
isDragging.value = false
if (deviceStore.status === 'IDLE') {
const empty = parseInt(vVal) * columns + parseInt(hVal)
const activated = total - empty
emit('update:TipNum', activated, props.order, true)
} else {
ElMessage.error('设备正在工作,无法修改数值')
}
}
//
const gridStyle = computed(() => ({
width: props.width,
height: props.height,
}))
//
const ballStyle = (index) => {
const ballSize = `${parseFloat(props.width) / props.columns - 2}px`
return {
width: ballSize,
height: ballSize,
}
}
//
const innerCircleStyle = (index) => {
const isActive = ballStates.value[index]
return {
width: '80%',
height: '80%',
backgroundColor: isActive ? props.activeColor : props.innerColor,
borderRadius: '50%',
transition: 'background-color 0.3s ease',
}
}
// `props.activated`
watch(
() => props.activated,
(newVal) => {
if (!isDragging.value) {
for (let i = 0; i < props.total; i++) {
ballStates.value[i] = i < newVal
}
}
},
{ immediate: true }, //
)
</script>
<style scoped lang="less">
.ball-area {
position: relative;
}
.ball-grid {
width: 100%;
height: 100%;
display: flex;
flex-wrap: wrap-reverse;
flex-direction: row-reverse;
background-color: #666;
border-radius: 10px;
padding-right: 4px;
}
.drag-area {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.ball {
margin: 0 2px 2px 0;
background-color: #fff;
border-radius: 999px;
display: flex;
justify-content: center;
align-items: center;
transition: background-color 0.3s ease;
}
.inner-circle {
transition: background-color 0.3s ease;
}
</style>

37
src/pages/Index/components/Consumables/DragAreaEx.vue

@ -5,16 +5,16 @@
@touchstart.prevent="handleStart"
>
<div class="slider-track" ref="sliderTrack">
<div
<!-- <div
class="slider-fill"
:style="{ width: `${(hValue / hTotal) * 100}%`, height: `${(vValue / vTotal) * 100}%` }"
:style="{ width: `${(hValue / hTotalVal) * 100}%`, height: `${(vValue / vTotalVal) * 100}%` }"
></div>
<div
<span
:style="{ visibility: `${isDragging ? 'visible' : 'hidden'}` }"
class="slider-thumb"
>
{{ `${hValue.toFixed()}, ${vValue.toFixed()}` }}
</div>
{{ `${hValue.toFixed()}; ${vValue.toFixed()}` }}
</span> -->
</div>
</div>
</template>
@ -41,7 +41,7 @@ const props = defineProps({
},
})
const emit = defineEmits(['update:hVal','update:vVal'])
const emit = defineEmits(['update:sliderValue','update:sliderEndValue'])
const hValue = ref(props.hVal)
const hTotalVal = ref(props.hTotal)
@ -57,10 +57,10 @@ const sliderTrack = useTemplateRef('sliderTrack')
watch(
[() => props.hVal, () => props.vVal],
(newX, newY) => {
(newVal) => {
if (!isDragging.value) {
hValue.value = newX
vValue.value = newY
hValue.value = newVal[0]
vValue.value = newVal[1]
}
},
)
@ -72,7 +72,7 @@ function handleStart(event) {
const rect = sliderTrack.value.getBoundingClientRect()
startXValue.value = (hValue.value / hTotalVal.value) * rect.width
startYValue.value = (vValue.value / vTotalVal.value) * rect.height
isDragging.value = true
document.addEventListener('mousemove', handleDrag)
document.addEventListener('touchmove', handleDrag, { passive: false })
@ -92,12 +92,12 @@ function handleDrag(event) {
hValue.value = Math.min(1, Math.max(0, percentX)) * hTotalVal.value
vValue.value = Math.min(1, Math.max(0, percentY)) * vTotalVal.value
// console.log(percent, sliderValue.value)
emit('update:hVal', hValue.value)
emit('update:vVal', vValue.value)
emit('update:sliderValue', hValue.value.toFixed(), vValue.value.toFixed())
}
function handleEnd() {
emit('update:sliderEndValue', hValue.value.toFixed(), vValue.value.toFixed())
isDragging.value = false
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
@ -108,16 +108,14 @@ function handleEnd() {
<style scoped>
.slider-container {
width: 300px;
height: 300px;
width: 100%;
height: 100%;
touch-action: none; /* Prevents the browser's default panning behavior */
}
.slider-track {
position: relative;
height: 100%;
background-color: #ddd;
cursor: pointer;
}
.slider-fill {
@ -131,8 +129,9 @@ function handleEnd() {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
font-size: 32px;
color: gray;
font-size: 36px;
color: #FFF;
-webkit-text-stroke: 2px #000;
}
.slider-thumb:active {

3
src/pages/Index/components/Consumables/InfoBar.vue

@ -39,9 +39,8 @@ const { projectName, currentCount, maxCount } = toRefs(props)
justify-content: space-around;
align-items: center;
background-color: #808080;
border-radius: 5px;
font-size: 32px;
height: 33px;
height: 32px;
}
.project-name {

8
src/pages/Index/components/Consumables/MainComponent.vue

@ -1,7 +1,7 @@
<template>
<div class="combined-container">
<!-- 小球组件 -->
<BallGrid :total="totalBalls" :activated="activatedBalls" :columns="columns" :width="gridWidth" :height="gridHeight"
<BallGrid2 :total="totalBalls" :activated="activatedBalls" :columns="columns" :width="gridWidth" :height="gridHeight"
:activeColor="activeColor" />
<!-- 项目信息组件 -->
@ -10,7 +10,7 @@
</template>
<script setup>
import BallGrid from './BallGrid.vue' //
import BallGrid2 from './BallGrid2.vue' //
import InfoBar from './InfoBar.vue' //
// Props
@ -62,8 +62,4 @@ const props = defineProps({
background-color: #555555;
border-radius: 10px;
}
.combined-container>* {
margin-bottom: 5px;
}
</style>

50
src/pages/Index/components/Consumables/MoveLiquidArea.vue

@ -28,48 +28,51 @@
class="controller"
v-for="(controller, index) in tempTipNum"
:key="index"
:class="{ 'selected-controller': currentPlate === index }"
:class="{ 'selected-controller': activeTab === index }"
@click="changePlate(index)"
>
<div class="controller-title">移液区{{ index + 1 }}</div>
<div class="controller-title">移液{{ index + 1 }}</div>
<div class="controller-number">{{ controller }}/120</div>
</div>
</div>
<div class="move-liquid-graph">
<keep-alive>
<BallGrid
<BallGrid2
v-if="activeTab === 0"
:order="1"
:total="120"
:activated="moveLiquids![activeTab]?.tipNum"
width="350px"
width="346px"
height="310px"
:columns="12"
@deactivate="handleDeactivate"
@syncActivatedCount="syncActivatedCount"
:canUpdate="true"
@update:TipNum="handleUpdateTipNum"
/>
</keep-alive>
<keep-alive>
<BallGrid
<BallGrid2
v-if="activeTab === 1"
:order="2"
:total="120"
:activated="moveLiquids![activeTab].tipNum"
width="350px"
width="346px"
height="310px"
:columns="12"
@deactivate="handleDeactivate"
@syncActivatedCount="syncActivatedCount"
:canUpdate="true"
@update:TipNum="handleUpdateTipNum"
/>
</keep-alive>
<keep-alive>
<BallGrid
<BallGrid2
v-if="activeTab === 2"
:order="3"
:total="120"
:activated="moveLiquids![activeTab]?.tipNum"
width="350px"
width="346px"
height="310px"
:columns="12"
@deactivate="handleDeactivate"
@syncActivatedCount="syncActivatedCount"
:canUpdate="true"
@update:TipNum="handleUpdateTipNum"
/>
</keep-alive>
</div>
@ -161,6 +164,7 @@
<script setup lang="ts">
import BallGrid from './BallGrid.vue'
import BallGrid2 from './BallGrid2.vue'
import IdCardInfo from './IdCardInfo.vue'
import { ref, watch, reactive } from 'vue'
import { useRouter } from 'vue-router'
@ -207,6 +211,7 @@ const handleIsLoad = () => {
const handleIsUnload = () => {
emit('unloadConsumables')
}
//
const activeTab = ref(0)
//
const wasteStatus = ref(props.wasteStatus)
@ -263,24 +268,14 @@ watch(
{ immediate: true, deep: true },
)
// remainingCount
const handleDeactivate = (remainingCount: number) => {
emit('updateTipNum', { index: activeTab.value, tipNum: remainingCount })
}
const syncActivatedCount = (count: number) => {
if (props.moveLiquids && props.moveLiquids[activeTab.value]) {
props.moveLiquids[activeTab.value].tipNum = count // tab tipNum
}
const handleUpdateTipNum = (remainingCount: number, order: number, sync: boolean) => {
emit('updateTipNum', { index: order - 1, tipNum: remainingCount, sync })
}
//
const currentPlate = ref(0)
//
const changePlate = (index: number) => {
console.log('切换到', index)
activeTab.value = index
currentPlate.value = index
}
</script>
@ -359,10 +354,11 @@ const changePlate = (index: number) => {
}
.controller-number {
font-size: 32px;
font-size: 30px;
font-weight: 600;
border-radius: 10px;
border: 3px solid #54a4e8;
padding: 0 4px;
}
}

35
src/pages/Index/components/Consumables/Plate.vue

@ -32,19 +32,18 @@ defineProps({
<style lang="less" scoped>
.colored-bar-container {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100px;
height: 80px;
}
.colored-bar {
display: flex;
flex-direction: row-reverse;
justify-content: space-between;
gap: 0.5px;
width: 355px;
height: 100%;
// height: 100%;
background-color: #f0f0f0;
border-radius: 10px;
overflow: hidden;
position: relative;
@ -52,20 +51,20 @@ defineProps({
flex: 1;
position: relative;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
// display: flex;
// align-items: center;
// justify-content: center;
&::after {
content: "";
width: 1px;
height: 80%;
position: absolute;
right: 0;
top: 40%;
transform: translateY(-50%);
background-color: black;
}
// &::after {
// content: "";
// width: 1px;
// height: 80%;
// position: absolute;
// right: 0;
// top: 40%;
// transform: translateY(-50%);
// background-color: black;
// }
}
.colored {

113
src/pages/Index/components/Consumables/SliderArea.vue

@ -1,113 +0,0 @@
<template>
<div
class="slider-container"
@mousedown="handleStart"
@touchstart.prevent="handleStart"
>
<div class="slider-track" ref="sliderTrack">
<div class="slider-fill" :style="{ width: `${sliderValue}%` }"></div>
<div
:style="{ visibility: `${isDragging ? 'visible' : 'hidden'}` }"
class="slider-thumb"
>
{{ sliderValue.toFixed() }}
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted, watch, useTemplateRef } from 'vue'
const props = defineProps({
modelValue: {
type: Number,
default: 50,
},
})
const emit = defineEmits(['update:modelValue'])
const sliderValue = ref(props.modelValue)
const isDragging = ref(false)
const startX = ref(0)
const startValue = ref(0)
// const sliderThumb = ref(null);
const sliderTrack = useTemplateRef('sliderTrack')
watch(
() => props.modelValue,
(newValue) => {
if (!isDragging.value) {
sliderValue.value = newValue
}
},
)
function handleStart(event) {
const touchEvent = event.touches ? event.touches[0] : event
startX.value = touchEvent.clientX
const rect = sliderTrack.value.getBoundingClientRect()
startValue.value = (sliderValue.value * rect.width) / 100
isDragging.value = true
document.addEventListener('mousemove', handleDrag)
document.addEventListener('touchmove', handleDrag, { passive: false })
document.addEventListener('mouseup', handleEnd)
document.addEventListener('touchend', handleEnd)
}
function handleDrag(event) {
if (!isDragging.value) return
const touchEvent = event.touches ? event.touches[0] : event
const deltaX = touchEvent.clientX - startX.value
const rect = sliderTrack.value.getBoundingClientRect()
let percent = ((deltaX + startValue.value) / rect.width) * 100
// Limit the value between 0 and 100
sliderValue.value = Math.min(100, Math.max(0, percent))
emit('update:modelValue', sliderValue.value)
}
function handleEnd() {
isDragging.value = false
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
document.removeEventListener('mouseup', handleEnd)
document.removeEventListener('touchend', handleEnd)
}
</script>
<style scoped>
.slider-container {
width: 300px;
touch-action: none; /* Prevents the browser's default panning behavior */
}
.slider-track {
position: relative;
height: 4rem;
background-color: #ddd;
cursor: pointer;
}
.slider-fill {
position: absolute;
height: 100%;
background-color: #4caf50;
}
.slider-thumb {
position: absolute;
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
font-size: 32px;
color: gray;
}
.slider-thumb:active {
cursor: grabbing;
}
</style>

30
src/pages/Index/components/Consumables/SliderAreaEx.vue

@ -5,7 +5,7 @@
@touchstart.prevent="handleStart"
>
<div class="slider-track" ref="sliderTrack">
<div
<!-- <div
class="slider-fill"
:style="{
width: `${(sliderValue / totalVal) * 100}%`,
@ -17,7 +17,7 @@
class="slider-thumb"
>
{{ sliderValue.toFixed() }}
</div>
</div> -->
</div>
</div>
</template>
@ -36,8 +36,8 @@ const props = defineProps({
},
order: {
type: Number,
default: 0
}
default: 0,
},
})
const sliderValue = ref(props.currValue)
@ -47,7 +47,7 @@ const startX = ref(0)
const startValue = ref(0)
const sliderTrack = useTemplateRef('sliderTrack')
const emit = defineEmits(['update:sliderValue'])
const emit = defineEmits(['update:sliderValue', 'update:sliderEndValue'])
watch(
() => props.currValue,
@ -62,7 +62,8 @@ function handleStart(event) {
const touchEvent = event.touches ? event.touches[0] : event
startX.value = touchEvent.clientX
const rect = sliderTrack.value.getBoundingClientRect()
startValue.value = (sliderValue.value / totalVal.value) * rect.width
startValue.value =
rect.width - (sliderValue.value / totalVal.value) * rect.width
// console.log(sliderValue.value, totalVal.value)
// console.log(startValue.value, rect.width)
isDragging.value = true
@ -80,14 +81,14 @@ function handleDrag(event) {
const rect = sliderTrack.value.getBoundingClientRect()
let percent = (deltaX + startValue.value) / rect.width
sliderValue.value = Math.min(1, Math.max(0, percent)) * totalVal.value
sliderValue.value =
totalVal.value - Math.min(1, Math.max(0, percent)) * totalVal.value
// console.log(percent, sliderValue.value)
// emit('update:modelValue', sliderValue.value)
emit('update:sliderValue', sliderValue.value.toFixed(), props.order)
}
function handleEnd() {
emit('update:sliderValue', sliderValue.value, props.order)
emit('update:sliderEndValue', sliderValue.value.toFixed(), props.order)
isDragging.value = false
document.removeEventListener('mousemove', handleDrag)
document.removeEventListener('touchmove', handleDrag)
@ -110,6 +111,7 @@ function handleEnd() {
.slider-fill {
position: absolute;
right: 0;
height: 100%;
background-color: rgba(255, 255, 255, 0.2);
}
@ -119,12 +121,10 @@ function handleEnd() {
top: 50%;
left: 50%;
transform: translateX(-50%) translateY(-50%);
font-size: 40px;
font-size: 36px;
font-weight: 600;
color: #FFF;
color: #fff;
-webkit-text-stroke: 2px #000;
}
.slider-thumb:active {
cursor: grabbing;
}
</style>

93
src/pages/Index/components/Consumables/SpttingPlates.vue

@ -15,36 +15,41 @@
<div class="sptting-plates">
<div class="card" v-for="i in 6" :key="i">
<div class="plate-area">
<div class="plate-bottom" v-if="plates.length > 0">
<span></span>
<span class="project-name">{{ platesValue[i - 1]?.projShortName }}</span>
<span class="project-number">{{ platesValue[i - 1]?.num }}/25</span>
</div>
<div class="plate-bottom" v-else>
<span></span>
<span class="project-name"></span>
<span class="project-number"></span>
</div>
<!-- plates 数组长度不为 0 渲染 Plate 组件否则显示默认的图片 -->
<div v-if="plates.length > 0" class="plate">
<Plate :value="plates[i - 1].num" :color="plates[i - 1].color" />
<Plate
:value="parseInt(platesValue[i - 1].num)"
:color="platesValue[i - 1].color"
/>
<SliderAreaEx
v-if="plates[i - 1].num > 0"
v-if="platesValue[i - 1].num > 0"
class="slider-area"
:currValue="plates[i - 1].num"
:currValue="parseInt(platesValue[i - 1].num)"
:totalValue="totalVal"
:order="i"
@update:sliderValue="updateSliderVal"
@update:sliderEndValue="updateSliderEndVal"
/>
</div>
<div v-else class="close-circle">
<img
src="@/assets/Index/stop.svg"
alt=""
style="width: 40px; height: 40px; margin-bottom: 20px"
style="width: 40px; height: 40px"
/>
</div>
</div>
<div class="plate-bottom" v-if="plates.length > 0">
<span></span>
<span class="project-name">{{ plates[i - 1]?.projShortName }}</span>
<span class="project-number">{{ plates[i - 1]?.num }}/25</span>
</div>
<div class="plate-bottom" v-else>
<span></span>
<span class="project-name"></span>
<span class="project-number"></span>
</div>
</div>
</div>
<!-- 修改数字弹框 -->
@ -54,7 +59,7 @@
<script setup>
import Plate from './Plate.vue'
import { ref, onMounted, onBeforeUnmount } from 'vue'
import { ref, watch, onMounted, onBeforeUnmount } from 'vue'
import SliderAreaEx from './SliderAreaEx.vue'
import { eventBus } from '../../../../eventBus'
// import ChangeNum from './ChangeNum.vue';
@ -73,22 +78,44 @@ const props = defineProps({
},
})
const platesValue = ref(props.plates)
const totalVal = ref(25)
const isDragging = ref(false)
const deviceStore = useDeviceStore()
watch(
() => props.plates,
(newValue) => {
if (!isDragging.value) {
platesValue.value = newValue
}
},
)
const updateSliderVal = async (plate, order) => {
// eventBus.emit('confirm', { type: 'Plate', value: plate, index })
if (deviceStore.status === 'IDLE') {
isDragging.value = true
// eventBus.emit('confirm', {
// type: 'Plate',
// value: plate,
// index: order - 1,
// })
platesValue.value[order - 1].num = plate
} else {
ElMessage.error('设备正在工作,无法修改数值')
}
}
const updateSliderEndVal = async (plate, order) => {
isDragging.value = false
if (deviceStore.status === 'IDLE') {
try {
const newVal = plate.toFixed()
const res = await updateConsumables({group: `CG${order}`, num: newVal})
const res = await updateConsumables({ group: `CG${order}`, num: plate })
if (res.success) {
// eventBus.emit('confirm', {
// type: 'Plate',
// value: newVal,
// value: plate,
// index: order - 1,
// })
// isOpen.value = false
}
} catch (error) {
console.error('更新失败:', error)
@ -169,7 +196,6 @@ const updateSliderVal = async (plate, order) => {
//
.sptting-plates {
margin: 0 auto;
display: flex;
gap: 10px; //
flex-direction: column;
@ -178,32 +204,37 @@ const updateSliderVal = async (plate, order) => {
width: 355px;
height: 100px;
padding: 0px;
display: grid;
place-content: center;
border: 1px solid black;
position: relative;
border-radius: 10px;
overflow: hidden;
position: relative;
background-color: #f6f6f6;
.plate-area {
width: 100%;
height: 100%;
display: flex;
justify-content: space-between;
flex-direction: column;
align-items: center;
position: relative;
.close-circle {
flex: 1 1 auto;
display: flex;
justify-content: center;
align-items: center;
}
.slider-area {
position: absolute;
left: 0;
top: 0;
right: 0;
bottom: 30px;
bottom: 0;
}
.plate {
width: 100%;
height: 76px;
margin-bottom: 32px;
flex: 1 1 auto;
}
}
@ -211,10 +242,6 @@ const updateSliderVal = async (plate, order) => {
width: 100%;
height: 32px;
background-color: #808080;
border-bottom-left-radius: 10px;
border-bottom-right-radius: 10px;
position: absolute;
bottom: 0;
display: flex;
justify-content: space-around;
color: white;

2
src/pages/Index/components/Running/SampleDisplay.vue

@ -41,7 +41,7 @@
<script setup lang="ts">
import { ref } from 'vue'
import { generateSampleBackground, getBloodTypeLabel } from '../../utils'
import { generateSampleBackground, getBloodTypeLabel } from '../../Utils'
import type { TubeHolderStateMessage } from '../../../../websocket/socket'
defineProps<{
samples: TubeHolderStateMessage['data']['tubes'] //

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

@ -92,7 +92,7 @@ import {
getBloodTypeLabel,
processTubeSettings,
generateSampleBackground,
} from '../../utils'
} from '../../Utils'
import { useTestTubeStore } from '../../../../store'
import { updateTubeActivationStatus } from '../../../../services/index'
import { ConsumableGroupBase } from '../../../../websocket/socket'

Loading…
Cancel
Save