xiaochan 9 月之前
父節點
當前提交
fefbf9a6cd

+ 2 - 0
src/api/ca/ca-api.ts

@@ -62,6 +62,7 @@ export function sendMoreEventSign(data: {
   data: MoreEventSignData[];
   documentId: string;
   sendCode: string;
+  sendCodeName: string;
 }) {
   return requestV2({
     url: "/thyyca/sendMoreEventSign",
@@ -85,6 +86,7 @@ export function hBoardSignV2(data: {
   name: string;
   idCard: string;
   signType: number;
+  signOpinion: string;
 }) {
   return requestV2({
     url: "/thyyca/hBoardSignV2",

+ 19 - 0
src/components/cy/CyDialog/CyDialogContainer.vue

@@ -0,0 +1,19 @@
+<template>
+  <div class="layout_container">
+    <div class="layout_main">
+      <slot name="default" />
+    </div>
+    <div class="cy_dialog-container-footer">
+      <slot name="footer" />
+    </div>
+  </div>
+</template>
+
+<style>
+.cy_dialog-container-footer {
+  display: flex;
+  padding: 5px;
+  height: max-content;
+  justify-content: end;
+}
+</style>

+ 2 - 0
src/components/cy/CyDialog/index.ts

@@ -42,6 +42,7 @@ export interface DialogOptions<P = Component> {
   params?: FirstParam<P>;
   showCancel?: boolean;
   showConfirm?: boolean;
+  showFooter?: boolean;
   cancelText?: string;
   confirmText?: string;
   visible?: boolean;
@@ -71,6 +72,7 @@ export function useDialog<R = any, C = Component>(
       component: shallowRef(component),
       showCancel: true,
       showConfirm: true,
+      showFooter: true,
       ...props,
     };
 

+ 18 - 21
src/components/cy/CyDialog/index.vue

@@ -73,29 +73,26 @@ function setRef(el, item) {
       @cyDialogConfirm="
         (value: any, isEmits = true) => handleConfirm(item, isEmits, value)
       "
-      :teleport="`Cy-dialog-${item.dialogKey}`"
+      :cyDialog="item"
     />
+    <template #footer v-if="item.showFooter">
+      <el-button
+        size="default"
+        v-if="item.showCancel"
+        @click="handleCancel(item)"
+      >
+        {{ item.cancelText || "取消" }}
+      </el-button>
 
-    <template #footer>
-      <div :id="`Cy-dialog-${item.dialogKey}_footer`">
-        <el-button
-          size="default"
-          v-if="item.showCancel"
-          @click="handleCancel(item)"
-        >
-          {{ item.cancelText || "取消" }}
-        </el-button>
-
-        <el-button
-          v-if="item.showConfirm"
-          type="primary"
-          size="default"
-          color="hsl(240 5.9% 10%)"
-          @click="handleConfirm(item)"
-        >
-          {{ item.confirmText || "确认" }}
-        </el-button>
-      </div>
+      <el-button
+        v-if="item.showConfirm"
+        type="primary"
+        size="default"
+        color="hsl(240 5.9% 10%)"
+        @click="handleConfirm(item)"
+      >
+        {{ item.confirmText || "确认" }}
+      </el-button>
     </template>
   </el-dialog>
 </template>

+ 172 - 163
src/components/zhu-yuan-yi-sheng/emr/web-socket/EmrWebSocket.vue

@@ -1,111 +1,126 @@
 <template>
   <EmrChatBox
-      v-if="dialog"
-      :userSize
-      ref="dialogRef"
-      :current-editor-user="props.currentEditorUser"
-      @closed="dialog = false"
-      :user-list="userList"
-      :sid="sid"/>
-
-  <el-dialog v-model="errDialog" title="与服务器断开连接"
-             :show-close="false"
-             :close-on-press-escape="false"
-             :close-on-click-modal="false">
-    <div v-loading="errDialog"
-         element-loading-text="正在尝试重新连接..."
-         style="width: 100%;height: 400px"/>
+    v-if="dialog"
+    :userSize
+    ref="dialogRef"
+    :current-editor-user="props.currentEditorUser"
+    @closed="dialog = false"
+    :user-list="userList"
+    :sid="sid"
+  />
+
+  <el-dialog
+    v-model="errDialog"
+    title="与服务器断开连接"
+    :show-close="false"
+    :close-on-press-escape="false"
+    :close-on-click-modal="false"
+  >
+    <div
+      v-loading="errDialog"
+      element-loading-text="正在尝试重新连接..."
+      style="width: 100%; height: 400px"
+    />
   </el-dialog>
-
 </template>
 
 <script setup lang="ts">
-import {stringIsBlank} from "@/utils/blank-utils";
-import {ref, defineProps, onMounted, nextTick, computed, onBeforeUnmount} from 'vue'
-import {xcMessage} from '@/utils/xiaochan-element-plus'
-import {getRoomPeople, sendAMessage} from '@/api/zhu-yuan-yi-sheng/emr-socket'
-import {ElMessageBox, ElNotification} from "element-plus";
-import EmrChatBox from "@/components/zhu-yuan-yi-sheng/emr/web-socket/EmrChatBox.vue";
+import { stringIsBlank } from "@/utils/blank-utils";
 import {
-  emrMitt
-} from '@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init'
+  ref,
+  defineProps,
+  onMounted,
+  nextTick,
+  computed,
+  onBeforeUnmount,
+} from "vue";
+import { xcMessage } from "@/utils/xiaochan-element-plus";
 import {
-  forceRefreshDialog
-} from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/force-refresh-func";
-import {useUserStore} from "@/pinia/user-store";
-import {isDev} from "@/utils/public";
-import {CyMessageBox} from "@/components/cy/message-box";
+  getRoomPeople,
+  sendAMessage,
+} from "@/api/zhu-yuan-yi-sheng/emr-socket";
+import { ElMessageBox, ElNotification } from "element-plus";
+import EmrChatBox from "@/components/zhu-yuan-yi-sheng/emr/web-socket/EmrChatBox.vue";
+import { emrMitt } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init";
+import { forceRefreshDialog } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/force-refresh-func";
+import { useUserStore } from "@/pinia/user-store";
+import { isDev } from "@/utils/public";
+import { CyMessageBox } from "@/components/cy/message-box";
 
 const props = defineProps<{
-  patInfo: {
-    inpatientNo: string,
-    admissTimes: string
-  } | any,
-  currentEditorUser: any
-}>()
-const userData = useUserStore().userInfo
-const roomRef = ref<HTMLElement | null>(null)
+  patInfo:
+    | {
+        inpatientNo: string;
+        admissTimes: string;
+      }
+    | any;
+  currentEditorUser: any;
+}>();
+const userData = useUserStore().userInfo;
+const roomRef = ref<HTMLElement | null>(null);
 
-const dialog = ref<boolean>(false)
-const errDialog = ref<boolean>(false)
-const userMap = ref<any>({})
-const dialogRef = ref(null)
+const dialog = ref<boolean>(false);
+const errDialog = ref<boolean>(false);
+const userMap = ref<any>({});
+const dialogRef = ref(null);
 
-const SOCKET_URL = import.meta.env.VITE_SOCKET_URL
+const SOCKET_URL = import.meta.env.VITE_SOCKET_URL;
 
-let webSocket = null
+let webSocket = null;
 
-
-const sid = ref(null)
+const sid = ref(null);
 
 const onmessageFunc = {
-  "connect": async (data) => {
-    await queryTheNumberOfPeopleInTheRoom()
-    xcMessage.success('连接成功')
+  connect: async data => {
+    await queryTheNumberOfPeopleInTheRoom();
+    xcMessage.success("连接成功");
   },
-  "connectToJoin": async (val) => {
-    await queryTheNumberOfPeopleInTheRoom()
-    let userInfo = userMap.value[val]
+  connectToJoin: async val => {
+    await queryTheNumberOfPeopleInTheRoom();
+    let userInfo = userMap.value[val];
     ElNotification({
-      type: 'success',
-      title: '有新的连接加入',
+      type: "success",
+      title: "有新的连接加入",
       dangerouslyUseHTMLString: true,
       message: `<span>姓名:${userInfo.name}</span>
                 <br>
-                <span>科室:${userInfo.deptName}</span>`
-    })
+                <span>科室:${userInfo.deptName}</span>`,
+    });
   },
-  "exitEmrEditor": async (val) => {
-    let userInfo = userMap.value[val]
+  exitEmrEditor: async val => {
+    let userInfo = userMap.value[val];
     ElNotification({
-      type: 'error',
-      title: '有连接退出',
+      type: "error",
+      title: "有连接退出",
       dangerouslyUseHTMLString: true,
       message: `<span>姓名:${userInfo.name}</span>
                 <br>
-                <span>科室:${userInfo.deptName}</span>`
-    })
-    await queryTheNumberOfPeopleInTheRoom()
+                <span>科室:${userInfo.deptName}</span>`,
+    });
+    await queryTheNumberOfPeopleInTheRoom();
   },
-  "message": async (val) => {
+  message: async val => {
     if (dialog.value) {
       dialogRef.value.queryJump();
     }
-    dialog.value = true
+    dialog.value = true;
   },
-  "notice": (val) => {
+  notice: val => {
     ElNotification({
-      type: 'success',
-      title: '查看或编辑',
-      message: val
-    })
+      type: "success",
+      title: "查看或编辑",
+      message: val,
+    });
   },
-  "closeSoctek": (val) => {
+  closeSoctek: val => {
     if (isDev) {
-      window.location.reload()
-      return
+      window.location.reload();
+      return;
     }
-    if (navigator.userAgent.indexOf("Firefox") !== -1 || navigator.userAgent.indexOf("Chrome") !== -1) {
+    if (
+      navigator.userAgent.indexOf("Firefox") !== -1 ||
+      navigator.userAgent.indexOf("Chrome") !== -1
+    ) {
       window.location.href = "about:blank";
       window.close();
     } else {
@@ -114,157 +129,151 @@ const onmessageFunc = {
       window.close();
     }
   },
-  "forceRefresh": (val) => {
-    emrMitt.emit('forceRefresh', val)
+  forceRefresh: val => {
+    emrMitt.emit("forceRefresh", val);
   },
-  "receivedMessage": (val) => {
-    forceRefreshDialog.value.message.push(val.message)
+  receivedMessage: val => {
+    forceRefreshDialog.value.message.push(val.message);
     if (val.flag && val.flag === 1) {
-      emrMitt.emit('closeForceRefreshDialog')
+      emrMitt.emit("closeForceRefreshDialog");
     }
-  }
-}
+  },
+};
 
 const userSize = computed(() => {
-  return Object.keys(userMap.value).length
-})
+  return Object.keys(userMap.value).length;
+});
 
 const userList = computed(() => {
-  return Object.values(userMap.value)
-})
+  return Object.values(userMap.value);
+});
 
 const queryTheNumberOfPeopleInTheRoom = async () => {
-  userMap.value = await getRoomPeople(sid.value) as any
-}
+  userMap.value = (await getRoomPeople(sid.value)) as any;
+};
 
 function initWebSocket(patNo, times) {
   if (stringIsBlank(patNo)) {
-    return null
+    return null;
   }
 
-  if ('WebSocket' in window) {
-    sid.value = 'emr_' + patNo.trim() + '_' + times;
-    const url = SOCKET_URL + sid.value + '?userCode=' + userData.code;
+  if ("WebSocket" in window) {
+    sid.value = "emr_" + patNo.trim() + "_" + times;
+    const url = SOCKET_URL + sid.value + "?userCode=" + userData.code;
     webSocket = new WebSocket(url);
   } else {
-    alert('该浏览器不支持websocket!');
-    webSocket = 'unsupport';
+    alert("该浏览器不支持websocket!");
+    webSocket = "unsupport";
   }
 
   webSocket.onopen = () => {
-    errDialog.value = false
-  }
-
+    errDialog.value = false;
+  };
 
   webSocket.onmessage = async function (e) {
-    let data = JSON.parse(e.data)
+    let data = JSON.parse(e.data);
     for (let key in data) {
-      onmessageFunc[key](data[key])
+      onmessageFunc[key](data[key]);
     }
-  }
+  };
 
-  webSocket.onclose = (e) => {
-    if (e.code === 1000 && e.reason === '服务器主动断开连接') {
-      documentSocket = null
-      webSocket = null
-      onmessageFunc.closeSoctek(e.reason)
-      return
+  webSocket.onclose = e => {
+    if (e.code === 1000 && e.reason === "服务器主动断开连接") {
+      documentSocket = null;
+      webSocket = null;
+      onmessageFunc.closeSoctek(e.reason);
+      return;
     }
-    errDialog.value = true
-    initWebSocket(props.patInfo.inpatientNo, props.patInfo.admissTimes)
-  }
-
-}
-
-const medicalRecordSwitching = async (name) => {
-  await sendAMessage(sid.value, "notice", name)
+    errDialog.value = true;
+    initWebSocket(props.patInfo.inpatientNo, props.patInfo.admissTimes);
+  };
 }
 
+const medicalRecordSwitching = async name => {
+  await sendAMessage(sid.value, "notice", name);
+};
 
-let documentSocket = null
-let documentSid = null
-const documentChange = (id) => {
-  documentSid = 'documentEmr_' + id + '_' + userData.code
+let documentSocket = null;
+let documentSid = null;
+const documentChange = id => {
+  documentSid = "documentEmr_" + id + "_" + userData.code;
   if (documentSocket != null) {
-    documentSocket.close()
-    documentSocket = null
+    documentSocket.close();
+    documentSocket = null;
   } else {
-    if ('WebSocket' in window) {
-      initDocumentSocket()
+    if ("WebSocket" in window) {
+      initDocumentSocket();
     } else {
-      alert('该浏览器不支持websocket!');
+      alert("该浏览器不支持websocket!");
       documentSocket = null;
     }
   }
-}
+};
 
 const initDocumentSocket = () => {
-  if (documentSid == null) return
-  let temp = SOCKET_URL + documentSid
+  if (documentSid == null) return;
+  let temp = SOCKET_URL + documentSid;
 
-  documentSocket = new WebSocket(temp)
+  documentSocket = new WebSocket(temp);
   documentSocket.onopen = () => {
-    console.log('连接成功')
-  }
+    console.log("连接成功");
+  };
 
   documentSocket.onmessage = function (e) {
-    let data = JSON.parse(e.data)
+    let data = JSON.parse(e.data);
     for (let key in data) {
-      onmessageFunc[key](data[key])
+      onmessageFunc[key](data[key]);
     }
-  }
+  };
 
-  documentSocket.onclose = (e) => {
-    if (e.code === 1000 && e.reason === '服务器主动断开连接') {
-      documentSocket = null
-      emrMitt.emit('setEditorReadonly');
+  documentSocket.onclose = e => {
+    if (e.code === 1000 && e.reason === "服务器主动断开连接") {
+      documentSocket = null;
+      emrMitt.emit("setEditorReadonly");
       CyMessageBox.alert({
-        message: '服务器主动断开连接,有医生把您踢下线'
-      })
-      return
+        message: "服务器主动断开连接,有医生把您踢下线",
+      });
+      return;
     }
-    initDocumentSocket()
-  }
-}
-
+    initDocumentSocket();
+  };
+};
 
 const clearDocument = () => {
   if (documentSocket != null) {
-    documentSocket.close()
-    documentSocket = null
-    documentSid = null
+    documentSocket.close();
+    documentSocket = null;
+    documentSid = null;
   }
-}
+};
 
 const clearSocket = () => {
   if (webSocket != null) {
-    webSocket.close()
-    webSocket = null
+    webSocket.close();
+    webSocket = null;
   }
-}
+};
 
 onMounted(async () => {
-  await nextTick()
-  initWebSocket(props.patInfo.inpatientNo, props.patInfo.admissTimes)
-  emrMitt.on('getDocumentSocket', () => {
-    console.log(documentSocket)
+  await nextTick();
+  initWebSocket(props.patInfo.inpatientNo, props.patInfo.admissTimes);
+  emrMitt.on("getDocumentSocket", () => {
     return documentSocket;
-  })
-  emrMitt.on('openChatRoom', () => {
-    dialog.value = true
-  })
-})
+  });
+  emrMitt.on("openChatRoom", () => {
+    dialog.value = true;
+  });
+});
 
 onBeforeUnmount(() => {
-  clearDocument()
-  clearSocket()
-})
+  clearDocument();
+  clearSocket();
+});
 
 defineExpose({
   medicalRecordSwitching,
   documentChange,
   clearDocument,
-  clearSocket
-})
-
+  clearSocket,
+});
 </script>

+ 10 - 5
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/EmrMain.vue

@@ -1069,11 +1069,6 @@ function generalMedicalRecords() {
 
 // 设置编辑器的模式
 const setEditorModeFun = () => {
-  // if (XEUtils.has(editor, "documentData.properties.thyysign")) {
-  //   setEditorModel("readonly");
-  //   return;
-  // }
-
   // 判断是不是编辑模式
   if (!emrConfig.value.editor) {
     setEditorModel("readonly");
@@ -1501,6 +1496,7 @@ const restoreDefaultSettings = () => {
  * @param param
  */
 const loadDocument = (param: EmrParam): Promise<void> => {
+  console.log(param);
   return new Promise(async (resolve, reject) => {
     const { loadDocument: load } = editMain;
     if (!showIframeIsList(IframeTabs.正在编辑, IframeTabs.同时打开)) {
@@ -1668,6 +1664,7 @@ const initEdit = () => {
       user: userInfo.code,
       name: userInfo.name,
     },
+    enableSignData: true,
     userCode: userInfo.code,
     login: {
       token: localStorage.token,
@@ -1700,6 +1697,14 @@ const initEdit = () => {
         documentId: "924629067488102400",
         emrPatientData: {},
       });
+      // loadDocument({
+      //   categoryCode: "ruyuanjiluzhuanyong",
+      //   categoryId: "e6723e80ed6511ed85a9691047891ea7",
+      //   name: "入院记录",
+      //   parent: "4959e2c054fd11edb28ac955a5f5cad1",
+      //   emrPatientData: {},
+      //   courseJumpId: null,
+      // });
     }
   });
 };

+ 20 - 28
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/components/GenerateSignature.vue

@@ -1,5 +1,6 @@
 <template>
   <el-form label-width="80px" label-position="top">
+    <el-alert type="warning" title="密码和授权码二选一" />
     <el-form-item label="工号:">
       <SystemStaffSelect style="width: 100%" v-model="code" />
     </el-form-item>
@@ -9,30 +10,20 @@
         @keyup.enter.stop.prevent="emits('cyDialogConfirm', null, false)"
       />
     </el-form-item>
+    <el-alert type="warning">
+      电子病历签名令牌在 企业微信 > 工具台 > 工作集成平台移动端 > 我的 <br />
+      电子病历签名令牌只作用与电子病历的签名,<br />
+      如果你不想把密码告诉别人又想别人可以生成你的签名可以使用这个功能
+    </el-alert>
+    <el-form-item label="电子病历签名令牌:">
+      <el-input
+        v-model="signCode"
+        @keyup.enter.stop.prevent="emits('cyDialogConfirm', null, false)"
+      />
+    </el-form-item>
     <el-form-item>
       <div style="text-align: right" class="layout_h-w_max">
-        <el-button
-          size="default"
-          type="primary"
-          @click="signUser(props.patientInfo.referPhysician)"
-          >管床医生
-        </el-button>
-        <el-button
-          size="default"
-          type="primary"
-          @click="signUser(props.patientInfo.consultPhysician)"
-          >主治医生
-        </el-button>
-        <el-button
-          size="default"
-          type="primary"
-          @click="signUser(props.patientInfo.deptDirector)"
-          >主任/副主任
-        </el-button>
-        <el-button
-          size="default"
-          type="primary"
-          @click="signUser(props.currentCode)"
+        <el-button size="default" type="primary" @click="signUser()"
           >生成自己签名
         </el-button>
       </div>
@@ -42,20 +33,20 @@
 <script setup lang="ts">
 import SystemStaffSelect from "@/components/system/staff-select/SystemStaffSelect.vue";
 import { magicApi } from "@/utils/database/magic-api-request";
+import { useUserStore } from "@/pinia/user-store";
 
-const props = defineProps<{
-  currentCode: string;
-  patientInfo: any;
-}>();
+const us = useUserStore().userInfo;
 
 const emits = defineEmits(["cyDialogConfirm"]);
 
 const code = ref("");
 const password = ref("");
+const signCode = ref("");
 
-function signUser(code: string) {
+function signUser() {
   emits("cyDialogConfirm", {
-    code,
+    code: us.code,
+    name: us.name,
   });
 }
 
@@ -66,6 +57,7 @@ async function confirmClick() {
     data: {
       code: code.value,
       password: password.value,
+      signCode: signCode.value,
     },
   });
 }

+ 141 - 65
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/EmrCAComp.vue

@@ -16,28 +16,18 @@ import { useUserStore } from "@/pinia/user-store";
 import { patientInfo } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init";
 import { stringIsBlank } from "@/utils/blank-utils";
 import { isDev } from "@/utils/public";
+import sleep from "@/utils/sleep";
 
 const { store } = inject(emrRootContextKey);
 const selectList = ref<SignComp[]>([]);
-const signTypeValue = ref(isDev ? 1 : null);
+const signOpinionList = ref<{ id: string; content: string }[]>([]);
+
+const signTypeValue = ref(null);
 const idCardPublicKey =
   "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgHPo86lSh7IzXcrgxi+7us2UeeY/O5ftBhk+P8hFfivGbS9RqCIDg+r7+nhINojsJeloeoavF0eHCMs1crzGd+XSMN6BjfPTm7bgoWu4CzplmFCBlZ5FAZCxXiHcHuBfG6tateaO2s7E1XpEsjSX/8J8VjKfIm7bzuNU/AyM2TwIDAQAB";
 
 const us = useUserStore().userInfo;
 
-/**
- * 扫码专用判断是否第一次扫码
- */
-const forTheFirstTime = computed(() => {
-  for (let i = 0; i < selectList.value.length; i++) {
-    const item = selectList.value[i];
-    if (item.complete) {
-      return false;
-    }
-  }
-  return true;
-});
-
 const changeSignType = val => {
   if (selectList.value.length === 0) {
     BizException(ExceptionEnum.MESSAGE_ERROR, "没有签名组件无法使用。");
@@ -64,30 +54,43 @@ function change() {
   const editor = store.getEditor();
 
   const data = getInternalByCode(editor, "患者CA签名", false);
-  if (!data) {
-    return;
+  if (data) {
+    selectList.value = data.map((item, index) => {
+      const name = "CA签名" + (index + 1);
+      const { view } = store.editFun.getViewById(item.id);
+
+      view.setValue(name);
+      editor.setViewStyle(view, { color: "#fff" });
+      view.setReadonly(true);
+      return {
+        content: name,
+        id: item.id,
+        signType: null,
+        idCard: null,
+        signName: "",
+        signTypeName: "",
+        emrSignOpinion: {
+          id: "",
+          content: "",
+        },
+        signOpinion: "",
+      };
+    });
+  }
+  const opinion = getInternalByCode(editor, "患者CA意见", false);
+  if (opinion) {
+    signOpinionList.value = opinion.map((item, index) => {
+      const { view } = store.editFun.getViewById(item.id);
+      const value = "患者CA意见签名处" + (index + 1);
+      view.setValue(value);
+      editor.setViewStyle(view, { color: "#fff" });
+      view.setReadonly(true);
+      return {
+        id: item.id,
+        content: value,
+      };
+    });
   }
-  selectList.value = data.map((item, index) => {
-    const value = XEUtils.toInteger(item.element.code.dataElement);
-    const name = "CA签名" + (index + 1);
-    const { view } = store.editFun.getViewById(item.id);
-
-    view.setValue(name);
-    editor.setViewStyle(view, { color: "#fff" });
-
-    return {
-      value,
-      content: name,
-      id: item.id,
-      signType: null,
-      idCard: null,
-      signName: "",
-      complete: false,
-      signTypeName: "",
-    };
-  });
-
-  store.store.isEditorChange = false;
 }
 
 function handleClick(id) {
@@ -98,15 +101,6 @@ const sendSignature = async () => {
   const editor = store.getEditor();
   const documentId = editor.documentData._id;
 
-  // const rst = await hBoardSignV2({
-  //   documentId,
-  //   content: value.content,
-  //   clear: forTheFirstTime.value,
-  //   name: value.signName,
-  //   idCard: XEUtils.encrypt(value.idCard, idCardPublicKey),
-  //   signType: value.signType,
-  // });
-
   useDialog(EmrCaSign, {
     dialogProps: {
       title: "签名",
@@ -115,21 +109,29 @@ const sendSignature = async () => {
     },
     params: {
       documentId,
+      data: selectList.value,
     },
-    showCancel: false,
-    showConfirm: false,
-  }).then(res => {
+    showFooter: false,
+  }).then(() => {
     completeSignature();
   });
 };
 
+onMounted(async () => {
+  if (!isDev) return;
+  await nextTick();
+  await sleep(1000);
+  signTypeValue.value = 1;
+  selectList.value.forEach((item, index) => {
+    item.signType = 1;
+    item.idCard = "430922200002078519";
+    item.signName = "肖蟾";
+  });
+
+  // sendClick();
+});
+
 const completeSignature = () => {
-  for (let i = 0; i < selectList.value.length; i++) {
-    const item = selectList.value[i];
-    if (item.complete === false) {
-      return;
-    }
-  }
   const editor = store.getEditor();
   const documentId = editor.documentData._id;
   completeQrCode(documentId);
@@ -174,10 +176,25 @@ const checkParams = () => {
       }
     });
   });
+
+  const group = XEUtils.countBy(
+    selectList.value,
+    item => item?.emrSignOpinion?.content || "none"
+  );
+
+  for (let key in group) {
+    if (key === "none") {
+      break;
+    }
+    const item = group[key];
+    if (item > 1) {
+      BizException(ExceptionEnum.MESSAGE_ERROR, "患者意见不能签署在同一位置。");
+    }
+  }
 };
 
 function sendClick() {
-  // checkParams();
+  checkParams();
   if (signTypeValue.value === 1) {
     sendSignature();
   }
@@ -225,6 +242,7 @@ function sendMoveSign() {
             return f.code === item.signType;
           })?.name;
         }
+        item.signOpinion = item.emrSignOpinion.content;
       });
       sendMoreEventSign({
         documentId: store.getEditor().documentData._id,
@@ -242,6 +260,38 @@ const signType = [
   { code: 3, name: "有线手写板" },
 ];
 
+const addCompByInternal = (name: string) => {
+  const editor = store.getEditor();
+  editor.execute("insertContents", {
+    value: [
+      {
+        type: "smarttext",
+        tips: name,
+        element: {
+          code: { business: "", internal: name, dataElement: "" },
+          name: name,
+          type: "element",
+        },
+      },
+    ],
+    isPuzzlepiece: true,
+    defaultValue: "",
+  });
+
+  change();
+};
+
+const changeSignOpinion = (i, v) => {
+  if (!v) {
+    return;
+  }
+  selectList.value.forEach((item, index) => {
+    if (index !== i) {
+      item.signOpinion = false;
+    }
+  });
+};
+
 defineExpose({
   change,
 });
@@ -266,23 +316,35 @@ defineExpose({
       <el-form label-position="top">
         <el-form-item label="签名方式">
           <el-select
+            style="width: 40%"
             :disabled="signTypeValue !== null"
             :model-value="signTypeValue"
             @update:modelValue="changeSignType"
           >
             <xc-el-option :data="signType" />
           </el-select>
+          <el-divider direction="vertical" />
+          <el-button @click="addCompByInternal('患者CA签名')"
+            >签名组件
+          </el-button>
+          <el-button @click="addCompByInternal('患者CA意见')"
+            >意见组件
+          </el-button>
         </el-form-item>
-        <el-form-item v-if="signTypeValue === 2">
-          <el-select v-model="moveSendCode" value-key="value">
+        <el-form-item>
+          <el-select
+            v-if="signTypeValue === 2"
+            v-model="moveSendCode"
+            style="width: 50%; margin-right: 6px"
+            value-key="value"
+          >
             <el-option
               v-for="item in sendCodeData"
               :value="item"
               :label="item.label"
             />
           </el-select>
-        </el-form-item>
-        <el-form-item>
+
           <el-button @click="sendClick" type="primary">发送</el-button>
         </el-form-item>
       </el-form>
@@ -291,7 +353,7 @@ defineExpose({
       <el-card
         shadow="hover"
         class="ca-sign_info"
-        v-for="item in selectList"
+        v-for="(item, index) in selectList"
         @click="handleClick(item.id)"
       >
         <template #header>
@@ -299,8 +361,8 @@ defineExpose({
             {{ item.content }}
           </div>
         </template>
-        <el-form label-position="top">
-          <el-form-item label="与患者的关系">
+        <el-form label-position="left">
+          <el-form-item label="关系">
             <el-select v-model="item.signType" style="width: 30%">
               <xc-el-option :data="selectData" />
             </el-select>
@@ -308,12 +370,26 @@ defineExpose({
               <template #prepend>补充</template>
             </el-input>
           </el-form-item>
-          <el-form-item label="签名人身份证">
+          <el-form-item label="身份证">
             <el-input v-model="item.idCard" />
           </el-form-item>
-          <el-form-item label="签名人姓名">
+          <el-form-item label="姓名">
             <el-input v-model="item.signName" />
           </el-form-item>
+          <el-form-item label="签名意见">
+            <el-select
+              v-model="item.emrSignOpinion"
+              value-key="id"
+              clearable
+              @change="() => handleClick(item.emrSignOpinion?.id)"
+            >
+              <el-option
+                v-for="sign in signOpinionList"
+                :value="sign"
+                :label="sign.content"
+              />
+            </el-select>
+          </el-form-item>
         </el-form>
       </el-card>
     </div>

+ 76 - 14
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/EmrCaSign.vue

@@ -1,27 +1,89 @@
-<script setup lang="ts">
-import { scanCodeVerification } from "@/api/ca/ca-api";
+<script setup lang="tsx">
+import { hBoardSignV2, scanCodeVerification } from "@/api/ca/ca-api";
+import CyDialogContainer from "@/components/cy/CyDialog/CyDialogContainer.vue";
+import XEUtils from "xe-utils";
+import { SignComp } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/emr-ca";
+import { stringIsBlank } from "@/utils/blank-utils";
+import { BizException, ExceptionEnum } from "@/utils/BizException";
+import { ElMessageBox } from "element-plus";
 
 const props = defineProps<{
   documentId: string;
-  teleport: string;
+  data: any[];
+  cyDialog: any;
 }>();
 
+const emits = defineEmits(["cyDialogCancel", "cyDialogConfirm"]);
+
+const idCardPublicKey =
+  "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCgHPo86lSh7IzXcrgxi+7us2UeeY/O5ftBhk+P8hFfivGbS9RqCIDg+r7+nhINojsJeloeoavF0eHCMs1crzGd+XSMN6BjfPTm7bgoWu4CzplmFCBlZ5FAZCxXiHcHuBfG6tateaO2s7E1XpEsjSX/8J8VjKfIm7bzuNU/AyM2TwIDAQAB";
 const url = ref("");
-const currentId = ref("");
+const clear = ref(true);
+const currentIndex = ref(0);
+
+const rst = ref({
+  result: "",
+  id: "",
+});
+
+const buttonMsg = ref("下一个");
+const send = async (value: SignComp) => {
+  if (currentIndex.value + 1 === props.data.length) {
+    buttonMsg.value = `完成`;
+  } else {
+    buttonMsg.value = `下一个 (${currentIndex.value + 1} / ${props.data.length})`;
+  }
+
+  rst.value = await hBoardSignV2({
+    documentId: props.documentId,
+    content: value.content,
+    clear: clear.value,
+    name: value.signName,
+    idCard: XEUtils.encrypt(value.idCard, idCardPublicKey),
+    signType: value.signType,
+    signOpinion: value?.emrSignOpinion?.content ?? null,
+  });
+  url.value = rst.value.result;
+  clear.value = false;
+};
+
+const confirm = async () => {
+  if (stringIsBlank(rst.value.id)) {
+    BizException(ExceptionEnum.MESSAGE_ERROR, "请先完成签名");
+  }
+  await scanCodeVerification(rst.value.id, props.documentId);
+  currentIndex.value++;
+
+  if (currentIndex.value === props.data.length) {
+    emits("cyDialogConfirm");
+  } else {
+    await send(props.data[currentIndex.value]);
+  }
+};
+
+const cancel = async () => {
+  await ElMessageBox.confirm("是否取消签名", "提示", {
+    type: "warning",
+  });
+  emits("cyDialogCancel");
+};
 
-defineExpose({
-  async confirm() {
-    return await scanCodeVerification(currentId.value, props.documentId);
-  },
+onMounted(async () => {
+  await nextTick();
+  await send(props.data[currentIndex.value]);
 });
 </script>
 
 <template>
-  <div class="layout_h-w_max">
+  <CyDialogContainer>
     <iframe :src="url" class="layout_full_iframe" />
-    <Teleport :to="`${props.teleport}_footer`">
-      <el-button>取消</el-button>
-      <el-button>下一个</el-button>
-    </Teleport>
-  </div>
+    <template #footer>
+      <el-button type="danger" size="default" @click="cancel"
+        >取消签名
+      </el-button>
+      <el-button type="primary" size="default" @click="confirm"
+        >{{ buttonMsg }}
+      </el-button>
+    </template>
+  </CyDialogContainer>
 </template>

+ 0 - 11
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/ca-sign-type/CaQr.vue

@@ -1,11 +0,0 @@
-<script setup lang="ts">
-import { SignComp } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/emr-ca";
-
-const props = defineProps<{
-  data: SignComp[];
-}>();
-</script>
-
-<template></template>
-
-<style lang="scss"></style>

+ 25 - 9
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/ca/emr-ca.tsx

@@ -111,21 +111,23 @@ const testInput = {
 };
 
 export type SignComp = {
-  value: number;
   id: string;
   signType: number | null;
   signTypeName: string;
   idCard: string | null;
   signName: string;
-  // 是否完成签名
-  complete: boolean;
   content: string;
+  emrSignOpinion: {
+    id: string;
+    content: string;
+  };
+  signOpinion: string;
 };
 
 export const selectData = [
   {
     code: 1,
-    name: "患者本人",
+    name: "本人",
   },
   {
     code: 2,
@@ -230,11 +232,6 @@ export function emrCa(store: EmrStore) {
       dialogProps: {
         title: "授权签名",
       },
-      // @ts-ignore
-      params: {
-        currentCode: us.code,
-        patientInfo,
-      },
     });
     const code = res.code;
     const msg = `由${us.name}发起签名,患者:【${patientInfo.name}】,性别:【${patientInfo.sexName}】,签名病历【${emrName}】`;
@@ -253,6 +250,24 @@ export function emrCa(store: EmrStore) {
     store.store.right = EmrRightTabs.ca;
   }
 
+  async function 老授权签名(_evt, view, eleInfo, { patientInfo, emrName }) {
+    if (eleInfo?.code?.internal !== "授权签名") {
+      return;
+    }
+    const res = await useDialog(GenerateSignature, {
+      dialogProps: {
+        title: "授权签名",
+      },
+    });
+    view.sign([
+      {
+        name: res.name,
+        code: res.code,
+        signature: `http://172.16.32.167:8077/doctorSignatureImage/${res.code}.png`,
+      },
+    ]);
+  }
+
   onMounted(() => {
     store.store.rightComp[0] = {
       name: EmrRightTabs.ca,
@@ -306,6 +321,7 @@ export function emrCa(store: EmrStore) {
     async componentClick(evt, view, eleInfo, value) {
       授权CA签名(evt, view, eleInfo, value).then(XEUtils.noop);
       患者CA签名(evt, view, eleInfo, value);
+      老授权签名(evt, view, eleInfo, value);
     },
     testFunc,
     loaded(editor: EditType) {

+ 1 - 0
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-func/emr-fun-utils.ts

@@ -16,6 +16,7 @@ const emrFunUtils = () => {
       return find[0];
     },
     jumpById(id: string) {
+      if (!id) return;
       const find = editor?.view?.container?.find(`#${id}`);
       if (XEUtils.isEmpty(find)) {
         return;