Explorar o código

住院医生优化

DESKTOP-0GD05B0\Administrator %!s(int64=2) %!d(string=hai) anos
pai
achega
41991c759e

+ 4 - 0
.env.dev

@@ -0,0 +1,4 @@
+ENV = 'dev'
+
+VITE_BASE_URL = 'http://172.16.30.140:8706'
+VITE_SOCKET_URL = 'ws://172.16.30.140:8706/websocket/'

+ 1 - 0
package.json

@@ -6,6 +6,7 @@
     "start": "vite",
     "build": "vite build --mode=production",
     "build:stag": "vite build --mode=staging",
+    "build:dev": "vite build --mode=dev",
     "serve": "vite preview"
   },
   "dependencies": {

+ 285 - 0
src/components/xiao-chan/combo-grid/XcComboGrid.vue

@@ -0,0 +1,285 @@
+<template>
+  <div class="combo_grid">
+    <el-popover placement="bottom"
+                :width="tableConfig.width"
+                trigger="manual"
+                :visible="tableConfig.isShow">
+      <template #reference>
+        <el-input
+            ref="inputRef"
+            :placeholder="placeholder"
+            @input="queryData"
+            v-model="inputData"
+            @focus="inputFocus"
+            @click="clickInput"
+            @blur="inputBlur"
+            @keydown.enter.prevent="keyEnter"
+            @mousedown.stop
+            @keydown.up.prevent="keyUp"
+            @keydown.down.prevent="keyDown"
+            @keydown.left.prevent="keyLeft"
+            @keydown.right.prevent="keyRight"
+            :clearable="clearable"/>
+      </template>
+      <div class="table-container"
+           @mousedown.prevent>
+        <el-table :data="tempTableList"
+                  ref="tableRef"
+                  @row-click="clickRow">
+          <el-table-column v-for="item in tableHeader"
+                           :label="item.label"
+                           show-overflow-tooltip
+                           :prop="item.prop"
+          ></el-table-column>
+          <slot/>
+        </el-table>
+        <el-pagination
+            small
+            background
+            :pager-count="5"
+            :total="tableData.total"
+            :current-page="tableData.currentPage"
+            :page-size="tableData.pageSize"
+            @size-change="handleSizeChange"
+            layout="total,prev, pager, next, jumper"
+            @current-change="handleCurrentChange"/>
+      </div>
+    </el-popover>
+  </div>
+
+</template>
+
+<script setup name='XcComboGrid'>
+import {debounce} from "@/utils/debounce";
+
+const props = defineProps({
+  modelValue: {
+    type: String,
+  },
+  rowKey: {
+    type: String,
+    default: 'id'
+  },
+  currentKey: {
+    type: String,
+    default: null
+  },
+  data: {
+    type: Array,
+    default: []
+  },
+  clearable: {
+    type: Boolean,
+    default: false
+  },
+  queryDataFunc: {
+    type: Function,
+    default: null
+  },
+  tableHeader: {
+    type: Array,
+    default: []
+  },
+})
+
+const emit = defineEmits(['input', 'rowClick'])
+
+const placeholder = ref('')
+const inputData = ref('')
+const tableData = ref({
+  list: [],
+  currentPage: 1,
+  pageSize: 5,
+  total: 0,
+})
+
+const tableConfig = ref({
+  isLoad: false,
+  isShow: false,
+  width: 0
+})
+
+const queryData = debounce((value) => {
+  emit('input', value)
+  tableConfig.value.isLoad = true
+  if (props.queryDataFunc === null) return
+  props.queryDataFunc(value).then((res) => {
+    tableConfig.value.isShow = true
+    tableData.value.list = res
+    tableData.value.total = tableData.value.list.length
+    theNumberOfPages = Math.ceil(tableData.value.total / tableData.value.pageSize)
+    currentIndex = 0
+    rowAddClass()
+  }).finally((res) => {
+    tableConfig.value.isLoad = false
+  })
+
+}, 200)
+
+let currentIndex = 0;
+
+const tableRef = ref(null)
+
+const keyUp = () => {
+  if (tableConfig.value.isShow) {
+    currentIndex--;
+    if (currentIndex < 0) {
+      currentIndex = tableData.value.pageSize - 1
+    }
+    rowAddClass()
+  }
+}
+
+const keyDown = () => {
+  if (tableConfig.value.isShow) {
+    currentIndex++;
+    if (currentIndex > tableData.value.pageSize - 1) {
+      currentIndex = 0
+    }
+    rowAddClass()
+  }
+}
+
+const keyEnter = () => {
+  if (tableConfig.value.isShow) {
+    clickRow(tempTableList.value[currentIndex])
+  } else {
+    clickInput()
+  }
+}
+
+let theNumberOfPages = 0
+const keyLeft = () => {
+  if (!tableConfig.value.isShow) return
+  if (tableData.value.currentPage - 1 < 1) {
+    tableData.value.currentPage = theNumberOfPages
+  } else {
+    tableData.value.currentPage--
+  }
+}
+
+const keyRight = () => {
+  if (!tableConfig.value.isShow) return
+  if (tableData.value.currentPage + 1 > theNumberOfPages) {
+    tableData.value.currentPage = 1
+  } else {
+    tableData.value.currentPage++
+  }
+}
+
+const rowAddClass = async () => {
+  if (!tableConfig.value.isShow) {
+    return
+  }
+  await nextTick()
+  let row = tableRef.value.$el.querySelectorAll('.el-table__row')
+  for (let i = 0; i < row.length; i++) {
+    let item = row[i]
+    item.classList.remove('selected')
+    if (i === currentIndex) {
+      item.classList.add('selected')
+    }
+  }
+}
+
+const inputFocus = () => {
+  placeholder.value = props.modelValue
+  inputData.value = ''
+}
+
+const clickInput = () => {
+  placeholder.value = props.modelValue
+  inputData.value = ''
+  tableConfig.value.isShow = !tableConfig.value.isShow
+}
+
+const highlightRow = async () => {
+  await nextTick()
+  if (props.currentKey === null) return
+  let row = tableRef.value.$el.querySelectorAll('.el-table__row')
+  for (let i = 0; i < tempTableList.value.length; i++) {
+    let item = tempTableList.value[i]
+    row[i].classList.remove('current_key')
+    if (item[props.rowKey] === props.currentKey) {
+      row[i].classList.add('current_key')
+    }
+  }
+}
+
+const inputBlur = () => {
+  inputData.value = props.modelValue
+  tableConfig.value.isShow = false
+}
+
+const tempTableList = computed(() => {
+  highlightRow()
+  return tableData.value.list.slice((tableData.value.currentPage - 1) * tableData.value.pageSize, tableData.value.currentPage * tableData.value.pageSize);
+})
+
+const handleSizeChange = (val) => {
+  tableData.value.pageSize = val
+}
+
+const handleCurrentChange = async (val) => {
+  tableData.value.currentPage = val
+}
+
+const clickRow = async (row) => {
+  selectedRowStyle()
+  emit('rowClick', row)
+  tableConfig.value.isShow = false
+}
+
+const selectedRowStyle = () => {
+  let row = tableRef.value.$el.querySelectorAll('.el-table__row')
+  for (let i = 0; i < tempTableList.value.length; i++) {
+    row[i].classList.remove('current_key')
+    if (i === currentIndex) {
+      row[i].classList.add('current_key')
+    }
+  }
+}
+
+watch(() => props.modelValue, () => {
+  inputData.value = props.modelValue
+}, {immediate: true})
+
+const inputRef = ref()
+const focus = () => {
+  inputRef.value.focus()
+}
+
+onMounted(() => {
+  rowAddClass()
+})
+
+defineExpose({
+  focus
+})
+
+</script>
+
+<style scoped lang="scss">
+.combo_grid {
+  display: inline-block;
+}
+
+.table-container {
+  :deep(.el-table) {
+    tbody tr:hover > td {
+      background-color: unset !important
+    }
+
+    .selected {
+      background-color: #6faffa;
+      color: white;
+    }
+
+    .current_key {
+      background-color: #ffff00b8;
+      color: #000;
+    }
+
+  }
+}
+</style>

+ 4 - 1
src/components/xiao-chan/table-v3/XcTableV3.vue

@@ -14,7 +14,6 @@
              @blur="blur"
              :id="props.id"
              @mousedown.stop
-             @click="setUpSearchPlaceholders"
              :placeholder="placeholder"
              v-model="inputData"
              ref="selectInputRef"/>
@@ -72,6 +71,7 @@
 <script setup name='XcSelectV3'>
 import {onClickOutside} from '@vueuse/core'
 import {functionDebounce} from "@/utils/debounce";
+import sleep from "@/utils/sleep";
 
 const props = defineProps({
   modelValue: {
@@ -190,6 +190,8 @@ const getfocus = async () => {
   await nextTick()
   selectInputRef.value?.focus()
   isFocus = true
+  await sleep(100)
+  setUpSearchPlaceholders()
 }
 
 const blur = () => {
@@ -244,6 +246,7 @@ let inputData = $ref('')
 const onKeyboardSelect = () => {
   if (currentIndex === -1) {
     boxClick()
+    setUpSearchPlaceholders()
   } else {
     clickToSelect(props.data[currentIndex])
   }

+ 30 - 0
src/utils/xiaochan-element-plus.js

@@ -0,0 +1,30 @@
+import {ElMessage} from "element-plus";
+
+export const xcMessage = {
+    success: (msg, isHtml = false) => {
+        message(msg, 'success', isHtml)
+    },
+    danger: (msg, isHtml = false) => {
+        message(msg, 'danger', isHtml)
+    },
+    info: (msg, isHtml = false) => {
+        message(msg, 'info', isHtml)
+    },
+    warning: (msg, isHtml = false) => {
+        message(msg, 'warning', isHtml)
+    },
+    error: (msg, isHtml = false) => {
+        message(msg, 'error', isHtml)
+    }
+}
+
+function message(msg, type, isHtml) {
+    ElMessage({
+        type: type,
+        duration: 2800,
+        dangerouslyUseHTMLString: isHtml,
+        message: msg,
+        showClose: true,
+        grouping: true,
+    })
+}

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

@@ -451,13 +451,13 @@ const clickToSubmitTheMedicalRecord = () => {
 
 const visibility = useDocumentVisibility()
 
-watch(() => visibility, () => {
+watch(() => visibility.value, () => {
   if (visibility.value === 'hidden' && isEditorChange) {
     document.title = `患者【${props.huanZheXinXi.name}】,未保存数据`
   } else {
     document.title = `电子病历-正在编辑【${props.huanZheXinXi.name}】`
   }
-}, {deep: true, immediate: true})
+}, {immediate: true})
 
 
 const queryingBasicPatientInformation = async () => {

+ 88 - 34
src/views/hospitalization/zhu-yuan-yi-sheng/yi-zhu-lu-ru/TianJiaYiZhu.vue

@@ -35,12 +35,20 @@
     <el-row>
       <el-col :span="span">
         <el-form-item class="bi_tian" label="医嘱名称:" prop="orderName">
-          <el-input v-model="yiZhuData.orderName" placeholder="placeholder"
-                    type="textarea"
-                    v-if="yiZhuData.orderCode === '06054'" :maxlength="50" show-word-limit></el-input>
-          <div style="border-bottom: 1px solid #000; height: 29px;width: 100%" v-else @click="yiZhuMingDialog = true">
-            {{ yiZhuData.orderName }}
-          </div>
+          <xc-combo-grid
+              ref="searchRef"
+              v-model="yiZhuData.orderName"
+              :table-header="tableHeader"
+              :query-data-func="huoQuXiangMu"
+              :current-key="yiZhuData.orderCode + yiZhuData.orderName"
+              @rowClick="xuanZhongFeiYong">
+          </xc-combo-grid>
+          <!--          <el-input v-model="yiZhuData.orderName" placeholder="placeholder"-->
+          <!--                    type="textarea"-->
+          <!--                    v-if="yiZhuData.orderCode === '06054'" :maxlength="50" show-word-limit></el-input>-->
+          <!--          <div style="border-bottom: 1px solid #000; height: 29px;width: 100%" v-else @click="yiZhuMingDialog = true">-->
+          <!--            {{ yiZhuData.orderName }}-->
+          <!--          </div>-->
         </el-form-item>
       </el-col>
       <el-col :span="span">
@@ -353,9 +361,7 @@
       </template>
     </el-table-column>
   </el-table>
-  <!-- 这里是搜索医嘱的 -->
-  <sou-suo-yi-zhu v-if="yiZhuMingDialog" @close="closeTheDoctorSOrderSearchBox()"
-                  @xuanZhongFeiYong="xuanZhongFeiYong"></sou-suo-yi-zhu>
+
   <!-- 这个是过敏源的 -->
   <AllergenEntry v-if="allergenDialog"
                  :pat-no="props.patientInfo.inpatientNo"
@@ -388,7 +394,8 @@ import {
   huoQuZhiXinKeShi,
   huoQuZhuYuanPinLv,
   saveTheDoctorSOrder,
-  singleDataCheck
+  singleDataCheck,
+  huoQuXiangMu
 } from '@/api/zhu-yuan-yi-sheng/yi-zhu-lu-ru'
 import {
   fuZhiYiZhu,
@@ -415,6 +422,9 @@ import {setScrollTop} from "@/utils/el-table-scroll";
 import XcTableV3 from "@/components/xiao-chan/table-v3/XcTableV3.vue";
 import sleep from "@/utils/sleep";
 import BaoCunXinXi from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/BaoCunXinXi.vue";
+import {useDocumentVisibility} from "@vueuse/core";
+import {xcMessage} from "@/utils/xiaochan-element-plus";
+import XcComboGrid from "@/components/xiao-chan/combo-grid/XcComboGrid";
 
 
 const props = defineProps({
@@ -424,6 +434,23 @@ const props = defineProps({
   }
 })
 
+let tableHeader = [
+  {label: '编码', prop: 'orderCode'},
+  {label: '名称', prop: 'orderName'},
+  {label: '规格', prop: 'drugSpecification'},
+  {label: '描述', prop: 'discription'},
+  {label: '库存', prop: 'stockAmount'},
+  {label: '大包装', prop: 'specPack'},
+  {label: '医保类型', prop: 'ybFlagNew'},
+  {label: '医保编码', prop: 'nationalCode'},
+  {label: '医保名称', prop: 'nationalName'},
+  {label: '医保备注', prop: 'ybComment'},
+  {label: '大输液', prop: 'infusionFlagName'},
+  {label: '厂家', prop: 'manuName'},
+  {label: '类型', prop: 'orderType'},
+  {label: '毒麻类型', prop: 'drugFlagName'}
+]
+
 const windowSize = computed(() => {
   return store.state.app.windowSize
 })
@@ -510,12 +537,10 @@ watch(
 )
 
 // 搜索医嘱
-let yiZhuMingDialog = $ref(false)
 const xuanZhongFeiYong = async (row) => {
   fuYiZhuClick()
   qingKong()
   await Sleep(200)
-  closeTheDoctorSOrderSearchBox()
   yiZhuData.value = row
   if (row.serial !== '00') {
     huoQuFeiYongXinXi(row.orderCode, row.serial, props.patientInfo.smallDept)
@@ -543,8 +568,8 @@ const xuanZhongFeiYong = async (row) => {
             yiZhuData.value.doseUnit = yaoPinJiLiangData.value[0].code
             yiZhuData.value.dose = yaoPinJiLiangData.value[0].value
             jiLiangValue.value = yaoPinJiLiangData.value[0].value
-            jiSuanLingLiang(yiZhuData.value.dose)
           }
+          jiSuanLingLiang(yiZhuData.value.dose)
           // 加载默认频率 如果已经填写了 就用有的
           if (stringIsBlank(row.frequCode)) {
             if (stringNotBlank(res.data.frequCode)) {
@@ -619,11 +644,6 @@ const xuanZhongFeiYong = async (row) => {
   }
 }
 
-const closeTheDoctorSOrderSearchBox = () => {
-  yiZhuMingDialog = false
-  xcHotKey(shortcutKeyRegistration)
-}
-
 /* 频率 */
 let pingLv = $ref(null)
 const yaoPinPingLvData = ref([])
@@ -643,7 +663,6 @@ const xuanZheJiLiang = (val) => {
 }
 /*计算领量*/
 const jiSuanLingLiang = (val) => {
-
   if (jiLiangValue.value <= 0) return
   let temp = Math.ceil(val / jiLiangValue.value);
 
@@ -659,7 +678,7 @@ const jiSuanLingLiang = (val) => {
 
 }
 
-let minimumPickingQuantity = $ref(0)
+let minimumPickingQuantity = $ref(1)
 
 
 /* 给药方式 */
@@ -766,11 +785,11 @@ const whetherToResetTheDataSubscript = () => {
 /* 这个是点击单个修改的 */
 const xiuGaiYiZhu = (val, index) => {
   if (yiZhuData.value.orderCode) {
-    ElMessage.warning('请先保存正在编辑的内容')
+    xcMessage.warning('请先保存正在编辑的内容')
     return
   }
   xuanZhongFeiYong(val)
-  ElMessage.success('你点击了修改')
+  xcMessage.success('你点击了修改')
   yiZhuList.value.splice(index, 1)
   tianJiaYiZhuWeiYiBiaoShi.value.splice(index, 1)
   whetherToResetTheDataSubscript()
@@ -780,7 +799,7 @@ const xiuGaiYiZhu = (val, index) => {
 const shanChuBiaoGeYiZhu = (val) => {
   yiZhuList.value.splice(val, 1)
   tianJiaYiZhuWeiYiBiaoShi.value.splice(val, 1)
-  ElMessage.success('删除成功')
+  xcMessage.success('删除成功')
   whetherToResetTheDataSubscript()
 }
 
@@ -788,7 +807,7 @@ const shanChuBiaoGeYiZhu = (val) => {
 const dianJiFuZhiYiZhu = (val) => {
   yiZhuData.value = clone(val)
   yiZhuData.value.id = uuid(8, 10)
-  ElMessage.success('复制成功')
+  xcMessage.success('复制成功')
 }
 
 // 展开table
@@ -839,7 +858,6 @@ const baoCunYiZhuClick = () => {
     saveTheDoctorSOrder(data).then((res) => {
       ElMessage.success('医嘱确认成功')
     }).catch((e) => {
-      console.log(e)
       errorMessageData = e
       openErrorDialog()
     }).finally(() => {
@@ -1063,13 +1081,6 @@ const saveOrAddADoctorSOrder = () => {
   }
 }
 
-/**
- * 打开医嘱搜索框
- */
-const openSearchBox = () => {
-  yiZhuMingDialog = true
-}
-
 /// 这里是 ctrl 快捷键 结束
 
 const regainFocus = () => {
@@ -1153,11 +1164,16 @@ const deleteSelectedOrders = () => {
   shanChuBiaoGeYiZhu(yiZhuList.value[dataIndex])
 }
 
+const searchRef = ref()
+const openSearch = () => {
+  searchRef.value.focus()
+}
+
 // alt 快捷键结束
 
 // 注册快捷键的 list
 let shortcutKeyRegistration = {
-  ctrl: {s: saveOrAddADoctorSOrder, f: openSearchBox, 1: regainFocus},
+  ctrl: {s: saveOrAddADoctorSOrder, f: openSearch, 1: regainFocus},
   alt: {
     PageUp: upData,
     PageDown: downData,
@@ -1228,10 +1244,48 @@ onMounted(async () => {
     ]
   }
   fuZhiYiZhu.value = []
-  testData()
+  // testData()
 })
 
 
+const visibility = useDocumentVisibility()
+
+let copyOrderPopup = null
+
+watch(() => visibility.value, () => {
+
+  if (visibility.value === "visible") {
+    let copy = localStorage.getItem('copyDoctorSOrder')
+    if (copy === null) {
+      ElMessageBox.close()
+      return
+    }
+    copy = JSON.parse(copy)
+
+    if (copyOrderPopup !== null) return;
+
+    copyOrderPopup = ElMessageBox.confirm('是否要粘贴医嘱', '提示', {
+      type: 'info',
+    }).then(async () => {
+      let serverTime = await getServerDateApi();
+      copy.forEach(item => {
+        item.orderTime = serverTime
+        item.startTime = serverTime
+        item.endTime = serverTime
+        item.frequCodeName = '一次'
+        item.frequCode = 'ONCE'
+        yiZhuList.value.push(item)
+      })
+      localStorage.removeItem('copyDoctorSOrder')
+    }).catch(() => {
+    })
+  } else {
+    if (yiZhuList.value.length > 0) {
+      document.title = `医嘱录入:【${props.patientInfo.name}】*`
+    }
+  }
+}, {immediate: true})
+
 const cloneWindow = () => {
   window.opener = null
   window.close()

+ 3 - 2
src/views/hospitalization/zhu-yuan-yi-sheng/yi-zhu-lu-ru/YiZhuLuRu.vue

@@ -214,7 +214,7 @@ const dateRange = ref([])
 let orderName = $ref('')
 const yiZhuMingZiData = ref([])
 // 获取频率
-const pinLv = ref(1)
+const pinLv = ref(2)
 // 表格
 let tableRef = $ref(null)
 // 状态
@@ -411,7 +411,8 @@ const huoQuXuanZhongDeShuJu = (val) => {
  */
 const dianJiFuZhuXuanZhongYiZhu = () => {
   fuZhiYiZhu.value = xuanZhongDeShuJu.value
-  addYiZhuClick()
+  localStorage.setItem('copyDoctorSOrder', JSON.stringify(xuanZhongDeShuJu.value))
+  // 清空选择
   tableRef.clearSelection(false)
 }
 

+ 57 - 10
src/views/settings/Test.vue

@@ -1,23 +1,70 @@
 <template>
-  <xc-dialog-v2 v-model="dialog" :allow-maximization="false">
-    <xc-table :local-data="data" :height="200">
-      <el-table-column label="线程" prop="name"></el-table-column>
-      <el-table-column label="data" prop="data"></el-table-column>
-    </xc-table>
-  </xc-dialog-v2>
-
+  <xc-combo-grid
+      style="width: 240px"
+      clearable
+      row-key="id"
+      :current-key="obj.code + obj.serial"
+      @rowClick="rowClick"
+      :table-header="tableHeader"
+      :query-data-func="huoQuXiangMu"
+      v-model="obj.name">
+    <el-table-column label="药品详情" fixed="left">
+      <template #default="scope">
+        <el-button
+            v-if="scope.row.serial !== '00'"
+            @click.stop.prevent="queryDrugDetails(scope.row.orderCode.trim() + '_' + scope.row.serial.trim())">
+          药品详情
+        </el-button>
+      </template>
+    </el-table-column>
+  </xc-combo-grid>
 </template>
 
 <script setup name='Test'>
-import XcDialogV2 from "@/components/xiao-chan/dialog/XcDialogV2";
-import XcTable from "@/components/xiao-chan/xc-table/XcTable";
 
 
-let dialog = $ref(true)
+import XcComboGrid from "@/components/xiao-chan/combo-grid/XcComboGrid";
+import {huoQuXiangMu} from "@/api/zhu-yuan-yi-sheng/yi-zhu-lu-ru";
+
+let obj = $ref({
+  code: '01128',
+  name: '注射用脂溶性维生素(Ⅱ)',
+  serial: '01'
+})
+
+const rowClick = (row) => {
+  obj.code = row.orderCode
+  obj.name = row.orderName
+  obj.serial = row.serial
+  console.log(row)
+}
+
+const queryDrugDetails = (row) => {
+  console.log(row)
+}
 
+watch(() => obj, () => {
+  console.log(obj)
+}, {deep: true})
 
 let data = $ref([])
 
+let tableHeader = [
+  {label: '编码', prop: 'orderCode'},
+  {label: '名称', prop: 'orderName'},
+  {label: '规格', prop: 'drugSpecification'},
+  {label: '描述', prop: 'discription'},
+  {label: '库存', prop: 'stockAmount'},
+  {label: '大包装', prop: 'specPack'},
+  {label: '医保类型', prop: 'ybFlagNew'},
+  {label: '医保编码', prop: 'nationalCode'},
+  {label: '医保名称', prop: 'nationalName'},
+  {label: '医保备注', prop: 'ybComment'},
+  {label: '大输液', prop: 'infusionFlagName'},
+  {label: '厂家', prop: 'manuName'},
+  {label: '类型', prop: 'orderType'},
+  {label: '毒麻类型', prop: 'drugFlagName'}
+]
 for (let i = 0; i < 100; i++) {
   data.push({
     name: i + 'name',