Ver código fonte

电子病历

DESKTOP-MINPJAU\Administrator 3 anos atrás
pai
commit
e0efa17780

+ 35 - 5
src/api/zhu-yuan-yi-sheng/emr-api.js

@@ -1,11 +1,41 @@
-import request from "@/utils/request";
+import axios from "axios";
+import {getTree} from "@/utils/tree-utils";
 
-let url = '/Emr/'
+let url = '/emr/runtime/api/v1/'
 
+const service = axios.create({
+    baseURL: url,
+    withCredentials: true,
+    timeout: 0,
+})
 
-export function getEmrTree() {
-    return request({
-        url: url + 'getEmrTree',
+service.interceptors.response.use(
+    (response) => {
+        if (response.status === 200) {
+            return response.data
+        } else {
+            return Promise.reject(response.message)
+        }
+    })
+
+export async function getEmrTree() {
+    let data = await service({
+        url: 'category/tree',
         method: 'get',
     })
+    return getTree(data, "_id", "parent")
+}
+
+export function getTemplate(code) {
+    return service({
+        url: `template/${code}`,
+        method: 'get',
+    })
+}
+
+export function getPatientsEmr(patNo) {
+    return service({
+        url: `document/docs/patient/${patNo}`,
+        method: 'GET'
+    })
 }

+ 60 - 0
src/components/zhu-yuan-yi-sheng/emr/EmrSelection.vue

@@ -0,0 +1,60 @@
+<template>
+  <el-select v-model="value" @change="selectionChange">
+    <el-option v-for="item in props.data"
+               :label="item[props.name]"
+               :value="item[props.code]"
+               :key="item[props.code]">
+      <span v-if="props.styles" :style="item[props.styles]">{{ item[props.name] }} </span>
+    </el-option>
+  </el-select>
+</template>
+
+<script setup name='EmrSelection'>
+const props = defineProps({
+  modelValue: {
+    type: String
+  },
+  data: {
+    type: Array
+  },
+  code: {
+    type: String,
+    default: 'code'
+  },
+  name: {
+    type: String,
+    default: 'name'
+  },
+  styles: {
+    type: String,
+  },
+  execute: {
+    type: String,
+  }
+})
+
+
+let value = $ref('')
+const emit = defineEmits(['change', 'update:modelValue'])
+
+const selectionChange = (val) => {
+  for (let a = 0, len = props.data.length; a < len; a++) {
+    let item = props.data[a];
+    if (item[props.code] === val) {
+      emit('update:modelValue', val)
+      emit('change', props.execute, val, item)
+      return
+    }
+  }
+
+
+}
+
+watch(() => props.modelValue, () => {
+  value = props.modelValue
+}, {deep: true, immediate: true})
+</script>
+
+<style scoped>
+
+</style>

+ 47 - 0
src/components/zhu-yuan-yi-sheng/emr/EmrSidebar.vue

@@ -0,0 +1,47 @@
+<template>
+  <el-radio-group v-model="templateType" @change="typeChange">
+    <el-radio-button :label="0">全院模板</el-radio-button>
+    <el-radio-button :label="1">科室模板</el-radio-button>
+    <el-radio-button :label="2">个人模板</el-radio-button>
+  </el-radio-group>
+  <el-tree :data="templateData"
+           :props="defaultProps"
+           @node-click="handleNodeClick"
+           node-key="_id"
+           default-expand-all/>
+</template>
+
+<script setup name='EmrSidebar'>
+import {getEmrTree} from "@/api/zhu-yuan-yi-sheng/emr-api";
+
+const emit = defineEmits(['nodeClick', 'typeChange'])
+
+let templateData = $ref([])
+const defaultProps = {
+  children: 'children',
+  label: 'name',
+}
+
+const templateType = $ref(0)
+
+const handleNodeClick = (val) => {
+  emit('nodeClick', val)
+}
+
+const typeChange = (val) => {
+  emit("typeChange", val)
+}
+
+onMounted(() => {
+  nextTick(() => {
+    getEmrTree().then((res) => {
+      templateData = res
+    })
+  })
+})
+
+</script>
+
+<style scoped>
+
+</style>

+ 7 - 3
src/icons/iconfont.css

@@ -1,8 +1,8 @@
 @font-face {
   font-family: "iconfont"; /* Project id 2473230 */
-  src: url('iconfont.woff2?t=1658306999852') format('woff2'),
-       url('iconfont.woff?t=1658306999852') format('woff'),
-       url('iconfont.ttf?t=1658306999852') format('truetype');
+  src: url('iconfont.woff2?t=1658450215671') format('woff2'),
+       url('iconfont.woff?t=1658450215671') format('woff'),
+       url('iconfont.ttf?t=1658450215671') format('truetype');
 }
 
 .iconfont {
@@ -13,6 +13,10 @@
   -moz-osx-font-smoothing: grayscale;
 }
 
+.icon-mobanweihu:before {
+  content: "\e6d6";
+}
+
 .icon-dianzibingli:before {
   content: "\e746";
 }

BIN
src/icons/iconfont.ttf


BIN
src/icons/iconfont.woff


BIN
src/icons/iconfont.woff2


+ 5 - 0
src/router/modules/dashboard.js

@@ -344,6 +344,11 @@ const route = [
                 component: createNameComponent(() => import('@/views/hospitalization/MakeDbzFees.vue')),
                 meta: {title: '单病种费用生成', icon: 'iconfont icon-danbingzhong'},
             },
+            {
+                path: 'emrEditor',
+                component: createNameComponent(() => import('@/views/hospitalization/EmrEditor.vue')),
+                meta: {title: '电子病历模板维护', icon: 'iconfont icon-mobanweihu'},
+            },
             {
                 path: 'advrsEvnt/fillAdverseEvent',
                 component: createNameComponent(() => import('@/views/hospitalization/adverse-event/FillAderverseEvent.vue')),

+ 25 - 0
src/utils/tree-utils.js

@@ -0,0 +1,25 @@
+export function getTree(data, id, parent) {
+    let map = {};
+    let tree = []
+    if (data) {
+        for (let i = 0; i < data.length; i++) {
+            let item = data[i]
+            if (item.parent === null) {
+                tree.push(item)
+            }
+            map[item[id]] = item
+        }
+        for (let i = 0; i < data.length; i++) {
+            let child = data[i];
+            let parentList = (map[child[parent]])
+
+            if (parentList) {
+                if (!parentList['children']) {
+                    parentList['children'] = []
+                }
+                parentList.children.push(child)
+            }
+        }
+    }
+    return tree
+}

+ 255 - 0
src/views/hospitalization/EmrEditor.vue

@@ -0,0 +1,255 @@
+<template>
+  <el-row style="height: 100%;padding: 0" v-loading="isLoading">
+
+    <el-col :span="4" style="background-color: white">
+      <el-col>
+        <emr-sidebar @nodeClick="handleNodeClick" @typeChange="typeChange"/>
+      </el-col>
+
+    </el-col>
+
+    <el-col :span="20">
+      <el-col>
+        <div style="height: 40px">
+          <emr-selection style="width: 120px" v-model="editorModify.editorTitle"
+                         execute="defaultStyleId"
+                         @change="selectionChange"
+                         :data="editorTitle" styles="styles"/>
+          <emr-selection v-model="editorModify.availableFonts" :data="editorData.availableFonts"
+                         @change="selectionChange"
+                         code="font"
+                         execute="fontFamily"
+                         styles="styles"
+                         name="text"/>
+          <emr-selection style="width: 120px"
+                         v-model="editorModify.fontSizes"
+                         execute="fontSize"
+                         @change="selectionChange" :data="editorData.fontSizes"/>
+        </div>
+      </el-col>
+
+      <el-col style="width: 100%;height: calc(100% - 50px);overflow-y: clip">
+        <el-row style="height: 100%">
+          <el-col :span="2">
+            as范德贝克哈设计好的呢
+          </el-col>
+
+          <el-col :span="17">
+            <iframe style="width: 100%;height: 100%" :src="iframeSrc" ref="iframeRef"/>
+          </el-col>
+
+          <el-col :span="5">
+            这里是存放数据源的
+          </el-col>
+        </el-row>
+
+
+      </el-col>
+
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+import {getTemplate} from "@/api/zhu-yuan-yi-sheng/emr-api";
+import store from "@/store";
+import {huanZheXinXi} from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
+import {ref} from 'vue'
+import EmrSidebar from "@/components/zhu-yuan-yi-sheng/emr/EmrSidebar.vue";
+import EmrSelection from "@/components/zhu-yuan-yi-sheng/emr/EmrSelection.vue";
+
+export default {
+  name: 'EmrEditor',
+  components: {EmrSelection, EmrSidebar},
+  setup() {
+    const iframeSrc = '/emr/runtime/#/editor'
+    const templateType = ref(0)
+
+    let emrInit = null
+    const iframeRef = ref(null)
+    const isLoading = ref(true)
+    const editorData = ref({})
+    const editorTitle = [
+      {
+        code: "global_style_text",
+        name: "正文",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 12pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title",
+        name: "标题",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 22pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_subtitle",
+        name: "副标题",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 20pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title_1",
+        name: "标题1",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 18pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title_2",
+        name: "标题2",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 16pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title_3",
+        name: "标题3",
+        styles: "font-family: &quot;Microsoft YaHei&quot; font-size: 14pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title_4",
+        name: "标题4",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 12pt; font-weight: normal;"
+      },
+      {
+        code: "global_style_title_5",
+        name: "标题5",
+        styles: "font-family: &quot;Microsoft YaHei&quot;; font-size: 11pt; font-weight: normal;"
+      }
+    ]
+    const editorModify = ref({
+      editorTitle: 'global_style_text',
+      availableFonts: 'SimSun',
+      fontSizes: '12pt'
+    })
+
+    const typeChange = (val) => {
+      console.log(val)
+    }
+
+    const handleNodeClick = (val) => {
+      getTemplate(val.code).then((res) => {
+        emrInit.editor.setDocument(res, true, true)
+      })
+    }
+
+
+    const getEditorData = () => {
+      let availableFonts = emrInit.editor.getAvailableFonts()
+      for (let i = 0, len = availableFonts.length; i < len; i++) {
+        availableFonts[i]['styles'] = `font-family:${availableFonts[i]['font']}`
+      }
+      dataAssignment('availableFonts', availableFonts)
+      dataAssignment('fontSizes', emrInit.editor.getFontSizes(), 'code')
+      console.log(editorData.value);
+    }
+
+    const dataAssignment = (name, data, code) => {
+      if (code) {
+        let keys = {}
+        let deduplication = []
+        for (let i = 0, len = data.length; i < len; i++) {
+          let item = data[i]
+          if (!keys[item[code]]) {
+            deduplication.push(item)
+            keys[item[code]] = item
+          }
+        }
+        editorData.value[name] = deduplication;
+      } else {
+        editorData.value[name] = data;
+      }
+    }
+
+    const selectionChange = (name, val, data) => {
+      console.log(name, val, data)
+      emrInit.editor.execute(name, {value: val})
+    }
+
+    onMounted(() => {
+      nextTick(() => {
+        emrInit = new EMRInteractive()
+        iframeRef.value.parentElement.emr = emrInit
+      })
+
+    })
+
+    return {
+      iframeSrc,
+      templateType,
+      handleNodeClick,
+      iframeRef,
+      isLoading,
+      editorModify,
+      typeChange,
+      editorData,
+      editorTitle,
+      selectionChange
+    }
+
+    function EMRInteractive(data) {
+      this.setEditor = (editor, runtime) => {
+        this.editor = editor;
+        this.runtime = runtime;
+
+        editor.on('loaded', function (event) {
+          getEditorData()
+          isLoading.value = false
+        })
+
+      };
+
+      this.getAppContext = () => {
+        return {
+          endpoints: {
+            app: "/bdp/dataservice/api",
+          },
+          input: {
+            user: store.state.user.info.code,
+            name: store.state.user.info.name
+          },
+          login: {
+            token: store.state.user.info.token
+          },
+          data
+        };
+      };
+
+      this.loadDocument = (result) => {
+        this.runtime.loadDocument(
+            result => {
+              console.log(result)
+            }
+        )
+      }
+
+      this.saveDocument = (cb) => {
+        let data = this.editor.getDocument();
+        data.properties.patientId = huanZheXinXi.value.inpatientNo;
+        data.properties.creator = store.state.user.info.code;
+        data.properties.createTime = new Date()
+        data.properties.modifier = store.state.user.info.code;
+        data.properties.modifyTime = new Date()
+        this.runtime.saveDocument(
+            data,
+            (res) => {
+              console.log("emr保存成功", res);
+            },
+            (err) => {
+              console.log("emr保存失败", err);
+            }
+        );
+      };
+      this.deleteDocument = (cb) => {
+        this.runtime.delDocument(documentId, (res) => {
+          console.log(res)
+        });
+      };
+      this.printDocument = (showPreview, docs = null) => {
+        console.log(showPreview, docs)
+        console.log('print document.')
+      };
+    }
+  }
+}
+
+
+</script>
+
+<style scoped>
+
+</style>

+ 72 - 0
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-init.js

@@ -0,0 +1,72 @@
+import store from "@/store";
+import {huanZheXinXi} from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
+import {ElMessage} from "element-plus";
+
+export function EMRInteractive(data) {
+    this.setEditor = (editor, runtime) => {
+        this.editor = editor;
+        this.runtime = runtime;
+    };
+
+    this.getAppContext = () => {
+        return {
+            endpoints: {
+                app: "/bdp/dataservice/api",
+            },
+            input: {
+                user: store.state.user.info.code,
+                name: store.state.user.info.name
+            },
+            login: {
+                token: store.state.user.info.token
+            },
+            data
+        };
+    };
+
+    this.saveDocument = (categoryCode) => {
+        let data = this.editor.getDocument();
+        data.properties.categoryCode = categoryCode
+        data.properties.patientId = huanZheXinXi.value.inpatientNo;
+        data.properties.creator = store.state.user.info.code;
+        data.properties.createTime = new Date()
+        data.properties.modifier = store.state.user.info.code;
+        data.properties.modifyTime = new Date()
+        this.runtime.saveDocument(
+            data,
+            (res) => {
+                // let validator = this.editor.getValidator();
+                console.log(this.editor)
+                // let valid = validator.valid();
+                // console.log(valid)
+                ElMessage.success("保存成功。")
+            },
+            (err) => {
+                ElMessage.success("emr保存失败," + err)
+            }
+        );
+
+    };
+
+    this.deleteDocument = (documentId) => {
+        this.runtime.delDocument(documentId,
+            (res) => {
+                ElMessage.success("删除成功")
+            },
+            (err) => {
+                ElMessage.success("删除失败" + err)
+            }
+        );
+    };
+
+    this.printDocument = (showPreview, docs = null) => {
+
+        this.editor && this.editor.execute("print", {
+
+            value: {
+                showPreview
+            }
+
+        })
+    };
+}

+ 73 - 46
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/index.vue

@@ -1,17 +1,31 @@
 <template>
   <el-container>
+
     <el-header>
-      <el-button type="primary" @click="testSave">testSave</el-button>
+      <el-button type="primary" @click="clickSaveData">testSave</el-button>
       <el-button type="primary" @click="testPrint">testPrint</el-button>
       <el-button type="primary" @click="testDelete">testDelete</el-button>
-      <el-button type="primary" @click="getAppContext">getAppContext</el-button>
     </el-header>
-    <el-main>
-      <div class="emr-iframe" name="emr">
-        <iframe id="emrIframe"
-                :src="caseHistoryUrl"/>
-      </div>
-    </el-main>
+
+    <el-container>
+
+      <el-aside>
+        <emr-sidebar></emr-sidebar>
+      </el-aside>
+
+      <el-main>
+
+        <div class="emr-iframe" name="emr">
+
+          <iframe id="emrIframe"
+                  :src="caseHistoryUrl"/>
+
+        </div>
+
+      </el-main>
+
+    </el-container>
+
   </el-container>
 </template>
 
@@ -20,60 +34,68 @@
 import {huanZheXinXi} from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
 import store from "@/store";
 import {uuid} from "@/utils/getUuid";
-import {getEmrTree} from "@/api/zhu-yuan-yi-sheng/emr-api";
 import {getEmrInpatientData} from "@/api/dictionary/emr-data-maintenance-api";
+import EmrSidebar from "@/components/zhu-yuan-yi-sheng/emr/EmrSidebar.vue";
+import {ElMessage} from "element-plus";
+import {getPatientsEmr} from "@/api/zhu-yuan-yi-sheng/emr-api";
 
 export default {
+  components: {EmrSidebar},
   setup() {
 
     const currentEmr = ref(null)
+
     const caseHistoryUrl = ref('')
 
-    const testSave = () => {
-      currentEmr.value.saveDocument(null);
+    let categoryCode = $ref('7e25f0c0070511edbc820dada413ba28')
+    let documentId = $ref('')
+
+    const clickSaveData = () => {
+      currentEmr.value.saveDocument(categoryCode);
     }
 
     const testPrint = () => {
-      currentEmr.value.printDocument(null);
+      currentEmr.value.printDocument(true, null);
     }
 
     const testDelete = () => {
-      currentEmr.value.deleteDocument(null);
-    }
-
-    const getAppContext = () => {
-      console.log(currentEmr.value.getAppContext());
+      currentEmr.value.deleteDocument(documentId);
     }
 
-
     const getCaseHistoryUrl = (documentId, categoryCode, categroyId, patientId) => {
       caseHistoryUrl.value = `/emr/runtime/?documentId=${documentId}&categoryCode=${categoryCode}&categroyId=${categroyId}&patientId=${patientId}#/`;
     }
+
     onMounted(() => {
+
       nextTick(async () => {
-        getCaseHistoryUrl('', '7e25f0c0070511edbc820dada413ba28', '', huanZheXinXi.value.inpatientNo)
+
+        getCaseHistoryUrl(documentId, categoryCode, '', huanZheXinXi.value.inpatientNo)
+
         let data = await getEmrInpatientData({
           patNo: huanZheXinXi.value.inpatientNo,
           times: huanZheXinXi.value.admissTimes
         })
+
         data['_id'] = uuid(31, 62)
         currentEmr.value = new EMRInteractive(data);
         const iframe = document.getElementById('emrIframe')
         iframe.parentElement.emr = currentEmr.value
 
-        getEmrTree().then((res) => {
+        // 获取患者已经存在的
+        getPatientsEmr(huanZheXinXi.value.inpatientNo).then((res) => {
           console.log(res)
         })
 
       })
+
     })
 
     return {
-      testSave,
+      clickSaveData,
       testPrint,
       testDelete,
       caseHistoryUrl,
-      getAppContext
     }
   }
 }
@@ -98,24 +120,12 @@ function EMRInteractive(data) {
         token: store.state.user.info.token
       },
       data
-      // data: {
-      //   dept: [
-      //     {
-      //       "code": "1020700",
-      //       "name": "内分泌科"
-      //     },
-      //     {
-      //       "code": "125125",
-      //       "name": "asfasdg"
-      //     }
-      //   ]
-      // }
     };
   };
 
-  this.saveDocument = (cb) => {
+  this.saveDocument = (categoryCode) => {
     let data = this.editor.getDocument();
-    data.properties.categoryCode = '3f3e2d6006ff11edbc820dada413ba28';
+    data.properties.categoryCode = categoryCode
     data.properties.patientId = huanZheXinXi.value.inpatientNo;
     data.properties.creator = store.state.user.info.code;
     data.properties.createTime = new Date()
@@ -124,22 +134,39 @@ function EMRInteractive(data) {
     this.runtime.saveDocument(
         data,
         (res) => {
-          console.log("emr保存成功", res);
+          // let validator = this.editor.getValidator();
+          console.log(this.editor)
+          // let valid = validator.valid();
+          // console.log(valid)
+          ElMessage.success("保存成功。")
         },
         (err) => {
-          console.log("emr保存失败", err);
+          ElMessage.success("emr保存失败," + err)
         }
     );
+
   };
-  this.deleteDocument = (cb) => {
-    let documentId = '3f3e2d6006ff11edbc820dada413ba28';
-    this.runtime.delDocument(documentId, (res) => {
-      console.log(res)
-    });
+
+  this.deleteDocument = (documentId) => {
+    this.runtime.delDocument(documentId,
+        (res) => {
+          ElMessage.success("删除成功")
+        },
+        (err) => {
+          ElMessage.success("删除失败" + err)
+        }
+    );
   };
+
   this.printDocument = (showPreview, docs = null) => {
-    console.log(showPreview, docs)
-    console.log('print document.')
+
+    this.editor && this.editor.execute("print", {
+
+      value: {
+        showPreview
+      }
+
+    })
   };
 }
 
@@ -150,4 +177,4 @@ function EMRInteractive(data) {
   width: 100%;
   height: 700px;
 }
-</style>
+</style>