From a99886eb9c73018235e2c373f3d82a2b0b2311d5 Mon Sep 17 00:00:00 2001
From: zhangchen <1652267879@qq.com>
Date: 星期二, 16 九月 2025 13:36:40 +0800
Subject: [PATCH] Merge branch 'ID1766-添加推送登录功能' into test

---
 src/api/user/index.ts                                                      |   17 
 src/api/patient_hemo_med_start/types/index.type.ts                         |   70 ++
 src/utils/httpApi.ts                                                       |   18 
 src/assets/font/iconfont.woff                                              |    0 
 src/api/patient_hemo_med_start/index.ts                                    |   67 +
 src/api/dict/types/index.type.ts                                           |    9 
 src/store/type/user.type.ts                                                |   27 
 src/api/dict/index.ts                                                      |   14 
 src/assets/font/iconfont.woff2                                             |    0 
 src/assets/font/iconfont.ttf                                               |    0 
 src/views/mobile/bedsideAuxiliaryScreen/pages/SignedIn.vue                 |   91 +
 src/utils/axios.ts                                                         |  134 +++
 src/api/inventory/itemDict.ts                                              |   15 
 src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue              |   34 
 src/store/bedsideAuxiliaryScreen.ts                                        |   29 
 src/api/user.ts                                                            |   16 
 src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue           |   26 
 src/views/mobile/bedsideAuxiliaryScreen/components/StartDialysis/index.vue |  865 +++++++++++++++++++++++++
 src/main.ts                                                                |    4 
 src/views/mobile/bedsideAuxiliaryScreen/components/Login/userInfo.vue      |   70 +
 src/views/mobile/bedsideAuxiliaryScreen/components/EndDialysis/index.vue   |  360 ++++++++++
 src/store/type/bedsideAuxiliaryScreen.type.ts                              |   19 
 src/views/mobile/bedsideAuxiliaryScreen/components/Login/index.vue         |   34 
 src/assets/css/iconfont.css                                                |    7 
 src/api/inventory/types/itemDict.type.ts                                   |    6 
 src/api/commom.type.ts                                                     |    6 
 postcss.config.js                                                          |   49 
 27 files changed, 1,868 insertions(+), 119 deletions(-)

diff --git a/postcss.config.js b/postcss.config.js
index 86ee644..d4d24ba 100644
--- a/postcss.config.js
+++ b/postcss.config.js
@@ -1,24 +1,41 @@
+// module.exports = {
+//   plugins: {
+//     'postcss-pxtorem': {
+//       rootValue: 37.5,
+//       propList: ['*'],
+//       include: file => {
+//         if (!file) return false;
+//         const normalized = file.replace(/\\/g, '/');
+//         // 只转换 mobile 目录里的样式 OR element-plus 目录里的样式
+//         // 但 element-plus 样式只在 mobile 目录被引用时才生效,需保证引用范围
+//         return normalized.includes('/src/views/mobile/') || normalized.includes('/node_modules/element-plus/');
+//       },
+//       exclude: file => {
+//         if (!file) return false;
+//         const normalized = file.replace(/\\/g, '/');
+//         // 排除除 element-plus 外的 node_modules,防止无关样式被转
+//         if (/node_modules/.test(normalized) && !normalized.includes('/node_modules/element-plus/')) {
+//           return true;
+//         }
+//         return false;
+//       },
+//       minPixelValue: 2,
+//     }
+//   }
+// }
+
 module.exports = {
   plugins: {
     'postcss-pxtorem': {
       rootValue: 37.5,
       propList: ['*'],
-      // include: file => {
-      //   if (!file) return false;
-      //   const normalized = file.replace(/\\/g, '/');
-      //   // 只转换 mobile 目录里的样式 OR element-plus 目录里的样式
-      //   // 但 element-plus 样式只在 mobile 目录被引用时才生效,需保证引用范围
-      //   return normalized.includes('/src/views/mobile/') || normalized.includes('/node_modules/element-plus/');
-      // },
-      // exclude: file => {
-      //   if (!file) return false;
-      //   const normalized = file.replace(/\\/g, '/');
-      //   // 排除除 element-plus 外的 node_modules,防止无关样式被转
-      //   if (/node_modules/.test(normalized) && !normalized.includes('/node_modules/element-plus/')) {
-      //     return true;
-      //   }
-      //   return false;
-      // },
+      // ✅ 用正则匹配路径,不依赖字符串 includes
+      include: [
+        /src[\/\\]views[\/\\]mobile/, // 只转 mobile 目录
+        /node_modules[\/\\]element-plus/ // 转 element-plus 样式
+      ],
+      // ✅ 排除其他 node_modules
+      exclude: (file) => /node_modules/.test(file) && !/node_modules[\/\\]element-plus/.test(file),
       minPixelValue: 2,
     }
   }
diff --git a/src/api/commom.type.ts b/src/api/commom.type.ts
new file mode 100644
index 0000000..431408a
--- /dev/null
+++ b/src/api/commom.type.ts
@@ -0,0 +1,6 @@
+export interface ListParams {
+    page: number;
+    size: number;
+    wherecondition: string;
+    ordercondition: string;
+}
\ No newline at end of file
diff --git a/src/api/dict/index.ts b/src/api/dict/index.ts
new file mode 100644
index 0000000..8a531a6
--- /dev/null
+++ b/src/api/dict/index.ts
@@ -0,0 +1,14 @@
+import request from "@/utils/axios";
+import type { DictTypeParams } from './types/index.type';
+
+
+export function getDictTypeApi(params: DictTypeParams) {
+  return request({
+      url: '/dict/base/info/listDictValuesByType',
+      method: 'post',
+      headers: {
+          'Content-Type': 'application/x-www-form-urlencoded'
+      },
+      params,
+  })
+}
\ No newline at end of file
diff --git a/src/api/dict/types/index.type.ts b/src/api/dict/types/index.type.ts
new file mode 100644
index 0000000..0451bb5
--- /dev/null
+++ b/src/api/dict/types/index.type.ts
@@ -0,0 +1,9 @@
+export enum DictType {
+    穿刺方式 = 'in_med_cc_method',
+    穿刺针类型 = 'in_med_cc_zhen_type',
+    穿刺方向 = 'in_med_cc_ccfx'
+}
+
+export interface DictTypeParams {
+    dictType: DictType;
+}
diff --git a/src/api/inventory/itemDict.ts b/src/api/inventory/itemDict.ts
new file mode 100644
index 0000000..3a95a1e
--- /dev/null
+++ b/src/api/inventory/itemDict.ts
@@ -0,0 +1,15 @@
+import request from "@/utils/axios";
+import type { InventoryItemListParams } from './types/itemDict.type';
+
+/**
+ * 获取库存字典api
+ * @param params 
+ * @returns 
+ */
+export function getInventoryItemListApi(params: InventoryItemListParams) {
+    return request({
+      url: '/inventory/item/detail/info/list',
+      method: 'post',
+      params
+    })
+}
\ No newline at end of file
diff --git a/src/api/inventory/types/itemDict.type.ts b/src/api/inventory/types/itemDict.type.ts
new file mode 100644
index 0000000..5c3329a
--- /dev/null
+++ b/src/api/inventory/types/itemDict.type.ts
@@ -0,0 +1,6 @@
+import { ListParams } from '@/api/commom.type';
+
+
+export interface InventoryItemListParams extends ListParams {
+    isPure?: number;
+}
\ No newline at end of file
diff --git a/src/api/patient_hemo_med_start/index.ts b/src/api/patient_hemo_med_start/index.ts
new file mode 100644
index 0000000..05f7b70
--- /dev/null
+++ b/src/api/patient_hemo_med_start/index.ts
@@ -0,0 +1,67 @@
+import request from "@/utils/axios";
+import type {
+  StartRecord,
+  RecordData,
+} from "../patient_hemo_med_start/types/index.type";
+
+/**
+ * 获取开始透析数据api
+ * @param params `recordCode=${recordCode}`
+ * @returns
+ */
+export function addDefaultRowApi(
+  params: string
+): Promise<{ data: StartRecord }> {
+  return request({
+    url: "/patient/hemo/med/start/addDefaultRow",
+    method: "post",
+    headers: {
+      "Content-Type": "application/x-www-form-urlencoded",
+    },
+    data: params,
+  });
+}
+
+/**
+ * 修改开始透析数据api
+ * @param params
+ * @returns
+ */
+export function updateMedstartDataApi(params: StartRecord) {
+  return request({
+    url: "/patient/hemo/med/start/update",
+    method: "post",
+    data: params,
+  });
+}
+
+/**
+ * 获取结束透析数据api
+ * @param params `recordCode=${recordCode}`
+ * @returns
+ */
+export function addDefaultEndRowApi(
+  params: string
+): Promise<{ data: RecordData }> {
+  return request({
+    url: "/patient/hemo/med/end/addDefaultRow",
+    method: "post",
+    headers: {
+      "Content-Type": "application/x-www-form-urlencoded",
+    },
+    data: params,
+  });
+}
+
+/**
+ * 修改结束透析数据api
+ * @param params 
+ * @returns 
+ */
+export function updateEndRowApi(params: RecordData) {
+  return request({
+    url: "/patient/hemo/med/end/update",
+    method: "post",
+    data: params,
+  });
+}
diff --git a/src/api/patient_hemo_med_start/types/index.type.ts b/src/api/patient_hemo_med_start/types/index.type.ts
new file mode 100644
index 0000000..013e1ce
--- /dev/null
+++ b/src/api/patient_hemo_med_start/types/index.type.ts
@@ -0,0 +1,70 @@
+export interface StartRecord {
+  code: string;
+  createTime: string; // "2025-09-12 17:06:42"
+  createUser: string | null;
+  deletedTime: string | null;
+  doneByg: string;
+  doneBygName: string;
+  doneBygSpecName: string;
+  doneGl: string;
+  doneGlName: string;
+  doneGlSpecName: string;
+  id: number | null;
+  isDeleted: number | null;
+  monitorDataCode: string;
+  recordCode: string;
+  remark: string;
+  schemeName: string;
+  /** 穿刺方向 */
+  startCcASideDirection: string;
+  startCcNurse: string;
+  startCcNurseName: string;
+  /** 穿刺方式 */
+  startCcType: string;
+  startCcZhenA: string;
+  startCcZhenAName: string;
+  startCcZhenASpecName: string;
+  startCcZhenType: string;
+  startCcZhenV: string;
+  startCcZhenVName: string;
+  startCcZhenVSpecName: string;
+  startEntranceType: string;
+  startIsCcOrHy: number | null;
+  startIsSave: number | null;
+  startMedNurse: string;
+  startMedNurseName: string;
+  startTime: string | null;
+  startTxqNo: string;
+  startUpNurse: string;
+  startUpNurseName: string;
+  startUseHlb: string;
+  startUseHlbName: string;
+  startUseHlbSpecName: string;
+  startUseNsy: string;
+  startUseNsyName: string;
+  startUseNsySpecName: string;
+  startYcNurse: string;
+  startYcNurseName: string;
+  startYingxue: string;
+  updateTime: string;
+  updateUser: string | null;
+}
+
+export interface RecordData {
+  code: string;
+  createTime: string;
+  createUser: string | null;
+  deletedTime: string | null;
+  endDownNurse: string;
+  endDownNurseName: string;
+  endHuixue: string;
+  endTime: string;
+  id: number | null;
+  isDeleted: number | null;
+  lastTimeMonitorDataTime: string;
+  monitorDataCode: string;
+  recordCode: string;
+  remark: string;
+  updateTime: string;
+  updateUser: string | null;
+}
diff --git a/src/api/user.ts b/src/api/user.ts
new file mode 100644
index 0000000..f033caf
--- /dev/null
+++ b/src/api/user.ts
@@ -0,0 +1,16 @@
+import request from "@/utils/axios";
+
+/**
+ * 副屏退出登录Api
+ * @returns
+ */
+export function logOutApi(params: string) {
+  return request({
+    url: "/user/info/logoutForSubScreen",
+    method: "post",
+    headers: {
+      "Content-Type": "application/x-www-form-urlencoded",
+    },
+    data: params,
+  });
+}
diff --git a/src/api/user/index.ts b/src/api/user/index.ts
new file mode 100644
index 0000000..4f9fa00
--- /dev/null
+++ b/src/api/user/index.ts
@@ -0,0 +1,17 @@
+import request from "@/utils/axios";
+
+/**
+ * 根据角色获取用户列表api
+ * @param params 
+ * @returns 
+ */
+export function getUsersByRoleCodeApi(params: string) {
+  return request({
+    url: "/user/info/getUsersByRoleCode",
+    method: "post",
+    headers: {
+      "Content-Type": "application/x-www-form-urlencoded",
+    },
+    data: params,
+  });
+}
diff --git a/src/assets/css/iconfont.css b/src/assets/css/iconfont.css
index ada5478..9780895 100644
--- a/src/assets/css/iconfont.css
+++ b/src/assets/css/iconfont.css
@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 5011061 */
-  src: url('//at.alicdn.com/t/c/font_5011061_crebeujq91a.woff2?t=1756705233110') format('woff2'),
-       url('//at.alicdn.com/t/c/font_5011061_crebeujq91a.woff?t=1756705233110') format('woff'),
-       url('//at.alicdn.com/t/c/font_5011061_crebeujq91a.ttf?t=1756705233110') format('truetype');
+  src: url('../font/iconfont.woff2') format('woff2'),
+       url('../font/iconfont.woff') format('woff'),
+       url('../font/iconfont.ttf') format('truetype');
 }
 
 .iconfont {
@@ -20,3 +20,4 @@
 .icon-saoma:before {
   content: "\e749";
 }
+
diff --git a/src/assets/font/iconfont.ttf b/src/assets/font/iconfont.ttf
index 0ed3ab5..a017a4c 100644
--- a/src/assets/font/iconfont.ttf
+++ b/src/assets/font/iconfont.ttf
Binary files differ
diff --git a/src/assets/font/iconfont.woff b/src/assets/font/iconfont.woff
index 71bb5d9..24b64cc 100644
--- a/src/assets/font/iconfont.woff
+++ b/src/assets/font/iconfont.woff
Binary files differ
diff --git a/src/assets/font/iconfont.woff2 b/src/assets/font/iconfont.woff2
index 66aa294..6dee77b 100644
--- a/src/assets/font/iconfont.woff2
+++ b/src/assets/font/iconfont.woff2
Binary files differ
diff --git a/src/main.ts b/src/main.ts
index 267b11f..b3d124c 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -10,6 +10,8 @@
 import VConsole from 'vconsole'
 import { createPinia } from 'pinia'
 import '@/assets/css/iconfont.css'
+import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
+
 if (import.meta.env.VITE_ENV === 'development') {
 // 如果需要在手机平板上打开控制台,安装一个这个
     const vConsole = new VConsole()
@@ -18,4 +20,4 @@
 
 const app = createApp(App)
 
-app.use(router).use(pinia).use(ElementPlus).use(Vant).mount('#app')
+app.use(router).use(pinia).use(ElementPlus, { locale: zhCn }).use(Vant).mount('#app')
diff --git a/src/store/bedsideAuxiliaryScreen.ts b/src/store/bedsideAuxiliaryScreen.ts
index de157fa..d40dbc8 100644
--- a/src/store/bedsideAuxiliaryScreen.ts
+++ b/src/store/bedsideAuxiliaryScreen.ts
@@ -14,7 +14,8 @@
 } from "./type/bedsideAuxiliaryScreen.type";
 import { ElMessage } from "element-plus/es";
 import { Local } from "@/utils/storage";
-import type { UserInfo } from './type/user.type';
+import type { DeviceLoginRecord } from './type/user.type';
+import { logOutApi } from "@/api/user";
 
 export const useBedsideAuxiliaryScreenStore = defineStore(
   "bedsideAuxiliaryScreen",
@@ -32,10 +33,7 @@
     const taskData = ref<Task[]>([]);
 
     /** 用户信息 */
-    const userInfo = ref<UserInfo | null>(Local.get('userInfo'));
-
-    /** 用户token */
-    const token = ref<string>(Local.get('token'));
+    const userInfo = ref<DeviceLoginRecord | null>(null);
 
     /** 设置副屏版本号 */
     const setVersion = (val: string) => {
@@ -78,23 +76,15 @@
     };
 
     /** 设置用户信息 */
-    const setUserInfo = (user: UserInfo) => {
+    const setUserInfo = (user: DeviceLoginRecord | null) => {
       userInfo.value = user;
-      Local.set('userInfo', user);
     };
 
-    /** 设置token */
-    const setToken = (str: string) => {
-      token.value = str;
-      Local.set('token', str);
-    }
 
     /** 退出登录 */
-    const logout = () => {
-      Local.remove('token');
-      Local.remove('userInfo');
-      token.value = '';
-      userInfo.value = null;
+    const logout = async (deviceCodeStr: string) => {
+      await logOutApi(deviceCodeStr);
+      setUserInfo(null);
     };
 
     // SSE 相关状态
@@ -185,6 +175,9 @@
           }
 
           deviceData.value = formatDeviceData(deviceData.value, dataBody);
+          // 当前登录的用户信息
+          setUserInfo(dataBody.当前登录状态);
+
           // 判断本地的版本号与远程的版本号是否一致,如果不一致则执行刷新操作
           if (dataBody.服务端版本号 !== version.value) {
             refreshVersion(dataBody.服务端版本号);
@@ -249,9 +242,7 @@
       setVersion,
       refreshVersion,
       userInfo,
-      token,
       setUserInfo,
-      setToken,
       logout,
     };
   }
diff --git a/src/store/type/bedsideAuxiliaryScreen.type.ts b/src/store/type/bedsideAuxiliaryScreen.type.ts
index 59b28af..1552f67 100644
--- a/src/store/type/bedsideAuxiliaryScreen.type.ts
+++ b/src/store/type/bedsideAuxiliaryScreen.type.ts
@@ -1,6 +1,7 @@
 import { tryConvertToInt, deepClone } from "@/utils/utils";
 import { Local } from "@/utils/storage";
 import dayjs from "dayjs";
+import type { DeviceLoginRecord } from './user.type';
 export interface IotInfo {
   属性历史列表: any[];
   床号: string;
@@ -24,6 +25,7 @@
   管路: string[];
   透析器: string[];
   透析模式: string[];
+  透析单编号: string;
 }
 
 enum EPushType {
@@ -212,6 +214,7 @@
   自定义配置项: Customconfiguration;
   服务端版本号: string;
   是否需要立即刷新: 0 | 1;
+  当前登录状态: DeviceLoginRecord | null;
 }
 
 interface Customconfiguration {
@@ -247,6 +250,7 @@
   sphygmomanometer: Sphygmomanometer;
   customConfiguration: Customconfiguration;
   患者出生日期: string;
+  客户编号: string;
 }
 
 export const defaultSphygmomanometer = () :Sphygmomanometer => {
@@ -315,6 +319,7 @@
   carePackage: any[];
   punctureNeedle: PunctureNeedle[];
   vascularAccess: VascularAccess[];
+  透析单编号: string;
 }
 
 export const defaultconsumablesCollection = (): ConsumablesCollection => {
@@ -326,6 +331,7 @@
     管路: [],
     透析器: [],
     透析模式: [],
+    透析单编号: '',
   };
 };
 
@@ -339,6 +345,7 @@
     carePackage: [], // 一次性使用透析护理包列表
     punctureNeedle: [], // 穿刺针列表
     vascularAccess: [], // 血管通路列表
+    透析单编号: ''
   };
 };
 
@@ -373,6 +380,7 @@
   体重增长_透前_上次透后: number | null; // 体重增长(透前-上次透后)
   体重增持_透前_干体重: number | null; // 体重增长(透前-干体重)
   四点血压图数据: 四点血压图数据[];
+  透析单编号: string;
 }
 
 export const defaultSignedIn = (): SignedIn => {
@@ -407,6 +415,7 @@
     体重增长_透前_上次透后: null, // 体重增长(透前-上次透后)
     体重增持_透前_干体重: null, // 体重增长(透前-干体重)
     四点血压图数据: [],
+    透析单编号: '',
   };
 };
 
@@ -443,6 +452,7 @@
   venousPressure2: number | null; // 静脉压
   transmembranePressure2: number | null; // 跨膜压
   处方脱水量: number | null; // 处方脱水量
+  透析单编号: string;
 }
 
 export interface MonitoringRecord {
@@ -485,6 +495,7 @@
     venousPressure2: null, // 静脉压
     transmembranePressure2: null, // 跨膜压
     处方脱水量: null,
+    透析单编号: ''
   };
 };
 
@@ -510,6 +521,7 @@
     // @ts-ignore
     pageType, // 当前要展示的页面
     treatmentStatus: EMedStatus.NOT_CHECKED_IN, // 透析状态
+    客户编号: "",
     consumablesCollection: defaultconsumablesCollection(), // 未排班时需要的数据
     notSignedIn: defalutNotSignedIn(), // 未签到时需要的数据
     signedIn: defaultSignedIn(), // 已签到时需要的数据
@@ -540,6 +552,7 @@
   // 默认床号(设备号)
   result.devicdeNo = seeMsg.IOT信息?.床号;
   result.deviceCode = seeMsg.IOT信息?.设备唯一编号;
+
 
   // 自定义配置项
   result.customConfiguration = seeMsg.自定义配置项 ?? defaultCustomconfiguration();
@@ -577,6 +590,9 @@
       result.patFormNumber = seeMsg.透析状态?.患者门诊住院号;
       result.患者出生日期 = seeMsg.透析状态?.患者出生日期 || "";
 
+      result.客户编号 = seeMsg.透析状态?.clientCode;
+
+
       // 未签到页面需要显示的
       if (treatmentStatus === EMedStatus.NOT_CHECKED_IN) {
         result.pageType = EPageType.NOT_SIGNED_IN;
@@ -589,6 +605,7 @@
         notSignedIn.punctureNeedle = seeMsg.透析状态?.穿刺针列表 ?? [];
         notSignedIn.vascularAccess = seeMsg.透析状态?.血管通路列表 ?? [];
         notSignedIn.anticoagulant = seeMsg.透析状态?.抗凝剂列表 ?? [];
+        notSignedIn.透析单编号 = seeMsg.透析状态?.透析单编号;
 
         result.notSignedIn = notSignedIn;
       }
@@ -635,6 +652,7 @@
           signedIn.上次透析单所属日期 = dayjs(signedIn.四点血压图数据[0].透析日期).format("YYYY-MM-DD");
           // signedIn.本次透析单所属日期 = dayjs(signedIn.四点血压图数据[0].透析日期).format("YYYY-MM-DD");
         }
+        signedIn.透析单编号 = seeMsg.透析状态?.透析单编号;
         result.signedIn = signedIn;
       }
       // 剩下的全使用治疗中的页面
@@ -698,6 +716,7 @@
             ?.realTimeKtvCalcDetailResultInfo ?? [];
         underTreatment.dialysisAge = seeMsg.透析状态?.透析龄 ?? null;
         underTreatment.处方脱水量 = seeMsg.透析状态?.处方脱水量 ?? null;
+        underTreatment.透析单编号 = seeMsg.透析状态?.透析单编号;
 
         result.underTreatment = underTreatment;
       }
diff --git a/src/store/type/user.type.ts b/src/store/type/user.type.ts
index da40248..5e69b20 100644
--- a/src/store/type/user.type.ts
+++ b/src/store/type/user.type.ts
@@ -1,11 +1,18 @@
-export interface UserInfo {
-    用户头像: string;
-    用户昵称: string;
-}
-
-export const defaultUserInfo = (): UserInfo => {
-    return {
-        用户头像: '',
-        用户昵称: ''
-    }
+export interface DeviceLoginRecord {
+  clientCode: string;
+  code: string;
+  createTime: string;      // 格式:YYYY-MM-DD HH:mm:ss
+  createUser: string | null;
+  deletedTime: string | null;
+  deviceCode: string;
+  id: number;
+  isDeleted: number;       // 0/1 标识
+  loginTime: string;       // 格式:YYYY-MM-DD HH:mm:ss
+  remark: string | null;
+  token: string | null;
+  updateTime: string;      // 格式:YYYY-MM-DD HH:mm:ss
+  updateUser: string | null;
+  userCode: string;
+  userName: string;
+  userAvatar: string | null;
 }
\ No newline at end of file
diff --git a/src/utils/axios.ts b/src/utils/axios.ts
new file mode 100644
index 0000000..6dcc26f
--- /dev/null
+++ b/src/utils/axios.ts
@@ -0,0 +1,134 @@
+import axios, { AxiosError, AxiosResponse, InternalAxiosRequestConfig } from 'axios'
+import Qs from 'qs'
+import { useBedsideAuxiliaryScreenStore } from '@/store/bedsideAuxiliaryScreen'
+import { ElMessage } from 'element-plus/es'
+
+// ====== 配置 ======
+const BASE_URL = import.meta.env.VITE_API_BASE_URL
+const TIME_OUT = 60 * 1 * 1000
+
+// 正在进行中的请求列表,避免重复请求
+let reqList: string[] = []
+
+/**
+ * 允许某个请求再次发送
+ */
+const allowRequest = (url?: string) => {
+  if (!url) return
+  const idx = reqList.indexOf(url)
+  if (idx > -1) reqList.splice(idx, 1)
+}
+
+/**
+ * 创建 axios 实例
+ */
+const instance = axios.create({
+  baseURL: BASE_URL,
+  timeout: TIME_OUT,
+  paramsSerializer: (params) => Qs.stringify(params, { arrayFormat: 'brackets' }),
+})
+
+// ====== 错误处理函数 ======
+const errorHandle = (status: number, error: any): void => {
+  const msg = error?.msg || error?.message || ''
+  switch (status) {
+    case 401:
+      ElMessage.error(`Error Code: 401, Message: ${msg || '登录失效,请重新登录'}`);
+      break
+    case 403:
+      ElMessage.error(`Error Code: 403, Message: ${msg || '你没有访问权限'}`);
+      break
+    case 500:
+      ElMessage.error(`Error Code: 500, Message: ${msg || '后台错误,请联系管理员'}`);
+      break
+    case 502:
+      alert()
+      ElMessage.error(`Error Code: 502, Message: ${msg || '平台环境异常'}`);
+      break
+    default:
+      ElMessage.error(`Error Code: ${status}, Message: ${msg || '未知错误,请刷新重试'}`);
+  }
+}
+
+// ====== 请求拦截 ======
+instance.interceptors.request.use(
+  (config: InternalAxiosRequestConfig) => {
+    const store = useBedsideAuxiliaryScreenStore()
+
+    // 加 token
+    if (store.userInfo?.token) {
+      config.headers.set('Authorization', `Bearer ${store.userInfo.token}`)
+    }
+
+    // 防止同一接口短时间内重复请求
+    if (config.url) {
+      if (reqList.includes(config.url)) {
+        // return Promise.reject(new axios.Cancel(`重复请求:${config.url}`))
+      } else {
+        reqList.push(config.url)
+      }
+    }
+
+    return config
+  },
+  (error) => Promise.reject(error),
+)
+
+// ====== 响应拦截 ======
+instance.interceptors.response.use(
+  (res: AxiosResponse) => {
+
+    // setTimeout(() => allowRequest(res.config.url), 1000)
+    if (String(res.status).charAt(0) !== '2') {
+      return Promise.reject({
+        code: res.status,
+        message: res.data?.message || '请求异常,请刷新重试',
+      })
+    }
+    if (res.data?.code === 200) return res.data
+    const whiteList: string[] = []
+    const requestUrl: string = res?.config?.url || ''
+    const isInWhiteList = whiteList.some((item) => requestUrl.includes(item))
+    ElMessage.error(res.data.message);
+    if (isInWhiteList) return Promise.reject(res.data)
+    return Promise.reject(res.data)
+  },
+
+  
+  (err: unknown) => {
+
+    const anyErr = err as any
+    const configUrl = anyErr?.config?.url
+    // 失败后 1s 允许再次请求
+    // setTimeout(() => allowRequest(configUrl), 1000)
+
+    // 先判断是否是取消请求
+    // if (axios.isCancel(err as any)) {
+    //   console.warn('请求被取消:', (err as any)?.message)
+    //   return Promise.reject(err)
+    // }
+
+    // 判断是否为 axios 的错误类型(有 response)
+    if (axios.isAxiosError(err)) {
+      const axErr = err // 现在 TypeScript 知道这是 AxiosError
+      if (axErr.response) {
+        // 使用你现有的错误处理函数
+        errorHandle(axErr.response.status, axErr.response.data)
+
+        if (axErr.response.status === 401) {
+            ElMessage.error('请登录');
+        }
+        return Promise.reject(axErr.response.data || axErr.response)
+      }
+
+      ElMessage.error('网络请求失败, 请刷新重试');
+      return Promise.reject(axErr)
+    }
+
+    //(未知错误)
+    ElMessage.error('网络请求失败, 请刷新重试');
+    return Promise.reject(err)
+  },
+)
+
+export default instance
diff --git a/src/utils/httpApi.ts b/src/utils/httpApi.ts
index d8f0c2e..632b95f 100644
--- a/src/utils/httpApi.ts
+++ b/src/utils/httpApi.ts
@@ -93,4 +93,22 @@
     } catch (error) {
         throw error;
     }
+}
+
+/**
+ * 推出登录
+ * @param deviceCode 
+ * @returns 
+ */
+export const logoutForSubScreen = async(deviceCode: string) => {
+    try {
+        const response = await axios.post(`${apiBaseUrl}/user/info/logoutForSubScreen`, { deviceCode }, {
+            headers: {
+                'Content-Type': 'application/x-www-form-urlencoded'
+            }
+        });
+        return response.data
+    } catch (error) {
+        throw error;
+    }
 }
\ No newline at end of file
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/EndDialysis/index.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/EndDialysis/index.vue
new file mode 100644
index 0000000..a292564
--- /dev/null
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/EndDialysis/index.vue
@@ -0,0 +1,360 @@
+<template>
+  <div class="end-dialysis-container">
+    <el-dialog
+      v-model="show"
+      center
+      title="结束透析"
+      width="80%"
+      :show-close="false"
+      class="end-dialysis-dialog"
+      :destroy-on-close="true"
+      :close-on-click-modal="false"
+    >
+      <template #header>
+        <div class="end-dialysis-header">
+          <span class="header-title">结束透析</span>
+          <img
+            :src="closeImg"
+            class="header-close"
+            @click="handleCancel"
+            alt=""
+          />
+        </div>
+      </template>
+      <div class="end-dialysis-content" v-loading="loading">
+        <el-form :model="formData" size="large">
+          <el-row :gutter="10">
+            <el-col :span="24">
+              <el-form-item label="结束时间">
+                <el-row>
+                  <el-col :span="8">
+                    <el-date-picker
+                      v-model="formData.endData"
+                      :clearable="false"
+                      type="date"
+                      placeholder="选择日期"
+                      format="YYYY/MM/DD"
+                      value-format="YYYY-MM-DD"
+                      style="width: 100%"
+                    />
+                  </el-col>
+                  <el-col :span="8">
+                    <el-time-select
+                      :clearable="false"
+                      v-model="formData.endTime"
+                      start="00:00"
+                      step="00:01"
+                      end="23:59"
+                      placeholder="选择时间"
+                      style="width: 100%"
+                    />
+                  </el-col>
+                  <el-col :span="3">
+                    <el-button type="primary" @click="addTime(1)"
+                      >时间+1</el-button
+                    >
+                  </el-col>
+                  <el-col :span="3">
+                    <el-button type="primary" @click="addTime(0.5)"
+                      >时间+0.5</el-button
+                    >
+                  </el-col>
+                </el-row>
+              </el-form-item>
+            </el-col>
+          </el-row>
+          <el-row :gutter="10">
+            <el-col :span="8">
+              <el-form-item label="下机护士">
+                <el-select
+                  style="width: 100%"
+                  v-model="formData.endDownNurse"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="item in nurseOptions"
+                    :key="item.code"
+                    :label="item.userName"
+                    :disabled="item.isValid === 0"
+                    :value="item.code"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="8">
+              <el-form-item label="回血(ml/min)">
+                <el-input style="width: 100%" v-model="formData.endHuixue" />
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="my-button cancel" @click="handleCancel">取消</div>
+        <div
+          class="my-button confirm"
+          :class="loading ? 'cancel' : ''"
+          @click="handleConfirm"
+        >
+          确认
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, reactive, toRefs } from "vue";
+import closeImg from "@/img/close.png";
+import {
+  addDefaultEndRowApi,
+  updateEndRowApi,
+} from "@/api/patient_hemo_med_start/index";
+import type { RecordData } from "@/api/patient_hemo_med_start/types/index.type";
+import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
+import dayjs from "dayjs";
+import { getUsersByRoleCodeApi } from "@/api/user/index";
+import { ElMessage } from "element-plus";
+
+interface FormData {
+  id: number;
+  recordCode: string;
+  endTime: string;
+  endDownNurse: string;
+  endDownNurseName: string;
+  lastTimeMonitorDataTime: string;
+  endHuixue: string;
+  endData: string;
+}
+
+interface State {
+  show: boolean;
+  loading: boolean;
+  formData: FormData;
+  nurseOptions: any[];
+  defaultEndTime: string;
+}
+export default {
+  name: "EndDialysis",
+  setup() {
+    const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
+
+    const state = reactive<State>({
+      show: false,
+      loading: false,
+      formData: {
+        id: 0,
+        recordCode: "",
+        endTime: "",
+        endDownNurse: "",
+        endDownNurseName: "",
+        lastTimeMonitorDataTime: "",
+        endHuixue: "",
+        endData: "",
+      },
+      nurseOptions: [],
+      defaultEndTime: "", // 默认的日期,原始数据,用于计算
+    });
+
+    const recordCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.deviceData.underTreatment.透析单编号;
+    });
+
+    const clientCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.deviceData.客户编号;
+    });
+
+    const userCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.userInfo.userCode;
+    });
+
+    const openDialog = async () => {
+      state.loading = true;
+      state.show = true;
+      try {
+        const { data } = await addDefaultEndRowApi(
+          `recordCode=${recordCode.value}`
+        );
+
+        const dataCopy = JSON.parse(JSON.stringify(data));
+        const notLs = [null, ""];
+
+        if (notLs.includes(data.endDownNurse)) {
+          dataCopy.endDownNurse = userCode.value;
+        }
+
+        if (notLs.includes(data.endTime)) {
+          dataCopy.endTime = dayjs().format("HH:mm");
+          // @ts-ignore
+          dataCopy.endData = dayjs().format("YYYY-MM-DD");
+          state.defaultEndTime = dayjs().format("YYYY-MM-DD HH:mm");
+        } else {
+          dataCopy.endTime = dayjs(data.endTime).format("HH:mm");
+          // @ts-ignore
+          dataCopy.endData = dayjs(data.endTime).format("YYYY-MM-DD");
+          state.defaultEndTime = dayjs(data.endTime).format("YYYY-MM-DD HH:mm");
+        }
+        state.formData = dataCopy as unknown as FormData;
+        await getNurses();
+      } catch (error) {
+        setTimeout(() => {
+          state.show = false;
+        }, 1000);
+      } finally {
+        state.loading = false;
+      }
+    };
+
+    const handleCancel = () => {
+      state.show = false;
+    };
+
+    const handleConfirm = async () => {
+      if (state.loading) return false;
+      state.loading = true;
+      try {
+        const paramsData = Object.assign({}, state.formData, {
+          endTime: dayjs(
+            `${state.formData.endData} ${state.formData.endTime}`
+          ).format("YYYY-MM-DD HH:mm:ss"),
+        });
+        await updateEndRowApi(paramsData as unknown as RecordData);
+        ElMessage.success("操作成功!");
+        state.show = false;
+      } finally {
+        state.loading = false;
+      }
+    };
+
+    const addTime = (hourNum: number) => {
+      if (["", null].includes(state.formData.lastTimeMonitorDataTime))
+        return false;
+      const addDate = addHours(
+        state.formData.lastTimeMonitorDataTime + "",
+        hourNum
+      );
+      state.formData.endData = dayjs(addDate).format("YYYY-MM-DD");
+      state.formData.endTime = dayjs(addDate).format("HH:mm");
+    };
+
+    const addHours = (dateStr: string, hours: number): string => {
+      return dayjs(dateStr).add(hours, "hour").format("YYYY-MM-DD HH:mm:ss");
+    };
+
+    /**
+     * 获取护士数据
+     */
+    const getNurses = async () => {
+      const { data } = await getUsersByRoleCodeApi(
+        `clientCode=${clientCode.value}&roleClass=nurse`
+      );
+      state.nurseOptions = data;
+    };
+
+    return {
+      ...toRefs(state),
+      closeImg,
+      handleCancel,
+      handleConfirm,
+      openDialog,
+      addTime,
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.end-dialysis-container {
+  ::v-deep(.el-dialog) {
+    padding: 0;
+    border-radius: 6px;
+    overflow: hidden;
+  }
+  ::v-deep(.el-dialog__footer) {
+    padding: 4px;
+  }
+  ::v-deep(.el-upload-dragger) {
+    height: 65px;
+    padding: 0 !important;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  ::v-deep(.el-upload-dragger .el-icon--upload) {
+    display: none;
+  }
+  ::v-deep(.el-dialog__header) {
+    padding-bottom: 6px;
+  }
+  .end-dialysis-header {
+    position: relative;
+    height: 16px;
+    background: #769aff;
+    .header-title {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translateX(-50%) translateY(-50%);
+      font-family: AlibabaPuHuiTi, AlibabaPuHuiTi;
+      font-weight: 500;
+      font-size: 8px;
+      color: #ffffff;
+      line-height: 11px;
+      text-align: center;
+    }
+    .header-close {
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 6px;
+      width: 15px;
+      height: 15px;
+      transition: transform 0.2s;
+
+      &:active {
+        opacity: 0.6;
+        transform: translateY(-50%) scale(0.95);
+      }
+    }
+  }
+  .end-dialysis-content {
+    padding: 0 12px 0px 12px;
+    margin-bottom: 4px;
+    border-bottom: 1px solid #d8d8d8;
+  }
+  .my-button {
+    display: inline-block;
+    border-radius: 2px;
+    padding: 0px 10px;
+    font-family: PingFangSC, PingFang SC;
+    font-weight: 500;
+    font-size: 7px;
+    color: #ffffff;
+    line-height: 16px;
+    letter-spacing: 1px;
+    text-align: center;
+    font-style: normal;
+    transition: transform 0.1s ease, opacity 0.1s ease;
+    cursor: pointer;
+    &:active {
+      transform: scale(0.95);
+      opacity: 0.8;
+    }
+
+    &:not(:first-child) {
+      margin-left: 6px;
+    }
+
+    &.confirm {
+      background: #769aff;
+    }
+    &.cancel {
+      background: #bbc6dd;
+    }
+    &.refresh {
+      background: #e6a23c;
+    }
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue
index cb20147..2807483 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue
@@ -65,7 +65,8 @@
         alt=""
         @click="openSettingDeviceDialog"
       />
-      <img :src="userImg" class="btn-img" alt="" @click="openLoginDialog" />
+      <img v-if="userInfo?.code" :src="userInfo?.userAvatar" class="btn-img" alt="" @click="openLoginDialog" />
+      <img v-else :src="userImg" class="btn-img" alt="" @click="openLoginDialog" />
     </div>
   </div>
   <!-- 设置设备编号组件 -->
@@ -164,6 +165,14 @@
   }
 });
 
+
+const userInfo = computed(() => {
+  if (!bedsideAuxiliaryScreenStore.userInfo) return null
+  return Object.assign({}, bedsideAuxiliaryScreenStore.userInfo, {
+    userAvatar: bedsideAuxiliaryScreenStore.userInfo?.userAvatar ? bedsideAuxiliaryScreenStore.userInfo?.userAvatar : generateCircularAvatar(bedsideAuxiliaryScreenStore.userInfo?.userName)
+  });
+});
+
 watch(
   () => bedsideAuxiliaryScreenStore.taskData?.[0]?.countdown,
   (val) => {
@@ -246,6 +255,29 @@
     loginDialogRef.value.openDialog();
 };
 
+function generateCircularAvatar(name: string, size = 100): string {
+  console.log('name: ', name)
+  const canvas = document.createElement('canvas');
+  canvas.width = size;
+  canvas.height = size;
+  const ctx = canvas.getContext('2d')!;
+  
+  // 绘制圆形背景
+  ctx.fillStyle = '#dae5ec';
+  ctx.beginPath();
+  ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
+  ctx.fill();
+  
+  // 绘制文字(第一个字)
+  ctx.fillStyle = '#70a3dd'; // 字体颜色
+  ctx.font = `${size * 0.5}px sans-serif`; // 字体大小为头像大小的一半
+  ctx.textAlign = 'center';
+  ctx.textBaseline = 'middle';
+  ctx.fillText(name.charAt(0), size / 2, size / 2);
+  
+  return canvas.toDataURL('image/png'); // 返回 Base64 图片
+}
+
 onUnmounted(() => {
   clearTimer();
 });
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/Login/index.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/Login/index.vue
index 43c53fc..a30b281 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/components/Login/index.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/Login/index.vue
@@ -59,13 +59,24 @@
     });
 
     const isLoginng = computed(() => {
-      return !!bedsideAuxiliaryScreenStore.token;
+      return !!bedsideAuxiliaryScreenStore.userInfo?.code;
     });
 
     const qrCodeData = computed(() => {
       return {
         deviceCode: bedsideAuxiliaryScreenStore.deviceCode,
+        deviceNo: bedsideAuxiliaryScreenStore.deviceData.devicdeNo,
       };
+    });
+
+    const userInfo = computed(() => {
+      const userInfo = bedsideAuxiliaryScreenStore.userInfo;
+      if (!userInfo) return null;
+      return {
+        ...userInfo,
+        床号: bedsideAuxiliaryScreenStore.deviceData.devicdeNo,
+        设备编号: bedsideAuxiliaryScreenStore.deviceCode
+      }
     });
 
     const openDialog = () => {
@@ -77,17 +88,25 @@
     };
 
     const onLogout = () => {
+      if (state.loading) return ElMessage.warning('正在退出中...')
       ElMessageBox.confirm("是否确认退出当前登录用户?", "提示", {
         confirmButtonText: "确认",
         cancelButtonText: "取消",
         type: "warning",
       })
-        .then(() => {
-            bedsideAuxiliaryScreenStore.logout();
-          ElMessage({
-            type: "success",
-            message: "退出成功!",
-          });
+        .then(async() => {
+            state.loading = true
+            try {
+              await bedsideAuxiliaryScreenStore.logout(`deviceCode=${bedsideAuxiliaryScreenStore.deviceCode}`);
+              handleCancel();
+              ElMessage.success('退出登录成功')
+              handleCancel();
+            } catch (error) {
+              console.error('error: ', error)
+              // ElMessage.error('退出登录失败')
+            } finally {
+              state.loading = false;
+            }
         })
         .catch(() => {});
     };
@@ -100,6 +119,7 @@
       handleCancel,
       openDialog,
       onLogout,
+      userInfo,
     };
   },
 };
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/Login/userInfo.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/Login/userInfo.vue
index f5e9fa2..ced0bff 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/components/Login/userInfo.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/Login/userInfo.vue
@@ -1,7 +1,7 @@
 <template>
   <div class="userinfo-container">
-    <img :src="userInfo.用户头像" alt="" srcset="" class="user-avatar">
-    <div class="user-text">当前账户:{{ userInfo.用户昵称 }}</div>
+    <img :src="userInfo.userAvatar" alt="" srcset="" class="user-avatar" />
+    <div class="user-text">当前账户:{{ userInfo.userName }}</div>
     <div class="user-text">床号:{{ userInfo.床号 }}</div>
     <div class="user-text">设备号:{{ userInfo.设备编号 }}</div>
   </div>
@@ -10,7 +10,6 @@
 <script lang="ts">
 import { computed } from "vue";
 import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
-import { defaultUserInfo } from '@/store/type/user.type';
 
 export default {
   name: "UserInfo",
@@ -18,16 +17,41 @@
     const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
 
     const userInfo = computed(() => {
-      const userInfo = bedsideAuxiliaryScreenStore.userInfo || defaultUserInfo();
+      const userInfo = bedsideAuxiliaryScreenStore.userInfo;
+      if (!userInfo) return null;
       return {
         ...userInfo,
         床号: bedsideAuxiliaryScreenStore.deviceData.devicdeNo,
-        设备编号: bedsideAuxiliaryScreenStore.deviceCode
-      }
+        设备编号: bedsideAuxiliaryScreenStore.deviceCode,
+        userAvatar: userInfo?.userAvatar ? userInfo?.userAvatar : generateCircularAvatar(userInfo?.userName)
+      };
     });
 
+    function generateCircularAvatar(name: string, size = 100): string {
+      console.log("name: ", name);
+      const canvas = document.createElement("canvas");
+      canvas.width = size;
+      canvas.height = size;
+      const ctx = canvas.getContext("2d")!;
+
+      // 绘制圆形背景
+      ctx.fillStyle = "#769aff";
+      ctx.beginPath();
+      ctx.arc(size / 2, size / 2, size / 2, 0, Math.PI * 2);
+      ctx.fill();
+
+      // 绘制文字(第一个字)
+      ctx.fillStyle = "#FFFFFF"; // 字体颜色
+      ctx.font = `${size * 0.5}px sans-serif`; // 字体大小为头像大小的一半
+      ctx.textAlign = "center";
+      ctx.textBaseline = "middle";
+      ctx.fillText(name.charAt(0), size / 2, size / 2);
+
+      return canvas.toDataURL("image/png"); // 返回 Base64 图片
+    }
+
     return {
-        userInfo
+      userInfo,
     };
   },
 };
@@ -35,21 +59,21 @@
 
 <style lang="less" scoped>
 .userinfo-container {
-    display: flex;
-    flex-direction: column;
-    align-items: center;
-    justify-content: center;
-    .user-avatar {
-        width: 15px;
-        height: 15px;
-        border-radius: 50%;
-        overflow: hidden;
-        margin-bottom: 5px;
-    }
-    .user-text {
-        font-size: 5px;
-        color: #000;
-        margin-bottom: 4px;
-    }
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  .user-avatar {
+    width: 15px;
+    height: 15px;
+    border-radius: 50%;
+    overflow: hidden;
+    margin-bottom: 5px;
+  }
+  .user-text {
+    font-size: 5px;
+    color: #000;
+    margin-bottom: 4px;
+  }
 }
 </style>
\ No newline at end of file
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/components/StartDialysis/index.vue b/src/views/mobile/bedsideAuxiliaryScreen/components/StartDialysis/index.vue
new file mode 100644
index 0000000..0a9958f
--- /dev/null
+++ b/src/views/mobile/bedsideAuxiliaryScreen/components/StartDialysis/index.vue
@@ -0,0 +1,865 @@
+<template>
+  <div class="start-dialysis-container">
+    <el-dialog
+      v-model="show"
+      center
+      title="开始透析"
+      width="80%"
+      :show-close="false"
+      class="start-dialysis-dialog"
+      :destroy-on-close="true"
+      :close-on-click-modal="false"
+    >
+      <template #header>
+        <div class="start-dialysis-header">
+          <span class="header-title">开始透析</span>
+          <img
+            :src="closeImg"
+            class="header-close"
+            @click="handleCancel"
+            alt=""
+          />
+        </div>
+      </template>
+      <div class="start-dialysis-content" v-loading="loading">
+        <el-form :model="formData" size="large">
+          <el-row :gutter="10">
+            <el-col :span="6">
+              <el-form-item label="开始时间">
+                <el-time-select
+                  v-model="formData.startTime"
+                  start="00:00"
+                  step="00:01"
+                  end="23:59"
+                  placeholder="请选择"
+                  :clearable="false"
+                  style="width: 100%"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="治疗护士">
+                <el-select
+                  @change="onStartMedNurseChange"
+                  style="width: 100%"
+                  v-model="formData.startMedNurse"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="item in nurseOptions"
+                    :key="item.code"
+                    :label="item.userName"
+                    :disabled="item.isValid === 0"
+                    :value="item.code"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="上机护士">
+                <el-select
+                  style="width: 100%"
+                  v-model="formData.startUpNurse"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="item in nurseOptions"
+                    :key="item.code"
+                    :label="item.userName"
+                    :disabled="item.isValid === 0"
+                    :value="item.code"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="预充管路">
+                <el-select
+                  style="width: 100%"
+                  v-model="formData.startYcNurse"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="item in nurseOptions"
+                    :key="item.code"
+                    :label="item.userName"
+                    :disabled="item.isValid === 0"
+                    :value="item.code"
+                  >
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="穿刺/换药">
+                <el-select
+                  style="width: 100%"
+                  v-model="formData.startIsCcOrHy"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="(item, index) in startIsCcOrHyOptions"
+                    :key="index"
+                    :label="item.label"
+                    :value="item.value"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <template v-if="formData.startIsCcOrHy === 1">
+              <el-col :span="6">
+                <el-form-item label="换药护士">
+                  <el-select
+                    style="width: 100%"
+                    v-model="formData.startCcNurse"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in nurseOptions"
+                      :key="item.code"
+                      :label="item.userName"
+                      :disabled="item.isValid === 0"
+                      :value="item.code"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </template>
+            <template v-else-if="formData.startIsCcOrHy === 0">
+              <el-col :span="6">
+                <el-form-item label="穿刺护士">
+                  <el-select
+                    style="width: 100%"
+                    v-model="formData.startCcNurse"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in nurseOptions"
+                      :key="item.code"
+                      :label="item.userName"
+                      :disabled="item.isValid === 0"
+                      :value="item.code"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </template>
+            <template v-else-if="formData.startIsCcOrHy === 2">
+              <el-col :span="6">
+                <el-form-item label="换药护士">
+                  <el-select
+                    style="width: 100%"
+                    v-model="formData.startCcNurse"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in nurseOptions"
+                      :key="item.code"
+                      :label="item.userName"
+                      :disabled="item.isValid === 0"
+                      :value="item.code"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </template>
+            <template
+              v-if="
+                formData.startIsCcOrHy === 2 || formData.startIsCcOrHy === 0
+              "
+            >
+              <el-col :span="6">
+                <el-form-item label="穿刺方式" prop="doneCcType">
+                  <el-select
+                    style="width: 100%"
+                    multiple
+                    v-model="formData.startCcType"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in punctureMethodOptions"
+                      :key="item.code"
+                      :label="item.dictText"
+                      :value="item.dictText"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="穿刺针A">
+                  <el-select
+                    clearable
+                    style="width: 100%"
+                    v-model="formData.startCcZhenA"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in punctureNeedleOptions"
+                      :key="item.code"
+                      :label="item.itemName + ' ' + item.itemSpec"
+                      :value="item.code"
+                      :disabled="item.isShow !== 1"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item clearable label="穿刺针V">
+                  <el-select
+                    style="width: 100%"
+                    v-model="formData.startCcZhenV"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in punctureNeedleOptions"
+                      :key="item.code"
+                      :label="item.itemName + ' ' + item.itemSpec"
+                      :value="item.code"
+                      :disabled="item.isShow !== 1"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+              <el-col :span="6">
+                <el-form-item label="穿刺方向">
+                  <el-select
+                    style="width: 100%"
+                    multiple
+                    v-model="formData.startCcASideDirection"
+                    placeholder="请选择"
+                  >
+                    <el-option
+                      v-for="item in punctureDirectionOptions"
+                      :key="item.code"
+                      :label="item.dictText"
+                      :value="item.dictText"
+                    >
+                    </el-option>
+                  </el-select>
+                </el-form-item>
+              </el-col>
+            </template>
+            <el-col :span="6">
+              <el-form-item label="引血(ml/min)">
+                <el-input style="width: 100%" v-model="formData.startYingxue" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="透析器编号">
+                <el-input style="width: 100%" v-model="formData.startTxqNo" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="入科方式">
+                <el-select
+                  style="width: 100%"
+                  v-model="formData.startEntranceType"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="(item, index) in startEntranceTypeOptions"
+                    :key="index"
+                    :label="item.label"
+                    :value="item.value"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="一次性使用管路">
+                <el-select v-model="formData.doneGl" placeholder="请选择">
+                  <el-option
+                    v-for="(item, index) in disposablePipelineOptions"
+                    :key="index"
+                    :label="item.itemName + ' ' + item.itemSpec"
+                    :value="item.code"
+                    :disabled="item.isShow !== 1"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="血滤管">
+                <el-select
+                  v-model="formData.doneByg"
+                  :disabled="formData.schemeName !== 'HDF'"
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="(item, index) in hemofiltrationTubeOptions"
+                    :key="index"
+                    :label="item.itemName + ' ' + item.itemSpec"
+                    :value="item.code"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="透析浓缩液">
+                <el-select
+                  v-model="formData.startUseNsy"
+                  multiple
+                  filterable
+                  clearable
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="(item, index) in doneUseNsyOptions"
+                    :key="index"
+                    :label="item.itemName + ' ' + item.itemSpec"
+                    :value="item.code"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="6">
+              <el-form-item label="透析护理包">
+                <el-select
+                  v-model="formData.startUseHlb"
+                  filterable
+                  clearable
+                  placeholder="请选择"
+                >
+                  <el-option
+                    v-for="(item, index) in doneUseHlbOptions"
+                    :key="index"
+                    :label="item.itemName + ' ' + item.itemSpec"
+                    :value="item.code"
+                  ></el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </div>
+      <template #footer>
+        <div class="my-button cancel" @click="handleCancel">取消</div>
+        <div
+          class="my-button confirm"
+          :class="loading ? 'cancel' : ''"
+          @click="handleConfirm"
+        >
+          确认
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script lang="ts">
+import { computed, reactive, toRefs } from "vue";
+import closeImg from "@/img/close.png";
+import {
+  addDefaultRowApi,
+  updateMedstartDataApi,
+} from "@/api/patient_hemo_med_start/index";
+import type { StartRecord } from "@/api/patient_hemo_med_start/types/index.type";
+import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
+import { getInventoryItemListApi } from "@/api/inventory/itemDict";
+import { getUsersByRoleCodeApi } from "@/api/user/index";
+import { getDictTypeApi } from "@/api/dict/index";
+import { DictType } from "@/api/dict/types/index.type";
+import dayjs from "dayjs";
+import { ElMessage } from "element-plus/es";
+
+interface FormData
+  extends Omit<
+    StartRecord,
+    "startCcType" | "startCcASideDirection" | "startUseNsy"
+  > {
+  /** 穿刺方式 */
+  startCcType: string[];
+  /** 穿刺方向 */
+  startCcASideDirection: string[];
+  startUseNsy: string[];
+}
+
+interface State {
+  loading: boolean;
+  show: boolean;
+  formData: FormData;
+  doneUseNsyOptions: any[];
+  doneUseHlbOptions: any[];
+  disposablePipelineOptions: any[];
+  hemofiltrationTubeOptions: any[];
+  nurseOptions: any[];
+  punctureMethodOptions: any[];
+  typePunctureNeedleOptions: any[];
+  punctureDirectionOptions: any[];
+  punctureNeedleOptions: any[];
+  startIsCcOrHyOptions: any[];
+  startEntranceTypeOptions: any[];
+  startDate: string;
+}
+export default {
+  name: "StartDialysis",
+  setup() {
+    const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
+
+    const state = reactive<State>({
+      loading: false,
+      show: false,
+      formData: {
+        code: "",
+        createTime: "",
+        createUser: null,
+        deletedTime: null,
+        doneByg: "",
+        doneBygName: "",
+        doneBygSpecName: "",
+        doneGl: "",
+        doneGlName: "",
+        doneGlSpecName: "",
+        id: null,
+        isDeleted: null,
+        monitorDataCode: "",
+        recordCode: "",
+        remark: "",
+        schemeName: "",
+        /** 穿刺方向 */
+        startCcASideDirection: [],
+        startCcNurse: "",
+        startCcNurseName: "",
+        /** 穿刺方式 */
+        startCcType: [],
+        startCcZhenA: "",
+        startCcZhenAName: "",
+        startCcZhenASpecName: "",
+        startCcZhenType: "",
+        startCcZhenV: "",
+        startCcZhenVName: "",
+        startCcZhenVSpecName: "",
+        startEntranceType: "",
+        startIsCcOrHy: null,
+        startIsSave: null,
+        startMedNurse: "",
+        startMedNurseName: "",
+        startTime: null,
+        startTxqNo: "",
+        startUpNurse: "",
+        startUpNurseName: "",
+        startUseHlb: "",
+        startUseHlbName: "",
+        startUseHlbSpecName: "",
+        startUseNsy: [],
+        startUseNsyName: "",
+        startUseNsySpecName: "",
+        startYcNurse: "",
+        startYcNurseName: "",
+        startYingxue: "",
+        updateTime: "",
+        updateUser: null,
+      },
+      doneUseNsyOptions: [], // 透析浓缩液列表
+      doneUseHlbOptions: [], // 透析护理包列表
+      disposablePipelineOptions: [], // 一次性使用管路列表
+      hemofiltrationTubeOptions: [], // 血滤管列表
+      nurseOptions: [], // 护士列表
+      punctureMethodOptions: [], // 穿刺方式列表
+      typePunctureNeedleOptions: [], // 穿刺针类型列表
+      punctureDirectionOptions: [], // 穿刺方向列表
+      punctureNeedleOptions: [], // 穿刺针列表
+      startIsCcOrHyOptions: [
+        { label: "穿刺", value: 0 },
+        { label: "换药", value: 1 },
+        { label: "穿刺+换药", value: 2 },
+      ],
+      startEntranceTypeOptions: [
+        { label: "步行", value: "步行" },
+        { label: "扶行", value: "扶行" },
+        { label: "轮椅", value: "轮椅" },
+        { label: "平床", value: "平床" },
+        { label: "床旁", value: "床旁" },
+        { label: "助行器", value: "助行器" },
+      ],
+      startDate: "", // 日期部分
+    });
+
+    const recordCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.deviceData.signedIn.透析单编号;
+    });
+
+    const clientCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.deviceData.客户编号;
+    });
+
+    const userCode = computed(() => {
+      return bedsideAuxiliaryScreenStore.userInfo.userCode;
+    });
+
+    const openDialog = async () => {
+      state.show = true;
+      state.loading = true;
+      try {
+        const promiseFuns = [
+          getDoneUseNsys(), 
+          getdoneUseHlbs(), 
+          getDisposablePipelines(), 
+          getHemofiltrationTubes(),
+          getPunctureNeedles(),
+          getNurses(),
+          getPunctureMethods(),
+          getTypePunctureNeedles(),
+          getPunctureDirectionOptions(),
+        ];
+        // if (state.doneUseNsyOptions.length <= 0) {
+        //   promiseFuns.push(getDoneUseNsys());
+        // }
+        // if (state.doneUseHlbOptions.length <= 0) {
+        //   promiseFuns.push(getdoneUseHlbs());
+        // }
+        // if (state.disposablePipelineOptions.length <= 0) {
+        //   promiseFuns.push(getDisposablePipelines());
+        // }
+        // if (state.hemofiltrationTubeOptions.length <= 0) {
+        //   promiseFuns.push(getHemofiltrationTubes());
+        // }
+        // if (state.punctureNeedleOptions.length <= 0) {
+        //   promiseFuns.push(getPunctureNeedles());
+        // }
+        // if (state.nurseOptions.length <= 0) {
+        //   promiseFuns.push(getNurses());
+        // }
+        // if (state.punctureMethodOptions.length <= 0) {
+        //   promiseFuns.push(getPunctureMethods());
+        // }
+        // if (state.typePunctureNeedleOptions.length <= 0) {
+        //   promiseFuns.push(getTypePunctureNeedles());
+        // }
+        // if (state.punctureDirectionOptions.length <= 0) {
+        //   promiseFuns.push(getPunctureDirectionOptions());
+        // }
+        await Promise.all(promiseFuns);
+        await getStartData();
+      } catch (error) {
+        console.error("开始透析初始化失败", error);
+        state.show = false;
+      } finally {
+        state.loading = false;
+      }
+    };
+
+    /**
+     * 获取开始透析默认数据
+     */
+    const getStartData = async () => {
+      const { data } = await addDefaultRowApi(`recordCode=${recordCode.value}`);
+      const dataCopy = JSON.parse(JSON.stringify(data));
+      const nots = ["", null];
+
+      if (!nots.includes(data.startCcType)) {
+        // @ts-ignore
+        dataCopy.startCcType = data.startCcType.split(",");
+      } else {
+        // @ts-ignore
+        dataCopy.startCcType = [];
+      }
+
+      if (!nots.includes(data.startCcASideDirection)) {
+        // @ts-ignore
+        dataCopy.startCcASideDirection = data.startCcASideDirection.split(",");
+      } else {
+        // @ts-ignore
+        dataCopy.startCcASideDirection = [];
+      }
+
+      if (nots.includes(data.startTime)) {
+        dataCopy.startTime = dayjs().format("HH:mm");
+        state.startDate = dayjs().format("YYYY-MM-DD");
+      } else {
+        dataCopy.startTime = dayjs(data.startTime).format("HH:mm");
+        state.startDate = dayjs(data.startTime).format("YYYY-MM-DD");
+      }
+
+      if (nots.includes(data.startMedNurse)) {
+        dataCopy.startMedNurse = userCode.value;
+      }
+
+      if (nots.includes(data.startCcNurse)) {
+        dataCopy.startCcNurse = userCode.value;
+      }
+      if (nots.includes(data.startUpNurse)) {
+        dataCopy.startUpNurse = userCode.value;
+      }
+      if (nots.includes(data.startYcNurse)) {
+        dataCopy.startYcNurse = userCode.value;
+      }
+      if (!nots.includes(data.startUseNsy)) {
+        // @ts-ignore
+        dataCopy.startUseNsy = data.startUseNsy.split(",");
+      } else {
+        // @ts-ignore
+        dataCopy.startUseNsy = [];
+      }
+
+      if (data.schemeName !== "HDF") {
+        // 如果透析模式不算HDF的话血滤管置空
+        dataCopy.doneByg = "";
+        dataCopy.doneBygName = "";
+      } else {
+        const defaultXlg = state.hemofiltrationTubeOptions.find(
+          (e) => e.itemName === "义鑫血液净化补液管路(HDIT-1)"
+        );
+        if (defaultXlg) {
+          dataCopy.doneByg = defaultXlg.code;
+          dataCopy.doneBygName = defaultXlg.itemName;
+        }
+      }
+      state.formData = dataCopy as unknown as FormData;
+    };
+
+    /**
+     * 获取透析浓缩液字典数据
+     */
+    const getDoneUseNsys = async () => {
+      const params = {
+        page: 0,
+        size: 0,
+        wherecondition: `inventory_type_code='IT2112291619002680' and is_show = 1 and client_code='${clientCode.value}'`,
+        ordercondition: "item_is_use desc, item_sort_order asc",
+        isPure: 0,
+      };
+      const { data } = await getInventoryItemListApi(params);
+      state.doneUseNsyOptions = data.list;
+    };
+
+    /**
+     * 获取透析护理包字典数据
+     */
+    const getdoneUseHlbs = async () => {
+      const params = {
+        page: 0,
+        size: 0,
+        wherecondition: `inventory_type_code='IT2112291619003161' and is_show = 1 and client_code='${clientCode.value}'`,
+        ordercondition: "item_is_use desc, item_sort_order asc",
+        isPure: 0,
+      };
+      const { data } = await getInventoryItemListApi(params);
+      state.doneUseHlbOptions = data.list;
+    };
+
+    /**
+     * 获取一次性使用管路字典数据
+     */
+    const getDisposablePipelines = async () => {
+      const params = {
+        page: 0,
+        size: 0,
+        wherecondition: `inventory_type_code='IT2112291619006054' and is_show = 1 and client_code='${clientCode.value}'`,
+        ordercondition: "item_is_use desc, item_sort_order asc",
+        isPure: 0,
+      };
+      const { data } = await getInventoryItemListApi(params);
+      state.disposablePipelineOptions = data.list;
+    };
+
+    /**
+     * 获取血滤管字典数据
+     */
+    const getHemofiltrationTubes = async () => {
+      const params = {
+        page: 1,
+        size: 10,
+        wherecondition: `inventory_type_code='IT9024215920713quQV' and is_show = 1 and client_code='${clientCode.value}'`,
+        ordercondition: "item_is_use desc, item_sort_order asc",
+        isPure: 0,
+      };
+      const { data } = await getInventoryItemListApi(params);
+      state.hemofiltrationTubeOptions = data.list;
+    };
+
+    /**
+     * 获取穿刺针字典数据
+     */
+    const getPunctureNeedles = async () => {
+      const params = {
+        page: 1,
+        size: 10,
+        wherecondition: `(inventory_type_code='IT3561011130526051' OR inventory_type_code='IT2112291619000061') and client_code='${clientCode.value}'`,
+        ordercondition: "item_is_use desc, item_sort_order asc",
+      };
+      const { data } = await getInventoryItemListApi(params);
+      state.punctureNeedleOptions = data.list;
+    };
+
+    /**
+     * 获取护士数据
+     */
+    const getNurses = async () => {
+      const { data } = await getUsersByRoleCodeApi(
+        `clientCode=${clientCode.value}&roleClass=nurse`
+      );
+      state.nurseOptions = data;
+    };
+
+    /**
+     * 获取穿刺方式字典数据
+     */
+    const getPunctureMethods = async () => {
+      const { data } = await getDictTypeApi({ dictType: DictType.穿刺方式 });
+      state.punctureMethodOptions = data;
+    };
+
+    /**
+     * 获取穿刺针类型字典数据
+     */
+    const getTypePunctureNeedles = async () => {
+      const { data } = await getDictTypeApi({ dictType: DictType.穿刺针类型 });
+      state.typePunctureNeedleOptions = data;
+    };
+
+    /**
+     * 获取穿刺针方向字典数据
+     */
+    const getPunctureDirectionOptions = async () => {
+      const { data } = await getDictTypeApi({ dictType: DictType.穿刺方向 });
+      state.punctureDirectionOptions = data;
+    };
+
+    const handleCancel = () => {
+      state.show = false;
+      state.loading = false;
+    };
+
+    const handleConfirm = async () => {
+      state.loading = true;
+      try {
+        const paramsData = Object.assign({}, state.formData, {
+          startCcASideDirection:
+            state.formData.startCcASideDirection.toString(),
+          startCcType: state.formData.startCcType.toString(),
+          startUseNsy: state.formData.startUseNsy.join(","),
+          startTime: `${state.startDate} ${state.formData.startTime}:00`,
+        });
+        await updateMedstartDataApi(paramsData);
+        ElMessage.success("操作成功!");
+        state.show = false;
+      } finally {
+        state.loading = false;
+      }
+    };
+
+    /**
+     * 治疗护士的改变
+     * @param val
+     */
+    const onStartMedNurseChange = (val: string) => {
+      state.formData.startCcNurse = val;
+      state.formData.startUpNurse = val;
+      state.formData.startYcNurse = val;
+    };
+
+    return {
+      ...toRefs(state),
+      closeImg,
+      handleCancel,
+      openDialog,
+      handleConfirm,
+      onStartMedNurseChange,
+    };
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.start-dialysis-container {
+  ::v-deep(.el-dialog) {
+    padding: 0;
+    border-radius: 6px;
+    overflow: hidden;
+  }
+  ::v-deep(.el-dialog__footer) {
+    padding: 4px;
+  }
+  ::v-deep(.el-upload-dragger) {
+    height: 65px;
+    padding: 0 !important;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+  ::v-deep(.el-upload-dragger .el-icon--upload) {
+    display: none;
+  }
+  ::v-deep(.el-dialog__header) {
+    padding-bottom: 6px;
+  }
+  .start-dialysis-header {
+    position: relative;
+    height: 16px;
+    background: #769aff;
+    .header-title {
+      position: absolute;
+      left: 50%;
+      top: 50%;
+      transform: translateX(-50%) translateY(-50%);
+      font-family: AlibabaPuHuiTi, AlibabaPuHuiTi;
+      font-weight: 500;
+      font-size: 8px;
+      color: #ffffff;
+      line-height: 11px;
+      text-align: center;
+    }
+    .header-close {
+      position: absolute;
+      top: 50%;
+      transform: translateY(-50%);
+      right: 6px;
+      width: 15px;
+      height: 15px;
+      transition: transform 0.2s;
+
+      &:active {
+        opacity: 0.6;
+        transform: translateY(-50%) scale(0.95);
+      }
+    }
+  }
+  .start-dialysis-content {
+    padding: 0 12px 0px 12px;
+    margin-bottom: 4px;
+    border-bottom: 1px solid #d8d8d8;
+  }
+  .my-button {
+    display: inline-block;
+    border-radius: 2px;
+    padding: 0px 10px;
+    font-family: PingFangSC, PingFang SC;
+    font-weight: 500;
+    font-size: 7px;
+    color: #ffffff;
+    line-height: 16px;
+    letter-spacing: 1px;
+    text-align: center;
+    font-style: normal;
+    transition: transform 0.1s ease, opacity 0.1s ease;
+    cursor: pointer;
+    &:active {
+      transform: scale(0.95);
+      opacity: 0.8;
+    }
+
+    &:not(:first-child) {
+      margin-left: 6px;
+    }
+
+    &.confirm {
+      background: #769aff;
+    }
+    &.cancel {
+      background: #bbc6dd;
+    }
+    &.refresh {
+      background: #e6a23c;
+    }
+  }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/pages/SignedIn.vue b/src/views/mobile/bedsideAuxiliaryScreen/pages/SignedIn.vue
index 9e9d42f..6b33391 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/pages/SignedIn.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/pages/SignedIn.vue
@@ -251,7 +251,8 @@
               <tr>
                 <td>体重增长</td>
                 <td class="flex-td">
-                  <span class="sub-text">(透前-干体重)</span><span class="text-right">{{ weightGain2 }}kg</span>
+                  <span class="sub-text">(透前-干体重)</span
+                  ><span class="text-right">{{ weightGain2 }}kg</span>
                 </td>
               </tr>
               <!-- <tr>
@@ -294,14 +295,15 @@
         @click="() => onScheduledTasksClick()"
         class="btn"
       />
-      <BlockBotttom
+      <!-- <BlockBotttom
         :icon="jiaoHaoImg"
         text="叫号"
         backgroundColor="#20C6B6"
         @click="() => onCallBumberClick()"
         class="btn"
-      />
+      /> -->
       <BlockBotttom
+        v-if="isShowStartBtn"
         :icon="kaiShiImg"
         text="开始"
         backgroundColor="#409EFF"
@@ -311,6 +313,8 @@
     </div>
     <!-- 定时任务 -->
     <ScheduledTaskDialog ref="scheduledTaskDialogRef" />
+    <!-- 开始透析组件 -->
+    <StartDialysis ref="startDialysisRef" />
   </div>
 </template>
 <script lang="ts" setup name="SignedIn">
@@ -345,9 +349,11 @@
 // @ts-ignore
 import BlockBotttom from "../components/BlockBotttom.vue";
 import { ElMessage } from "element-plus";
+import { EMedStatus } from '@/store/type/bedsideAuxiliaryScreen.type';
 const ScheduledTaskDialog = defineAsyncComponent(
   () => import("../components/ScheduledTask.vue")
 );
+const StartDialysis = defineAsyncComponent(() => import('../components/StartDialysis/index.vue'));
 
 interface Props {
   height: number;
@@ -358,7 +364,7 @@
 
 const bloodPressureRectangularChart = ref<HTMLElement | null>(null);
 const scheduledTaskDialogRef = ref<any>(null);
-
+const startDialysisRef = ref<any>(null);
 
 const pageData = computed(() => {
   return Object.assign(bedsideAuxiliaryScreenStore.deviceData.signedIn, {
@@ -416,12 +422,19 @@
   return formattedValue;
 });
 
+/**
+ * 是否显示开始透析按钮
+ */
+const isShowStartBtn = computed(() => {
+  return +bedsideAuxiliaryScreenStore.deviceData.treatmentStatus === EMedStatus.SIGNED_IN;
+})
 
 watch(
   () => pageData.value.四点血压图数据,
   (newVal) => {
     genderBloodPressureRectangularChart(newVal);
-});
+  }
+);
 
 /** 点击定时任务 */
 const onScheduledTasksClick = () => {
@@ -441,20 +454,27 @@
 };
 
 const onStartClick = () => {
-  ElMessage({
-    message: "功能开发中,敬请期待!",
-    type: "warning",
-  });
+  if (
+    !bedsideAuxiliaryScreenStore.deviceCode ||
+    !bedsideAuxiliaryScreenStore.deviceData.deviceCode
+  )
+    return ElMessage.warning("未初始化或正在进行初始化操作中");
+  if (!bedsideAuxiliaryScreenStore.userInfo?.token)
+    return ElMessage.warning("请登录");
+  startDialysisRef.value?.openDialog();
 };
 
-const genderBloodPressureRectangularChart = (datas: 四点血压图数据[] | null) => {
+const genderBloodPressureRectangularChart = (
+  datas: 四点血压图数据[] | null
+) => {
   if (!bloodPressureRectangularChart.value) return;
 
   const benchmarkData = { width: 386, height: 280 };
 
   // 获取容器宽高(90% 缩放)
   const containerWidth = bloodPressureRectangularChart.value.offsetWidth * 0.9;
-  const containerHeight = bloodPressureRectangularChart.value.offsetHeight * 0.9;
+  const containerHeight =
+    bloodPressureRectangularChart.value.offsetHeight * 0.9;
 
   // 获取设备像素比
   const dpr = window.devicePixelRatio || 1;
@@ -515,10 +535,22 @@
 
   // ========== 血压数据 ==========
   const measurements = [
-    { systolic: datas?.[0]?.血压1_透前收缩压 ?? 0, diastolic: datas?.[0]?.血压1_透前舒张压 ?? 0 },
-    { systolic: datas?.[0]?.血压2_前半程最低收缩压 ?? 0, diastolic: datas?.[0]?.血压2_前半程最低舒张压 ?? 0 },
-    { systolic: datas?.[0]?.血压3_后半程最低收缩压 ?? 0, diastolic: datas?.[0]?.血压3_后半程最低舒张压 ?? 0 },
-    { systolic: datas?.[0]?.血压4_透后收缩压 ?? 0, diastolic: datas?.[0]?.血压4_透后舒张压 ?? 0 },
+    {
+      systolic: datas?.[0]?.血压1_透前收缩压 ?? 0,
+      diastolic: datas?.[0]?.血压1_透前舒张压 ?? 0,
+    },
+    {
+      systolic: datas?.[0]?.血压2_前半程最低收缩压 ?? 0,
+      diastolic: datas?.[0]?.血压2_前半程最低舒张压 ?? 0,
+    },
+    {
+      systolic: datas?.[0]?.血压3_后半程最低收缩压 ?? 0,
+      diastolic: datas?.[0]?.血压3_后半程最低舒张压 ?? 0,
+    },
+    {
+      systolic: datas?.[0]?.血压4_透后收缩压 ?? 0,
+      diastolic: datas?.[0]?.血压4_透后舒张压 ?? 0,
+    },
   ];
 
   const measurementWidth = chartWidth / (measurements.length + 1);
@@ -580,10 +612,7 @@
   const cylinderHeight = 计算脱水量刻度 * (datas?.[0]?.超滤总量 || 0);
   const 体重增长_透前减干体重 = weightGain2.value;
   const 透前减干体重减超滤总量差值 =
-  Math.round(
-    (体重增长_透前减干体重 - (datas?.[0]?.超滤总量 || 0)) * 10
-  ) / 10;
-
+    Math.round((体重增长_透前减干体重 - (datas?.[0]?.超滤总量 || 0)) * 10) / 10;
 
   drawCylinder(
     ctx,
@@ -597,13 +626,11 @@
     datas?.[0]?.脱水百分比 ?? 0,
     datas?.[0]?.透后体重减干体重的差值 ?? 0,
     体重增长_透前减干体重,
-    透前减干体重减超滤总量差值,
+    透前减干体重减超滤总量差值
   );
 
   ctx.restore(); // 恢复
 };
-
-
 
 const drawCylinder = (
   ctx: CanvasRenderingContext2D,
@@ -617,7 +644,7 @@
   脱水百分比: number, // 脱水百分比
   透后体重减干体重的差值: number, // 透后体重减干体重的差值
   体重增长_透前减干体重: number, // 体重增长_透前减干体重
-  透前减干体重减超滤总量差值: number, // 透前减干体重减超滤总量差值
+  透前减干体重减超滤总量差值: number // 透前减干体重减超滤总量差值
 ) => {
   ctx.beginPath();
   ctx.arc(x, y + height, radius, 0, Math.PI * 2);
@@ -652,24 +679,26 @@
 
   // 如果超滤总量 与 体重增长_透前减干体重 相等,则显示 "/超滤总量"
   if (Number(超滤总量) && 体重增长_透前减干体重 === Number(超滤总量)) {
-      ctx.font = `${baseFontSize * scale}px Arial`;
-      ctx.textAlign = "center";
-      ctx.fillStyle = "#07c160";
-      ctx.fillText('/' + 超滤总量, textX, canvasHeight - height + height * 0.3 + 10); 
+    ctx.font = `${baseFontSize * scale}px Arial`;
+    ctx.textAlign = "center";
+    ctx.fillStyle = "#07c160";
+    ctx.fillText(
+      "/" + 超滤总量,
+      textX,
+      canvasHeight - height + height * 0.3 + 10
+    );
   } else {
     // 透析前-干体重
     ctx.font = `${baseFontSize * scale}px Arial`;
     ctx.textAlign = "center";
     ctx.fillStyle = "#409EFF";
-    ctx.fillText(体重增长_透前减干体重 + '', textX, canvasHeight - height + 10);
+    ctx.fillText(体重增长_透前减干体重 + "", textX, canvasHeight - height + 10);
     // 超滤总量
     ctx.font = `${baseFontSize * scale}px Arial`;
     ctx.textAlign = "center";
     ctx.fillStyle = "#07c160";
     ctx.fillText(超滤总量, textX, canvasHeight - height + height * 0.3 + 10);
   }
-
-
 
   if (透前减干体重减超滤总量差值 > 0) {
     ctx.font = `${baseFontSize * scale}px Arial`;
@@ -1095,7 +1124,7 @@
         font-size: 3px;
         color: #666;
         margin-right: 1.2px;
-         white-space: nowrap;
+        white-space: nowrap;
       }
       // .text-right {
       //   position: absolute;
diff --git a/src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue b/src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue
index c8f442b..359069f 100644
--- a/src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue
+++ b/src/views/mobile/bedsideAuxiliaryScreen/pages/UnderTreatment.vue
@@ -304,20 +304,20 @@
             @click="() => onScheduledTasksClick()"
             class="btn"
           />
-          <BlockBotttom
+          <!-- <BlockBotttom
             :icon="jiaoHaoImg"
             text="叫号"
             backgroundColor="#20C6B6"
             @click="() => onCallBumberClick()"
             class="btn"
-          />
-          <BlockBotttom
+          /> -->
+          <!-- <BlockBotttom
             :icon="addImg"
             text="添加记录"
             backgroundColor="#409EFF"
             @click="() => onAddRecordClick()"
             class="btn"
-          />
+          /> -->
           <BlockBotttom
             v-if="!whetherDialysisHasBeenEnded"
             :icon="kaiShiImg"
@@ -331,6 +331,8 @@
     </div>
     <!-- 定时任务 -->
     <ScheduledTaskDialog ref="scheduledTaskDialogRef" />
+    <!-- 结束透析组件 -->
+    <EndDialysis ref="endDialysisRef" />
   </div>
 </template>
 <script lang="ts" setup name="UnderTreatment">
@@ -355,6 +357,7 @@
 const ScheduledTaskDialog = defineAsyncComponent(
   () => import("../components/ScheduledTask.vue")
 );
+const EndDialysis = defineAsyncComponent(() => import("../components/EndDialysis/index.vue"));
 import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
 import {
   formatSubstituteMode,
@@ -390,6 +393,7 @@
 const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
 
 const scheduledTaskDialogRef = ref<any>(null);
+const endDialysisRef = ref<any>(null);
 
 // ktv趋势图的
 const ktvListEchartRef = ref<HTMLElement | null>(null);
@@ -419,6 +423,8 @@
     +bedsideAuxiliaryScreenStore.deviceData.treatmentStatus >= EMedStatus.END
   );
 });
+
+
 
 watch(
   () => pageData.value.ktvList,
@@ -785,10 +791,14 @@
 
 /** 结束透析 */
 const onEndClick = () => {
-  ElMessage({
-    message: "功能开发中,敬请期待!",
-    type: "warning",
-  });
+  if (
+    !bedsideAuxiliaryScreenStore.deviceCode ||
+    !bedsideAuxiliaryScreenStore.deviceData.deviceCode
+  )
+    return ElMessage.warning("未初始化或正在进行初始化操作中");
+  if (!bedsideAuxiliaryScreenStore.userInfo?.token)
+    return ElMessage.warning("请登录");
+  endDialysisRef.value?.openDialog();
 };
 
 onMounted(() => {

--
Gitblit v1.8.0