Browse Source

优化医嘱体验

DESKTOP-0GD05B0\Administrator 2 years ago
parent
commit
85140659ea

+ 137 - 54
src/components/xiao-chan/xc-table-v2/XcTableV2.vue

@@ -1,44 +1,47 @@
 <template>
-  <div class="xc-table"
+  <div class="xc-table_v2"
        ref="tableRef">
     <div class="header"
          ref="headerRef">
-      <div v-for="item in props.columns"
-           class="th"
-           @click="headerClick(item)"
-           :style="{width :item.width + 'px' }">
-        <template v-if="item.type">
-          <component :is="typeEnum[item.type].header()"/>
-        </template>
-        {{ item.name }}
-      </div>
+      <template v-for="(item,index) in props.columns">
+        <div class="th"
+             @click="headerClick(item)"
+             :style="fixedStyle(item,index)">
+          <template v-if="item.type">
+            <component :is="typeEnum[item.type].header()"/>
+          </template>
+          {{ item.name }}
+        </div>
+      </template>
+
     </div>
     <div v-bind="containerProps"
-         class="xc-body"
+         class="xc-body_v2"
          :style="{height : props.height + 'px'}">
       <div v-bind="wrapperProps"
            ref="dataRef">
         <div v-for="item in list"
-             :key="item.index"
+             :key="props.rowKey === null ?  item.index : props.rowKey"
              @contextmenu.prevent.stop="contextmenu(item.data,item.index,$event)"
              :style="rowStyle(item.data, item.index)"
              @dblclick.stop="dbRowClick(item.data, $event, item.index)"
              @click="rowClick(item.data, $event, item.index)"
              class="row">
-          <div v-for="(he) in props.columns"
-               :title="item.data[he.code]"
-               class="cell"
-               :style="{width :he.width + 'px' }">
-            <template v-if="he.type">
-              <component :is="typeEnum[he.type].body(item)"/>
-            </template>
-            <template v-else-if="he.cellRenderer">
-              <component :is="he.cellRenderer({data: item.data,index:item.index,cellData: item.data[he.code]})"/>
-            </template>
-            <template v-else>
-              {{ item.data[he.code] }}
-            </template>
-          </div>
+          <template v-for="(he,heindex) in props.columns">
+            <div :title="item.data[he.code]"
+                 class="cell"
+                 :style="fixedStyle(he,heindex)">
+              <template v-if="he.type">
+                <component :is="typeEnum[he.type].body(item)"/>
+              </template>
+              <template v-else-if="he.cellRenderer">
+                <component :is="he.cellRenderer({data: item.data,index:item.index,cellData: item.data[he.code]})"/>
+              </template>
+              <template v-else>
+                {{ item.data[he.code] }}
+              </template>
+            </div>
+          </template>
         </div>
       </div>
     </div>
@@ -47,10 +50,8 @@
 
 <script setup name='XcTableV2' lang="tsx">
 import {useVirtualList} from "@vueuse/core";
-import {computed, nextTick, onMounted, ref} from 'vue'
-import {Ref} from "vue-demi";
+import {computed, nextTick, onMounted, ref, Ref} from 'vue'
 import {ElCheckbox} from "element-plus";
-import {applyMorphAnimation} from "echarts/types/src/animation/morphTransitionHelper";
 
 const props = defineProps({
   data: Array,
@@ -62,6 +63,10 @@ const props = defineProps({
   rowStyle: {
     type: Function,
     default: null
+  },
+  rowKey: {
+    type: String,
+    default: null
   }
 })
 
@@ -71,7 +76,7 @@ const tempData = computed(() => {
 
 const emits = defineEmits(['rowClick', 'dbRowClick', 'contextmenu'])
 
-const {list, containerProps, wrapperProps} = useVirtualList(
+const {list, containerProps, wrapperProps, scrollTo} = useVirtualList(
     tempData,
     {
       itemHeight: 24,
@@ -114,12 +119,6 @@ const tableRef: Ref<HTMLElement | null> = ref(null)
 const dataRef: Ref<HTMLElement | null> = ref(null)
 const headerRef: Ref<HTMLElement | null> = ref(null)
 
-onMounted(async () => {
-  await nextTick()
-  containerProps.ref.value.addEventListener('scroll', () => {
-    headerRef.value.scrollLeft = containerProps.ref.value.scrollLeft
-  })
-})
 
 const checkAll: Ref<boolean> = ref(false)
 const isIndeterminate = computed(() => {
@@ -140,28 +139,26 @@ const typeEnum = {
                           }}
                           indeterminate={isIndeterminate.value}/>)
     },
-    body: ({data, index}) => {
+    body: ({data}) => {
       return (<ElCheckbox
-          onChange={(e) => clickSelected(data, e)}
           modelValue={data.tempCheckbox}
-          onclick={(e) => clickSelected(data, e, true)}/>)
+          onclick={(e) => clickSelected(data, e)}/>)
     }
   },
   "index": {
     header: () => (<span>排序</span>),
-    body: ({data, index}) => (
+    body: ({index}) => (
         <span>{index + 1}</span>
     )
   }
 }
 
-const clickSelected = (data, e: Event, flag = false) => {
-  console.log(123)
-  e.preventDefault()
-  e.stopImmediatePropagation();
-  if (flag) {
-    data.tempCheckbox = !data.tempCheckbox
+const clickSelected = (data, e: Event) => {
+  if (e !== null) {
+    e.preventDefault()
+    e.stopImmediatePropagation();
   }
+  data.tempCheckbox = !data.tempCheckbox
   if (data.tempCheckbox) {
     selectedData.value.push(data)
   } else {
@@ -172,8 +169,10 @@ const clickSelected = (data, e: Event, flag = false) => {
   }
 }
 
+
 const clickSelectedAll = (e: Event) => {
   e.preventDefault()
+  e.stopPropagation()
   checkAll.value = !checkAll.value
 
   if (checkAll.value) {
@@ -190,13 +189,100 @@ const clickSelectedAll = (e: Event) => {
   }
 }
 
+const selectedRow = (data) => {
+  clickSelected(data, null)
+}
+
+const getSelectedData = () => {
+  return selectedData.value
+}
+
+const scrollToByData = (data) => {
+  let temp = JSON.stringify(data)
+  let index = -1
+  for (let i = 0, len = tempData.value.length; i < len; i++) {
+    let item = JSON.stringify(tempData.value[i])
+    if (item === temp) {
+      index = i
+      break;
+    }
+  }
+  if (index > 0) {
+    scrollTo(index)
+  }
+}
+
+const scrollToByKey = (key) => {
+  if (props.rowKey === null) {
+    throw new Error('请先选择一个 key')
+  }
+  let index = -1
+  for (let i = 0, len = tempData.value.length; i < len; i++) {
+    if (tempData.value[i][props.rowKey] == key) {
+      index = i
+      break;
+    }
+  }
+  if (index > 0) {
+    scrollTo(index - 2)
+  }
+  return index
+}
+
+const fixedStyle = (item, index) => {
+  let str = {width: item.width + 'px'}
+  let px = 0
+  if (index > 0) {
+    for (let i = 0; i < index; i++) {
+      let he = props.columns[i]
+      if (he.fixed && he.fixed === item.fixed) {
+        px += he.width
+      }
+    }
+  }
+  if (item.fixed) {
+    str['position'] = 'sticky!important';
+    str[item.fixed] = px + 'px'
+    str['z-index'] = 2
+    str['background'] = '#f5f5f5'
+  }
+  return str
+}
+
+const clearSelected = () => {
+  if (selectedData.value.length > 0) {
+    selectedData.value.forEach(item => {
+      item.tempCheckbox = false
+    })
+    selectedData.value = []
+  }
+}
+
+
+onMounted(async () => {
+  await nextTick()
+  containerProps.ref.value.addEventListener('scroll', () => {
+    headerRef.value.scrollLeft = containerProps.ref.value.scrollLeft
+  })
+})
+
+defineExpose({
+  selectedRow,
+  getSelectedData,
+  scrollToByData,
+  scrollToByKey,
+  scrollTo,
+  clearSelected
+})
+
 
 </script>
 
-<style scoped lang="scss">
+<style lang="scss">
 
-.xc-table {
+.xc-table_v2 {
   overflow-x: hidden;
+  color: black;
 
   .header {
     height: max-content;
@@ -207,7 +293,6 @@ const clickSelectedAll = (e: Event) => {
 
     .th {
       overflow: hidden;
-      border: 1px solid #fff;
       text-overflow: ellipsis;
       white-space: nowrap;
       flex-grow: 0;
@@ -215,9 +300,7 @@ const clickSelectedAll = (e: Event) => {
     }
   }
 
-  .xc-body {
-
-    //overflow-x: hidden;
+  .xc-body_v2 {
 
     &::-webkit-scrollbar {
       height: 6px;
@@ -231,7 +314,8 @@ const clickSelectedAll = (e: Event) => {
 
     .cell {
       overflow: hidden;
-      border: 1px solid #fff;
+      border-bottom: 1px solid #fff;
+      border-top: 1px solid #fff;
       display: flex;
       align-items: center;
       text-overflow: ellipsis;
@@ -241,7 +325,6 @@ const clickSelectedAll = (e: Event) => {
       flex-shrink: 0;
     }
 
-
   }
 }
 

+ 27 - 0
src/components/xiao-chan/xc-table-v2/XcTableV2Type.ts

@@ -0,0 +1,27 @@
+export interface XcTableV2Type {
+    /**
+     * 设置选中和取消选中
+     */
+    selectedRow: Function,
+    /**
+     * 获取选中的数据
+     */
+    getSelectedData: Function,
+    /**
+     * 根据数据跳转到指定行
+     */
+    scrollToByData: Function,
+    /**
+     * 根据 key 跳转到指定行
+     */
+    scrollToByKey: Function,
+    /**
+     * 滚动到指定位置
+     */
+    scrollTo: Function,
+    /**
+     * 清理选择的数据
+     */
+    clearSelected: Function
+}
+

+ 0 - 429
src/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/table/YzTable.vue

@@ -1,429 +0,0 @@
-<template>
-  <el-scrollbar>
-    <div class="yz_table">
-      <table>
-        <colgroup>
-          <col v-for="item in header" :style="{width: item.width + 'px'}"/>
-        </colgroup>
-        <thead>
-        <tr>
-          <th v-for="(item,index) in header">
-            <div v-if="index === 0" @click="clearSelected" style="cursor: default">
-              #
-            </div>
-            <span v-else>
-          {{ item.name }}
-          </span>
-          </th>
-        </tr>
-        </thead>
-      </table>
-      <el-scrollbar :height="maxHeight"
-                    @scroll="scroll"
-                    ref="elScrollbarRef">
-        <div :style="{height: virtualHeight + 'px'}"
-             ref="virtualHeightRef"
-             style="position: relative">
-          <table :style="{top:` ${translateY}px`}" style="position: absolute">
-            <colgroup>
-              <col v-for="item in header" :style="{width: item.width + 'px'}"/>
-            </colgroup>
-            <tbody>
-            <tr v-for="(item,index) in tempYzData.slice(startIndex, startIndex + itemCount)"
-                @click="rowClick(item)"
-                @dblclick.stop="setDefaultStopTime(item)"
-                @contextmenu="contextmenuItem(item,index,$event)"
-                :style="rowStyle(item,index)">
-              <td v-for="(he,heindex) in header"
-                  :title="item[he.code]">
-                <div v-if="heindex === 0">
-                  <input type="checkbox"
-                         @click.stop="clickSelected(item)"
-                         v-model="item.tempCheckbox">
-                </div>
-                <div
-                    v-else-if="heindex === 10
-                    && stringIsBlank(item.endTime)
-                    && stringIsBlank(item.parentNo)
-                    && item.frequCode !== 'ONCE' ">
-                  <input type="datetime-local"
-                         v-model="item.endTimeTemp"
-                         :style="endDateStyle(item)"
-                         @click.stop
-                         @contextmenu.stop.prevent="cancelStopTime(item)"
-                         @dblclick.stop="setDefaultStopTime(item)">
-                </div>
-                <div v-else-if="heindex === 20">
-                  <button @click.stop="props.voidOrders(item)">作废</button>
-                </div>
-                <div v-else-if="he.func" v-html="he.func(item,startIndex + index)"/>
-                <div v-else>
-                  {{ item[he.code] }}
-                </div>
-              </td>
-            </tr>
-            </tbody>
-          </table>
-        </div>
-      </el-scrollbar>
-    </div>
-  </el-scrollbar>
-
-  <right-click-menu :mouse-position="mousePosition" :config="opt"/>
-
-</template>
-
-<script setup name='YzTable' lang="tsx">
-import {stringIsBlank} from "@/utils/blank-utils";
-import {
-  selectedData,
-  tempYzData,
-  yiZhuData,
-  associateOrders,
-  clearAssociate,
-  drugManual,
-  yzData
-} from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
-import {getFormatDatetime} from "@/utils/date";
-import {defineEmits, defineProps, nextTick, ref, watch} from 'vue'
-import sleep from "@/utils/sleep";
-import store from "@/store"
-import {ElScrollbar} from "element-plus";
-import RightClickMenu from "@/components/menu-item/RightClickMenu.vue";
-import {useIntersectionObserver} from "@vueuse/core";
-
-const props = defineProps({
-  data: {
-    type: Array
-  },
-  voidOrders: Function
-})
-
-const emit = defineEmits(['rowClick', 'clickAssociate'])
-
-const header = [
-  {
-    width: 15, name: '#'
-  },
-  {
-    func: (val, index) => {
-      return `${index + 1}`
-    },
-    width: 20
-  },
-  {code: 'orderGroup', name: '组', width: 10},
-  {
-    code: 'statusFlag', name: '状态', func: (val) => {
-      return getYiZhuFlag(val.statusFlag)
-    },
-    width: 20
-  },
-  {
-    code: 'actOrderNo', name: '医嘱号', width: 40, func: (val) => {
-      if (val.error) {
-        return `<span style="color: red" class="${val.newOrderFlag > 0 ? 'new_order' : ''}">${nu(val.actOrderNo)}</span>`
-      } else {
-        return `<span class="${val.newOrderFlag > 0 ? 'new_order' : ''}">${nu(val.actOrderNo)}</span>`
-      }
-    }
-  },
-  {
-    code: 'orderName', name: '医嘱名称', width: 120
-  },
-  {
-    code: 'dose', name: '剂量', width: 40, func: (val) => {
-      return nu(val.dose) + ' ' + nu(val.doseUnitName)
-    }
-  },
-  {code: 'frequCode', name: '频率', width: 42},
-  {code: 'supplyCodeName', name: '给药方式', width: 50},
-  {
-    code: 'startTime', name: '开始时间', width: 50, func: (val) => {
-      return timeFomat(val.startTime)
-    }
-  },
-  {
-    // 出院时间的大小不能改
-    code: 'endTime', name: '结束时间', width: 90, func: (val) => {
-      return timeFomat(val.endTime)
-    }
-  },
-  {
-    code: 'emergencyFlag', name: '紧急', func: (val) => {
-      return `${val.emergencyFlag === '1' ? '√' : ''}`
-    },
-    width: 20
-  },
-  {
-    code: 'ybSelfFlag', name: '自费', width: 20, func: (val) => {
-      return val.ybSelfFlag === 1 ? '√' : ''
-    }
-  },
-  {code: 'physicianName', name: '医生', width: 30},
-  {code: 'selfBuyName', name: '费用标志', width: 30},
-  {code: 'execUnitName', name: '执行科室', width: 40},
-  {
-    code: 'drugQuan', name: '领量', func: (val) => {
-      return nu(val.drugQuan) + nu(val.drugQuanName)
-    },
-    width: 20
-  },
-  {code: 'groupNoName', name: '药房', width: 50},
-  {code: 'serial', name: '序号', width: 20},
-  {code: 'instruction', name: '嘱托', width: 80},
-  {name: '操作', width: 30}
-];
-
-const virtualHeightRef = ref(null)
-const elScrollbarRef = ref<InstanceType<typeof ElScrollbar>>()
-/*
-* 不知道为什么路由切换的时候 elScrollbar 会没有滚动到指定的位置,
-* 判断页面元素是否存在如果在的话,设置为0后在设置到指定的位置
-* */
-useIntersectionObserver(elScrollbarRef,
-    ([{isIntersecting}], observerElement) => {
-      if (isIntersecting) {
-        elScrollbarRef.value!.setScrollTop(0)
-        elScrollbarRef.value!.setScrollTop(translateY.value)
-      }
-    },
-)
-
-const timeFomat = (val) => {
-  return getFormatDatetime(val, 'YY-MM-DD HH:mm')
-}
-
-const endDateStyle = (item) => {
-  if (item.endTimeTemp && item.tempCheckbox) {
-    return {
-      color: 'red'
-    }
-  }
-}
-
-const rowStyle = (item, index) => {
-  if (associateOrders.value.actOrderNo === item.actOrderNo) {
-    return {
-      background: 'rgba(3,255,15,0.68)',
-      color: '#fff'
-    }
-  }
-  if (item.actOrderNo === yiZhuData.value.actOrderNo) {
-    return {
-      background: 'rgba(0, 58, 241, 0.68)',
-      color: '#fff'
-    }
-  }
-  if (item.associationFlag) {
-    return {
-      background: 'red',
-      color: '#fff'
-    }
-  }
-}
-
-const clearSelected = () => {
-  selectedData.value = []
-  for (let i = 0, len = yzData.value.length; i < len; i++) {
-    yzData.value[i].tempCheckbox = false
-  }
-}
-
-const clickSelected = async (row) => {
-  await nextTick()
-  await sleep(100)
-  let index = selectedData.value.indexOf(row)
-  if (index > -1) {
-    row.tempCheckbox = false
-    selectedData.value.splice(index, 1)
-  } else {
-    row.tempCheckbox = true
-    selectedData.value.push(row)
-  }
-}
-
-const setDefaultStopTime = (val) => {
-  if (val.parentNo) return
-  val.endTimeTemp = getFormatDatetime(new Date(), 'yyyy-MM-DD HH:mm')
-  if (!val.tempCheckbox) {
-    clickSelected(val)
-  }
-}
-
-const cancelStopTime = (val) => {
-  if (val.parentNo) return
-  val.endTimeTemp = ''
-  if (val.tempCheckbox) {
-    clickSelected(val)
-  }
-}
-
-// div 的最大高度
-const maxHeight = ref(500)
-// 开始位置
-const startIndex = ref(0)
-// 可以看见 的 内容大小
-const itemCount = ref(0)
-//  实际的高度
-const virtualHeight = ref(0)
-// 每一个 元素的大小
-const itemHeight = ref(27)
-// 偏移量
-const translateY = ref(0)
-
-const scroll = ({scrollTop}) => {
-  translateY.value = scrollTop
-  startIndex.value = Math.floor(scrollTop / itemHeight.value)
-}
-
-const rowClick = (val) => {
-  emit('rowClick', val)
-}
-
-watch(() => tempYzData.value, (oldData, newData) => {
-  if (oldData.length !== newData.length) {
-    itemCount.value = Math.ceil(maxHeight.value / itemHeight.value)
-    virtualHeight.value = itemHeight.value * tempYzData.value.length
-  }
-})
-
-
-const calculateTableHeight = async () => {
-  await nextTick()
-  let windowSize = store.state['app']['windowSize']
-  maxHeight.value = Math.ceil(windowSize.h / 2) - 10
-  virtualHeight.value = itemHeight.value * tempYzData.value.length
-}
-
-watch(() => store.state['app']['windowSize'], () => {
-  calculateTableHeight()
-}, {immediate: true})
-
-const scrollToTheEnd = () => {
-  elScrollbarRef.value!.setScrollTop(virtualHeight.value)
-}
-
-const mousePosition = ref()
-const opt = [
-  {
-    name: '关联', click: (data, index) => {
-      emit('clickAssociate', data)
-    }
-  }, {
-    name: '退出关联模式', click: () => {
-      clearAssociate()
-    }
-  }, {
-    name: '药品说明书', click: (data) => {
-      if (data.groupNo !== '00') {
-        drugManual.value.open(data.orderCode, data.serial);
-      }
-    }
-  }, {
-    name: '停止医嘱', click: (data) => {
-      if (data.tempCheckbox) {
-        cancelStopTime(data)
-      } else {
-        setDefaultStopTime(data)
-      }
-    },
-    validator: (data) => {
-      return !data.parentNo;
-    }
-  }
-]
-const contextmenuItem = async (item, index, event) => {
-  event.returnValue = false
-  mousePosition.value = {
-    event,
-    data: item,
-    index
-  }
-}
-
-defineExpose({
-  scrollToTheEnd
-})
-
-
-const nu = (val) => {
-  if (val === null) {
-    return ''
-  } else if (typeof val === 'undefined') {
-    return ''
-  } else {
-    return val
-  }
-}
-
-function getYiZhuFlag(val) {
-  if (stringIsBlank(val)) {
-    return val
-  }
-  switch (val) {
-    case '1':
-      return '<span style="color: #05ff00">R</span>'
-    case '2':
-      return '<span style="color: #0000fb">Q</span>'
-    case '3':
-      return '<span style="color: #ff07f3">Z</span>'
-    case '4':
-      return '<span style="color:  #ff07f3">Z</span>'
-    case '5':
-      return '<span style="color: red">T</span>'
-    case '-1':
-      return '<span style="color: #00ffe0">D</span>'
-    default:
-      return 'warning'
-  }
-}
-
-</script>
-
-<style lang="scss">
-.yz_table {
-  width: 1600px;
-  height: max-content;
-
-  table {
-    width: 100%;
-    border-spacing: 0;
-    border-collapse: separate;
-    table-layout: fixed;
-
-    tr {
-      height: 25px;
-    }
-
-    tr, td, th {
-      border: 1px solid #fff;
-    }
-
-    td {
-      height: 20px;
-      width: 100%;
-      word-break: keep-all; /* 不换行 */
-      white-space: nowrap; /* 不换行 */
-      overflow: hidden; /* 内容超出宽度时隐藏超出部分的内容 */
-      text-overflow: ellipsis; /* 当对象内文本溢出时显示省略标记(...) ;需与overflow:hidden;一起使用*/
-
-      .new_order {
-        position: relative;
-
-        &:before {
-          position: absolute;
-          right: -6px;
-          font-size: 15px;
-          top: -5px;
-          content: "*";
-          color: red;
-        }
-      }
-    }
-
-
-  }
-}
-
-
-</style>

+ 167 - 35
src/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/table/YzTableV2.vue

@@ -1,50 +1,46 @@
 <template>
-  <xc-table-v2 :data="tempYzData"
-               :rowStyle="rowStyle"
-               @rowClick="rowClick"
-               :height="getWindowSize.h / 1.7"
-               @dbRowClick="setDefaultStopTime"
-               :columns="header"/>
+  <div>
+    <xc-table-v2 :data="tempYzData.sort((a,b) => {
+                        return a.actOrderNo -b.actOrderNo
+                  })"
+                 :rowStyle="rowStyle"
+                 @rowClick="rowClick"
+                 row-key="actOrderNo"
+                 @contextmenu="contextmenuItem"
+                 :height="getWindowSize.h / 1.7"
+                 @dbRowClick="setDefaultStopTime"
+                 ref="tableRef"
+                 :columns="header"/>
+    <right-click-menu :mouse-position="mousePosition" :config="opt"/>
+  </div>
 </template>
 
 
 <script setup name='Test' lang="tsx">
-import {ref} from 'vue'
+import {Ref, ref} from 'vue'
 import XcTableV2 from "@/components/xiao-chan/xc-table-v2/XcTableV2.vue";
 import {stringIsBlank} from "@/utils/blank-utils";
 import {getFormatDatetime} from "@/utils/date";
 import {getServerDateApi} from '@/api/public-api'
 import {getWindowSize} from "@/utils/window-size";
 import {
-  selectedData,
   tempYzData,
   yiZhuData,
   associateOrders,
   clearAssociate,
   drugManual,
-  yzData
 } from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
+import {XcTableV2Type} from "@/components/xiao-chan/xc-table-v2/XcTableV2Type";
+import sleep from "@/utils/sleep";
+import RightClickMenu from "@/components/menu-item/RightClickMenu.vue";
 
-const emits = defineEmits(['rowClick'])
+const emits = defineEmits(['rowClick', 'clickAssociate', 'voidOrders'])
+
+const tableRef: Ref<XcTableV2Type> = ref(null)
 
 const header = [
-  {
-    width: 20, name: '#',
-    cellRenderer: ({data}) => (
-        <input type="checkbox"
-               checked={data.tempCheckbox}
-               onClick={(event) => clickSelected(data)}/>
-    ),
-    headerFunc: () => {
-      console.log(123)
-    }
-  },
-  {
-    width: 30, name: '排序',
-    cellRenderer: ({index}) => (
-        <span>{index + 1}</span>
-    )
-  },
+  {type: 'selected', width: 20},
+  {width: 30, type: 'index'},
   {width: 20, code: 'orderGroup', name: '组'},
   {
     width: 20, code: 'statusFlag', cellRenderer: ({cellData}) => {
@@ -54,9 +50,10 @@ const header = [
   {
     width: 70, code: 'actOrderNo', name: '医嘱号', cellRenderer: ({data}) => {
       if (data.error) {
-        return (<span style="color: red" class={data.newOrderFlag > 0 ? 'new_order' : ''}>{nu(data.actOrderNo)}</span>)
+        return (<span style="color: red"
+                      class={orderNoClass(data)}>{data.actOrderNo}</span>)
       } else {
-        return (<span class={data.newOrderFlag > 0 ? 'new_order' : ''}>{nu(data.actOrderNo)}</span>)
+        return (<span class={orderNoClass(data)}>{data.actOrderNo}</span>)
       }
     }
   },
@@ -83,8 +80,7 @@ const header = [
                       oncontextmenu={(event) => {
                         event.preventDefault();
                         cancelStopTime(data)
-                      }}
-        />
+                      }}/>
       }
       return <span>{timeFomat(data.endTime)}</span>
     }
@@ -118,14 +114,18 @@ const header = [
         <button onclick={(e) => {
           e.preventDefault();
           e.stopImmediatePropagation();
-          console.log(data)
+          emits('voidOrders', data)
         }}>作废</button>
     )
   }
 ];
 
-const clickSelected = (row) => {
-  row.tempCheckbox = !row.tempCheckbox
+const orderNoClass = (data) => {
+  let c = ''
+  if (data.newOrderFlag > 0) {
+    c = 'new_order'
+  }
+  return c
 }
 
 const timeFomat = (val) => {
@@ -175,6 +175,38 @@ const rowClick = (data) => {
 }
 
 const rowStyle = (data) => {
+  if (twinkleList.value.length > 0) {
+    let temp = twinkleList.value.findIndex(item => {
+      return item == data.actOrderNo
+    })
+
+    // 新增医嘱闪烁
+    if (temp > -1) {
+      sleep(3000).then(() => {
+        twinkleList.value.splice(temp, 1)
+      })
+      return {
+        animation: 'hzfirst 1s linear infinite',
+      }
+    }
+  }
+
+  // 父级
+  if (data.associationFlag) {
+    return {
+      background: 'red',
+      color: '#fff'
+    }
+  }
+
+  // 子级
+  if (associateOrders.value.actOrderNo === data.actOrderNo) {
+    return {
+      background: 'rgba(3,255,15,0.68)',
+      color: '#fff'
+    }
+  }
+
   if (data.actOrderNo === yiZhuData.value.actOrderNo) {
     return {
       background: 'rgba(0, 58, 241, 0.68)',
@@ -200,6 +232,72 @@ const cancelStopTime = (val) => {
   }
 }
 
+const clickSelected = (row) => {
+  tableRef.value.selectedRow(row)
+}
+
+const twinkleList = ref([])
+const scrollTo = (key) => {
+  twinkleList.value.push(key)
+  let index = tableRef.value.scrollToByKey(key)
+  if (index < 0) {
+    scrollToEnd()
+  }
+}
+
+const scrollToEnd = () => {
+  tableRef.value.scrollTo(tempYzData.value.length)
+}
+
+const mousePosition = ref()
+const opt = [
+  {
+    name: '关联', click: (data, index) => {
+      emits('clickAssociate', data)
+    }
+  }, {
+    name: '退出关联模式', click: () => {
+      clearAssociate()
+    }
+  }, {
+    name: '药品说明书', click: (data) => {
+      if (data.groupNo !== '00') {
+        drugManual.value.open(data.orderCode, data.serial);
+      }
+    }
+  }, {
+    name: '停止医嘱', click: (data) => {
+      if (data.tempCheckbox) {
+        cancelStopTime(data)
+      } else {
+        setDefaultStopTime(data)
+      }
+    },
+    validator: (data) => {
+      return !data.parentNo;
+    }
+  }, {
+    name: '作废', click: (data) => {
+      emits('voidOrders', data)
+    }
+  }
+]
+
+const contextmenuItem = (data: any, index: Number, event: Event) => {
+  mousePosition.value = {
+    event,
+    data: data,
+    index
+  }
+}
+
+const getSelectedData = () => {
+  return tableRef.value.getSelectedData()
+}
+
+const clearSelected = () => {
+  tableRef.value.clearSelected()
+}
 
 const nu = (val) => {
   if (val === null) {
@@ -211,9 +309,43 @@ const nu = (val) => {
   }
 }
 
+const callTemplate = (list) => {
+  twinkleList.value = list
+  scrollToEnd()
+}
+
+defineExpose({
+  scrollTo,
+  scrollToEnd,
+  getSelectedData,
+  clearSelected,
+  callTemplate
+})
+
 </script>
 
-<style lang="scss" scoped>
+<style lang="scss">
 
+.new_order {
+  position: relative;
+
+  &:before {
+    position: absolute;
+    right: -6px;
+    font-size: 15px;
+    top: -5px;
+    content: "*";
+    color: red;
+  }
+}
+
+@keyframes hzfirst {
+  from {
+    background-color: #95d475
+  }
+  to {
+    background-color: white;
+  }
+}
 </style>
 

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

@@ -301,11 +301,10 @@ const props = defineProps({
     type: Object,
     default: null
   },
-  successfullyEntered: Function,
   openGroupOrderTemplate: Function,
 })
 
-const emits = defineEmits(['duplicateAndPaste'])
+const emits = defineEmits(['duplicateAndPaste', 'successfullyEntered'])
 
 
 let parentOrder = [
@@ -601,7 +600,7 @@ const addOrderNo = async () => {
         errorMessageData.value = res.data
         return
       }
-      props.successfullyEntered(yiZhuData.value)
+      emits('successfullyEntered', yiZhuData.value)
       // 医嘱保存了 就删除错误提示
       if (errorMessageData.value) {
         for (let key in errorMessageData.value) {
@@ -731,8 +730,8 @@ const toAddAnOrder = async () => {
     if (Object.keys(errorMessageData.value).length === 0) {
       baoCunXinXiRef.value.openOrClose(false)
     }
+    emits('successfullyEntered', yiZhuData.value)
     qingKong()
-    props.successfullyEntered(yiZhuData.value)
   } catch (e) {
     console.log(e)
   }
@@ -825,8 +824,8 @@ const toDeleteAnOrderClick = () => {
     dangerouslyUseHTMLString: true
   }).then(() => {
     toDeleteAnOrder(yiZhuData.value.actOrderNo).then(() => {
+      emits('successfullyEntered', yiZhuData.value)
       qingKong()
-      props.successfullyEntered(yiZhuData.value)
     })
   })
 

+ 6 - 39
src/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/yz-header/YzQueryCondition.vue

@@ -60,18 +60,16 @@
 <script setup name='YzQueryCondition'>
 import {
   confirmTheDoctorSOrderWithMedicine,
-  deleteMultipleOrders,
   huoQuYiZhuShuJu
 } from "@/api/zhu-yuan-yi-sheng/yi-zhu-lu-ru";
 import StatusColor from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/yz-header/StatusColor";
 import {
-  errorMsg, getYzIndex, isCydy,
+  errorMsg,
+  isCydy,
   queryParam,
-  selectedData,
   yzData
 } from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
 import {BizException, ExceptionEnum} from "@/utils/BizException";
-import {ElMessageBox} from "element-plus";
 
 const props = defineProps({
   patientInfo: {
@@ -87,6 +85,8 @@ const props = defineProps({
   clickOnTheOrderTemplate: Function
 })
 
+const emits = defineEmits(['batchDeleteOrdersClick', 'clearSelect'])
+
 const queryYz = async () => {
   if (!props.patientInfo.inpatientNo) {
     BizException(ExceptionEnum.MESSAGE_ERROR, '请先选择患者')
@@ -97,7 +97,7 @@ const queryYz = async () => {
       patNo: props.patientInfo.inpatientNo,
       times: props.patientInfo.admissTimes
     })
-    selectedData.value = []
+    emits('clearSelect')
   } catch (e) {
     yzData.value = []
   }
@@ -130,40 +130,7 @@ const confirmTheDoctorSOrderWithMedicineClick = () => {
  * 点击批量删除数据
  */
 const batchDeleteOrdersClick = () => {
-  if (selectedData.value.length === 0) {
-    BizException(ExceptionEnum.LOGICAL_ERROR, "请先选择要删除的数据");
-  }
-  ElMessageBox.confirm('是否要批量删除这些医嘱?', '提示', {
-    type: 'warning'
-  }).then((res) => {
-    let param = {
-      inpatientNo: props.patientInfo.inpatientNo,
-      admissTimes: props.patientInfo.admissTimes,
-      list: selectedData.value
-    }
-    deleteMultipleOrders(param).then((res) => {
-      if (res !== null && res.error) {
-        let errData = []
-        for (const key in res.data) {
-          let index = getYzIndex(key)
-          let tempYzData = yzData.value[index]
-          yzData.value[index].error = true
-          errData.push({
-            actOrderNo: key,
-            orderName: tempYzData.orderName,
-            errorMessage: res.data[key]
-          })
-        }
-        errorMsg.value.dialog = true
-        errorMsg.value.type = 2
-        errorMsg.value.data = errData
-      } else {
-        queryYz();
-      }
-    })
-  }).catch(() => {
-
-  })
+  emits('batchDeleteOrdersClick')
 }
 
 watch(() => props.patientInfo, () => {

+ 80 - 26
src/views/hospitalization/zhu-yuan-yi-sheng/yi-zhu-lu-ru/YiZhuLuRu.vue

@@ -2,6 +2,8 @@
   <div :style="{width: winsize.editor + 40 + 'px'}" style="margin: 0">
     <yz-query-condition :patient-info="huanZheXinXi"
                         ref="yzQueryRef"
+                        @clearSelect="clearSelect"
+                        @batchDeleteOrdersClick="batchDeleteOrdersClick"
                         :to-delete-an-order="toDeleteAnOrder"
                         :add-yi-zhu-click="addYiZhuClick"
                         :click-on-the-order-template="clickOnTheOrderTemplate"
@@ -34,16 +36,13 @@
                @duplicate-and-paste="duplicateAndPaste"
                :current-page="currentPage"
                :open-group-order-template="openGroupOrderTemplate"
-               :successfullyEntered="successfullyEntered"/>
+               @successfullyEntered="successfullyEntered"/>
     <div style="overflow-x: auto; " :style="{width: winsize.main - 20  + 'px'}">
-      <yz-table :data="tempYzData"
-                ref="tableRef"
-                @rowClick="rowClick"
-                @clickAssociate="clickAssociate"
-                :void-orders="voidOrdersClick"/>
-<!--      <yz-table-v2 :data="tempYzData"-->
-<!--                   ref="tableRef"-->
-<!--                   @rowClick="rowClick"/>-->
+      <yz-table-v2 :data="tempYzData"
+                   ref="tableRef"
+                   @void-orders="voidOrdersClick"
+                   @clickAssociate="clickAssociate"
+                   @rowClick="rowClick"/>
     </div>
 
     <doctor-s-order-fee :data="chargeDetails.data"
@@ -84,7 +83,7 @@
 
 <script name="YiZhuLuRuZhuJian" setup>
 import {
-  associateOrdersApi,
+  associateOrdersApi, deleteMultipleOrders, huoQuYiZhuShuJu,
   insertTemplateOrder,
   stopOrder, voidOrders,
 } from '@/api/zhu-yuan-yi-sheng/yi-zhu-lu-ru'
@@ -93,13 +92,12 @@ import {
   getYzIndex,
   winsize,
   zkList,
-  selectedData,
   queryParam,
   currentPage,
   clickOnThePatient,
   drugManual,
   associateOrders,
-  clearAssociate, youWuXuanZheHuanZhe
+  clearAssociate, youWuXuanZheHuanZhe, yzData, errorMsg
 } from '../public-js/zhu-yuan-yi-sheng'
 import store from '@/store'
 import {listIsBlank, stringIsBlank, stringNotBlank} from '@/utils/blank-utils'
@@ -110,7 +108,6 @@ import DoctorSOrderFee from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/DoctorS
 import {BizException, ExceptionEnum} from "@/utils/BizException";
 import {ElMessageBox} from "element-plus";
 import YzQueryCondition from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/yz-header/YzQueryCondition";
-import YzTable from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/table/YzTable";
 import YzEditor from "@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/yz-edit/YzEditor.vue";
 import {tempYzData} from "@/views/hospitalization/zhu-yuan-yi-sheng/public-js/zhu-yuan-yi-sheng";
 import HuoQuMuBan from '@/components/zhu-yuan-yi-sheng/yi-zhu-lu-ru/HuoQuMuBan.vue'
@@ -148,11 +145,17 @@ const orderProgressRef = ref(null)
 
 // 医嘱编辑
 const yzEditorRef = ref(null)
-const successfullyEntered = async () => {
-  await yzQueryRef.value.queryYz()
+const successfullyEntered = async (data) => {
   queryParam.value.displayRange = 0
   queryParam.value.zhuangTai = 0
-  tableRef.value.scrollToTheEnd()
+  yzData.value = await huoQuYiZhuShuJu({
+    patNo: huanZheXinXi.value.inpatientNo,
+    times: huanZheXinXi.value.admissTimes
+  })
+  clearSelect()
+  if (data != null) {
+    tableRef.value.scrollTo(parseInt(data.actOrderNo))
+  }
   addYiZhuClick()
 }
 /**
@@ -172,7 +175,6 @@ const confirmOrdersClick = async () => {
   let temp = await reasonableRef.value.check(huanZheXinXi.value.inpatientNo, huanZheXinXi.value.admissTimes)
   if (temp) {
     await confirmOrder()
-    console.log('无错误药品直接确认');
   }
 }
 
@@ -196,7 +198,7 @@ const openRationalDrugUse = () => {
 }
 
 // 表格
-let tableRef = ref(null)
+const tableRef = ref(null)
 const rowClick = (val) => {
   orderProgressRef.value.fillOrder(val)
   if (associateOrders.value.actOrderNo) {
@@ -232,20 +234,22 @@ const stopOrderDialog = ref({
  * 停止医嘱
  */
 const clickToStopTheOrder = () => {
-  selectedData.value.forEach(item => {
+  let tempData = tableRef.value.getSelectedData()
+  tempData.forEach(item => {
     item.endTime = getFormatDatetime(item.endTimeTemp)
   })
+
   let param = {
     inpatientNo: huanZheXinXi.value.inpatientNo,
     admissTimes: huanZheXinXi.value.admissTimes,
-    list: selectedData.value
+    list: tempData
   }
   stopOrder(param).then(res => {
     if (res?.error) {
       stopOrderDialog.value.error = res.data
       stopOrderDialog.value.dialog = true
-      for (let i = 0; i < selectedData.value.length; i++) {
-        let item = selectedData.value[i]
+      for (let i = 0; i < tempData.length; i++) {
+        let item = tempData[i]
         item.endTime = ''
       }
     } else {
@@ -348,9 +352,10 @@ const muBanShuJu = (val) => {
     groupNo: queryParam.value.groupNo,
     list: val
   }
-  insertTemplateOrder(param).then(() => {
-    successfullyEntered()
+  insertTemplateOrder(param).then(async (list) => {
     mubanRef.value.openOrCloseDialog(false)
+    await successfullyEntered()
+    tableRef.value.callTemplate(list)
   })
 }
 
@@ -376,7 +381,9 @@ const orderTemplateClick = () => {
 }
 
 const orderTemplateClickCopy = () => {
-  if (listIsBlank(selectedData.value)) {
+  let temp = tableRef.value.getSelectedData()
+
+  if (listIsBlank(temp)) {
     xcMessage.error('请先选中医嘱。')
     return
   }
@@ -387,7 +394,7 @@ const orderTemplateClickCopy = () => {
   router.push({
     name: 'orderTemplateMaintenance',
     params: {
-      data: JSON.stringify(selectedData.value),
+      data: JSON.stringify(temp),
       dept: JSON.stringify(dept)
     }
   })
@@ -462,9 +469,56 @@ const duplicateAndPaste = async () => {
   tableRef.value.scrollToTheEnd()
 }
 
+const clearSelect = () => {
+  tableRef.value.clearSelected()
+}
+
+/**
+ * 点击批量删除数据
+ */
+const batchDeleteOrdersClick = () => {
+  let tempData = tableRef.value.getSelectedData()
+  if (tempData.length === 0) {
+    BizException(ExceptionEnum.LOGICAL_ERROR, "请先选择要删除的数据");
+  }
+  ElMessageBox.confirm('是否要批量删除这些医嘱?', '提示', {
+    type: 'warning'
+  }).then(() => {
+    let param = {
+      inpatientNo: huanZheXinXi.value.inpatientNo,
+      admissTimes: huanZheXinXi.value.admissTimes,
+      list: tempData
+    }
+    deleteMultipleOrders(param).then((res) => {
+      if (res !== null && res.error) {
+        let errData = []
+        for (const key in res.data) {
+          let index = getYzIndex(key)
+          let tempYzData = yzData.value[index]
+          yzData.value[index].error = true
+          errData.push({
+            actOrderNo: key,
+            orderName: tempYzData.orderName,
+            errorMessage: res.data[key]
+          })
+        }
+        errorMsg.value.dialog = true
+        errorMsg.value.type = 2
+        errorMsg.value.data = errData
+      } else {
+        yzQueryRef.value.queryYz()
+      }
+    })
+  }).catch(() => {
+  })
+}
+
 watch(() => router.currentRoute.value, () => {
   currentPage.value = router.currentRoute.value.query.pattern
   queryParam.value.frequCode = currentPage.value
+  if (router.currentRoute.value.path === '/inpatient/zhuYuanYiSheng/yiZhuLuRu' && !currentPage.value) {
+    router.push('/inpatient/zhuYuanYiSheng/yiZhuLuRu?pattern=all')
+  }
 }, {immediate: true})
 
 onActivated(async () => {

+ 16 - 26
src/views/settings/Test.vue

@@ -1,5 +1,6 @@
 <template>
   <xc-table-v2 :data="allItems"
+               ref="tableRef"
                @rowClick="rowClick"
                @dbRowClick="setDefaultStopTime"
                :columns="header"/>
@@ -7,11 +8,12 @@
 
 
 <script setup name='Test' lang="tsx">
-import {ref} from 'vue'
+import {Ref, ref} from 'vue'
 import XcTableV2 from "@/components/xiao-chan/xc-table-v2/XcTableV2.vue";
 import {stringIsBlank} from "@/utils/blank-utils";
 import {getFormatDatetime} from "@/utils/date";
 import {getServerDateApi} from '@/api/public-api'
+import {XcTableV2Type} from "@/components/xiao-chan/xc-table-v2/XcTableV2Type";
 
 const allItems = ref([
   {
@@ -2419,24 +2421,12 @@ for (let i = 0; i < 1000; i++) {
   })
 }
 
-
+const tableRef: Ref<XcTableV2Type | null> = ref(null)
 const header = [
-  {
-    type: 'selected'
-  },
-  {width: 30, type: 'index'},
+  {type: 'selected', width: 20, fixed: 'left'},
+  {width: 30, type: 'index', fixed: 'left'},
+
   {width: 20, code: 'tempCheckbox', name: '组'},
-  // {
-  //   width: 20, name: '#',
-  //   cellRenderer: ({data}) => (
-  //       <input type="checkbox"
-  //              checked={data.tempCheckbox}
-  //              onClick={(event) => clickSelected(data)}/>
-  //   ),
-  //   headerFunc: () => {
-  //     console.log(123)
-  //   }
-  // },
   {
     width: 30, name: '排序',
     cellRenderer: ({index}) => (
@@ -2518,12 +2508,12 @@ const header = [
           e.stopImmediatePropagation();
           console.log(data)
         }}>作废</button>
-    )
-  }
+    ), fixed: 'right'
+  },
 ];
 
 const clickSelected = (row) => {
-  row.tempCheckbox = !row.tempCheckbox
+  // row.tempCheckbox = !row.tempCheckbox
 }
 
 const timeFomat = (val) => {
@@ -2576,15 +2566,15 @@ const setDefaultStopTime = async (item) => {
   if (item.parentNo) return
   item.endTimeTemp = getFormatDatetime(await getServerDateApi(), 'yyyy-MM-DD HH:mm')
   if (!item.tempCheckbox) {
-    clickSelected(item)
+    tableRef.value.selectedRow(item)
   }
 }
 
-const cancelStopTime = (val) => {
-  if (val.parentNo) return
-  val.endTimeTemp = ''
-  if (val.tempCheckbox) {
-    clickSelected(val)
+const cancelStopTime = (item) => {
+  if (item.parentNo) return
+  item.endTimeTemp = ''
+  if (item.tempCheckbox) {
+    tableRef.value.selectedRow(item)
   }
 }