|
|
@@ -0,0 +1,463 @@
|
|
|
+import { useUserStore } from "@/pinia/user-store";
|
|
|
+import { xcMessage } from "@/utils/xiaochan-element-plus";
|
|
|
+import router from "@/router";
|
|
|
+import XEUtils from "xe-utils";
|
|
|
+import {
|
|
|
+ delFile,
|
|
|
+ getEmrModelByPatientId,
|
|
|
+ getRecycleBinMedicalRecords,
|
|
|
+ queryMzPatientInfo,
|
|
|
+ updateFile,
|
|
|
+} from "@/api/mz-emr/mz-emr";
|
|
|
+import { EditType, Runtime } from "@/utils/emr/edit";
|
|
|
+import {
|
|
|
+ getCurrentPersonnelInformation,
|
|
|
+ UseEmrInitReturn,
|
|
|
+} from "@/utils/emr/emr-init-v2";
|
|
|
+import MzEditorMain from "@/views/mz-emr/emr-v2/comp/MzEditorMain.vue";
|
|
|
+import { BizException, ExceptionEnum } from "@/utils/BizException";
|
|
|
+import { magicApi } from "@/utils/database/magic-api-request";
|
|
|
+import { ElMessageBox, ElNotification } from "element-plus";
|
|
|
+import { UseWebSocketReturn } from "@vueuse/core";
|
|
|
+import * as socketFun from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/socket/useEmrSocket";
|
|
|
+import { useCySocket } from "@/utils/useCySocket";
|
|
|
+import { useEmrCaSignUtils } from "@/views/mz-emr/emr-v2/useEmrCaSign";
|
|
|
+import { isDev } from "@/utils/public";
|
|
|
+import { useDialog } from "@/components/cy/CyDialog/index";
|
|
|
+
|
|
|
+interface PatientInfo {
|
|
|
+ patientId: string;
|
|
|
+ times: number;
|
|
|
+ userIdCode: string;
|
|
|
+ userName: string;
|
|
|
+ deptCode: string;
|
|
|
+ deptName: string;
|
|
|
+ name: string;
|
|
|
+ sex: string;
|
|
|
+ age: number;
|
|
|
+ birthDay: string;
|
|
|
+ socialNo: string;
|
|
|
+ type: null;
|
|
|
+}
|
|
|
+
|
|
|
+export enum TemplateTableValue {
|
|
|
+ 当前,
|
|
|
+ 历史,
|
|
|
+}
|
|
|
+
|
|
|
+export enum MzEmrTag {
|
|
|
+ 当前 = "当前",
|
|
|
+ 历史 = "只读",
|
|
|
+ 同时打开 = "同时打开",
|
|
|
+}
|
|
|
+
|
|
|
+export interface MzEmrPatientData {
|
|
|
+ id?: number;
|
|
|
+ patNo?: string;
|
|
|
+ times?: number;
|
|
|
+ emrDocumentId?: string;
|
|
|
+ emrCategoryCode?: string;
|
|
|
+ delFlag?: number;
|
|
|
+ emrName?: string;
|
|
|
+ name?: string;
|
|
|
+ createId?: string;
|
|
|
+ createDate?: Date | string;
|
|
|
+ modifyId?: string;
|
|
|
+ modifyDate?: Date | string;
|
|
|
+ submit?: number;
|
|
|
+ parent?: string;
|
|
|
+ referPhysician?: string;
|
|
|
+ consultPhysician?: string;
|
|
|
+ deptDirector?: string;
|
|
|
+ submitId?: string;
|
|
|
+ reviewDoctors?: string;
|
|
|
+ reviewTime?: Date | string;
|
|
|
+ submitTime?: Date | string;
|
|
|
+ sort?: number;
|
|
|
+ children?: MzEmrPatientData[];
|
|
|
+ documentData?: any; // or a more specific type if you know the structure
|
|
|
+ userIdCode?: string;
|
|
|
+}
|
|
|
+
|
|
|
+function queryEmrPatientInfo(store) {
|
|
|
+ function query() {
|
|
|
+ return magicApi({
|
|
|
+ url: "/mzEmrData/getMzEmrInfo",
|
|
|
+ method: "get",
|
|
|
+ params: {
|
|
|
+ patientId: store.patientInfo.patientId,
|
|
|
+ times: store.patientInfo.times,
|
|
|
+ },
|
|
|
+ })
|
|
|
+ .then(res => {
|
|
|
+ store.emrFillData = res;
|
|
|
+ store.emrFillData.currentEditorUser = getCurrentPersonnelInformation();
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ notice();
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ function notice() {
|
|
|
+ const tmp = ElNotification.error({
|
|
|
+ title: "获取患者信息失败",
|
|
|
+ duration: 0,
|
|
|
+ message: (
|
|
|
+ <div style={{ textAlign: "right" }}>
|
|
|
+ <el-button
|
|
|
+ type="primary"
|
|
|
+ onClick={() => {
|
|
|
+ tmp.close();
|
|
|
+ query();
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ 重试
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ ),
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ query,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+export const VueFile = {
|
|
|
+ // 患者的基本信息这一块
|
|
|
+ MzHeader: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/MzHeader.vue")
|
|
|
+ ),
|
|
|
+ // 主要的电子病历编辑这一块
|
|
|
+ MzEditorMain: MzEditorMain,
|
|
|
+ // 左侧边栏的病历模板这一块
|
|
|
+ MzEmrTemplate: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/aside/MzEmrTemplate.vue")
|
|
|
+ ),
|
|
|
+ // 左侧边栏的当前患者病历树这一块
|
|
|
+ MzPatientTree: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/aside/MzPatientTree.vue")
|
|
|
+ ),
|
|
|
+ // 添加患者电子病历的弹窗这一块
|
|
|
+ AddEmrDialog: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/aside/AddEmrDialog.vue")
|
|
|
+ ),
|
|
|
+ // 电子病历的快捷功能这一块
|
|
|
+ MzEmrFun: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/MzEmrFun.vue")
|
|
|
+ ),
|
|
|
+ // 一个只读的编辑器
|
|
|
+ MzEditorHistory: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/MzEditorHistory.vue")
|
|
|
+ ),
|
|
|
+ MzEmrMedicalRecords: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/MzEmrMedicalRecords.vue")
|
|
|
+ ),
|
|
|
+ MzHistoryTree: defineAsyncComponent(
|
|
|
+ () => import("@/views/mz-emr/emr-v2/comp/aside/MzHistoryTree.vue")
|
|
|
+ ),
|
|
|
+};
|
|
|
+
|
|
|
+const useEditorSocket = () => {
|
|
|
+ // @ts-ignore
|
|
|
+ let socket: UseWebSocketReturn<any> = { close: () => 0 };
|
|
|
+
|
|
|
+ const open = (id: string) => {
|
|
|
+ socket.close();
|
|
|
+ const sid = `${socketFun.URL}/mzEmrEditor/${id}`;
|
|
|
+
|
|
|
+ socket = useCySocket(sid, {
|
|
|
+ socketMsg: {},
|
|
|
+ setInfo: true,
|
|
|
+ onDisconnected(ws, event) {
|
|
|
+ if (event.code == 3001) {
|
|
|
+ const value = JSON.parse(event.reason);
|
|
|
+ ElMessageBox.alert(
|
|
|
+ `编辑者:【${value.name}】,科室:【${value.deptName}】正在编辑病历,请医生退出后,重新打开病历。`,
|
|
|
+ "提示",
|
|
|
+ {
|
|
|
+ type: "error",
|
|
|
+ }
|
|
|
+ );
|
|
|
+ }
|
|
|
+ },
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ return {
|
|
|
+ open,
|
|
|
+ getSocket: () => socket,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const useMzEmrStore = () => {
|
|
|
+ const store = reactive({
|
|
|
+ urlParams: {} as {
|
|
|
+ times: number;
|
|
|
+ patientId: string;
|
|
|
+ userIdCode: string;
|
|
|
+ },
|
|
|
+
|
|
|
+ isSave: false,
|
|
|
+
|
|
|
+ // 是否准备好了
|
|
|
+ prepare: false,
|
|
|
+
|
|
|
+ // 患者信息
|
|
|
+ patientInfo: {} as PatientInfo,
|
|
|
+
|
|
|
+ // 左侧边栏的值
|
|
|
+ templateTableValue: TemplateTableValue.当前 as TemplateTableValue,
|
|
|
+ // 电子病历的模板树状图
|
|
|
+ templateTree: [],
|
|
|
+
|
|
|
+ emrFillData: {} as {
|
|
|
+ extractData: any;
|
|
|
+ patientData: any;
|
|
|
+ currentEditorUser: any;
|
|
|
+ copy: boolean;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 当前编辑的病历参数
|
|
|
+ currentEditParams: {
|
|
|
+ emrDocumentId: null,
|
|
|
+ },
|
|
|
+
|
|
|
+ // 患者的病历
|
|
|
+ emrData: [],
|
|
|
+
|
|
|
+ // 电子病历的tag值
|
|
|
+ emrTag: MzEmrTag.当前,
|
|
|
+ });
|
|
|
+
|
|
|
+ const userInfo = useUserStore().userInfo;
|
|
|
+
|
|
|
+ let editor: EditType, runtime: Runtime, mainEdit: UseEmrInitReturn;
|
|
|
+ let historyEditor: EditType,
|
|
|
+ historyRuntime: Runtime,
|
|
|
+ historyMainEdit: UseEmrInitReturn;
|
|
|
+
|
|
|
+ const editSocket = useEditorSocket();
|
|
|
+
|
|
|
+ const caUtils = useEmrCaSignUtils();
|
|
|
+
|
|
|
+ const mutation = {
|
|
|
+ setEditor(e: EditType, r: Runtime, value: UseEmrInitReturn) {
|
|
|
+ editor = e;
|
|
|
+ runtime = r;
|
|
|
+ mainEdit = value;
|
|
|
+
|
|
|
+ caUtils.install(e);
|
|
|
+
|
|
|
+ editor!.setShortcutKey("CTRL+S", () => {
|
|
|
+ emrMutation.save();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ setHistoryEditor(e: EditType, r: Runtime, value: UseEmrInitReturn) {
|
|
|
+ historyEditor = e;
|
|
|
+ historyRuntime = r;
|
|
|
+ historyMainEdit = value;
|
|
|
+ },
|
|
|
+ async queryPatientEmr() {
|
|
|
+ const data = await getEmrModelByPatientId(
|
|
|
+ store.patientInfo.patientId,
|
|
|
+ store.patientInfo.times
|
|
|
+ );
|
|
|
+ store.emrData = data;
|
|
|
+ },
|
|
|
+ getFillData() {
|
|
|
+ const { copy, ...val } = store.emrFillData;
|
|
|
+ return XEUtils.assign(
|
|
|
+ {},
|
|
|
+ val.extractData,
|
|
|
+ val.patientData,
|
|
|
+ val.currentEditorUser
|
|
|
+ );
|
|
|
+ },
|
|
|
+ async openMedicalRecords() {
|
|
|
+ const data = await getRecycleBinMedicalRecords(
|
|
|
+ store.patientInfo.patientId,
|
|
|
+ store.patientInfo.times
|
|
|
+ );
|
|
|
+
|
|
|
+ await useDialog(VueFile.MzEmrMedicalRecords, {
|
|
|
+ dialogProps: { fullscreen: true, title: "恢复病历" },
|
|
|
+ params: { data },
|
|
|
+ });
|
|
|
+ mutation.queryPatientEmr();
|
|
|
+ },
|
|
|
+ };
|
|
|
+ const tmpEmrFun = {
|
|
|
+ setEmrData(value, forcedEditing = false) {
|
|
|
+ if (value.emrCategoryCode === "This is Dir") {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ if (!forcedEditing) {
|
|
|
+ if (value.createId !== userInfo.code) {
|
|
|
+ // xcMessage.warning(
|
|
|
+ // "当前病历的创建人不是您,如果想要编辑请右键编辑打开"
|
|
|
+ // );
|
|
|
+ xcMessage.warning("当前病历的创建人不是您,无法编辑病历");
|
|
|
+ this.readOnly(value);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ mainEdit
|
|
|
+ .loadAndSetDocument({
|
|
|
+ documentId: value.emrDocumentId,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ editor.setValues(mutation.getFillData(), false, true);
|
|
|
+ editSocket.open(value.emrDocumentId);
|
|
|
+ store.currentEditParams = value;
|
|
|
+ if (store.emrTag === MzEmrTag.历史) {
|
|
|
+ store.emrTag = MzEmrTag.同时打开;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ readOnly(value) {
|
|
|
+ historyEditor.setEditorMode("readonly");
|
|
|
+ historyMainEdit
|
|
|
+ .loadAndSetDocument({
|
|
|
+ documentId: value.emrDocumentId,
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ if (this.getId() == null) {
|
|
|
+ store.emrTag = MzEmrTag.历史;
|
|
|
+ } else {
|
|
|
+ store.emrTag = MzEmrTag.同时打开;
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ print(name) {
|
|
|
+ mainEdit.print(name);
|
|
|
+ },
|
|
|
+ getId() {
|
|
|
+ if (editor) {
|
|
|
+ return editor.documentData._id;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ },
|
|
|
+ async save() {
|
|
|
+ if (store.isSave) return;
|
|
|
+ try {
|
|
|
+ if (store.currentEditParams.emrDocumentId === null) {
|
|
|
+ BizException(ExceptionEnum.MESSAGE_ERROR, "请先选择病历");
|
|
|
+ }
|
|
|
+ if (editSocket.getSocket().status.value !== "OPEN") {
|
|
|
+ BizException(ExceptionEnum.MESSAGE_ERROR, "未连接服务器无法保存");
|
|
|
+ }
|
|
|
+ store.isSave = true;
|
|
|
+
|
|
|
+ try {
|
|
|
+ const validator = editor.getValidator();
|
|
|
+ const valid = validator.valid(true);
|
|
|
+ if (valid) {
|
|
|
+ xcMessage.error("校验未通过");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ } catch {}
|
|
|
+
|
|
|
+ await caUtils.save();
|
|
|
+
|
|
|
+ const id = this.getId();
|
|
|
+ if (id == null) {
|
|
|
+ BizException(ExceptionEnum.MESSAGE_ERROR, "id解析失败,请刷新页面");
|
|
|
+ }
|
|
|
+ await ElMessageBox.confirm("是否保存病历", "提示", {
|
|
|
+ type: "success",
|
|
|
+ });
|
|
|
+ const tmpSaveParams = {
|
|
|
+ emrDataElement: editor.getDataElements("business", false, true),
|
|
|
+ emrDocumentId: id,
|
|
|
+ document: { document: editor!.getDocument() },
|
|
|
+ };
|
|
|
+ updateFile(tmpSaveParams);
|
|
|
+ } finally {
|
|
|
+ store.isSave = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async del() {
|
|
|
+ if (store.currentEditParams.emrDocumentId === null) {
|
|
|
+ BizException(ExceptionEnum.MESSAGE_ERROR, "请先选择病历");
|
|
|
+ }
|
|
|
+ await ElMessageBox.confirm("是否删除病历,可在回收站找回", "提示", {
|
|
|
+ type: "error",
|
|
|
+ });
|
|
|
+ const id = this.getId();
|
|
|
+ delFile(id).then(res => {
|
|
|
+ mutation.queryPatientEmr();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ const emrEventFun = {
|
|
|
+ componentClick: (evt, view) => {
|
|
|
+ const eleInfo = view.getAttribute("element");
|
|
|
+ caUtils.componentClick(evt, view);
|
|
|
+ isDev && eleInfo && console.log(eleInfo);
|
|
|
+ },
|
|
|
+ };
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 代理病历的方法,防止病历还没有加载成功就执行了一些方法
|
|
|
+ */
|
|
|
+ const emrMutation = new Proxy(tmpEmrFun, {
|
|
|
+ get(target: typeof tmpEmrFun, prop: string | symbol, receiver: any) {
|
|
|
+ const original = target[prop];
|
|
|
+
|
|
|
+ // 如果不是函数,直接返回
|
|
|
+ if (typeof original !== "function") {
|
|
|
+ return original;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回一个函数,确保正确的this绑定
|
|
|
+ return function (...args: any[]) {
|
|
|
+ if (!editor) {
|
|
|
+ xcMessage.error("病历还没有准备好。");
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ // 使用apply确保正确的this上下文
|
|
|
+ return original.apply(tmpEmrFun, args);
|
|
|
+ };
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ const useQueryEmr = queryEmrPatientInfo(store);
|
|
|
+
|
|
|
+ onMounted(async () => {
|
|
|
+ await useUserStore().getUserInfo;
|
|
|
+ const userInfo = useUserStore().userInfo;
|
|
|
+ const url = XEUtils.parseUrl(window.location.href);
|
|
|
+
|
|
|
+ store.urlParams = JSON.parse(window.atob(url.searchQuery.params));
|
|
|
+
|
|
|
+ if (userInfo.code !== store.urlParams.userIdCode) {
|
|
|
+ const url = XEUtils.parseUrl(window.location.href);
|
|
|
+ xcMessage.error("账号不一致请重新登录");
|
|
|
+ localStorage.clear();
|
|
|
+ // @ts-ignore
|
|
|
+ await router.push(`/login?redirect=${url?.path}`);
|
|
|
+ return;
|
|
|
+ } else {
|
|
|
+ queryMzPatientInfo(store.urlParams).then(res => {
|
|
|
+ store.patientInfo = res;
|
|
|
+ store.prepare = true;
|
|
|
+
|
|
|
+ useQueryEmr.query();
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ return {
|
|
|
+ store,
|
|
|
+ mutation,
|
|
|
+ userInfo,
|
|
|
+ emrMutation,
|
|
|
+ emrEventFun,
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+export const useMzEmrStoreKey = "useMzEmrStoreKey";
|
|
|
+export type UseMzEmrStoreType = ReturnType<typeof useMzEmrStore>;
|