Ver Fonte

初步完成socket

xiaochan há 4 meses atrás
pai
commit
28eddc3838

+ 0 - 32
src/api/zhu-yuan-yi-sheng/emr-socket.js

@@ -1,32 +0,0 @@
-import request from "@/utils/request";
-
-export function getRoomPeople(sid) {
-  return request({
-    url: "/emrSocket/getRoomPeople",
-    method: "get",
-    params: { sid },
-  });
-}
-
-export function sendAMessage(sid, key, message) {
-  return request({
-    url: "/emrSocket/sendAMessage",
-    method: "get",
-    params: { sid, key, message },
-  });
-}
-
-export function getChatHistoryBySid(sid) {
-  return request({
-    url: "/emrSocket/getChatHistoryBySid",
-    method: "get",
-    params: { sid },
-  });
-}
-
-export function getSocketToken() {
-  return request({
-    url: "/emrSocket/getSocketToken",
-    method: "get",
-  });
-}

+ 156 - 126
src/components/progress/Index.vue

@@ -1,16 +1,18 @@
 <template>
   <div style="position: fixed; z-index: 10000">
     <el-dialog
-        :title="progressbarAttr.title"
-        :close-on-click-modal="false"
-        :close-on-press-escape="false"
-        v-model="progressbarAttr.isOpen"
-        :before-close="closeModal">
+      :title="progressbarAttr.title"
+      :close-on-click-modal="false"
+      :close-on-press-escape="false"
+      v-model="progressbarAttr.isOpen"
+      :before-close="closeModal"
+    >
       <el-progress
-          :text-inside="true"
-          :stroke-width="22"
-          :percentage="percentage"
-          :status="percentage === 100 ? 'success' : ''">
+        :text-inside="true"
+        :stroke-width="22"
+        :percentage="percentage"
+        :status="percentage === 100 ? 'success' : ''"
+      >
         <span>
           {{ cptUpldRsTxt }}
           <i v-show="percentage < 100" class="el-icon-loading"></i>
@@ -22,16 +24,45 @@
         <el-tabs type="border-card" v-model="tabName">
           <el-tab-pane label="上传" name="上传">
             <ul style="overflow: auto" class="infinite-list" id="jdtRef">
-              <li v-for="i in jdtData" :key="i" class="infinite-list-item" :class="infiniteList(i)">
-                <div class="jdt-message" :title="i.message">{{ i.title }} 结果: {{ i.message }}</div>
+              <li
+                v-for="i in jdtData"
+                :key="i"
+                class="infinite-list-item"
+                :class="infiniteList(i)"
+              >
+                <div class="jdt-message" :title="i.message">
+                  {{ i.title }} 结果: {{ i.message }}
+                </div>
               </li>
             </ul>
           </el-tab-pane>
-          <el-tab-pane label="结果" name="结果" v-if="progressbarAttr.closeButton">
-            <el-result icon="success" :title="progressbarAttr.title" :sub-title="tips">
+          <el-tab-pane
+            label="结果"
+            name="结果"
+            v-if="progressbarAttr.closeButton"
+          >
+            <el-result
+              icon="success"
+              :title="progressbarAttr.title"
+              :sub-title="tips"
+            >
               <template #extra>
-                <el-button @click="closeModal" type="warning" icon="Close" index="small"> 关闭</el-button>
-                <el-button @click="exportExcel" type="primary" icon="Download" index="small"> 导出Excel</el-button>
+                <el-button
+                  @click="closeModal"
+                  type="warning"
+                  icon="Close"
+                  index="small"
+                >
+                  关闭</el-button
+                >
+                <el-button
+                  @click="exportExcel"
+                  type="primary"
+                  icon="Download"
+                  index="small"
+                >
+                  导出Excel</el-button
+                >
               </template>
             </el-result>
           </el-tab-pane>
@@ -42,183 +73,182 @@
 </template>
 
 <script setup>
-import {computed, onMounted, ref} from 'vue'
-import {setCallback} from '@/utils/websocket'
-import {Export} from '@/utils/ExportExcel'
-import {makePercentage} from './progUtils'
-import {stringNotBlank} from '@/utils/blank-utils'
-import {reactive, watch} from 'vue'
-import {endLoading} from "@/utils/loading";
-import {useProgressBarStore} from "@/pinia/progress-bar-store";
+import { computed, onMounted, ref } from "vue";
+import { setCallback } from "@/utils/websocket";
+import { Export } from "@/utils/ExportExcel";
+import { makePercentage } from "./progUtils";
+import { stringNotBlank } from "@/utils/blank-utils";
+import { reactive, watch } from "vue";
+import { endLoading } from "@/utils/loading";
+import { useProgressBarStore } from "@/pinia/progress-bar-store";
 
-const progressBarStore = useProgressBarStore()
+const progressBarStore = useProgressBarStore();
 const progressbarAttr = computed(() => {
-  return progressBarStore.getProgressAttr
-})
+  return progressBarStore.getProgressAttr;
+});
 
 const isProgressbarOpen = computed(() => {
-  return progressBarStore.getOpenState
-})
+  return progressBarStore.getOpenState;
+});
 
 const cptUpldRsTxt = computed(() => {
   if (percentage.value < 100) {
-    return `上传中 ( ${percentage.value}% ) ...`
+    return `上传中 ( ${percentage.value}% ) ...`;
   }
-  return '上传完成(100%)'
-})
-const jdtData = ref([])
+  return "上传完成(100%)";
+});
+const jdtData = ref([]);
 
 const closeModal = () => {
-  jdtData.value = []
-  excelTitle.value = {}
-  percentage.value = 0
-  tips.value = ''
-  tabName.value = '上传'
-  progressBarStore.closeProgressBar()
-}
+  jdtData.value = [];
+  excelTitle.value = {};
+  percentage.value = 0;
+  tips.value = "";
+  tabName.value = "上传";
+  progressBarStore.closeProgressBar();
+};
 
 const cuoWuLeiXin = reactive({
   success: 0,
   danger: 0,
   warning: 0,
   info: 0,
-})
+});
 
 // 提示语
-const tips = ref('')
-const notExcel = ref(false)
+const tips = ref("");
+const notExcel = ref(false);
 
-const total = ref(0)
-const index = ref(0)
-const excelTitle = ref({})
-const tabName = ref('上传')
+const total = ref(0);
+const index = ref(0);
+const excelTitle = ref({});
+const tabName = ref("上传");
 
-const socketCallback = (data) => {
+const socketCallback = data => {
   progressBarStore.initialize({
-    title: '数据上传',
+    title: "数据上传",
     isOpen: true,
     closeButton: false,
-  })
-  endLoading()
-  total.value = data.total
-  index.value = data.index
-  percentage.value = makePercentage(data.index, data.total)
-  if (stringNotBlank(data.jdtType) && data.jdtType === 'download') {
-    notExcel.value = false
-  } else if (stringNotBlank(data.jdtType) && data.jdtType === 'upload') {
+  });
+  endLoading();
+  total.value = data.total;
+  index.value = data.index;
+  percentage.value = makePercentage(data.index, data.total);
+  if (stringNotBlank(data.jdtType) && data.jdtType === "download") {
+    notExcel.value = false;
+  } else if (stringNotBlank(data.jdtType) && data.jdtType === "upload") {
     for (let key in data) {
-      if (key.indexOf('#') > -1) {
-        let newKey = key.split('#')
-        data[newKey[0]] = data[key]
-        if (newKey[0] === 'type') {
-          excelTitle.value.typeName = newKey[1]
+      if (key.indexOf("#") > -1) {
+        let newKey = key.split("#");
+        data[newKey[0]] = data[key];
+        if (newKey[0] === "type") {
+          excelTitle.value.typeName = newKey[1];
         } else {
-          excelTitle.value[newKey[0]] = newKey[1]
+          excelTitle.value[newKey[0]] = newKey[1];
         }
-        delete data[key]
+        delete data[key];
       }
     }
-    notExcel.value = true
-    jdtData.value.push(data)
-    const jdtRef = document.getElementById('jdtRef')
+    notExcel.value = true;
+    jdtData.value.push(data);
+    const jdtRef = document.getElementById("jdtRef");
     try {
-      jdtRef.scrollTop = jdtRef.scrollHeight
+      jdtRef.scrollTop = jdtRef.scrollHeight;
     } catch (e) {
-      console.error('小bug不用管')
+      console.error("小bug不用管");
     }
   }
-}
+};
 
-const percentage = ref(0)
+const percentage = ref(0);
 
-const infiniteList = (val) => {
+const infiniteList = val => {
   switch (val.type) {
     case 0:
-      val.typeName = '成功'
-      return 'jdt-success'
+      val.typeName = "成功";
+      return "jdt-success";
     case 1:
-      val.typeName = '上传错误'
-      return 'jdt-danger'
+      val.typeName = "上传错误";
+      return "jdt-danger";
     case 2:
-      val.typeName = '内部错误'
-      return 'jdt-warning'
+      val.typeName = "内部错误";
+      return "jdt-warning";
     default:
-      val.typeName = '未知错误'
-      return 'jdt-info'
+      val.typeName = "未知错误";
+      return "jdt-info";
   }
-}
+};
 
 const exportExcel = () => {
   if (Object.keys(excelTitle.value).length === 0) {
-    return (tips.value = '没有可以导出 Excel 的字段。')
+    return (tips.value = "没有可以导出 Excel 的字段。");
   }
-  const arr = {}
+  const arr = {};
   for (let key in excelTitle.value) {
     // 这里获取 | 符号后面的 数字
-    arr[key] = excelTitle.value[key].split('|')[1]
+    arr[key] = excelTitle.value[key].split("|")[1];
   }
   // 排序按照从小到大
   let result = Object.values(arr).sort((a, b) => {
-    return a - b
-  })
-  const new_obj = {}
+    return a - b;
+  });
+  const new_obj = {};
   // 按照排序生成新的对象
   for (let i = 0; i < result.length; i++) {
     Object.keys(arr).map((item, index) => {
       if (arr[item] === result[i]) {
-        new_obj[item] = result[i]
+        new_obj[item] = result[i];
       }
-    })
+    });
   }
-  const title = {}
+  const title = {};
   for (const a in new_obj) {
-    title[a] = excelTitle.value[a].split('|')[0]
+    title[a] = excelTitle.value[a].split("|")[0];
   }
-  Export(jdtData.value, title, progressBarStore.title)
-}
+  Export(jdtData.value, title, progressBarStore.title);
+};
 
 watch(
-    () => percentage.value,
-    () => {
-      if (percentage.value === 100) {
-        if (jdtData.value.length > 0) {
-          let success = 0
-          let danger = 0
-          let warning = 0
-          let info = 0
-          jdtData.value.forEach((item) => {
-            if (item.type === 0) {
-              success++
-            } else if (item.type === 1) {
-              danger++
-            } else if (item.type === 2) {
-              warning++
-            } else {
-              info++
-            }
-          })
-          tips.value = `数据上传完成:成功:【${success}】、错误:【${danger}】、内部错误:【${warning}】、未知:【${info}】、总条数:【${jdtData.value.length}】`
-        } else {
-          tips.value = '上传成功'
-        }
-        tabName.value = '结果'
+  () => percentage.value,
+  () => {
+    if (percentage.value === 100) {
+      if (jdtData.value.length > 0) {
+        let success = 0;
+        let danger = 0;
+        let warning = 0;
+        let info = 0;
+        jdtData.value.forEach(item => {
+          if (item.type === 0) {
+            success++;
+          } else if (item.type === 1) {
+            danger++;
+          } else if (item.type === 2) {
+            warning++;
+          } else {
+            info++;
+          }
+        });
+        tips.value = `数据上传完成:成功:【${success}】、错误:【${danger}】、内部错误:【${warning}】、未知:【${info}】、总条数:【${jdtData.value.length}】`;
+      } else {
+        tips.value = "上传成功";
       }
+      tabName.value = "结果";
     }
-)
+  }
+);
 
 watch(
-    () => isProgressbarOpen.value,
-    () => {
-      if (!isProgressbarOpen.value) {
-        closeModal()
-      }
+  () => isProgressbarOpen.value,
+  () => {
+    if (!isProgressbarOpen.value) {
+      closeModal();
     }
-)
+  }
+);
 
 onMounted(() => {
-  setCallback('jdtMessage', socketCallback)
-})
-
+  setCallback("jdtMessage", socketCallback);
+});
 </script>
 
 <style scoped>

+ 12 - 12
src/components/xiao-chan/progress-bar/ProgressBar.vue

@@ -1,26 +1,26 @@
 <template>
   <el-dialog v-model="dialog" title="进度">
-    <el-progress :percentage="percentage"/>
+    <el-progress :percentage="percentage" />
   </el-dialog>
 </template>
 
-<script setup name='ProgressBar'>
-import {setCallback} from "@/utils/websocket";
-import {makePercentage} from "@/components/progress/progUtils";
+<script setup name="ProgressBar">
+import { setCallback } from "@/utils/websocket";
+import { makePercentage } from "@/components/progress/progUtils";
 import sleep from "@/utils/sleep";
 
-const percentage = ref(0)
-const dialog = ref(false)
+const percentage = ref(0);
+const dialog = ref(false);
 
 onMounted(() => {
-  setCallback('emrRule', (data) => {
-    dialog.value = true
-    percentage.value = makePercentage(data.currentIndex, data.count)
+  setCallback("emrRule", data => {
+    dialog.value = true;
+    percentage.value = makePercentage(data.currentIndex, data.count);
     if (percentage.value === 100) {
       sleep(1000).then(() => {
-        dialog.value = false
-      })
+        dialog.value = false;
+      });
     }
   });
-})
+});
 </script>

+ 0 - 210
src/components/zhu-yuan-yi-sheng/emr/web-socket/EmrChatBox.vue

@@ -1,210 +0,0 @@
-<template>
-  <el-dialog v-model="dialog"
-             :title="`聊天室 ${userSize} 人`"
-             width="70%"
-             @closed="emits('closed')">
-    <div class="chat_room_dialog">
-      <div class="left">
-        <el-table :data="props.userList" :height="getWindowSize.h / 1.5">
-          <el-table-column prop="name" label="姓名">
-            <template #default="{row}">
-              <span v-if="row.code === userData.code">本人</span>
-            </template>
-          </el-table-column>
-          <el-table-column prop="deptName" label="科室"/>
-        </el-table>
-      </div>
-      <div class="right">
-        <el-scrollbar :height="getWindowSize.h / 1.8 " ref="scrollbarRef">
-          <div ref="innerRef">
-            <div v-for="item in chatHistory" class="list">
-              <div class="chat_right"
-                   v-if="whetherInPerson(item.code)">
-                <div style="display: flex; ">
-                  <div style="font-size: 12px;margin-right: 10px">
-                    <div style="text-align: right">
-                      {{ item.date }} {{ item.deptName }} {{ item.name }}
-                    </div>
-                    <div class="message_str" style="float: right;">
-                      {{ item.message }}
-                    </div>
-                  </div>
-
-                  <div>
-                    <el-avatar shape="square" :src="item.avatar" :size="30"/>
-                  </div>
-                </div>
-              </div>
-
-              <div class="chat_left" v-else>
-                <div style="display: flex;">
-                  <div>
-                    <el-avatar shape="square" :src="item.avatar" :size="30"/>
-                  </div>
-
-                  <div style="font-size: 12px;margin-left: 10px">
-                    <div>{{ item.name }} {{ item.deptName }} {{ item.date }}</div>
-                    <div class="message_str" :class="judgmentQMy(item.message) ? 'specify_me' : '' ">
-                      {{ item.message }}
-                    </div>
-                  </div>
-                </div>
-              </div>
-            </div>
-          </div>
-        </el-scrollbar>
-
-        <div>
-          <el-input type="textarea"
-                    :rows="3"
-                    v-model="messageStr"
-                    @keydown.enter.stop="clickSend"/>
-
-          <div style="float: right">
-            <el-button @click="modifyTheCurrentMedicalRecord">修改当前病历</el-button>
-            <el-button @click="clickSend">发送</el-button>
-          </div>
-
-        </div>
-      </div>
-    </div>
-  </el-dialog>
-</template>
-
-<script setup lang="ts">
-import {getWindowSize} from "@/utils/window-size";
-import {getChatHistoryBySid, sendAMessage} from "@/api/zhu-yuan-yi-sheng/emr-socket";
-import {stringIsBlank} from "@/utils/blank-utils";
-import {xcMessage} from "@/utils/xiaochan-element-plus";
-import {useUserStore} from "@/pinia/user-store";
-import {ElScrollbar} from "element-plus";
-import {useCompRef} from "@/utils/useCompRef";
-import {defineComponent} from "vue";
-
-const userData = useUserStore().userInfo
-
-defineComponent({
-  name: "EmrChatBox",
-})
-
-const chatHistory = ref([])
-const props = defineProps<{
-  userList: any[],
-  sid: string | null,
-  currentEditorUser: {
-    name: string
-  } | null,
-  userSize: number
-}>()
-
-const emits = defineEmits(['closed'])
-
-const dialog = ref(true)
-const messageStr = ref('')
-const scrollbarRef = useCompRef(ElScrollbar)
-const innerRef = ref<HTMLDivElement | null>(null)
-
-const clickSend = async () => {
-  if (stringIsBlank(messageStr.value)) {
-    return xcMessage.error('不能发送空白消息。')
-  }
-  await sendAMessage(props.sid, 'message', messageStr.value)
-  messageStr.value = ''
-  await queryJump()
-}
-
-const modifyTheCurrentMedicalRecord = async () => {
-  if (props.currentEditorUser === null) {
-    return xcMessage.error('当前没有人正在编辑');
-  }
-  if (stringIsBlank(props.currentEditorUser.name)) {
-    return xcMessage.error('当前没有人正在编辑');
-  }
-  await sendAMessage(props.sid, 'message', '@' + props.currentEditorUser.name + '    申请修改病历,请尽快保存,后离开当前病历【系统自动发送】');
-  messageStr.value = ''
-  await queryJump()
-}
-
-const queryJump = async () => {
-  chatHistory.value = await getChatHistoryBySid(props.sid) as any[]
-  await nextTick()
-  scrollbarRef.value!.setScrollTop(innerRef.value!.scrollHeight)
-}
-
-const whetherInPerson = (code) => {
-  return userData.code === code
-}
-
-const judgmentQMy = (val) => {
-  return val.startsWith('@' + userData.name)
-}
-
-onMounted(async () => {
-  await nextTick()
-  await queryJump()
-})
-
-defineExpose({
-  queryJump
-})
-
-</script>
-
-<style scoped lang="scss">
-.chat_room_dialog {
-  display: flex;
-  width: 100%;
-
-  .left {
-    width: 200px;
-  }
-
-  .right {
-    flex: 1;
-    height: max-content;
-  }
-}
-
-.list {
-  width: 100%;
-
-  .specify_me {
-    background-color: red;
-    color: white;
-  }
-
-  .chat_right {
-    display: flex;
-    justify-content: flex-end;
-    padding: 5px 10px;
-  }
-
-  .chat_left {
-    display: flex;
-    padding: 5px 10px;
-  }
-
-  .message_str {
-    border: 1px solid #000;
-    padding: 5px;
-    width: max-content;
-    border-radius: 5px;
-  }
-}
-
-.chat_history_div {
-  height: max-content;
-  display: flex;
-
-  .user_info {
-    padding: 0 10px;
-  }
-
-  .message {
-    border: 1px solid #000;
-    padding: 5px;
-    width: max-content;
-    border-radius: 10px;
-  }
-}
-</style>

+ 1 - 22
src/layout/index.vue

@@ -4,12 +4,7 @@
 
 <script setup lang="ts">
 import { onMounted } from "vue";
-import { useRouter } from "vue-router";
 import { initWebSocket } from "@/utils/websocket";
-import { uuid } from "@/utils/getUuid";
-import { useUserStore } from "@/pinia/user-store";
-import { stringNotBlank } from "@/utils/blank-utils";
-import { CyMessageBox } from "@/utils/cy-message-box";
 import { useSystemStore } from "@/pinia/system-store";
 
 const layouts = {
@@ -22,26 +17,10 @@ const layouts = {
 const pageName = computed(() => {
   return useSystemStore().userConfig.layOutPageName;
 });
-const userStore = useUserStore();
-const router = useRouter();
-
-const initSocket = () => {
-  if (stringNotBlank(userStore.userInfo.sid)) {
-    userStore.setRandomSid(uuid(8, 62));
-    initWebSocket(userStore.getSid, null);
-  } else {
-    CyMessageBox.alert({
-      message: "没有有效的 Sid 请重新登录",
-      type: "warning",
-    }).finally(() => {
-      router.push("/login");
-    });
-  }
-};
 
 onMounted(() => {
   if (localStorage.token) {
-    initSocket();
+    initWebSocket();
   }
 });
 </script>

+ 72 - 4
src/utils/useCySocket.ts

@@ -1,13 +1,81 @@
-import { useWebSocket, UseWebSocketOptions } from "@vueuse/core";
+import {
+  useWebSocket,
+  UseWebSocketOptions,
+  UseWebSocketReturn,
+} from "@vueuse/core";
+import { useUserStore } from "@/pinia/user-store";
 
 export type UsCyeWebSocketOptions = UseWebSocketOptions & {
-  msgFun: {
+  socketMsg?: {
     [key: string]: (value: any) => void;
   };
+  setInfo?: boolean;
 };
 
 export function useCySocket(url: any, options?: UsCyeWebSocketOptions) {
-  const { msgFun, ...val } = options;
+  const {
+    socketMsg,
+    setInfo = true,
+    autoReconnect = true,
+    immediate = true,
+    ...val
+  } = options;
+  const userStore = useUserStore().userInfo;
 
-  return useWebSocket(url, ...val);
+  // @ts-ignore
+  let socket: UseWebSocketReturn<any> = {
+    close: () => 0,
+  };
+
+  const getUserInfo = () => {
+    return JSON.stringify({
+      code: "USER_INFO",
+      mode: "USER_INFO",
+      data: {
+        deptName: userStore.deptName,
+        name: userStore.name,
+        code: userStore.code,
+      },
+    });
+  };
+
+  if (setInfo) {
+    val.heartbeat = {
+      pongTimeout: 1000,
+      message: getUserInfo(),
+      interval: 1000 * 60 * 3,
+    };
+  }
+
+  const oldOnMessage = val.onMessage;
+
+  val.onMessage = (ws, event) => {
+    const data = event.data;
+    try {
+      const val = JSON.parse(data);
+      const key = val.code || val.name;
+      socketMsg?.[key]?.(val.data);
+    } catch (e) {
+      console.error("json转化出错", e);
+    }
+
+    oldOnMessage?.(ws, event);
+  };
+
+  const oldDisconnected = val.onDisconnected;
+
+  val.onDisconnected = (ws, event) => {
+    if (event.code > 3000) {
+      socket.close();
+    }
+    oldDisconnected?.(ws, event);
+  };
+
+  socket = useWebSocket(url, {
+    autoReconnect,
+    immediate,
+    ...val,
+  });
+
+  return socket;
 }

+ 0 - 94
src/utils/websocket.js

@@ -1,94 +0,0 @@
-import { ElMessageBox } from "element-plus";
-import Cookies from "js-cookie";
-import router from "@/router";
-import { useUserStore } from "@/pinia/user-store";
-import env from "@/utils/setting";
-
-const socketUrl = env.VITE_SOCKET_URL;
-
-let webSocket = null;
-let globalCallback = new Map();
-
-export function closeWebSocket() {
-  if (webSocket !== null) {
-    webSocket.close();
-    webSocket = null;
-  }
-}
-
-export function setCallback(messageName, callback) {
-  globalCallback.set(messageName, callback);
-}
-
-export function sendAMessage(name, data) {
-  if (globalCallback.has(name)) {
-    try {
-      globalCallback.get(name)(data);
-    } catch {}
-  }
-}
-
-export const socketErrDialog = ref(false);
-
-export function initWebSocket(sid, force) {
-  if ("WebSocket" in window) {
-    if (webSocket === null || force) {
-      const url = socketUrl + sid;
-      webSocket = new WebSocket(url);
-    }
-  } else {
-    alert("该浏览器不支持websocket!");
-    webSocket = "unsupport";
-  }
-
-  webSocket.onopen = function () {
-    socketErrDialog.value = false;
-    console.log("WebSocket连接");
-  };
-
-  webSocket.onmessage = function (e) {
-    let data = JSON.parse(e.data);
-    sendAMessage(data.name, data.message);
-  };
-
-  webSocket.onclose = function () {
-    if (router.currentRoute.value.path === "/login") {
-      location.reload();
-    } else {
-      socketErrDialog.value = true;
-    }
-    webSocket = null;
-    let sid;
-    if (router.currentRoute.value.path === "/triageRoomScreen") {
-      sid = Cookies.get("room-screen-sid");
-    } else {
-      sid = useUserStore().getSid;
-    }
-    if (!sid) {
-      if (router.currentRoute.value.path === "/login") {
-        return;
-      }
-      ElMessageBox.confirm("未检测到WebSocket连接的sid,请重新登录。", "提示", {
-        showCancelButton: false,
-        type: "warning",
-      })
-        .then(async () => {
-          await router.push("/login");
-        })
-        .catch(async () => {
-          await router.push("/login");
-        });
-    } else {
-      if (router.currentRoute.value.path === "/triageFloorScreen") {
-        sid += "-triageFloorScreen";
-      }
-      setTimeout(() => {
-        initWebSocket(sid);
-      }, 3000);
-    }
-  };
-
-  webSocket.onerror = function () {
-    console.error("WebSocket连接发生错误");
-  };
-}

+ 58 - 0
src/utils/websocket.ts

@@ -0,0 +1,58 @@
+import { useUserStore } from "@/pinia/user-store";
+import env from "@/utils/setting";
+import { useCySocket } from "@/utils/useCySocket";
+import { UseWebSocketReturn } from "@vueuse/core";
+
+let webSocket: UseWebSocketReturn<any> | null = null;
+let globalCallback = new Map();
+
+export function closeWebSocket() {
+  if (webSocket !== null) {
+    webSocket.close();
+    webSocket = null;
+  }
+}
+
+export function setCallback(messageName, callback) {
+  globalCallback.set(messageName, callback);
+}
+
+export function sendAMessage(name, data) {
+  if (globalCallback.has(name)) {
+    try {
+      globalCallback.get(name)(data);
+    } catch {}
+  }
+}
+
+export const socketErrDialog = ref(false);
+
+export function initWebSocket() {
+  const userStore = useUserStore().userInfo;
+
+  const url = `${env.VITE_SOCKET_V2}/intergrationPlatform/${userStore.code}`;
+  // const url = `${env.VITE_SOCKET_URL}/02896-172.16.30.66-a4f2eddf9e114c7dad5a9473a3d75020fAhr5UY4`;
+
+  webSocket = useCySocket(url, {
+    setInfo: false,
+    heartbeat: {
+      pongTimeout: 1000,
+      message: "heart-beat",
+      interval: 1000 * 60,
+    },
+    onMessage: (ws, e) => {
+      const data = JSON.parse(e.data);
+      sendAMessage(data.name || data.code, data.message);
+    },
+  });
+}
+
+export function sendMeg(code: string, data = null) {
+  webSocket &&
+    webSocket.send(
+      JSON.stringify({
+        code,
+        data,
+      })
+    );
+}

+ 93 - 80
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr_control_rule/EmrControlRule.vue

@@ -2,51 +2,58 @@
   <div class="layout_container">
     <header>
       <el-switch
-          active-value="intervalPatients"
-          inactive-value="admissTimes"
-          v-model="queryParams.timeType"
-          active-text="质控时间"
-          inactive-text="入院时间"
+        active-value="intervalPatients"
+        inactive-value="admissTimes"
+        v-model="queryParams.timeType"
+        active-text="质控时间"
+        inactive-text="入院时间"
       />
       <el-divider direction="vertical"></el-divider>
-      <CyDateRange v-model="queryParams"
-                   start="startDate"
-                   end="endDate"
-                   :shortcuts-index="2"/>
-      <SystemDeptSelect v-model="queryParams.deptCode" clearable placeholder="入院科室"/>
-      <SystemStaffSelect v-model="queryParams.approver" clearable placeholder="审核人"/>
-      <el-button type="primary"
-                 @click="querySearch">
-        查询质控数据
-      </el-button>
-      <el-button @click="queryScale">
-        {{ proportion }} 比例
-      </el-button>
+      <CyDateRange
+        v-model="queryParams"
+        start="startDate"
+        end="endDate"
+        :shortcuts-index="2"
+      />
+      <SystemDeptSelect
+        v-model="queryParams.deptCode"
+        clearable
+        placeholder="入院科室"
+      />
+      <SystemStaffSelect
+        v-model="queryParams.approver"
+        clearable
+        placeholder="审核人"
+      />
+      <el-button type="primary" @click="querySearch"> 查询质控数据 </el-button>
+      <el-button @click="queryScale"> {{ proportion }} 比例 </el-button>
     </header>
     <div>
-      <el-progress :percentage="percentage"/>
+      <el-progress :percentage="percentage" />
     </div>
     <div class="layout_main">
       <el-tabs v-model="tabsModel" class="el-tabs__fill">
         <el-tab-pane label="质控数量" name="质控数量">
           <CyVxeTable :data="numberToArray">
-            <vxe-column field="name" title="质控名称"/>
-            <vxe-column field="total" title="总数"/>
+            <vxe-column field="name" title="质控名称" />
+            <vxe-column field="total" title="总数" />
             <vxe-column title="操作">
-              <template #default="{row}">
-                <el-button text @click="detailClick(row)" plain type="primary">详情</el-button>
+              <template #default="{ row }">
+                <el-button text @click="detailClick(row)" plain type="primary"
+                  >详情</el-button
+                >
               </template>
             </vxe-column>
           </CyVxeTable>
         </el-tab-pane>
 
         <el-tab-pane label="详情" name="详情">
-          <RuleSift :data="ruleReturnData.patient"/>
+          <RuleSift :data="ruleReturnData.patient" />
         </el-tab-pane>
 
         <template v-for="(val, key) in siftData">
           <el-tab-pane :label="key" :name="key">
-            <RuleSift :data="val"/>
+            <RuleSift :data="val" />
           </el-tab-pane>
         </template>
       </el-tabs>
@@ -55,100 +62,106 @@
 </template>
 
 <script setup>
-import {onMounted, ref} from "vue";
-import RuleSift
-  from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr_control_rule/RuleSift.vue";
-import {setCallback} from "@/utils/websocket";
-import {linkQualityControl, obtainTheProportionOfMedicalRecords} from "@/api/emr-control/emr-control";
-import {makePercentage} from "@/components/progress/progUtils";
-import {BizException, ExceptionEnum} from "@/utils/BizException";
-import {openSocket, sid} from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/emr-socket";
+import { onMounted, ref } from "vue";
+import RuleSift from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr_control_rule/RuleSift.vue";
+import { setCallback } from "@/utils/websocket";
+import {
+  linkQualityControl,
+  obtainTheProportionOfMedicalRecords,
+} from "@/api/emr-control/emr-control";
+import { makePercentage } from "@/components/progress/progUtils";
+import { BizException, ExceptionEnum } from "@/utils/BizException";
+import {
+  openSocket,
+  sid,
+} from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/emr-socket";
 import useVxeTable from "@/utils/cy-use/useVxeTable";
 import SystemDeptSelect from "@/components/system/dept-select/SystemDeptSelect.vue";
 import SystemStaffSelect from "@/components/system/staff-select/SystemStaffSelect.vue";
 import CyDateRange from "@/components/cy/date-range/CyDateRange.vue";
 
-
 const queryParams = reactive({
-  timeType: '',
-  deptCode: '',
+  timeType: "",
+  deptCode: "",
   approver: "",
-  startDate: '',
-  endDate: '',
-  sid: sid
-})
+  startDate: "",
+  endDate: "",
+  sid: sid,
+});
 
-const percentage = ref(0)
-const numberToArray = ref([])
-const tabsModel = ref('质控数量')
+const percentage = ref(0);
+const numberToArray = ref([]);
+const tabsModel = ref("质控数量");
 const ruleReturnData = ref({
   count: {},
-  patient: []
-})
+  patient: [],
+});
 
-const siftData = ref({})
+const siftData = ref({});
 
 const queryQualityControlData = async () => {
-  percentage.value = 0
-  siftData.value = {}
-  let res = await linkQualityControl(queryParams)
-  ruleReturnData.value = res
-  proportion.value = res.proportion
-  numberToArrayFunc()
-  mutation.setPageTotal(numberToArray.value)
-}
-
-const {CyVxeTable, querySearch, mutation} = useVxeTable({
+  percentage.value = 0;
+  siftData.value = {};
+  let res = await linkQualityControl(queryParams);
+  ruleReturnData.value = res;
+  proportion.value = res.proportion;
+  numberToArrayFunc();
+  mutation.setPageTotal(numberToArray.value);
+};
+
+const { CyVxeTable, querySearch, mutation } = useVxeTable({
   localPaging: true,
-  keyField: '',
-  remoteSearch: queryQualityControlData
+  keyField: "",
+  remoteSearch: queryQualityControlData,
 });
 
 const numberToArrayFunc = () => {
-  numberToArray.value = []
+  numberToArray.value = [];
   for (let key in ruleReturnData.value.count) {
     numberToArray.value.push({
       name: key,
-      total: ruleReturnData.value.count[key]
-    })
+      total: ruleReturnData.value.count[key],
+    });
   }
-}
+};
 
-const detailClick = ({name, total}) => {
+const detailClick = ({ name, total }) => {
   if (total === 0) {
-    BizException(ExceptionEnum.MESSAGE_ERROR, "没有指定的病历。")
+    BizException(ExceptionEnum.MESSAGE_ERROR, "没有指定的病历。");
   }
 
   let data = [];
   for (let i = 0, len = ruleReturnData.value.patient.length; i < len; i++) {
-    let item = JSON.parse(JSON.stringify(ruleReturnData.value.patient[i]))
+    let item = JSON.parse(JSON.stringify(ruleReturnData.value.patient[i]));
     if (item.mapMessage && item.mapMessage[name]) {
       for (let key in item.mapMessage) {
         if (key !== name) {
-          delete item.mapMessage[key]
+          delete item.mapMessage[key];
         }
       }
-      item.message = JSON.stringify(item.mapMessage[name])
-      data.push(item)
+      item.message = JSON.stringify(item.mapMessage[name]);
+      data.push(item);
     }
   }
   siftData.value[name] = data;
   tabsModel.value = name;
-}
+};
 
-const proportion = ref('0%')
+const proportion = ref("0%");
 const queryScale = async () => {
-  proportion.value = await obtainTheProportionOfMedicalRecords(queryParams.startTime, queryParams.endTime, 0)
-}
+  proportion.value = await obtainTheProportionOfMedicalRecords(
+    queryParams.startTime,
+    queryParams.endTime,
+    0
+  );
+};
 
 onMounted(() => {
-  openSocket()
-  setCallback('LINK_CONTROL', (data) => {
-    percentage.value = makePercentage(data.current, data.total)
-  })
-})
+  openSocket();
+  setCallback("LINK_CONTROL", data => {
+    percentage.value = makePercentage(data.current, data.total);
+  });
+});
 </script>
 
-<style lang="scss">
-
-</style>
+<style lang="scss"></style>

+ 443 - 314
src/views/medical-insurance/inpatient/InHospFeeUpload.vue

@@ -4,19 +4,44 @@
       <el-button type="success" @click="feeDtle">费用明细</el-button>
       <el-button type="success" @click="preCalculateCost">医保试算</el-button>
       <el-button type="success" @click="uploadFees">费用上传</el-button>
-      <el-button type="warning" :disabled="!isAdmin" @click="cancelFees">取消上传</el-button>
-      <el-button type="primary" @click="weiGuiTuiFeiFenXiDialogOpen(true)">违规费用分析</el-button>
+      <el-button type="warning" :disabled="!isAdmin" @click="cancelFees"
+        >取消上传</el-button
+      >
+      <el-button type="primary" @click="weiGuiTuiFeiFenXiDialogOpen(true)"
+        >违规费用分析</el-button
+      >
       <el-popover placement="left" width="730" trigger="click">
         <template #reference>
-          <el-button type="warning" icon="CircleClose" plain>错误信息 ({{ errorMessages.length }})</el-button>
+          <el-button type="warning" icon="CircleClose" plain
+            >错误信息 ({{ errorMessages.length }})</el-button
+          >
         </template>
         <el-tag type="info">错误信息</el-tag>
         <el-divider direction="vertical"></el-divider>
-        <el-button type="warning" @click="clearErrorMessages">清除内容</el-button>
-        <el-table width="700" max-height="300" class="errTable" :data="errorMessages">
-          <el-table-column width="80" property="patNo" label="住院号"></el-table-column>
-          <el-table-column width="80" property="patName" label="姓名"></el-table-column>
-          <el-table-column width="450" property="message" label="错误详情"></el-table-column>
+        <el-button type="warning" @click="clearErrorMessages"
+          >清除内容</el-button
+        >
+        <el-table
+          width="700"
+          max-height="300"
+          class="errTable"
+          :data="errorMessages"
+        >
+          <el-table-column
+            width="80"
+            property="patNo"
+            label="住院号"
+          ></el-table-column>
+          <el-table-column
+            width="80"
+            property="patName"
+            label="姓名"
+          ></el-table-column>
+          <el-table-column
+            width="450"
+            property="message"
+            label="错误详情"
+          ></el-table-column>
         </el-table>
       </el-popover>
       <el-tag type="info" size="small" style="margin-left: 8px">
@@ -27,26 +52,53 @@
     </div>
 
     <el-divider content-position="left" class="el-divider_shorter">
-      <span style="color: #0081ff">
-        治疗明细(合计:¥ {{ xmFeeSum }})
-      </span>
+      <span style="color: #0081ff"> 治疗明细(合计:¥ {{ xmFeeSum }}) </span>
     </el-divider>
 
     <div class="layout_flex_1-y">
       <el-table :data="xmFeeList" stripe height="100%">
-        <el-table-column prop="detailSn" label="流水号" width="80"></el-table-column>
-        <el-table-column prop="chargeCodeMx" label="院内码" width="100"></el-table-column>
-        <el-table-column prop="chargeAmount" label="数量" sortable width="80"></el-table-column>
-        <el-table-column prop="chargeFee" label="金额" width="80"></el-table-column>
+        <el-table-column
+          prop="detailSn"
+          label="流水号"
+          width="80"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeCodeMx"
+          label="院内码"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeAmount"
+          label="数量"
+          sortable
+          width="80"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeFee"
+          label="金额"
+          width="80"
+        ></el-table-column>
         <el-table-column prop="chargeDate" label="收费日期"></el-table-column>
         <el-table-column prop="ybCode" :label="ybCodeLabel"></el-table-column>
         <el-table-column prop="chargeName" label="项目名称"></el-table-column>
-        <el-table-column prop="ybSelfFlag" label="报销" width="80"></el-table-column>
+        <el-table-column
+          prop="ybSelfFlag"
+          label="报销"
+          width="80"
+        ></el-table-column>
         <el-table-column>
           <template #default="scope">
-            <el-button circle v-if="!injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0"
-                       @click="fixNegativeFeeUploadProblem(scope.row, 1)">
-              <i class="iconfont icon-tools-hardware" style="font-size: 10px;"></i>
+            <el-button
+              circle
+              v-if="
+                !injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0
+              "
+              @click="fixNegativeFeeUploadProblem(scope.row, 1)"
+            >
+              <i
+                class="iconfont icon-tools-hardware"
+                style="font-size: 10px"
+              ></i>
             </el-button>
           </template>
         </el-table-column>
@@ -54,14 +106,14 @@
     </div>
     <div>
       <el-pagination
-          small
-          @size-change="handleXmSizeChange"
-          @current-change="handleCurrentXmChange"
-          :current-page="page.xmPage"
-          :page-sizes="[10, 30, 50, 70, 100, 300]"
-          :page-size="page.xmPageSize"
-          layout="total, sizes, prev, pager, next"
-          :total="xmTotalSize"
+        small
+        @size-change="handleXmSizeChange"
+        @current-change="handleCurrentXmChange"
+        :current-page="page.xmPage"
+        :page-sizes="[10, 30, 50, 70, 100, 300]"
+        :page-size="page.xmPageSize"
+        layout="total, sizes, prev, pager, next"
+        :total="xmTotalSize"
       >
       </el-pagination>
     </div>
@@ -71,19 +123,48 @@
     </el-divider>
     <div class="layout_flex_1-y">
       <el-table :data="ypFeeList" stripe height="100%">
-        <el-table-column prop="detailSn" label="流水号" width="80"></el-table-column>
-        <el-table-column prop="chargeCodeMx" label="院内码" width="100"></el-table-column>
-        <el-table-column prop="chargeAmount" label="数量" sortable width="80"></el-table-column>
-        <el-table-column prop="chargeFee" label="金额" width="80"></el-table-column>
+        <el-table-column
+          prop="detailSn"
+          label="流水号"
+          width="80"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeCodeMx"
+          label="院内码"
+          width="100"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeAmount"
+          label="数量"
+          sortable
+          width="80"
+        ></el-table-column>
+        <el-table-column
+          prop="chargeFee"
+          label="金额"
+          width="80"
+        ></el-table-column>
         <el-table-column prop="chargeDate" label="收费日期"></el-table-column>
         <el-table-column prop="ybCode" :label="ybCodeLabel"></el-table-column>
         <el-table-column prop="chargeName" label="项目名称"></el-table-column>
-        <el-table-column prop="ybSelfFlag" label="报销" width="80"></el-table-column>
+        <el-table-column
+          prop="ybSelfFlag"
+          label="报销"
+          width="80"
+        ></el-table-column>
         <el-table-column>
           <template #default="scope">
-            <el-button circle v-if="!injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0"
-                       @click="fixNegativeFeeUploadProblem(scope.row, 2)">
-              <i class="iconfont icon-tools-hardware" style="font-size: 10px;"></i>
+            <el-button
+              circle
+              v-if="
+                !injuryMode && patient.mdtrtId && scope.row.chargeAmount < 0
+              "
+              @click="fixNegativeFeeUploadProblem(scope.row, 2)"
+            >
+              <i
+                class="iconfont icon-tools-hardware"
+                style="font-size: 10px"
+              ></i>
             </el-button>
           </template>
         </el-table-column>
@@ -91,14 +172,14 @@
     </div>
     <div>
       <el-pagination
-          small
-          @size-change="handleYpSizeChange"
-          @current-change="handleCurrentYpChange"
-          :current-page="page.ypPage"
-          :page-sizes="[10, 30, 50, 70, 100, 300]"
-          :page-size="page.ypPageSize"
-          layout="total, sizes, prev, pager, next"
-          :total="ypTotalSize"
+        small
+        @size-change="handleYpSizeChange"
+        @current-change="handleCurrentYpChange"
+        :current-page="page.ypPage"
+        :page-sizes="[10, 30, 50, 70, 100, 300]"
+        :page-size="page.ypPageSize"
+        layout="total, sizes, prev, pager, next"
+        :total="ypTotalSize"
       >
       </el-pagination>
     </div>
@@ -107,86 +188,124 @@
   <div class="m-wrapper" v-show="showProgress">
     <div class="dj-center-box-wrapper">
       <div class="el-message-box" style="font-size: 13px">
-        <div style="background: #409eff; color: white; padding: 7px 10px; font-weight: bold">
+        <div
+          style="
+            background: #409eff;
+            color: white;
+            padding: 7px 10px;
+            font-weight: bold;
+          "
+        >
           <i class="el-icon-loading"></i>
           上传进度
         </div>
         <div style="padding: 10px">
           <div style="margin-bottom: 10px">{{ uploadIndexText }} ...</div>
-          <div style="margin-bottom: 10px" v-show="percentage === 100">上传结束,正在进行费用计算 ...</div>
-          <el-progress :text-inside="true" :stroke-width="22" :percentage="percentage"
-                       status="success"></el-progress>
+          <div style="margin-bottom: 10px" v-show="percentage === 100">
+            上传结束,正在进行费用计算 ...
+          </div>
+          <el-progress
+            :text-inside="true"
+            :stroke-width="22"
+            :percentage="percentage"
+            status="success"
+          ></el-progress>
           <div style="height: 5px"></div>
         </div>
       </div>
     </div>
   </div>
 
-  <el-dialog v-model="weiGuiTuiFeiFenXiDialog" title="违规费用分析" :fullscreen="true">
-    <wei-gui-fei-yong-fen-xi :init="weiGuiTuiFeiInit" @open="weiGuiTuiFeiOpenDialog" ref="weiGui"
-                             :patient="weiGuiJiBenXinXi"></wei-gui-fei-yong-fen-xi>
+  <el-dialog
+    v-model="weiGuiTuiFeiFenXiDialog"
+    title="违规费用分析"
+    :fullscreen="true"
+  >
+    <wei-gui-fei-yong-fen-xi
+      :init="weiGuiTuiFeiInit"
+      @open="weiGuiTuiFeiOpenDialog"
+      ref="weiGui"
+      :patient="weiGuiJiBenXinXi"
+    ></wei-gui-fei-yong-fen-xi>
   </el-dialog>
-  <MedfeeAnalyse v-if="showFeeDetl" type="unsettled" :mdtrt-id="patient.mdtrtId" @close="showFeeDetl = false"/>
+  <MedfeeAnalyse
+    v-if="showFeeDetl"
+    type="unsettled"
+    :mdtrt-id="patient.mdtrtId"
+    @close="showFeeDetl = false"
+  />
 </template>
 
 <script setup>
-import {computed, onActivated, onDeactivated, onMounted, reactive, ref, watch} from 'vue'
-import {fetchNotUploadedFees, getPatientInfo} from '@/api/inpatient/patient'
-import {ElMessage, ElMessageBox} from 'element-plus'
-import {nullPatient} from '@/utils/validate'
+import {
+  computed,
+  onActivated,
+  onDeactivated,
+  onMounted,
+  reactive,
+  ref,
+  watch,
+} from "vue";
+import { fetchNotUploadedFees, getPatientInfo } from "@/api/inpatient/patient";
+import { ElMessage, ElMessageBox } from "element-plus";
+import { nullPatient } from "@/utils/validate";
 import {
   hospitalizationPreSettlement,
   multipleUpload,
   pairNegativeFee,
   revokeUploadFees,
-  uploadFeeDetail
-} from '@/api/medical-insurance/si-inpatient'
-import {revokeInpatientCost, inpatientSettlement, inpatientCostUpload} from '@/api/medical-insurance/si-injury'
-import {setCallback} from '@/utils/websocket'
-import {getGreatestRole} from '@/utils/permission'
-import {baseinfo, setBaseinfo} from '@/data/inpatient'
-import WeiGuiFeiYongFenXi from '@/components/inpatient/WeiGuiFeiYongFenXi.vue'
-import MedfeeAnalyse from '../../../components/medical-insurance/medfee-analyse/Index.vue'
-import {clone} from "@/utils/clone";
-import {useMedinsStore} from "@/pinia/medins-store";
-import {useUserStore} from "@/pinia/user-store";
-import {useProgressBarStore} from "@/pinia/progress-bar-store";
-
-const medinsStore = useMedinsStore()
+  uploadFeeDetail,
+} from "@/api/medical-insurance/si-inpatient";
+import {
+  revokeInpatientCost,
+  inpatientSettlement,
+  inpatientCostUpload,
+} from "@/api/medical-insurance/si-injury";
+import { setCallback } from "@/utils/websocket";
+import { getGreatestRole } from "@/utils/permission";
+import { baseinfo, setBaseinfo } from "@/data/inpatient";
+import WeiGuiFeiYongFenXi from "@/components/inpatient/WeiGuiFeiYongFenXi.vue";
+import MedfeeAnalyse from "../../../components/medical-insurance/medfee-analyse/Index.vue";
+import { clone } from "@/utils/clone";
+import { useMedinsStore } from "@/pinia/medins-store";
+import { useUserStore } from "@/pinia/user-store";
+import { useProgressBarStore } from "@/pinia/progress-bar-store";
+
+const medinsStore = useMedinsStore();
 const injuryMode = computed(() => {
-  return medinsStore.isInjuryMode
-})
-const isAdmin = getGreatestRole() < 10
+  return medinsStore.isInjuryMode;
+});
+const isAdmin = getGreatestRole() < 10;
 const patient = computed(() => {
-  return baseinfo()
-})
-
-const xmFeeSum = ref('0.00')
-const ypFeeSum = ref('0.00')
-const xmFeeList = ref([])
-const ypFeeList = ref([])
-const xmTotalSize = ref(0)
-const ypTotalSize = ref(0)
+  return baseinfo();
+});
+
+const xmFeeSum = ref("0.00");
+const ypFeeSum = ref("0.00");
+const xmFeeList = ref([]);
+const ypFeeList = ref([]);
+const xmTotalSize = ref(0);
+const ypTotalSize = ref(0);
 const ybCodeLabel = computed(() => {
-  return injuryMode.value ? '社保三大目录ID' : '国家医保编码'
-})
+  return injuryMode.value ? "社保三大目录ID" : "国家医保编码";
+});
 
-const page = initPage()
+const page = initPage();
 const fetchProjectFees = () => {
   const param = {
     patNo: patient.value.inpatientNo,
     times: patient.value.admissTimes,
     currentPage: page.xmPage,
     pageSize: page.xmPageSize,
-    zdTable: 'zd_charge_item',
+    zdTable: "zd_charge_item",
     injuryMode: injuryMode.value,
-  }
-  fetchNotUploadedFees(param).then((res) => {
-    xmFeeList.value = res.list
-    xmFeeSum.value = res.sum
-    xmTotalSize.value = res.totalSize
-  })
-}
+  };
+  fetchNotUploadedFees(param).then(res => {
+    xmFeeList.value = res.list;
+    xmFeeSum.value = res.sum;
+    xmTotalSize.value = res.totalSize;
+  });
+};
 
 const fetchMedicineFees = () => {
   const param = {
@@ -194,154 +313,156 @@ const fetchMedicineFees = () => {
     times: patient.value.admissTimes,
     currentPage: page.ypPage,
     pageSize: page.ypPageSize,
-    zdTable: 'yp_zd_dict',
+    zdTable: "yp_zd_dict",
     injuryMode: injuryMode.value,
-  }
-  fetchNotUploadedFees(param).then((res) => {
-    ypFeeList.value = res.list
-    ypFeeSum.value = res.sum
-    ypTotalSize.value = res.totalSize
-  })
-}
+  };
+  fetchNotUploadedFees(param).then(res => {
+    ypFeeList.value = res.list;
+    ypFeeSum.value = res.sum;
+    ypTotalSize.value = res.totalSize;
+  });
+};
 
 const clearFees = () => {
-  xmFeeList.value = []
-  ypFeeList.value = []
-  xmFeeSum.value = '0.00'
-  ypFeeSum.value = '0.00'
-  xmTotalSize.value = 0
-  ypTotalSize.value = 0
-}
+  xmFeeList.value = [];
+  ypFeeList.value = [];
+  xmFeeSum.value = "0.00";
+  ypFeeSum.value = "0.00";
+  xmTotalSize.value = 0;
+  ypTotalSize.value = 0;
+};
 
-const activated = ref(false)
+const activated = ref(false);
 
 onActivated(() => {
-  activated.value = true
-  medinsStore.setCurrentPageName('inHospFeeUpload')
-  setCallback('medInsFeeUploadProgress', socketCallback)
-})
+  activated.value = true;
+  medinsStore.setCurrentPageName("inHospFeeUpload");
+  setCallback("medInsFeeUploadProgress", socketCallback);
+});
 
 onDeactivated(() => {
-  activated.value = false
-  medinsStore.setCurrentPageName('')
-})
+  activated.value = false;
+  medinsStore.setCurrentPageName("");
+});
 
 watch(
-    () => patient.value.inpatientNo,
-    () => {
-      if (activated.value) {
-        if (patient.value.inpatientNo) {
-          fetchProjectFees()
-          fetchMedicineFees()
-          weiGuiTuiFeiFenXiDialogOpen(false)
-        } else {
-          clearFees()
-        }
+  () => patient.value.inpatientNo,
+  () => {
+    if (activated.value) {
+      if (patient.value.inpatientNo) {
+        fetchProjectFees();
+        fetchMedicineFees();
+        weiGuiTuiFeiFenXiDialogOpen(false);
+      } else {
+        clearFees();
       }
     }
-)
+  }
+);
 
-const errorMessages = ref([])
+const errorMessages = ref([]);
 const clearErrorMessages = () => {
-  errorMessages.value = []
-}
-const handleXmSizeChange = (val) => {
-  page.xmPageSize = val
-  fetchProjectFees()
-}
-const handleCurrentXmChange = (val) => {
-  page.xmPage = val
-  fetchProjectFees()
-}
-const handleYpSizeChange = (val) => {
-  page.ypPageSize = val
-  fetchMedicineFees()
-}
-const handleCurrentYpChange = (val) => {
-  page.ypPage = val
-  fetchMedicineFees()
-}
+  errorMessages.value = [];
+};
+const handleXmSizeChange = val => {
+  page.xmPageSize = val;
+  fetchProjectFees();
+};
+const handleCurrentXmChange = val => {
+  page.xmPage = val;
+  fetchProjectFees();
+};
+const handleYpSizeChange = val => {
+  page.ypPageSize = val;
+  fetchMedicineFees();
+};
+const handleCurrentYpChange = val => {
+  page.ypPage = val;
+  fetchMedicineFees();
+};
 
 const preCalculateCost = () => {
-  if (nullPatient()) return
+  if (nullPatient()) return;
   excutePreCal().then(() => {
     getPatientInfo(patient.value.inpatientNo).then(res => {
-      setBaseinfo(res)
-    })
-  })
-}
+      setBaseinfo(res);
+    });
+  });
+};
 
 const excutePreCal = () => {
   return new Promise((resolve, reject) => {
     if (injuryMode.value) {
-      inpatientSettlement(patient.value).then((res) => {
-        patient.value.chargeYb = res.fundPay
+      inpatientSettlement(patient.value).then(res => {
+        patient.value.chargeYb = res.fundPay;
         ElMessageBox.alert(res, {
-          type: 'success',
-          confirmButtonText: '确定',
+          type: "success",
+          confirmButtonText: "确定",
         }).then(() => {
-          resolve()
-        })
-      })
+          resolve();
+        });
+      });
     } else {
-      hospitalizationPreSettlement(patient.value).then((res) => {
+      hospitalizationPreSettlement(patient.value).then(res => {
         ElMessageBox.alert(res, {
-          type: 'success',
-          confirmButtonText: '确定',
+          type: "success",
+          confirmButtonText: "确定",
         }).then(() => {
-          resolve()
-        })
-      })
+          resolve();
+        });
+      });
     }
-  })
-}
+  });
+};
 
-const patientIndex = ref(1)
-const showProgress = ref(false)
-const percentage = ref(0)
+const patientIndex = ref(1);
+const showProgress = ref(false);
+const percentage = ref(0);
 
 const selections = computed(() => {
-  return medinsStore.overviewSelections
-})
+  return medinsStore.overviewSelections;
+});
 
 const uploadFees = () => {
   if (selections.value.length > 0) {
-    showProgress.value = true
-    doMultipleUpload()
+    showProgress.value = true;
+    doMultipleUpload();
   } else {
-    if (nullPatient()) return
-    showProgress.value = true
+    if (nullPatient()) return;
+    showProgress.value = true;
     if (injuryMode.value) {
-      doSingleInjuryUpload()
+      doSingleInjuryUpload();
     } else {
-      doSingleNormalUpload()
+      doSingleNormalUpload();
     }
   }
-}
+};
 
-const userInfo = useUserStore().userInfo
+const userInfo = useUserStore().userInfo;
 const doSingleInjuryUpload = () => {
-  patient.value.sid = userInfo.sid
-  inpatientCostUpload(patient.value).then((res) => {
-    fetchProjectFees()
-    fetchMedicineFees()
-    showProgress.value = false
-    percentage.value = 0
-    patient.value.chargeYb = res.fundPay
-    ElMessageBox.alert(res, '成功', {
-      type: 'success',
-      confirmButtonText: '确定',
-    })
-    getPatientInfo(patient.value.inpatientNo).then(res => {
-      setBaseinfo(res)
+  patient.value.sid = userInfo.sid;
+  inpatientCostUpload(patient.value)
+    .then(res => {
+      fetchProjectFees();
+      fetchMedicineFees();
+      showProgress.value = false;
+      percentage.value = 0;
+      patient.value.chargeYb = res.fundPay;
+      ElMessageBox.alert(res, "成功", {
+        type: "success",
+        confirmButtonText: "确定",
+      });
+      getPatientInfo(patient.value.inpatientNo).then(res => {
+        setBaseinfo(res);
+      });
     })
-  }).catch(() => {
-    showProgress.value = false
-    percentage.value = 0
-    fetchProjectFees()
-    fetchMedicineFees()
-  })
-}
+    .catch(() => {
+      showProgress.value = false;
+      percentage.value = 0;
+      fetchProjectFees();
+      fetchMedicineFees();
+    });
+};
 
 const doSingleNormalUpload = () => {
   const param = {
@@ -349,153 +470,161 @@ const doSingleNormalUpload = () => {
     admissTimes: patient.value.admissTimes,
     ledgerSn: patient.value.ledgerSn,
     sid: userInfo.sid,
-  }
-  uploadFeeDetail(param).then((res) => {
-    fetchProjectFees()
-    fetchMedicineFees()
-    showProgress.value = false
-    percentage.value = 0
-    ElMessageBox.alert(res, '成功', {
-      type: 'success',
-      confirmButtonText: '确定',
-    })
-    useProgressBarStore().closeProgressBar()
-    getPatientInfo(patient.value.inpatientNo).then(res => {
-      setBaseinfo(res)
+  };
+  uploadFeeDetail(param)
+    .then(res => {
+      fetchProjectFees();
+      fetchMedicineFees();
+      showProgress.value = false;
+      percentage.value = 0;
+      ElMessageBox.alert(res, "成功", {
+        type: "success",
+        confirmButtonText: "确定",
+      });
+      useProgressBarStore().closeProgressBar();
+      getPatientInfo(patient.value.inpatientNo).then(res => {
+        setBaseinfo(res);
+      });
     })
-  }).catch(() => {
-    fetchProjectFees()
-    fetchMedicineFees()
-    showProgress.value = false
-    percentage.value = 0
-  })
-}
+    .catch(() => {
+      fetchProjectFees();
+      fetchMedicineFees();
+      showProgress.value = false;
+      percentage.value = 0;
+    });
+};
 
 const doMultipleUpload = () => {
-  let list = clone(selections.value)
-  list.forEach((item) => {
-    item.sid = userInfo.sid
-  })
-  multipleUpload(list).then((res) => {
-    ElMessage({
-      message: '处理完成。',
-      type: 'success',
-      duration: 2500,
-      showClose: true,
+  let list = clone(selections.value);
+  list.forEach(item => {
+    item.sid = userInfo.sid;
+  });
+  multipleUpload(list)
+    .then(res => {
+      ElMessage({
+        message: "处理完成。",
+        type: "success",
+        duration: 2500,
+        showClose: true,
+      });
+      showProgress.value = false;
+      percentage.value = 0;
     })
-    showProgress.value = false
-    percentage.value = 0
-  }).catch(() => {
-    showProgress.value = false
-    percentage.value = 0
-  })
-}
-
-const socketCallback = (data) => {
-  if (typeof data === 'string') {
-    data = JSON.parse(data)
+    .catch(() => {
+      showProgress.value = false;
+      percentage.value = 0;
+    });
+};
+
+const socketCallback = data => {
+  if (typeof data === "string") {
+    data = JSON.parse(data);
   }
   switch (data.name) {
-    case 'uploadFeeResponse':
-      errorMessages.value.push(data)
-      break
-    case 'updatePatientIndex':
-      patientIndex.value = data.patientIndex
-      percentage.value = 0
-      break
-    case 'updateProgress':
-      percentage.value = data.percentage
-      break
+    case "uploadFeeResponse":
+      errorMessages.value.push(data);
+      break;
+    case "updatePatientIndex":
+      patientIndex.value = data.patientIndex;
+      percentage.value = 0;
+      break;
+    case "updateProgress":
+      percentage.value = data.percentage;
+      break;
     default:
-      break
+      break;
   }
-}
+};
 
 const uploadIndexText = computed(() => {
-  let total = selections.value.length
+  let total = selections.value.length;
   if (total === 0) {
-    total = 1
+    total = 1;
   }
-  return '共 ' + total + ' 人,正在处理第 ' + patientIndex.value + ' 人'
-})
+  return "共 " + total + " 人,正在处理第 " + patientIndex.value + " 人";
+});
 const cancelFees = () => {
-  ElMessageBox.confirm('是否确定取消此患者已上传的费用?', '提示', {
-    type: 'warning',
-    confirmButtonText: '确定',
-    cancelButtonText: '放弃',
-  }).then(() => {
-    if (injuryMode.value) {
-      revokeInpatientCost(patient.value).then(() => {
-        ElMessage({
-          message: '操作成功。',
-          type: 'success',
-          duration: 2500,
-          showClose: true,
-        })
-        fetchProjectFees()
-        fetchMedicineFees()
-      })
-    } else {
-      revokeUploadFees(patient.value).then(() => {
-        ElMessage({
-          message: '操作成功。',
-          type: 'success',
-          duration: 2500,
-          showClose: true,
-        })
-        fetchProjectFees()
-        fetchMedicineFees()
-      })
-    }
-  }).catch(() => {
+  ElMessageBox.confirm("是否确定取消此患者已上传的费用?", "提示", {
+    type: "warning",
+    confirmButtonText: "确定",
+    cancelButtonText: "放弃",
   })
-}
+    .then(() => {
+      if (injuryMode.value) {
+        revokeInpatientCost(patient.value).then(() => {
+          ElMessage({
+            message: "操作成功。",
+            type: "success",
+            duration: 2500,
+            showClose: true,
+          });
+          fetchProjectFees();
+          fetchMedicineFees();
+        });
+      } else {
+        revokeUploadFees(patient.value).then(() => {
+          ElMessage({
+            message: "操作成功。",
+            type: "success",
+            duration: 2500,
+            showClose: true,
+          });
+          fetchProjectFees();
+          fetchMedicineFees();
+        });
+      }
+    })
+    .catch(() => {});
+};
 
-const showFeeDetl = ref(false)
+const showFeeDetl = ref(false);
 const feeDtle = () => {
   if (nullPatient()) {
-    return
+    return;
   }
-  showFeeDetl.value = true
-}
+  showFeeDetl.value = true;
+};
 
 const fixNegativeFeeUploadProblem = (row, flag) => {
-  pairNegativeFee(row).then((res) => {
+  pairNegativeFee(row).then(res => {
     ElMessage({
       message: res,
-      type: 'success',
+      type: "success",
       duration: 2500,
-      showClose: true
-    })
-    flag === 1 ? fetchProjectFees() : fetchMedicineFees()
-  })
-}
+      showClose: true,
+    });
+    flag === 1 ? fetchProjectFees() : fetchMedicineFees();
+  });
+};
 
 ///////////////////////////////////////////// 违规退费分析 /////////////////////////////////////////////////////////////////////////
-const weiGuiTuiFeiInit = ref(0)
-const weiGuiTuiFeiFenXiDialog = ref(true)
+const weiGuiTuiFeiInit = ref(0);
+const weiGuiTuiFeiFenXiDialog = ref(true);
 
-const weiGuiJiBenXinXi = ref({})
+const weiGuiJiBenXinXi = ref({});
 
-const weiGui = ref()
+const weiGui = ref();
 
-const weiGuiTuiFeiFenXiDialogOpen = (val) => {
-  weiGuiTuiFeiInit.value += 1
-  weiGuiJiBenXinXi.value.deptCode = ''
-  weiGuiJiBenXinXi.value.inpatientNo = typeof patient.value.inpatientNo === 'undefined' ? '' : patient.value.inpatientNo
-  weiGuiJiBenXinXi.value.openDialog = val
-}
+const weiGuiTuiFeiFenXiDialogOpen = val => {
+  weiGuiTuiFeiInit.value += 1;
+  weiGuiJiBenXinXi.value.deptCode = "";
+  weiGuiJiBenXinXi.value.inpatientNo =
+    typeof patient.value.inpatientNo === "undefined"
+      ? ""
+      : patient.value.inpatientNo;
+  weiGuiJiBenXinXi.value.openDialog = val;
+};
 
-const weiGuiTuiFeiOpenDialog = (val) => {
-  weiGuiTuiFeiFenXiDialog.value = val
-}
+const weiGuiTuiFeiOpenDialog = val => {
+  weiGuiTuiFeiFenXiDialog.value = val;
+};
 
 onMounted(() => {
   if (patient.value.inpatientNo) {
-    fetchProjectFees()
-    fetchMedicineFees()
+    fetchProjectFees();
+    fetchMedicineFees();
   }
-})
+});
 
 function initPage() {
   return reactive({
@@ -503,7 +632,7 @@ function initPage() {
     xmPageSize: 10,
     ypPage: 1,
     ypPageSize: 10,
-  })
+  });
 }
 </script>
 
@@ -525,7 +654,7 @@ function initPage() {
 }
 
 .dj-center-box-wrapper::after {
-  content: '';
+  content: "";
   display: inline-block;
   height: 100%;
   width: 0px;

+ 58 - 52
src/views/medical-insurance/outpatient/component/EmergencyRescue.vue

@@ -2,22 +2,20 @@
   <div class="process-mask" v-if="showProcessMask">
     <div>
       <div class="title-box">
-        <div class="content">
-          急诊抢救医保进程
-        </div>
+        <div class="content">急诊抢救医保进程</div>
         <el-button
-            :disabled="processing"
-            icon="Close"
-            circle
-            type="danger"
-            @click="showProcessMask = false"
-            title="关闭"
+          :disabled="processing"
+          icon="Close"
+          circle
+          type="danger"
+          @click="showProcessMask = false"
+          title="关闭"
         ></el-button>
       </div>
       <div class="progress">
         <div v-for="item in processMsgs">
-          <div class="time">{{item.time}}</div>
-          <div class="msg">{{item.msg}}</div>
+          <div class="time">{{ item.time }}</div>
+          <div class="msg">{{ item.msg }}</div>
         </div>
       </div>
       <div class="footer">
@@ -28,66 +26,74 @@
 </template>
 
 <script setup>
-import {reactive, ref} from "vue";
-import {executeEmergencyRescue,revokeBusiness} from "@/api/medical-insurance/emergency-rescue";
-import {getDatetime} from "@/utils/date";
-import {useZIndex} from "element-plus";
-import {setCallback} from "@/utils/websocket";
+import { reactive, ref } from "vue";
+import {
+  executeEmergencyRescue,
+  revokeBusiness,
+} from "@/api/medical-insurance/emergency-rescue";
+import { getDatetime } from "@/utils/date";
+import { useZIndex } from "element-plus";
+import { setCallback } from "@/utils/websocket";
 
 defineExpose({
-  start,revoke
-})
+  start,
+  revoke,
+});
 const props = defineProps({
   param: {
     type: Object,
     required: true,
-  }
-})
+  },
+});
 
-const showProcessMask = ref(false)
+const showProcessMask = ref(false);
 
-const processMsgs = reactive([])
-const processing = ref(true)
+const processMsgs = reactive([]);
+const processing = ref(true);
 
 function start() {
-  showProcessMask.value = true
-  processing.value = true
-  processMsgs.splice(0, processMsgs.length)
-  executeEmergencyRescue(props.param).then(res => {
-    socketCallback(res)
-    processing.value = false
-  }).catch(e => {
-    socketCallback(e.toString())
-    processing.value = false
-  })
+  showProcessMask.value = true;
+  processing.value = true;
+  processMsgs.splice(0, processMsgs.length);
+  executeEmergencyRescue(props.param)
+    .then(res => {
+      socketCallback(res);
+      processing.value = false;
+    })
+    .catch(e => {
+      socketCallback(e.toString());
+      processing.value = false;
+    });
 }
 
 function revoke() {
-  showProcessMask.value = true
-  processing.value = true
-  processMsgs.splice(0, processMsgs.length)
-  revokeBusiness(props.param).then(res => {
-    socketCallback(res)
-    processing.value = false
-  }).catch(e => {
-    socketCallback(e.toString())
-    processing.value = false
-  })
+  showProcessMask.value = true;
+  processing.value = true;
+  processMsgs.splice(0, processMsgs.length);
+  revokeBusiness(props.param)
+    .then(res => {
+      socketCallback(res);
+      processing.value = false;
+    })
+    .catch(e => {
+      socketCallback(e.toString());
+      processing.value = false;
+    });
 }
 
 function socketCallback(data) {
   processMsgs.push({
     time: getDatetime(new Date()),
-    msg: data
-  })
+    msg: data,
+  });
 }
 
-const zIndex = ref(0)
+const zIndex = ref(0);
 onActivated(() => {
-  console.log('activated')
-  zIndex.value = useZIndex().nextZIndex()
-  setCallback('emergencyRescueProcess', socketCallback)
-})
+  console.log("activated");
+  zIndex.value = useZIndex().nextZIndex();
+  setCallback("emergencyRescueProcess", socketCallback);
+});
 </script>
 
 <style lang="scss" scoped>
@@ -100,7 +106,7 @@ onActivated(() => {
   left: 0;
   right: 0;
   bottom: 0;
-  background-color: rgba(0,0,0,.5);
+  background-color: rgba(0, 0, 0, 0.5);
   display: flex;
   justify-content: center;
   align-items: center;
@@ -156,4 +162,4 @@ onActivated(() => {
     }
   }
 }
-</style>
+</style>

+ 19 - 12
src/views/settings/SendNotification.vue

@@ -26,7 +26,7 @@
         </el-col>
         <el-col :span="12">
           <el-card>
-            <template #header> 发送系统更新消息 </template>
+            <template #header> 发送系统更新消息</template>
             <el-input
               type="textarea"
               @keydown.ctrl.enter="clickSendUpdateMessage"
@@ -39,14 +39,14 @@
                 type="primary"
                 icon="Upload"
                 @click="clickSendUpdateMessage"
-                >发送消息</el-button
-              >
+                >发送消息
+              </el-button>
             </template>
           </el-card>
         </el-col>
         <el-col :span="12" style="margin-top: 10px">
           <el-card>
-            <template #header> 发送滚动通知 </template>
+            <template #header> 发送滚动通知</template>
             <el-input
               type="textarea"
               :rows
@@ -59,8 +59,8 @@
                 type="primary"
                 icon="Upload"
                 @click="clickSendScrollingMessages"
-                >发送消息</el-button
-              >
+                >发送消息
+              </el-button>
             </template>
           </el-card>
         </el-col>
@@ -72,7 +72,6 @@
 <script setup lang="tsx">
 import { onMounted, ref, reactive } from "vue";
 import {
-  getOnlineCount,
   sendMessageToAll,
   sendScrollingMessages,
   sendSystemUpdatesMessage,
@@ -83,8 +82,9 @@ import { CyMessageBox } from "@/utils/cy-message-box";
 import XEUtils from "xe-utils";
 import { stringIsBlank } from "@/utils/blank-utils";
 import { xcMessage } from "@/utils/xiaochan-element-plus";
+import { sendMeg, setCallback } from "@/utils/websocket";
 
-const onlineCount = ref(10);
+const onlineCount = ref(0);
 const param = reactive<{
   message: null | string;
   refreshDelay: null | number;
@@ -101,9 +101,13 @@ const beforeSend = () => {
   } else {
     CyMessageBox.prompt({
       title: "是否发送以下消息",
-      message: <pre class={"pre-css"}>{param.message}</pre>,
+      message: (
+        <div>
+          <pre class={"pre-css"}>{param.message}</pre>
+          <span>刷新页面秒数(不刷新请勿填写)</span>
+        </div>
+      ),
       inputAllowEmpty: true,
-      placeholder: "刷新页面秒数(不刷新请勿填写)",
     })
       .then(({ value }) => {
         if (stringIsBlank(value)) {
@@ -135,9 +139,12 @@ const clickSendScrollingMessages = () => {
 };
 
 onMounted(() => {
-  getOnlineCount().then(res => {
-    onlineCount.value = res;
+  sendMeg("getOnlineCount");
+
+  setCallback("onlineCount", val => {
+    onlineCount.value = val;
   });
+
   getSystemAnnouncement().then(res => {
     systemUpdatesMessage.value = res["systemUpdatesMessage"];
     scrollingMessage.value = res["scrollingMessages"];

Diff do ficheiro suprimidas por serem muito extensas
+ 200 - 0
vite.config.js.timestamp-1744243577350-b79124f197649.mjs


Alguns ficheiros não foram mostrados porque muitos ficheiros mudaram neste diff