A8000
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

449 lines
11 KiB

8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
8 months ago
8 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
7 months ago
7 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
  1. <template>
  2. <div id="add-emergency-container">
  3. <!-- 添加急诊 -->
  4. <div class="page-header" @click="goBack">
  5. <img class="page-header-icon" src="@/assets/Index/left.svg" />
  6. <div class="page-header-title">添加急诊</div>
  7. </div>
  8. <div class="emergency-userid">
  9. <el-row>
  10. <el-col :span="5">
  11. <label>样本条形码</label>
  12. </el-col>
  13. <el-col :span="12" style="padding-left: 20px">
  14. <input
  15. type="text"
  16. placeholder="请输入样本条形码信息"
  17. @focus="showKeyboard('sampleBarcode')"
  18. style="width: 400px"
  19. :value="emergencyPosition.sampleBarcode"
  20. />
  21. </el-col>
  22. </el-row>
  23. <el-row>
  24. <el-col :span="5">
  25. <label>用户ID</label>
  26. </el-col>
  27. <el-col :span="12" style="padding-left: 20px">
  28. <input
  29. type="text"
  30. placeholder="请输入用户ID信息"
  31. @focus="showKeyboard('userid')"
  32. :value="emergencyPosition.userid"
  33. style="width: 400px"
  34. readonly
  35. />
  36. </el-col>
  37. </el-row>
  38. </div>
  39. <hr style="height: 1px; border: none" />
  40. <!-- 急诊项目 -->
  41. <div class="emergency-project">
  42. <!-- 项目选择内容根据需要添加 -->
  43. <div class="project-title">
  44. <span>项目选择</span>
  45. </div>
  46. <div class="project-list">
  47. <div
  48. v-for="item in consumableStore.projectsAvailable"
  49. :key="item.projId"
  50. class="project-item"
  51. :style="projectStyle(item)"
  52. @click="selectProject(item)"
  53. >
  54. <span class="proj-name">{{ item.projName }}</span>
  55. <span> {{ item.num }}</span>
  56. </div>
  57. </div>
  58. <div class="project-title">
  59. <span>血液类型</span>
  60. </div>
  61. <div class="type-list">
  62. <div
  63. v-for="item in settingTubeStore.bloodTypes"
  64. :key="item.key"
  65. @click="selectBloodType(item)"
  66. class="blood-button"
  67. :class="{ active: emergencyPosition.bloodType === item.key }"
  68. >
  69. {{ item.name }}
  70. </div>
  71. </div>
  72. </div>
  73. <hr style="height: 1px; border: none" />
  74. <!-- 急诊控制 -->
  75. <div class="emergency-btns" style="padding: 50px 0">
  76. <el-button class="cancel-button" @click="goBack">取消</el-button>
  77. <el-button class="ok-button" @click="confirmHandle">确定</el-button>
  78. </div>
  79. <!-- 键盘 -->
  80. <transition name="slide-up">
  81. <div class="keyboard" v-if="keyboardVisible">
  82. <SimpleKeyboard
  83. :input="currentInputValue"
  84. @onChange="handleKeyboardInput"
  85. @onKeyPress="handleKeyPress"
  86. />
  87. </div>
  88. </transition>
  89. </div>
  90. </template>
  91. <script setup lang="ts">
  92. import { ref, onMounted, onUnmounted } from 'vue'
  93. import { useRouter } from 'vue-router'
  94. import { insertEmergency } from '@/services/Index/index'
  95. import {
  96. useEmergencyStore,
  97. useConsumablesStore,
  98. useDeviceStore,
  99. useSettingTestTubeStore,
  100. } from '@/store'
  101. import type { AddEmergencyInfo } from '@/types/Index'
  102. import type { BloodType, ReactionPlateGroup } from '@/websocket/socket'
  103. import { ElMessage } from 'element-plus'
  104. defineOptions({
  105. name: 'EmergencyForm',
  106. })
  107. const consumableStore = useConsumablesStore()
  108. const emergencyStore = useEmergencyStore()
  109. const deviceStore = useDeviceStore()
  110. const settingTubeStore = useSettingTestTubeStore()
  111. const isProjectActivated = (item: ReactionPlateGroup) => {
  112. return emergencyPosition.value.projIds.includes(item.projId!)
  113. }
  114. //项目选中样式及背景色
  115. const projectStyle = (item: ReactionPlateGroup) => {
  116. const activated = isProjectActivated(item)
  117. if (activated) {
  118. return {
  119. border: 'solid 1px transparent',
  120. backgroundColor: consumableStore.projIdColorMap[item.projId!],
  121. color: '#FFF',
  122. }
  123. } else {
  124. return {
  125. border: `solid 1px ${consumableStore.projIdColorMap[item.projId!]}`,
  126. backgroundColor: '#FFF',
  127. color: consumableStore.projIdColorMap[item.projId!],
  128. }
  129. }
  130. }
  131. // 急诊位数据
  132. const emergencyPosition = ref<AddEmergencyInfo>({
  133. sampleBarcode: '', // 样本条形码
  134. userid: '', // 用户ID
  135. projIds: [], // 项目选择
  136. bloodType: '', // 血液类型
  137. })
  138. // 返回事件处理
  139. const router = useRouter()
  140. const goBack = () => {
  141. router.go(-1)
  142. }
  143. // 确认请求
  144. const confirmHandle = async () => {
  145. //设备运行状态不能添加急诊
  146. if (deviceStore.deviceState.workState === 'WORKING') {
  147. ElMessage.error('设备正在运行,无法添加急诊')
  148. return
  149. }
  150. const emergencyInfo = emergencyPosition.value
  151. if (emergencyInfo.projIds.length === 0) {
  152. ElMessage.error('请选择项目')
  153. return
  154. }
  155. if (!emergencyInfo.bloodType) {
  156. ElMessage.error('请选择血液类型')
  157. return
  158. }
  159. const res = await insertEmergency(emergencyPosition.value)
  160. if (res && res.success) {
  161. goBack()
  162. } else {
  163. res && res.data && res.data.info && ElMessage.error(res.data.info)
  164. }
  165. }
  166. const selectProject = (item: ReactionPlateGroup) => {
  167. const projectIndex = emergencyPosition.value.projIds.findIndex(
  168. (proj) => proj === item.projId,
  169. )
  170. if (projectIndex > -1) {
  171. emergencyPosition.value.projIds.splice(projectIndex, 1)
  172. } else {
  173. emergencyPosition.value.projIds.push(item.projId!)
  174. }
  175. }
  176. const selectBloodType = (item: { key: BloodType }) => {
  177. emergencyPosition.value.bloodType = item.key
  178. }
  179. // 处理回显数据
  180. onMounted(() => {
  181. if (!emergencyStore.emergencyInfo) {
  182. return
  183. }
  184. if (
  185. deviceStore.deviceState.workState === 'IDLE' ||
  186. (deviceStore.deviceState.workState === 'PAUSE' &&
  187. emergencyStore.emergencyInfo!.state === 'TO_BE_PROCESSED')
  188. ) {
  189. emergencyPosition.value = {
  190. sampleBarcode: emergencyStore.emergencyInfo.sampleBarcode,
  191. userid: emergencyStore.emergencyInfo.userid,
  192. projIds: emergencyStore.emergencyInfo.projIds,
  193. bloodType: emergencyStore.emergencyInfo.bloodType,
  194. }
  195. }
  196. })
  197. // 键盘相关状态
  198. const keyboardVisible = ref(false)
  199. const currentInputValue = ref('')
  200. const currentInputField = ref<'sampleBarcode' | 'userid' | ''>('')
  201. // 显示键盘
  202. const showKeyboard = (field: 'sampleBarcode' | 'userid') => {
  203. // 清空当前输入值,避免累加
  204. if (field == 'sampleBarcode') {
  205. currentInputValue.value = emergencyPosition.value.sampleBarcode
  206. }
  207. if (field == 'userid') {
  208. currentInputValue.value = emergencyPosition.value.userid
  209. }
  210. currentInputField.value = field
  211. keyboardVisible.value = true
  212. }
  213. // 处理键盘输入
  214. const handleKeyboardInput = (value: string) => {
  215. if (!currentInputField.value) return
  216. // 更新当前输入值
  217. currentInputValue.value = value
  218. // 更新对应字段的值
  219. if (currentInputField.value === 'sampleBarcode') {
  220. emergencyPosition.value.sampleBarcode = value
  221. } else {
  222. emergencyPosition.value.userid = value
  223. }
  224. }
  225. // 处理键盘按键
  226. const handleKeyPress = (button: string) => {
  227. if (button === '{enter}') {
  228. hideKeyboard()
  229. } else if (button === '{bksp}') {
  230. // 处理退格键
  231. const value = currentInputValue.value
  232. if (value.length > 0) {
  233. const newValue = value.slice(0, -1)
  234. handleKeyboardInput(newValue)
  235. }
  236. }
  237. }
  238. // 隐藏键盘
  239. const hideKeyboard = () => {
  240. keyboardVisible.value = false
  241. currentInputField.value = ''
  242. currentInputValue.value = ''
  243. }
  244. // 在组件卸载时清理状态
  245. onUnmounted(() => {
  246. hideKeyboard()
  247. })
  248. </script>
  249. <style lang="less" scoped>
  250. input {
  251. margin-bottom: 20px;
  252. padding: 8px 5px;
  253. border: 1px solid #ccc;
  254. border-radius: 4px;
  255. font-size: 32px;
  256. transition: box-shadow 0.2s ease;
  257. border-radius: 10px;
  258. &::placeholder {
  259. font-size: 32px;
  260. font-weight: 100;
  261. color: #d8d8d8;
  262. }
  263. }
  264. label {
  265. margin-bottom: 8px;
  266. font-size: 32px;
  267. font-weight: 700;
  268. }
  269. #add-emergency-container {
  270. > * {
  271. box-sizing: border-box;
  272. }
  273. margin: 0;
  274. padding: 0;
  275. position: relative;
  276. height: 100%;
  277. width: 100%;
  278. background-color: #f4f6f9;
  279. box-sizing: border-box;
  280. hr {
  281. background-color: #e0e0e0;
  282. }
  283. .page-header {
  284. width: 100%;
  285. height: 100px;
  286. display: flex;
  287. align-items: center;
  288. background-color: #ffffff;
  289. box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
  290. border-radius: 8px;
  291. .page-header-icon {
  292. width: 16.14px;
  293. height: 26.76px;
  294. margin: 0 20px;
  295. cursor: pointer;
  296. transition: transform 0.2s ease;
  297. &:hover {
  298. transform: scale(1.1);
  299. /* 放大效果 */
  300. }
  301. }
  302. .page-header-title {
  303. font-size: 36px;
  304. font-weight: 900;
  305. line-height: 1.2;
  306. color: #333;
  307. }
  308. }
  309. .emergency-userid {
  310. margin-top: 40px;
  311. margin-left: 60px;
  312. }
  313. .emergency-project {
  314. width: 100%;
  315. padding: 20px 40px;
  316. border-radius: 10px;
  317. margin-top: 20px;
  318. .project-title {
  319. width: 100%;
  320. text-align: left;
  321. margin-bottom: 10px;
  322. font-size: 32px;
  323. font-weight: 600;
  324. }
  325. .project-list {
  326. display: flex;
  327. align-items: center;
  328. gap: 12px;
  329. .project-item {
  330. display: flex;
  331. flex-direction: column;
  332. align-items: center;
  333. gap: 5px;
  334. padding: 8px 16px;
  335. font-size: 24px;
  336. border-radius: 8px;
  337. min-width: 64px;
  338. .proj-name {
  339. font-weight: 600;
  340. }
  341. }
  342. margin-bottom: 24px;
  343. }
  344. .type-list {
  345. display: flex;
  346. align-items: center;
  347. gap: 12px;
  348. padding: 10px 0 10px;
  349. .blood-button {
  350. border: 1px solid #5c94fe;
  351. border-radius: 8px;
  352. padding: 8px 16px;
  353. color: #4a90e2;
  354. font-size: 32px;
  355. &.active {
  356. border: 1px solid transparent;
  357. background-color: #4a90e2;
  358. color: #fff;
  359. }
  360. }
  361. }
  362. }
  363. .emergency-btns {
  364. width: 100%;
  365. height: 120px;
  366. display: flex;
  367. margin-top: 30px;
  368. display: flex;
  369. justify-content: center;
  370. align-items: center;
  371. .cancel-button,
  372. .ok-button {
  373. width: 360px;
  374. height: 100px;
  375. border-radius: 30px;
  376. font-size: 36px;
  377. font-weight: 400;
  378. margin: 0 10px;
  379. border: none;
  380. }
  381. .ok-button {
  382. background-color: #528dfe;
  383. color: white;
  384. box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  385. }
  386. .cancel-button {
  387. background-color: #f2f2f2;
  388. color: black;
  389. box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
  390. }
  391. }
  392. .keyboard {
  393. position: fixed;
  394. bottom: 0;
  395. left: 0;
  396. width: 100%;
  397. height: 300px;
  398. background-color: #f5f7fa;
  399. border-top-left-radius: 16px;
  400. border-top-right-radius: 16px;
  401. box-shadow: 0 -2px 12px rgba(0, 0, 0, 0.1);
  402. z-index: 1000;
  403. }
  404. // 键盘动画
  405. .slide-up-enter-active,
  406. .slide-up-leave-active {
  407. transition: transform 0.3s ease;
  408. }
  409. .slide-up-enter-from,
  410. .slide-up-leave-to {
  411. transform: translateY(100%);
  412. }
  413. }
  414. </style>