单应用项目,可以创建很多独立工具类页面 ,不用登录 初始化的页面
zhangchen
2025-07-25 fbb37cbba51cf4fff14cafad214935516434ac92
ID1625-暂存
4个文件已修改
1个文件已添加
253 ■■■■■ 已修改文件
src/store/bedsideAuxiliaryScreen.ts 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/type/bedsideAuxiliaryScreen.type.ts 51 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/type/task.type.ts 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/store/bedsideAuxiliaryScreen.ts
@@ -1,8 +1,10 @@
import { defineStore } from "pinia";
import { ref } from "vue";
import dayjs from "dayjs";
import cache from "../utils/cache";
import { EventSourcePolyfill } from "event-source-polyfill";
import type { BedsideAuxiliaryScreen } from "./type/bedsideAuxiliaryScreen.type";
import type { BedsideAuxiliaryScreen, SseMsgData } from "./type/bedsideAuxiliaryScreen.type";
import type { Task } from "./type/task.type";
import {
  defaultDeviceData,
  defaultconsumablesCollection,
@@ -19,6 +21,9 @@
    /** 设备信息数据 */
    const deviceData = ref<BedsideAuxiliaryScreen>(defaultDeviceData());
    /** 任务列表 */
    const taskData = ref<Task[]>([]);
    /**
     * 设置设备编号
     * @param code
@@ -26,6 +31,47 @@
    const setDeviceCode = (code: string) => {
      deviceCode.value = code;
      cache.set("devcieCode", code);
    };
    /**
     * 清除设备信息
     */
    const clearDevice = () => {
      deviceData.value = defaultDeviceData();
    };
    /**
     * 追加定时任务
     * @param taskItem
     */
    const pushTask = (taskItem: Task) => {
      taskData.value.push(taskItem);
    };
    /**
     * 是否将当前任务设置为已过期
     * @param i
     */
    // const deleteTask = (i: number) => {
    //   const task = taskData.value[i];
    //   if (task) {
    //     // 二次判断,判断任务时间是否早于或等于当前时间
    //     const taskTime = dayjs(task.taskDate).second(0).millisecond(0);
    //     const now = dayjs().second(0).millisecond(0); // 秒和毫秒都去掉
    //     if (!taskTime.isAfter(now)) {
    //       taskData.value[i].overdue = true
    //     }
    //   }
    // };
    /** 设置当前定时任务 */
    const setSyncTask = (taskItem: Task) => {
      taskData.value = [taskItem];
    };
    /** 清除当前定时任务 */
    const clearTask = () => {
      taskData.value = [];
    };
    // SSE 相关状态
@@ -85,10 +131,21 @@
        let end = msg.length - 1;
        if (beng !== -1 && end !== -1 && dif !== -1) {
          const datax = msg.slice(beng, end + 1);
          const dataBody = JSON.parse(datax);
          const dataBody = JSON.parse(datax) as SseMsgData;
          console.log("dataBody: ", dataBody);
          // 倒计时提示文本
          if (dataBody.倒计时?.提示文本) {
          if (dataBody.倒计时?.提醒文本) {
            const taskTime = dayjs(dataBody.倒计时?.当前服务器时间).add(dataBody.倒计时?.设定提醒倒计时, 'minute')
            setSyncTask({
              deviceCode: dataBody.IOT信息.设备唯一编号,
              recordCode: dataBody.透析状态?.透析单编号,
              taskDate: taskTime.format('YYYY-MM-DD HH:mm'),
              taskName: dataBody.倒计时?.提醒文本,
              overdue: false,
              countdown: dataBody.倒计时?.设定提醒倒计时
            })
          } else {
            clearTask();
          }
          deviceData.value = formatDeviceData(dataBody);
@@ -104,6 +161,8 @@
        source.value.close();
        source.value = null;
        isConnected.value = false;
        clearDevice();
        clearTask();
        console.log("[SSE] 连接已关闭");
      }
    };
@@ -125,6 +184,10 @@
      connect,
      close,
      refresh,
      taskData,
      pushTask,
      setSyncTask,
      clearTask,
    };
  }
);
src/store/type/bedsideAuxiliaryScreen.type.ts
@@ -57,7 +57,10 @@
  分区编号: string;
  处方脱水量: number | null;
  实时ktv: string;
  实时ktv计算结果列表: null | { realTimeKtvCalcDetailResultInfo: KtvItem[], 透析单编号: string };
  实时ktv计算结果列表: null | {
    realTimeKtvCalcDetailResultInfo: KtvItem[];
    透析单编号: string;
  };
  实时脱水量: number | null;
  干体重: number | null;
  年龄: number | null;
@@ -117,7 +120,7 @@
  透析状态: string; // '0.0'这种格式的,得格式化一下
  透析结束时间: number | null;
  透析处方备注: string;
  最近最大脱水量透析时长: string
  最近最大脱水量透析时长: string;
}
export interface VascularAccess {
@@ -156,15 +159,23 @@
  项目结果: string;
}
export interface Countdown {
  当前服务器时间?: string;
  提醒文本?: string;
  提醒文本字典?: any[];
  设定提醒倒计时?: number;
}
export interface SseMsgData {
  IOT信息: IotInfo | null;
  使用耗材字典: ConsumablesCollection | null; // 当透析状态为治疗中时该字段为null
  倒计时: "";
  倒计时: Countdown | null;
  推送类型: PushType;
  透析状态: DialysisStatus | null;
}
export interface BedsideAuxiliaryScreen {
  deviceCode: string;
  devicdeNo: string | number;
  recordCode: string;
  patientCode: string;
@@ -294,7 +305,7 @@
    dryWeight: null, // 干体重
    preDialysisWeight: null, // 透前称重
    weightAfterLastDialysis: null, // 上次透后称重
    weightIncrease: null, // 体重增加
    weightIncrease: null, // 体重增加
    weightIncreaseRate: null, // 体重增长率
  };
};
@@ -374,6 +385,7 @@
    ? EPageType.LOADING
    : EPatForm.OUTPATIENT_SERVICE;
  return {
    deviceCode: "", // 设备code
    devicdeNo: "", // 设备号
    recordCode: "", // 透析单code
    patientCode: "", // 患者code
@@ -396,10 +408,12 @@
export const formatDeviceData = (
  seeMsg: SseMsgData
): BedsideAuxiliaryScreen => {
  const result = defaultDeviceData();
  // 默认床号(设备号)
  result.devicdeNo = seeMsg.IOT信息?.床号;
  result.deviceCode = seeMsg.IOT信息?.设备唯一编号;
  if (seeMsg.推送类型 === EPushType.SPHYGMOMANOMETR) {
    result.pageType = EPageType.SPHYGMOMANOMETER;
@@ -456,7 +470,8 @@
          seeMsg.透析状态?.最近最大脱水量日期 ?? "";
        signedIn.dryWeight = seeMsg.透析状态?.干体重 ?? null;
        signedIn.preDialysisWeight = seeMsg.透析状态?.透前称重 ?? null;
        signedIn.weightAfterLastDialysis = seeMsg.透析状态?.上次透后称重 ?? null;
        signedIn.weightAfterLastDialysis =
          seeMsg.透析状态?.上次透后称重 ?? null;
        signedIn.weightIncrease = seeMsg.透析状态?.体重增加 ?? null;
        signedIn.weightIncreaseRate = seeMsg.透析状态?.体重增长率 ?? null;
@@ -475,8 +490,10 @@
          seeMsg.透析状态?.最近最大脱水量 ?? "";
        underTreatment.maximumDehydrationCapacityDate =
          seeMsg.透析状态?.最近最大脱水量日期 ?? "";
        underTreatment.maximumDehydrationDuration = seeMsg.透析状态?.最近最大脱水量透析时长 ?? "";
        underTreatment.prescriptionRemarks = seeMsg.透析状态?.透析处方备注 ?? "";
        underTreatment.maximumDehydrationDuration =
          seeMsg.透析状态?.最近最大脱水量透析时长 ?? "";
        underTreatment.prescriptionRemarks =
          seeMsg.透析状态?.透析处方备注 ?? "";
        underTreatment.abnormalItems = seeMsg.透析状态?.异常检验指标 ?? [];
        underTreatment.prescriptionDialysisDuration =
          seeMsg.透析状态?.透析处方的时长 ?? null;
@@ -503,9 +520,13 @@
        underTreatment.monitoringRecord = seeMsg.透析状态?.监测记录列表 ?? [];
        underTreatment.doctorAdvice = seeMsg.透析状态?.透析单医嘱列表 ?? [];
        underTreatment.bloodFlow = "";
        underTreatment.dialysisFluidFlowRate = seeMsg.透析状态?.iot_血流量 ?? null;
        underTreatment.bloodVolumeMonitoring = seeMsg.透析状态?.iot_透析液流速 ?? null;
        underTreatment.ktvList = seeMsg.透析状态?.实时ktv计算结果列表?.realTimeKtvCalcDetailResultInfo ?? [];
        underTreatment.dialysisFluidFlowRate =
          seeMsg.透析状态?.iot_血流量 ?? null;
        underTreatment.bloodVolumeMonitoring =
          seeMsg.透析状态?.iot_透析液流速 ?? null;
        underTreatment.ktvList =
          seeMsg.透析状态?.实时ktv计算结果列表
            ?.realTimeKtvCalcDetailResultInfo ?? [];
        result.underTreatment = underTreatment;
      }
    }
@@ -563,10 +584,10 @@
export const formatSubstituteMode = (mode: string) => {
  let result = "";
  if (mode === '前置换') {
    result = '前'
  } else if (mode === '后置换') {
    result = '后'
  if (mode === "前置换") {
    result = "前";
  } else if (mode === "后置换") {
    result = "后";
  }
  return result;
}
};
src/store/type/task.type.ts
New file
@@ -0,0 +1,12 @@
export interface Task {
  /** 设备code */
  deviceCode: string;
  /** 透析单code */
  recordCode?: string;
  /** 任务提醒时间 */
  taskDate: string;
  /** 任务名称 */
  taskName: string;
  /** 是否过期 */
  overdue: boolean;
}
src/views/mobile/bedsideAuxiliaryScreen/components/Header.vue
@@ -23,10 +23,16 @@
            {{ patientInfo.patForm }}:{{ patientInfo.patFormNumber }}</span
          >
        </template>
        {{ taskCountdown }}
      </template>
    </div>
    <div class="header-right">
      <img :src="atRegularTimeImg" class="btn-img" alt="" @click="openScheduledTaskDialog" />
      <img
        :src="atRegularTimeImg"
        class="btn-img"
        alt=""
        @click="openScheduledTaskDialog"
      />
      <img
        :src="setUpImg"
        class="btn-img"
@@ -43,13 +49,22 @@
</template>
<script lang="ts" setup name="Header">
import { ref, computed, defineAsyncComponent } from "vue";
import {
  ref,
  computed,
  defineAsyncComponent,
  onMounted,
  onUnmounted,
  watch,
} from "vue";
import dayjs from "dayjs";
import type { Task } from "@/store/type/task.type";
const SettingDeviceDialog = defineAsyncComponent(
  () => import("./SettingDeviceDialog.vue")
);
const ScheduledTaskDialog = defineAsyncComponent(
  () => import("./ScheduledTask.vue")
)
);
import atRegularTimeImg from "../../../../img/dingshi.png";
import setUpImg from "../../../../img/shezhi.png";
import userImg from "../../../../img/user.png";
@@ -60,8 +75,11 @@
const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
let timer: number;
const settingDeviceDialogRef = ref<any>(null);
const scheduledTaskDialogRef = ref<any>(null);
const taskCountdown = ref(""); // 定时任务倒计时文本
const pageType = computed(() => {
  return bedsideAuxiliaryScreenStore.deviceData.pageType;
@@ -82,6 +100,23 @@
  };
});
watch(
  () => bedsideAuxiliaryScreenStore.taskData,
  (newData: Task[]) => {
    console.log('定时任务更新了')
    if (
      bedsideAuxiliaryScreenStore.deviceData.deviceCode &&
      newData.length > 0
    ) {
      console.log('newData: ', newData)
      updateCountdown(newData[0].taskDate);
    } else {
      taskCountdown.value = "";
    }
  },
  { deep: true }
);
const openSettingDeviceDialog = () => {
  settingDeviceDialogRef.value?.openDialog();
};
@@ -96,6 +131,38 @@
    type: "warning",
  });
};
const getCountdown = (taskDate: string) => {
  const now = dayjs();
  const target = dayjs(taskDate).second(0).millisecond(0);
  const diff = target.diff(now, "second");
  if (diff <= 0) return "";
  const minutes = Math.floor(diff / 60);
  const seconds = diff % 60;
  return `${minutes}m${seconds}s`;
};
const updateCountdown = (taskDate: string) => {
  taskCountdown.value = getCountdown(taskDate);
  timer = window.setInterval(updateCountdown, 1000);
};
onMounted(() => {
  if (
    bedsideAuxiliaryScreenStore.deviceData.deviceCode &&
    bedsideAuxiliaryScreenStore.taskData.length > 0
  ) {
    getCountdown(bedsideAuxiliaryScreenStore.taskData[0].taskDate);
  }
});
onUnmounted(() => {
  timer && clearInterval(timer);
});
</script>
<style lang="less" scoped>
src/views/mobile/bedsideAuxiliaryScreen/components/ScheduledTask.vue
@@ -38,7 +38,7 @@
          <div class="content-left-stereotyped-writing">
            <div class="stereotyped-writing">
              <input
                v-model="taskName"
                v-model.trim="taskName"
                type="text"
                :disabled="isInpDisabled"
                class="stereotyped-writing-input"
@@ -70,6 +70,10 @@
<script lang="ts" setup>
import { computed, ref } from "vue";
import dayjs from "dayjs";
import { setTimeoutAlert } from "@/utils/httpApi";
import { useBedsideAuxiliaryScreenStore } from "@/store/bedsideAuxiliaryScreen";
// @ts-ignore
import TimePicker from "./TimePicker.vue";
@@ -81,6 +85,7 @@
import tdddbaojing from "@/assets/tzddd.mp3";
import tzxllbaojing from "@/assets/tzxll.mp3";
import cgbaojing from "@/assets/cg.mp3";
import { ElMessage } from "element-plus";
interface TaskItem {
  label: string;
@@ -94,14 +99,18 @@
  value: number;
}
const bedsideAuxiliaryScreenStore = useBedsideAuxiliaryScreenStore();
const isShow = ref(false);
const taskName = ref(""); // 任务名称
const isInpDisabled = ref(false); // 输入框是否禁用
const timeValue = ref("19:16");
const timeValue = ref("");
const detaCheck = ref<number | null>(); // 这个是判断时间按钮的
const loading = ref(false);
const taskOptions = ref<TaskItem[]>([
  {
@@ -155,6 +164,8 @@
const openDialog = () => {
  isShow.value = true;
  const time = dayjs();
  timeValue.value = time.format("HH:mm");
};
const onStereotypedWritingClick = (item: TaskItem) => {
@@ -197,7 +208,38 @@
  isShow.value = false;
};
const handleConfirm = () => {};
const handleConfirm = async () => {
  const today = dayjs().format("YYYY-MM-DD");
  const fullDateTime = dayjs(`${today} ${timeValue.value}`).second(0).millisecond(0); // 秒和毫秒都去掉,要不然间隔短了就是0分钟
  const now = dayjs().second(0).millisecond(0);; // 秒和毫秒都去掉
  if (!fullDateTime.isAfter(now))
    return ElMessage.warning("任务提醒时间不能早于或等于当前时间");
  if (!taskName.value) return ElMessage.warning("任务内容不能为空");
  loading.value = true;
  try {
    const diffMinutes = fullDateTime.diff(now, "minute");
    const params = {
      deviceCode: bedsideAuxiliaryScreenStore.deviceData.deviceCode,
      minutes: diffMinutes,
      alertText: taskName.value,
    }
    const recordCode = bedsideAuxiliaryScreenStore.deviceData.recordCode;
    const { data, message } = await setTimeoutAlert(params);
    if (data !== "OK") return ElMessage.warning(message);
    ElMessage.success('操作成功');
    bedsideAuxiliaryScreenStore.setSyncTask({
        deviceCode: params.deviceCode,
        recordCode: recordCode,
        taskDate: dayjs(fullDateTime).format("YYYY-MM-DD HH:mm"),
        taskName: params.alertText,
        overdue: false
    });
    handleCancel();
  } finally {
    loading.value = false;
  }
};
defineExpose({
  openDialog,