Bladeren bron

修复电子病历模板,排序问题,和我的messageBox问题

xiaochan 2 jaren geleden
bovenliggende
commit
e9261ecd4c

+ 14 - 6
src/components/xiao-chan/cy-message-box/cy-message-box.ts

@@ -1,5 +1,5 @@
 // @ts-ignore
-import CyMessageBox from "./index.vue";
+import messageBox from "./index.vue";
 import {h, render} from "vue";
 import {uuid} from "../../../utils/getUuid";
 
@@ -18,12 +18,18 @@ interface CyMessageBox {
     inputMaxLength?: number,
     inputValidator?: (val: string) => boolean | string,
     inputErrorMessage?: string,
+    inputDefaultValue?: string,
     setClose?: (val: any) => void,
     inputRows?: number,
     boxId?: string,
     allowToBeEmpty?: boolean,
     dangerouslyUseHTMLString?: boolean,
-    title?: string
+    title?: string,
+    draggable?: boolean,
+    selectOption?: {
+        value: string,
+        label: string
+    }[]
 }
 
 export function getCyId(): string {
@@ -31,8 +37,9 @@ export function getCyId(): string {
 }
 
 function closeById(id: string) {
-    if (currentBox.has(id))
+    if (currentBox.has(id)) {
         currentBox.get(id)?.['close']()
+    }
 }
 
 function alert(message: string, type: 'success' | 'error' | 'warning' | 'info' = 'warning', config?: CyMessageBox): Promise<string> {
@@ -69,6 +76,7 @@ function renderFunc(message: string, type: 'success' | 'error' | 'warning' | 'in
     }
 
     const div = document.createElement('div');
+    div.style.setProperty('--cy-body-transform', 'translate(0px, 0px)')
     document.body.appendChild(div);
     div.id = withID ? config.boxId : getCyId();
 
@@ -97,16 +105,16 @@ function renderFunc(message: string, type: 'success' | 'error' | 'warning' | 'in
             type,
             setClose: (val: any) => {
                 div['close'] = val
-            }
+            },
         }
 
         Object.assign(data, config)
-        const vNode = h(CyMessageBox, data, () => null)
+        const vNode = h(messageBox, data, () => null)
         render(vNode, div)
     })
 }
 
-export const cyMessageBox = {
+export const CyMessageBox = {
     alert,
     confirm,
     prompt,

+ 97 - 37
src/components/xiao-chan/cy-message-box/index.vue

@@ -1,6 +1,6 @@
 <script lang="ts" setup>
-import {useZIndex, ElIcon, ElInput} from "element-plus";
-import {nextTick, onMounted, ref, h} from "vue";
+import {useZIndex, ElIcon, ElInput, ElSelect, ElOption} from "element-plus";
+import {nextTick, onMounted, ref, h, computed} from "vue";
 import sleep from "@/utils/sleep";
 // @ts-ignore
 import {
@@ -12,6 +12,7 @@ import {
 } from '@element-plus/icons-vue'
 import {getCyId} from "@/components/xiao-chan/cy-message-box/cy-message-box";
 import {useCompRef} from "@/utils/useCompRef";
+import {useCyDraggable} from "@/utils/use-cy-draggable";
 
 const props = withDefaults(defineProps<{
   message?: string;
@@ -26,27 +27,39 @@ const props = withDefaults(defineProps<{
   inputMaxLength?: number,
   inputValidator?: (val: string) => boolean | string,
   inputErrorMessage?: string,
+  inputDefaultValue?: string,
   setClose?: (val: any) => void,
   inputRows?: number,
   boxId?: string,
   allowToBeEmpty?: boolean,
   dangerouslyUseHTMLString?: boolean,
-  title?: string
+  title?: string,
+  draggable?: boolean,
+  selectOption?: {
+    value: string,
+    label: string
+  }[]
 }>(), {
   showCancel: true,
   confirmButtonText: '确认',
   cancelButtonText: '取消',
   inputErrorMessage: '校验未通过',
   allowToBeEmpty: false,
-  dangerouslyUseHTMLString: false
+  dangerouslyUseHTMLString: false,
+  draggable: true,
+  inputDefaultValue: '',
+  selectOption: null
 })
 
 const visible = ref(false)
 const headerRef = ref<HTMLHeadElement>(null)
-const inputValue = ref('')
+const inputValue = ref(props.inputDefaultValue)
 const errorMsg = ref('')
 const inputRef = useCompRef(ElInput)
-
+const confirmRef = ref<HTMLButtonElement>()
+const bodyRef = ref<HTMLDivElement>()
+const mainRef = ref<HTMLDivElement>()
+const isSelectInput = ref<boolean>(props.selectOption !== null)
 const inputId = ref(getCyId())
 
 let closeMode = ''
@@ -82,7 +95,12 @@ function onAfterLeave() {
  * 开启动画
  */
 function onAfterEnter() {
-  mainStyle.value.display = 'flex'
+  mainStyle.value.display = 'flex';
+  if (props.showInput && !isSelectInput.value) {
+    inputRef.value.focus();
+  } else {
+    confirmRef.value.focus()
+  }
 }
 
 function handleConfirm() {
@@ -131,6 +149,7 @@ function handlePrompt() {
 }
 
 function handleClose(val) {
+  bodyRef.value.style.transition = ''
   visible.value = false
   closeMode = val
 }
@@ -160,6 +179,25 @@ function titleRender() {
 }
 
 
+async function maskClick() {
+  const oldTransform = bodyRef.value.style.transform
+  bodyRef.value.style.transform = oldTransform + ' scale(1.1)';
+  await sleep(100)
+  bodyRef.value.style.transform = oldTransform;
+}
+
+const draggable = computed(() => props.draggable)
+
+useCyDraggable(bodyRef, headerRef, draggable,
+    () => {
+      bodyRef.value.style.transition = 'none'
+    }, () => {
+      mainRef.value.parentElement.style.setProperty('--cy-body-transform', bodyRef.value.style.transform)
+      bodyRef.value.style.transition = ''
+    }
+)
+
+
 onMounted(async () => {
   mainStyle.value.zIndex = nextZIndex()
   contentStyle.value.zIndex = nextZIndex()
@@ -168,21 +206,27 @@ onMounted(async () => {
   await sleep(100)
   visible.value = true
   props.setClose(close)
-  if (props.showInput) {
-    await inputRef.value.focus();
-  }
 })
 
 function close() {
   handleClose('close')
 }
-
 </script>
 
 <template>
-  <div class="cy-message-box_main" :style="mainStyle" role="dialog" aria-modal="true">
+  <div class="cy-message-box_main"
+       :style="mainStyle"
+       ref="mainRef"
+       role="dialog"
+       aria-modal="true"
+       @click.stop="maskClick">
     <transition name="cy-message_show" @after-enter="onAfterEnter" @after-leave="onAfterLeave">
-      <div class="cy-message-box_body" v-show="visible" :style="contentStyle">
+      <div class="cy-message-box_body"
+           tabindex="-1"
+           v-show="visible"
+           :style="contentStyle"
+           @click.stop
+           ref="bodyRef">
         <div class="cy-message-box_close" @click.stop="handleClose('close')">
           <ElIcon>
             <Close/>
@@ -210,7 +254,21 @@ function close() {
             </component>
           </div>
           <div v-if="props.showInput" style="margin-top: 9px">
+            <ElSelect v-model="inputValue"
+                      v-if="isSelectInput"
+                      :id="inputId"
+                      style="width: 100%"
+                      :multiple="false"
+                      :filterable="true"
+                      :allow-create="true"
+                      :default-first-option="true"
+                      :reserve-keyword="false"
+                      ref="inputRef">
+              <ElOption v-for="item in props.selectOption" :key="item.value" :label="item.label" :value="item.value"/>
+            </ElSelect>
+
             <ElInput
+                v-else
                 ref="inputRef"
                 :autofocus="true"
                 :rows="props.inputRows"
@@ -226,16 +284,19 @@ function close() {
           </div>
         </div>
         <footer>
-          <div class="cancel"
-               v-if="props.showCancel"
-               @click="handleClose('cancel')"
-               :style="{'--cy--bg-color': '245,108,108','--cy-color': '#F56C6C'}">
+          <button class="cancel"
+                  style="margin-right: 12px"
+                  v-if="props.showCancel"
+                  @click="handleClose('cancel')"
+                  :style="{'--cy--bg-color': '245,108,108','--cy-color': '#F56C6C'}">
             {{ props.cancelButtonText }}
-          </div>
-          <div class="confirm" :style="{'--cy--bg-color': '26, 92, 255','--cy-color': '#2C69FF'}"
-               @click="handleConfirm">
+          </button>
+          <button class="confirm"
+                  ref="confirmRef"
+                  :style="{'--cy--bg-color': '26, 92, 255','--cy-color': '#2C69FF'}"
+                  @click="handleConfirm">
             {{ props.confirmButtonText }}
-          </div>
+          </button>
         </footer>
       </div>
     </transition>
@@ -253,27 +314,23 @@ function close() {
 }
 
 .cy-message_show-leave-active {
-  transform: scale(1);
+  transform: var(--cy-body-transform) scale(1) !important;
 }
 
 .cy-message_show-leave-to {
-  transform: scale(0);
+  transform: var(--cy-body-transform) scale(0) !important;
 }
 
 .cy-message-box_main {
-  background: rgba(0, 0, 0, .2);
+  background: rgba(0, 0, 0, .5);
   position: fixed;
   top: 0;
   left: 0;
   width: 100%;
   height: 100%;
 
-  justify-content: center;
-  align-content: center;
-  -webkit-backdrop-filter: saturate(180%) blur(15px);
-  backdrop-filter: saturate(180%) blur(15px);
-
   .cy-message-box_body {
+    display: inline-block;
     height: max-content;
     width: 418px;
     margin: auto;
@@ -309,6 +366,7 @@ function close() {
     }
 
     header {
+      cursor: move;
       user-select: none;
       display: -webkit-box;
       display: -ms-flexbox;
@@ -385,10 +443,12 @@ function close() {
       display: flex;
       align-items: center;
       justify-content: flex-end;
+      padding: 0 16px 10px 16px;
 
-      div {
-        width: 85px;
-        line-height: 39px;
+      button {
+        border: 0;
+        width: 60px;
+        line-height: 28px;
         text-align: center;
         border-radius: 12px;
         cursor: pointer;
@@ -397,13 +457,15 @@ function close() {
         outline: none;
         list-style: none;
         user-select: none;
+        background: transparent;
 
         &:hover:before {
-          opacity: 1;
-          transform: scale(1);
+          transform: scale(1.3);
         }
 
         &:before {
+          opacity: 1;
+          transform: scale(1);
           content: "";
           background: rgba(var(--cy--bg-color), .1);
           position: absolute;
@@ -411,13 +473,11 @@ function close() {
           left: 0;
           width: 100%;
           height: 100%;
-          border-radius: inherit;
+          border-radius: 8px;
           pointer-events: none;
           -webkit-transition: all .25s ease;
           transition: all .25s ease;
           z-index: -1;
-          transform: scale(.5);
-          opacity: 0;
           box-sizing: border-box;
         }
       }

+ 2 - 1
src/components/zhu-yuan-yi-sheng/emr/emr-template/EmrSidebar.vue

@@ -127,7 +127,8 @@ const template = async (val) => {
     categoryId: val._id,
     patientId: patientInfo.value.inpatientNo + '_' + patientInfo.value.admissTimes,
     templateName: val.name,
-    createId: null
+    createId: null,
+    parent: val.parent
   })
 }
 

+ 0 - 1
src/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/table/YzTableV2.vue

@@ -13,7 +13,6 @@
                  :row-class="rowClass"
                  :columns="header"/>
   </div>
-
   <right-click-menu :mouse-position="mousePosition" :config="opt"/>
 </template>
 

+ 5 - 3
src/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/yz-edit/YzEditor.vue

@@ -299,7 +299,8 @@ import {
   onMounted
 } from 'vue'
 import {userInfoStore} from "@/utils/store-public";
-import {formatDateToStr, getServerDate} from "@/utils/moment-utils";
+import {getServerDate} from "@/utils/moment-utils";
+import {CyMessageBox} from "@/components/xiao-chan/cy-message-box/cy-message-box";
 
 const props = withDefaults(defineProps<{
   patientInfo: {
@@ -1129,8 +1130,8 @@ onMounted(() => {
     if (stringIsBlank(actOrderNo)) {
       BizException(ExceptionEnum.MESSAGE_ERROR, '请先选择要删除的医嘱')
     }
-    ElMessageBox.confirm(`确认是否要删除<span style="color: red"> ${orderName} </span>`, '提示', {
-      type: 'warning',
+
+    CyMessageBox.confirm(`确认是否要删除<span style="color: red"> ${orderName} </span>`, 'warning', {
       dangerouslyUseHTMLString: true
     }).then(() => {
       toDeleteAnOrder(actOrderNo).then(() => {
@@ -1142,6 +1143,7 @@ onMounted(() => {
     }).catch(() => {
 
     })
+
   })
 })
 

+ 107 - 0
src/utils/use-cy-draggable.ts

@@ -0,0 +1,107 @@
+import {onBeforeUnmount, onMounted, watchEffect} from 'vue'
+import type {ComputedRef, Ref} from 'vue'
+import {isNumber, isString} from "xe-utils";
+
+const isStringNumber = (val: string): boolean => {
+    if (!isString(val)) {
+        return false
+    }
+    return !Number.isNaN(Number(val))
+}
+
+export function addUnit(value?: string | number, defaultUnit = 'px') {
+    if (!value) return ''
+    if (isNumber(value) || isStringNumber(value)) {
+        return `${value}${defaultUnit}`
+    } else if (isString(value)) {
+        return value
+    }
+}
+
+export const useCyDraggable = (
+    targetRef: Ref<HTMLElement | undefined>,
+    dragRef: Ref<HTMLElement | undefined>,
+    draggable: ComputedRef<boolean>,
+    startMove?: () => void,
+    endMove?: () => void
+) => {
+    let transform = {
+        offsetX: 0,
+        offsetY: 0,
+    }
+
+    const onMousedown = (e: MouseEvent) => {
+        const downX = e.clientX
+        const downY = e.clientY
+        const {offsetX, offsetY} = transform
+
+        const targetRect = targetRef.value!.getBoundingClientRect()
+        const targetLeft = targetRect.left
+        const targetTop = targetRect.top
+        const targetWidth = targetRect.width
+        const targetHeight = targetRect.height
+
+        const clientWidth = document.documentElement.clientWidth
+        const clientHeight = document.documentElement.clientHeight
+
+        const minLeft = -targetLeft + offsetX
+        const minTop = -targetTop + offsetY
+        const maxLeft = clientWidth - targetLeft - targetWidth + offsetX
+        const maxTop = clientHeight - targetTop - targetHeight + offsetY
+
+        const onMousemove = (e: MouseEvent) => {
+            startMove && startMove()
+            const moveX = Math.min(
+                Math.max(offsetX + e.clientX - downX, minLeft),
+                maxLeft
+            )
+            const moveY = Math.min(
+                Math.max(offsetY + e.clientY - downY, minTop),
+                maxTop
+            )
+
+            transform = {
+                offsetX: moveX,
+                offsetY: moveY,
+            }
+            targetRef.value!.style.transform = `translate(${addUnit(
+                moveX
+            )}, ${addUnit(moveY)})`
+        }
+
+        const onMouseup = () => {
+            endMove && endMove()
+            document.removeEventListener('mousemove', onMousemove)
+            document.removeEventListener('mouseup', onMouseup)
+        }
+
+        document.addEventListener('mousemove', onMousemove)
+        document.addEventListener('mouseup', onMouseup)
+    }
+
+    const onDraggable = () => {
+        if (dragRef.value && targetRef.value) {
+            dragRef.value.addEventListener('mousedown', onMousedown)
+        }
+    }
+
+    const offDraggable = () => {
+        if (dragRef.value && targetRef.value) {
+            dragRef.value.removeEventListener('mousedown', onMousedown)
+        }
+    }
+
+    onMounted(() => {
+        watchEffect(() => {
+            if (draggable.value) {
+                onDraggable()
+            } else {
+                offDraggable()
+            }
+        })
+    })
+
+    onBeforeUnmount(() => {
+        offDraggable()
+    })
+}

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

@@ -809,7 +809,6 @@ const clickSaveData = async () => {
     emrDataElement: null,
     documentData: null
   }
-
   data.emrDataElement = editor.getDataElements('business')
 
   objectValuesCannotBeNull(data);
@@ -1619,6 +1618,7 @@ const loadDocument = (param: EmrParam): Promise<void> => {
         templateName.value = param.templateName
         categoryCode.value = param.categoryCode
         createId = param.createId
+        parent = param.parent;
         createName.value = param.createName
         editor.setDocument(res, true);
         fragmentOrOutline.value = isCourse() ? 1 : 2

+ 0 - 1
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/components/EmrSaveRules.vue

@@ -4,7 +4,6 @@ import {useDraggable} from '@vueuse/core'
 import {
   emrMitt,
 } from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init";
-import {EditType} from "@/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/edit";
 
 interface Item {
   name: string;

+ 1 - 0
src/views/hospitalization/zhu-yuan-yi-sheng/electronic-medical-record/emr-editor/emr-init.ts

@@ -344,6 +344,7 @@ export interface EmrParam extends LoadParams {
     // 创建人姓名
     createName?: string,
     trueCreationTime?: string
+    parent: string
 }
 
 export interface Audit {

+ 61 - 15
src/views/hospitalization/zhu-yuan-yi-sheng/yi-zhu-lu-ru/YiZhuLuRu.vue

@@ -89,6 +89,7 @@ import {applicationForRevocation} from "@/api/zhu-yuan-yi-sheng/qrder-quash";
 import XEUtils from 'xe-utils'
 import {isDev} from "@/utils/public";
 import {ref, onMounted, onActivated, nextTick} from 'vue'
+import {CyMessageBox} from "@/components/xiao-chan/cy-message-box/cy-message-box";
 
 let allergen = ref({
   dialog: false,
@@ -168,27 +169,34 @@ const orderQuash = async (val) => {
     BizException(ExceptionEnum.LOGICAL_ERROR, "录入医嘱无需撤销删除即可。");
   }
 
-  ElMessageBox.prompt(`
+  CyMessageBox.prompt(`
 申请撤销<span style="color: red">【${val.orderName}】</span>医嘱<br />
 可在数据修改 》 医嘱撤销审核页面中查看到自己的申请信息。<br />
 <span style="color:red">作废医嘱不会退费,需要在项目录入中退费,如果是药品先撤销后退费的话,会导致无法生成退药单,
 只能仅退费,停止医嘱会产生退费,作废前可以先进行停止操作看费用会不会退掉,如果停止医嘱没有产生退费可以让护士在项目录入中退费,退费前请记得做费用接收重算,避免重复退费。</span><br />
 <span style="color: #0900ff">医嘱未超过24小时,未产生任何费用,会直接撤销无需审核,其他需要医务部审核通过后才能撤销。</span>
-`, '提示', {
-    type: 'warning',
-    confirmButtonText: '确定',
-    cancelButtonText: '取消',
-    inputValidator: (val) => {
-      val = val.trim();
-      if (val === null || val.length < 1 || val.length > 50) {
-        return false;
-      }
-    },
+`, 'warning', {
     dangerouslyUseHTMLString: true,
-    inputErrorMessage: '作废原因,不能为空,最多可输入20个字。',
-    closeOnPressEscape: false,
-    closeOnClickModal: false
-  }).then(async ({value}) => {
+    inputDefaultValue: '医嘱开错',
+    selectOption: [
+      {value: '医嘱开错', label: '医嘱开错'},
+      {value: '未充分告知患者', label: '未充分告知患者'},
+      {value: '临时出院', label: '临时出院'},
+      {value: '患者拒绝用药', label: '患者拒绝用药'},
+      {value: '药房缺药', label: '药房缺药'},
+      {value: '药物不耐受或过敏', label: '药物不耐受或过敏'},
+      {value: '药品更换厂家或规格', label: '药品更换厂家或规格'},
+      {value: '转科或者转院', label: '转科或者转院'},
+      {value: '病情变化', label: '病情变化'},
+      {value: '患者欠费未生成药单', label: '患者欠费未生成药单'},
+    ],
+    inputValidator: (val: string) => {
+      if (val.length > 50) {
+        return '字数不得超过50个字。'
+      }
+      return true
+    }
+  }).then(async (value) => {
     let res = await applicationForRevocation({
       actOrderNo: val.actOrderNo,
       reqRemark: value,
@@ -202,7 +210,45 @@ const orderQuash = async (val) => {
       })
       setYzOrderGroup();
     }
+  }).catch(() => {
+
   })
+
+//   ElMessageBox.prompt(`
+// 申请撤销<span style="color: red">【${val.orderName}】</span>医嘱<br />
+// 可在数据修改 》 医嘱撤销审核页面中查看到自己的申请信息。<br />
+// <span style="color:red">作废医嘱不会退费,需要在项目录入中退费,如果是药品先撤销后退费的话,会导致无法生成退药单,
+// 只能仅退费,停止医嘱会产生退费,作废前可以先进行停止操作看费用会不会退掉,如果停止医嘱没有产生退费可以让护士在项目录入中退费,退费前请记得做费用接收重算,避免重复退费。</span><br />
+// <span style="color: #0900ff">医嘱未超过24小时,未产生任何费用,会直接撤销无需审核,其他需要医务部审核通过后才能撤销。</span>
+// `, '提示', {
+//     type: 'warning',
+//     confirmButtonText: '确定',
+//     cancelButtonText: '取消',
+//     inputValidator: (val) => {
+//       val = val.trim();
+//       if (val === null || val.length < 1 || val.length > 50) {
+//         return false;
+//       }
+//     },
+//     dangerouslyUseHTMLString: true,
+//     inputErrorMessage: '作废原因,不能为空,最多可输入20个字。',
+//     closeOnPressEscape: false,
+//     closeOnClickModal: false
+//   }).then(async ({value}) => {
+//     let res = await applicationForRevocation({
+//       actOrderNo: val.actOrderNo,
+//       reqRemark: value,
+//       patNo: val.inpatientNo,
+//       times: val.admissTimes
+//     });
+//     // res === 1的意思是这个医嘱被删除了
+//     if (res === 1) {
+//       XEUtils.remove(yzData.value, (item) => {
+//         return item.actOrderNo === val.actOrderNo;
+//       })
+//       setYzOrderGroup();
+//     }
+//   })
 }
 
 /**