Explorar o código

完善表格和护理记录用虚拟化表格

xiaochan %!s(int64=2) %!d(string=hai) anos
pai
achega
769b1c614c

+ 3 - 3
src/api/zhu-yuan-yi-sheng/emr-patient.js

@@ -272,11 +272,11 @@ export function getListOfDischargedPatients(patNo) {
     })
 }
 
-export function getYzTemperature(patNo, times) {
+export function getYzTemperature(data) {
     return request({
         url: url + 'getYzTemperature',
-        method: 'get',
-        params: {patNo, times}
+        method: 'post',
+        data
     })
 }
 

+ 140 - 46
src/components/xiao-chan/sd-table/SdTable.vue

@@ -1,61 +1,155 @@
 <template>
-    <div class="sd-table__inner-wrapper">
-        <div class="hidden-columns">
-            <slot/>
-        </div>
-        <sd-table-header/>
-        <sd-table-body :data="props.data"/>
+  <div class="sd-table__inner-wrapper" ref="sdTableRef">
+    <div class="hidden-columns">
+      <slot/>
     </div>
+    <sd-table-header v-bind="props"
+                     :temp-columns="tempColumns"
+                     :is-child="isChild"
+                     :sd-mitt="sdMitt"/>
+    <sd-table-body v-bind="props"
+                   ref="tableRef"
+                   :temp-columns="tempColumns"
+                   :sd-mitt="sdMitt"/>
+  </div>
 </template>
 
 
-<script name='SdTable' lang="ts">
-import {ref, defineComponent, getCurrentInstance} from 'vue'
-import {useNamespace} from "../sd-table/use-namespace";
+<script name='SdTable' setup lang="tsx">
+import {defineProps, onMounted, watch, ref, nextTick} from 'vue'
 import SdTableHeader from "../sd-table/table-header/SdTableHeader.vue";
 import SdTableBody from "./table-body/SdTableBody.vue";
+import XEUtils from 'xe-utils'
+import Mitt from "../../../utils/mitt";
+import {useIntersectionObserver} from "@vueuse/core";
 
-let tableIdSeed = 1
-
-export default defineComponent({
-    name: "SdTable",
-    components: {SdTableBody, SdTableHeader},
-    props: {
-        data: {
-            type: Array,
-            default: []
-        },
-        defaultSort: Object
-    },
-    setup(props) {
-        const store = ref({
-            column: []
-        })
-
-        const getStore = () => {
-            return store.value
-        }
-        const pushColumn = (val) => {
-            store.value.column.push(val)
-        }
-        const ns = useNamespace('table')
+const props = defineProps({
+  data: {
+    type: Array,
+    default: []
+  },
+  columns: {
+    type: Array,
+    default: []
+  },
+  scrollConfig: {
+    type: Object,
+    default: {
+      x: '15px',
+      y: '15px'
+    }
+  }
+})
 
-        const tableId = `${ns.namespace.value}-table_${tableIdSeed++}`
-        const table = getCurrentInstance()
-        table.tableId = tableId
-        table.store = {
-            column: []
-        }
+const sdMitt = new Mitt()
+const tableRef = ref()
+
+const pushColumns = (id, val) => {
+  for (let i = 0, len = props.columns.length; i < len; i++) {
+    let item = props.columns[i]
+    if (item.code === id) {
+      XEUtils.assign(item, val)
+      return
+    }
+  }
+}
+
+const selected = (item) => {
+  item.headerSelectRender = () => {
+    return <ElCheckbox/>
+  }
+}
+
+const sdTableRef = ref(null)
+const isChild = ref(false)
+
+const tempColumns = ref([])
+const fixedStyle = (item, index, location, val) => {
+  let str = item.fixedStyle
+
+  let tempList = []
+  if (location === 'right') {
+    tempList = XEUtils.slice(val, index, props.data.length - 1)
+  } else {
+    tempList = XEUtils.slice(val, 0, index)
+  }
+
+  let px = 0
+  tempList.forEach(item => {
+    px += item.width
+  })
+
+  str['position'] = 'sticky!important';
+  str[location] = px + 'pt'
+  str['background'] = '#fff'
+}
+
+const setStyle = () => {
+
+  tempColumns.value = XEUtils.clone(props.columns, true)
 
-        return {
-            getStore,
-            pushColumn,
-            tableId,
-            props
+  let left = {
+    fixedStyle: null
+  }
+  let right = {
+    fixedStyle: null
+  }
+
+  XEUtils.arrayEach(tempColumns.value, (item, index) => {
+
+    if (item.child) {
+      isChild.value = true
+    }
+
+    item.fixedStyle = {}
+    item.fixedStyle.width = item.width + 'pt'
+
+    if (item.fixed && item.fixed === 'left') {
+      fixedStyle(item, index, 'left', tempColumns.value)
+      left = item
+    } else if (item.fixed && item.fixed === 'right') {
+      fixedStyle(item, index, 'right', tempColumns.value)
+      if (right.fixedStyle === null) {
+        right = item
+        item.fixedStyle['border-left'] = '1px solid #ebeef5'
+        item.fixedStyle['box-shadow'] = 'inset 10px 0 10px -10px rgba(0, 0, 0, .15)'
+      }
+    }
+  })
+
+  left.fixedStyle['border-right'] = '1px solid #ebeef5'
+  left.fixedStyle['box-shadow'] = 'inset -10px 0 10px -10px rgba(0, 0, 0, .15)'
+}
+
+watch(() => props.columns, () => {
+  setStyle()
+}, {immediate: true, deep: true})
+
+onMounted(async () => {
+
+
+  watch(() => props.data.length, () => {
+    tableRef.value.changeData()
+  })
+
+  await nextTick()
+
+  useIntersectionObserver(
+      sdTableRef,
+      ([{isIntersecting}], observerElement) => {
+        if (isIntersecting) {
+          sdMitt.emit('setScrollTop')
         }
-    },
 
-});
+      },
+  )
+
+})
+
+defineExpose({
+  pushColumns
+})
+
 </script>
 
 <style scoped lang="scss">

+ 117 - 56
src/components/xiao-chan/sd-table/table-body/SdTableBody.vue

@@ -1,87 +1,141 @@
 <template>
-    <div v-bind="containerProps" style="height: 200px" class="sd-body">
-        <div v-bind="wrapperProps">
-            <div v-for="item in list"
-                 :key="item.index"
-                 class="row"
-                 @click="rowClick(item)"
-                 style="height: 23px">
-                <template v-for="(he,heindex) in column">
-                    <div class="cell" :style="{width: he.width +'px'}" :title="getTitle(item,he)">
-                        <component :is="createTd(item,he)"/>
-                    </div>
-                </template>
-            </div>
-        </div>
+  <div v-bind="containerProps" style="height: 200px" class="sd-body">
+    <div v-bind="wrapperProps">
+      <div v-for="item in list"
+           :key="item.index"
+           class="row"
+           :class="rowClass(item)"
+           @mouseenter="isHoverIndex = item.index"
+           @mouseleave="isHoverIndex = -1"
+           @click="rowClick(item)"
+           style="height: 23px">
+        <template v-for="(he,heindex) in tempColumns">
+          <div class="cell"
+               :style="he.fixedStyle"
+               :title="getTitle(item,he)">
+
+            <template v-if="he.type">
+              <template v-if="he.type === 'selected'">
+                <el-checkbox v-model="item.data.$selected"/>
+              </template>
+
+              <template v-else-if="he.type === 'index'">
+                {{ item.index + 1 }}
+              </template>
+
+            </template>
+
+
+            <template v-else>
+              <component :is="createTd(item,he)"/>
+            </template>
+
+          </div>
+        </template>
+      </div>
     </div>
+  </div>
 </template>
 
 <script setup name='SdTableBody' lang="tsx">
-import {defineProps, ref, onMounted, getCurrentInstance, computed} from 'vue'
+import {defineProps, ref, onMounted, nextTick} from 'vue'
 import {useVirtualList} from "@vueuse/core";
 
-const props = defineProps({
-    data: {
-        type: Array,
-        default: []
+const {data, tempColumns, columns, sdMitt} = defineProps({
+  data: {
+    type: Array,
+    default: []
+  },
+  tempColumns: {
+    type: Array,
+    default: []
+  },
+  columns: {
+    type: Array,
+    default: []
+  },
+  sdMitt: {
+    type: Object
+  },
+  scrollConfig: {
+    type: Object,
+    default: {
+      x: '9px',
+      y: '9px'
     }
+  }
 })
 
 const {list, containerProps, wrapperProps, scrollTo} = useVirtualList(
-    props.data,
+    data,
     {
-        itemHeight: 23,
+      itemHeight: 23,
     },
 )
 
-const column = ref([])
-
-const createTd = ({data, index}, columnRow): any => {
-    if (columnRow.slots) {
-        if (data.$edit) {
-            return <div>{columnRow.editCell(data, index)}</div>;
-        } else {
-            return <div>{columnRow.renderCell(data, index)}</div>;
-        }
-    } else {
-        return <div>{data[columnRow.prop]}</div>
-    }
-}
 
-const getTitle = ({data, index}, columnRow) => {
+const createTd = (row, columnRow): any => {
+  let {data, index} = row
+
+  if (columnRow.editCell) {
     try {
-        return data[columnRow.prop];
+      return <div>{columnRow.renderCell(data, index)}</div>;
     } catch {
-        return '';
+      return <div>{data[columnRow.code]}</div>
     }
+  } else {
+    return <div>{data[columnRow.code]}</div>
+  }
+}
+
+const getTitle = ({data, index}, columnRow) => {
+  try {
+    return data[columnRow.code];
+  } catch {
+    return '';
+  }
 }
 
 let oldRow = {
-    data: {$edit: false}
+  data: {$edit: false}
 }
 const rowClick = (item) => {
-    oldRow.data.$edit = false
-    item.data.$edit = true
-    oldRow = item
+  oldRow.data.$edit = false
+  item.data.$edit = true
+  oldRow = item
 }
 
-
+const isHoverIndex = ref(0)
+const rowClass = ({data, index}) => {
+  if (isHoverIndex.value === index) {
+    return 'is-hovered'
+  }
+}
 
 let scrollTop = 0;
 
-onMounted(() => {
-    const instance = getCurrentInstance()
-    let parent = instance.parent
-    column.value = parent.store.column
+onMounted(async () => {
+  await nextTick()
+
+  sdMitt.on('setScrollTop', () => {
+    containerProps.ref.value.scrollTop = scrollTop
+  })
+
 
-    containerProps.ref.value.addEventListener('scroll', (val) => {
-        scrollTop = containerProps.ref.value.scrollTop
+  containerProps.ref.value.addEventListener('scroll', (val) => {
+    scrollTop = containerProps.ref.value.scrollTop
+    sdMitt.emit('changeScrollX', containerProps.ref.value.scrollLeft)
+  })
 
-        parent.store.headerScrollLeft(containerProps.ref.value.scrollLeft)
+})
+
+const changeData = async () => {
+  await nextTick()
+  sdMitt.emit('changeScrollY', containerProps.ref.value.scrollHeight > containerProps.ref.value.clientHeight)
+}
 
-        // console.log(containerProps.ref.value.scrollTop);
-        // console.log(containerProps.ref.value.scrollLeft);
-    })
+defineExpose({
+  changeData
 })
 
 </script>
@@ -91,7 +145,15 @@ onMounted(() => {
   background-color: white;
 
   &::-webkit-scrollbar {
-    height: 6px;
+    height: v-bind('scrollConfig.x');
+    width: v-bind('scrollConfig.y');
+    cursor: pointer;
+  }
+
+  .is-hovered {
+    .cell {
+      background-color: #f5f7fa !important;
+    }
   }
 
   .row {
@@ -99,11 +161,9 @@ onMounted(() => {
     display: flex;
     align-items: center;
 
-    &:hover {
-      background-color: #e0ecff;
-    }
 
     .cell {
+      box-sizing: border-box;
       overflow: hidden;
       display: flex;
       align-items: center;
@@ -115,6 +175,7 @@ onMounted(() => {
       background-color: inherit;
       color: inherit;
       border-bottom: 1px solid #ebeef5;
+      padding: 0 5px;
     }
   }
 

+ 5 - 15
src/components/xiao-chan/sd-table/table-column/sd-column.ts

@@ -4,9 +4,6 @@ export default defineComponent({
     name: 'SdColumn',
     props: {
         prop: String,
-        label: String,
-        width: String,
-        type: String
     },
     setup(props, {slots}) {
         const renderCell = (data, index) => {
@@ -26,24 +23,17 @@ export default defineComponent({
                 return renderCell(data, index)
             }
         }
+
         const instance = getCurrentInstance()
+        let parent = instance.parent
         let data = {
-            label: props.label,
-            prop: props.prop,
-            width: props.width,
             renderCell: renderCell,
+            editCell: editCell,
             slots: slots.default,
             editSlots: slots.edit,
-            editCell: editCell
         }
-        const owner = computed(() => {
-            let parent = instance.parent as any
-            while (parent && !parent.tableId) {
-                parent = parent.parent
-            }
-            return parent
-        })
-        owner.value.store.column.push(data)
+
+        parent.exposed.pushColumns(props.prop, data)
     },
     render() {
         return h('div', null, [])

+ 104 - 26
src/components/xiao-chan/sd-table/table-header/SdTableHeader.vue

@@ -1,57 +1,135 @@
 <template>
-    <div class="sd-table-header" ref="sdHeaderRef">
-        <div v-for="item in column"
-             class="sd-th"
-             :style="{width: item.width +'px'}">
-            {{ item.label }}
+  <div class="sd-table-header"
+       ref="sdHeaderRef">
+    <div v-for="item in tempColumns"
+         class="sd-th"
+         :class="isChild ? 'show_border' : ''"
+         :style="item.fixedStyle">
+      <template v-if="item.type">
+        <template v-if="item.type === 'selected'">
+          <el-checkbox v-model="selectedAll"
+                       @click="clickSelectedAll($event,!selectedAll)"
+                       :indeterminate="intermediate"/>
+        </template>
+        <template v-else-if="item.type ==='index'">
+          #
+        </template>
+      </template>
+      <template v-else>
+        <div>
+          {{ item.name }}
         </div>
+      </template>
     </div>
+  </div>
 </template>
 
-<script setup lang="ts">
-import {computed, getCurrentInstance, nextTick, onMounted, ref} from "vue";
+<script setup lang="tsx">
+import {computed, defineProps, onMounted, ref} from "vue";
+import {ElCheckbox} from "element-plus";
+import {useElementSize} from "@vueuse/core";
 
-const instance = getCurrentInstance()
-let parent = instance.parent
-const column = ref([])
+const {data, columns, sdMitt, scrollConfig, isChild, tempColumns} = defineProps({
+  data: {
+    type: Array,
+    default: []
+  },
+  columns: {
+    type: Array,
+    default: []
+  },
+  tempColumns: {
+    type: Array,
+    default: []
+  },
+  sdMitt: {
+    type: Object
+  },
 
-const owner = computed(() => {
-    let parent = instance.parent as any
-    while (parent && !parent.tableId) {
-        parent = parent.parent
+  scrollConfig: {
+    type: Object,
+    default: {
+      x: '9px',
+      y: '9px'
     }
-    return parent
+  },
+  isChild: {
+    type: Boolean,
+    default: false
+  }
 })
 
-const sdHeaderRef = ref(null)
+const sdHeaderRef = ref<HTMLHtmlElement>(null)
 
-onMounted(async () => {
-    await nextTick()
-    column.value = owner.value.store.column
+const scrollY = ref('0px')
 
-    owner.value.store.headerScrollLeft = (val) => {
-        sdHeaderRef.value.scrollLeft = val
-        console.log(val)
-        console.log(sdHeaderRef.value.scrollLeft)
+const selectedAll = ref(false)
+const intermediate = computed(() => {
+  let len = data.length
+  let tempLen = 0;
+  for (let i = 0; i < len; i++) {
+    let item = data[i]
+    if (item.$selected) {
+      tempLen += 1
     }
+  }
+  if (tempLen === 0) {
+    selectedAll.value = false
+    return false
+  }
+  if (tempLen === len) {
+    selectedAll.value = true
+    return false
+  }
+  return true
 })
+const clickSelectedAll = (e: Event, val: boolean) => {
+  e.stopImmediatePropagation()
+  e.preventDefault();
+  for (let i = 0, len = data.length; i < len; i++) {
+    let item = data[i]
+    item.$selected = val
+  }
+}
+
+const {width: elWidth, height: elHeight} = useElementSize(sdHeaderRef)
+
+
+onMounted(() => {
+  sdMitt.on('changeScrollX', (val) => {
+    sdHeaderRef.value.scrollLeft = val
+  })
+  scrollY.value = scrollConfig.y
+  sdMitt.on('changeScrollY', (showScroll) => {
+    scrollY.value = showScroll ? scrollConfig.y : '0px'
+  })
+})
+
 </script>
 
 <style lang="scss">
 .sd-table-header {
-  height: max-content;
+  height: v-bind(elHeight + 'px');
   display: flex;
-  align-items: center;
   border-bottom: 1px solid #ebeef5;
   overflow-x: hidden;
+  padding-right: v-bind(scrollY);
+  align-items: center;
+
+  div {
+    box-sizing: border-box;
+  }
 
   .sd-th {
+    height: 100%;
     box-sizing: border-box;
     overflow: hidden;
     text-overflow: ellipsis;
     white-space: nowrap;
     flex-grow: 0;
-    flex-shrink: 0
+    flex-shrink: 0;
+    padding: 0 5px;
+
   }
 }
 </style>

+ 3 - 1
src/components/zhu-yuan-yi-sheng/emr/auxiliary-tools/EmrAuxiliaryTools.vue

@@ -41,7 +41,9 @@
     <emr-order-list v-if="index === 0"
                     :pat-no="patInfo.inpatientNo"
                     :times="patInfo.admissTimes"/>
-    <emr-yz-temperature v-if="index === 1"/>
+    <emr-yz-temperature v-if="index === 1"
+                        :pat-no="patInfo.inpatientNo"
+                        :times="patInfo.admissTimes"/>
     <inspection-report-index
         style="font-size: 12px;height: 100%"
         v-if="index === 2"

+ 40 - 15
src/components/zhu-yuan-yi-sheng/emr/auxiliary-tools/EmrYzTemperature.vue

@@ -12,15 +12,26 @@
   <div style="height: 90%">
     <el-auto-resizer>
       <template #default="{ height, width }">
-        <vxe-table :height="height"
-                   :max-height="height"
-                   border
-                   class="vxe-padding_zero hl-style"
-                   header-row-class-name="padding_zero"
+        <div style="height: 40px; ">
+          <el-date-picker v-model="dateRange"
+                          type="daterange"
+                          format="YYYY-MM-DD"
+                          value-format="YYYY-MM-DD"
+                          :shortcuts="shortcuts"/>
+          <el-button @click="query">查询</el-button>
+        </div>
+        <vxe-table :height="height - 40"
                    show-overflow
-                   :scroll-y="{enabled: false}"
+                   show-header-overflow
+                   show-footer-overflow
+                   show-footer
+                   border
+                   :scroll-x="{gt: 10}"
+                   :scroll-y="{gt: 10}"
                    :column-config="{resizable: true}"
                    :row-config="{ height: 24}"
+                   class="vxe-padding_zero hl-style"
+                   header-row-class-name="padding_zero"
                    :row-class-name="rowClassName"
                    @cell-click="rowClick"
                    :data="listRef">
@@ -96,15 +107,22 @@
 <script setup name='EmrYzTemperature' lang="tsx">
 import {Ref, ref, onMounted} from "vue";
 import {getYzTemperature} from "@/api/zhu-yuan-yi-sheng/emr-patient";
-import {patInfo} from './emr-tools-store';
 import {stringIsBlank} from '@/utils/blank-utils';
 import {stringNotBlank} from "@/utils/blank-utils";
+import {shortcuts} from '@/data/shortcuts'
+import {getServerDateApi} from "@/api/public-api";
+import moment from "moment";
+import {getFormatDatetime} from '@/utils/date'
+
 
 const props = defineProps({
   patNo: String,
   times: Number
 })
 
+const dateRange = ref([])
+
+
 const dateDisplay = (row, cellData, index) => {
   let temp = listRef.value[index - 1]
   if (temp === null || typeof temp === 'undefined') {
@@ -201,8 +219,16 @@ const rowClick = ({row}) => {
   }
   otherInfo.value = str
 }
-const query = (patNo, times) => {
-  getYzTemperature(patNo, times)
+const query = () => {
+
+  let param = {
+    startTime: dateRange.value[0],
+    endTime: dateRange.value[1],
+    patNo: props.patNo,
+    times: props.times
+  }
+
+  getYzTemperature(param)
       .then((res) => {
         let {list, map} = res
         drawer.value = true
@@ -218,12 +244,11 @@ const rowClassName = ({row}) => {
   return 'row_padding_zero'
 }
 
-onMounted(() => {
-  if (stringNotBlank(props.patNo)) {
-    query(props.patNo, props.times)
-  } else {
-    query(patInfo.value.inpatientNo, patInfo.value.admissTimes)
-  }
+onMounted(async () => {
+  let now = await getServerDateApi()
+  let start = getFormatDatetime(moment(now).subtract(7, "day"), 'YYYY-MM-DD')
+  dateRange.value = [start, getFormatDatetime(now, 'YYYY-MM-DD')]
+  query()
 })
 </script>
 

+ 45 - 6
src/views/settings/Test.vue

@@ -1,15 +1,54 @@
 <template>
-  <fluorescence-test pat-no="0403883" :times="3"/>
-
+  <el-button @click="test">测试</el-button>
+  <sd-table :data="data" style="width: 400px" :columns="columns">
+    <sd-column prop="code">
+      <template #default="scope">
+        {{ scope.row.$selected }}
+      </template>
+    </sd-column>
+  </sd-table>
 </template>
 
 <script setup>
-import FluorescenceTest
-  from "@/components/zhu-yuan-yi-sheng/emr/auxiliary-tools/fluorescence-test/FluorescenceTest.vue";
+import SdTable from "@/components/xiao-chan/sd-table/SdTable.vue";
+import SdColumn from "@/components/xiao-chan/sd-table/table-column/sd-column";
 
+const data = ref([])
 
-</script>
+onMounted(() => {
+  for (let i = 0; i < 100; i++) {
+    data.value.push({
+      code: 'code_' + i,
+      name: 'name_' + i,
+      te1: 'te1' + i,
+      te2: 'te2' + i
+    })
+  }
+})
+
+const columns = ref([
+  {type: 'index', width: 30, fixed: 'left'},
+  {type: 'selected', width: 30, fixed: 'left'},
+  {
+    code: 'code', name: '编码', width: 120,
+  },
+  {code: 'code', name: '编码', width: 120, fixed: 'right'},
+
+])
 
-<style>
+const test = () => {
+  columns.value.push({
+    code: 'name', name: '名称', width: 220
+  })
+}
+
+
+</script>
 
+<style lang="scss">
+.twe {
+  div {
+    border: 1px solid #000;
+  }
+}
 </style>