zs
2025-05-06 2fd8242a14241beba63e71c6f4222b702f0e2f30
HIAWms/web/src/components/Table/components/custom-vxe-column.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,555 @@
<template>
  <vxe-column
    width="60"
    v-if="currentSeq.seq || showDarg || isDrag || isChecked"
    :fixed="currentSeq.column.fixed"
  >
    <template #header v-if="!radio">
      <el-checkbox
        v-if="isChecked"
        class="th-td-checkbox"
        v-model="checkedAll.checked"
        :indeterminate="checkedAll.isIndeterminate"
        @change="selectChangeAllEvent"
      ></el-checkbox>
      <span v-else-if="currentSeq.seq">{{ _gt(currentSeq.column.title) }}</span>
    </template>
    <template #default="{ row, rowIndex }">
      <div class="custom-td-action">
        <div
          v-show="showDarg || isDrag"
          :class="{
            'drag-move': true,
            'information-row-td': true,
            'td-hover': true,
            disabled: disabledDrag,
          }"
        >
          <Icon icon="icon_move" class="btn-move"></Icon>
        </div>
        <span
          v-if="currentSeq.seq"
          :style="currentCheckedStyle(row, true)"
          class="information-row-sort td-sort-hover"
          >{{ rowIndex + 1 }}</span
        >
        <template v-if="radio">
          <el-radio
            style="top: 3px; right: 0px"
            :value="row[id || 'id']"
            :modelValue="radioValue"
            :style="currentCheckedStyle(row)"
            @change="onRadioChange(row)"
            class="information-row-td td-hover checkout-style"
          ></el-radio>
        </template>
        <template v-else>
          <a-checkbox
            v-if="
              isFunction(isChecked) ? isChecked({ row, rowIndex }) : isChecked
            "
            :key="row[props.id || 'id']"
            v-model="selectionMap[row[props.id || 'id']]"
            :style="currentCheckedStyle(row)"
            class="information-row-td td-hover checkout-style"
            @change="(checked: boolean) => currentCheckedEvent(checked, row)"
          ></a-checkbox>
        </template>
      </div>
    </template>
  </vxe-column>
  <template
    v-for="(column, index) in columns"
    :field="column.field"
    :key="column.field"
  >
    <!-- header -->
    <vxe-column
      v-bind="column"
      :sortable="column.sortable"
      v-if="slots[column?.field] || slots[`${column?.field}.header`]"
      show-header-overflow="title"
    >
      <template #header="{ row }: any" v-if="slots[`${column?.field}.header`]">
        <slot
          :name="`${column?.field}.header`"
          :row="row"
          :index="index"
          :column="column"
        ></slot>
      </template>
      <!-- column.required == true -->
      <template #header v-if="column?.required">
        <span style="color: #ff2929">*</span>
        {{ _t(column.title) }}
      </template>
      <!-- tipConfig header -->
      <template #header v-if="column?.tipConfig?.tip">
        <div class="header-tip-config-row">
          <span v-if="column?.required" style="color: #ff2929">*</span>
          <span :style="column?.tipConfig?.style">
            {{ _t(column.title) }}
          </span>
          <el-tooltip
            effect="dark"
            :content="column?.tipConfig?.tip + ''"
            placement="top"
            :persistent="false"
          >
            <Icon
              v-if="column?.tipConfig?.icon"
              :width="16"
              :height="16"
              :icon="column?.tipConfig?.icon"
            ></Icon>
          </el-tooltip>
        </div>
      </template>
      <!-- tip -->
      <template #default="{ row, rowIndex }">
        <div
          class="table-context-menu-content"
          slot-type="native"
          @click="(event: Event) => onRowClick(row, event)"
        >
          <div
            @contextmenu="(event) => onClickShowMenu(event, row, rowIndex)"
            slot-type="native"
            class="table-context-menu"
          >
            <slot
              :name="column.field"
              :row="row"
              :index="rowIndex"
              :column="column"
            ></slot>
          </div>
        </div>
      </template>
    </vxe-column>
    <vxe-column v-bind="column" :title="_t(column.title)" v-else>
      <template #default="{ row, rowIndex }">
        <div
          class="table-context-menu-content"
          slot-type="native"
          @click="(event: Event) => onRowClick(row, event)"
        >
          <template v-if="get(row, column.field)">
            <div
              @contextmenu="(event) => onClickShowMenu(event, row, rowIndex)"
              class="table-context-menu"
              slot-type="native"
            >
              <el-tooltip
                effect="dark"
                :content="get(row, column.field) + ''"
                placement="top"
                :persistent="false"
              >
                <span slot-type="native" class="over-ellipsis">{{
                  get(row, column.field)
                }}</span>
              </el-tooltip>
            </div>
          </template>
          <template v-else>
            <div
              @contextmenu="(event) => onClickShowMenu(event, row, rowIndex)"
              slot-type="native"
              class="table-context-menu"
            >
              <span slot-type="native" class="over-ellipsis">{{
                isNil(get(row, column.field)) ? '-' : get(row, column.field)
              }}</span>
            </div>
          </template>
        </div>
      </template>
    </vxe-column>
  </template>
</template>
<script setup lang="ts">
// @ts-nocheck
import { computed, ref, useSlots, useAttrs, Ref, watch, inject } from 'vue'
import Icon from '../../Icon/Icon'
import type { TablePropsItemType, contextMenuItemType } from '../index.d'
import { useVModel } from '@vueuse/core'
import VxeColumn from 'vxe-table/es/column/index'
import { getScopeT } from '@/libs/Language/Language'
import { isFunction, get, isNil, cloneDeep } from 'lodash'
interface vxePropsType extends TablePropsItemType {
  id: string
  contextMenuConfig: contextMenuItemType
  headBorder: boolean
  rowStyle: any
}
const props = defineProps<vxePropsType>()
const emit = defineEmits([
  'drag',
  'check',
  'sort',
  'page',
  'rowClick',
  'update:dataSource',
  'clickFooter',
  'update',
  'load',
  'beforeLoad',
  'change',
])
const _t = getScopeT(props.LanguageScopeKey)
const _gt = getScopeT()
const slots = useSlots()
const tableRef = inject<any>('tableRef')
const selectionMap = ref<Record<string, boolean>>({})
const radioValue = ref('')
/**
 * é‡æ–°å®šä¹‰åˆ—
 */
const columns = computed(() => {
  return props.columns.filter((column) => {
    return column.type !== 'seq' && !column.hide
  })
})
/**
 * æ˜¯å¦å«æœ‰åºå·
 */
const currentSeq = computed(() => {
  return {
    seq: props.columns.some((column) => column.type === 'seq'),
    column: props.columns.find((column) => column.type === 'seq') || {
      title: '序号',
      field: 'seq',
      type: 'seq',
    },
  }
})
const contextMenuConfig = useVModel(props, 'contextMenuConfig', emit)
const checkedAll = ref({
  isIndeterminate: false,
  checked: false,
})
const currentCheckedStyle = computed(() => {
  return (row: Record<string, any>, isSort?: boolean) => {
    const checkedStyle = {
      display: 'inline',
      marginLeft: '14px',
      marginTop: '-1px',
    }
    if (!currentSeq.value.seq) {
      return checkedStyle
    }
    if (
      selectionMap.value[row[props.id || 'id']] ||
      radioValue.value === row[props.id || 'id']
    ) {
      if (isSort) {
        if (props.isChecked || props.radio) {
          return { display: 'none' }
        }
        return {
          display: 'inline',
        }
      } else {
        return checkedStyle
      }
    } else {
      if (isSort && !props.isChecked && !props.radio) {
        return {
          display: 'inline',
          marginLeft: '24px',
        }
      }
      return ''
    }
  }
})
const dataSource = computed({
  get() {
    return props.dataSource
  },
  set(v) {
    if (v) {
      emit('update:dataSource', v)
    }
  },
})
/**
 * å³é”®èœå•
 * @param event
 * @param index
 */
const onClickShowMenu = (event: MouseEvent, row: any, index: number) => {
  if (event) {
    event?.preventDefault()
    contextMenuConfig.value.show = true
    contextMenuConfig.value.options.x = event.x
    contextMenuConfig.value.options.y = event.y
    contextMenuConfig.value.current = {
      row,
      index,
    }
  }
}
/**
 * æ¸…除选中效果
 */
const clearSelection = () => {
  const $table = tableRef.value
  $table?.setAllCheckboxRow(false)
  dataSource.value.forEach((row: Record<string, any>) => {
    delete selectionMap.value[row[props.id || 'id']]
  })
  $table?.clearCheckboxRow()
  checkedAll.value.isIndeterminate = false
  checkedAll.value.checked = false
  emitCheckboxChange()
}
/**
 * å•选
 */
const onRadioChange = (row) => {
  const id = row[props.id || 'id']
  if (radioValue.value === id) {
    radioValue.value = ''
  } else {
    radioValue.value = id
  }
  emit('change', row)
  emit('check', [row])
}
/**
 * å…¨é€‰
 */
const selectChangeAllEvent = (checked: boolean) => {
  const $table = tableRef.value
  const checkedAllFn = (checked: boolean) => {
    $table?.setAllCheckboxRow(checked)
    const records = $table.getCheckboxRecords()
    records.forEach((row: Record<string, any>) => {
      selectionMap.value[row[props.id || 'id']] = true
    })
  }
  if (!checked && checkedAll.value.isIndeterminate) {
    checkedAll.value.checked = true
    checkedAll.value.isIndeterminate = false
    checkedAllFn(true)
  } else if (!checked) {
    clearSelection()
    $table?.setAllCheckboxRow(false)
    return (selectionMap.value = {})
  }
  if (checked) {
    checkedAllFn(checked)
    checkedAll.value.isIndeterminate = false
    checkedAll.value.checked = true
  } else {
    clearSelection()
  }
  checkedAll.value.isIndeterminate = false
  selectChangeEvent()
}
/**
 *
 * @param rows
 */
const selectionHandle = (
  rows: Record<string, any>[],
  checked: boolean = true
) => {
  rows.forEach((row: Record<string, any>) => {
    selectionMap.value[row[props.id || 'id']] = checked
  })
}
/**
 * å•选
 */
const currentCheckedEvent = (checked: boolean, row: Record<string, any>) => {
  const $table = tableRef.value
  $table?.setCheckboxRow([row], checked)
  const records = Object.values(selectionMap.value).filter((v) => v)
  selectionHandle([row], checked)
  if (records.length > 0) {
    checkedAll.value.isIndeterminate = true
    if (records.length === dataSource.value.length) {
      checkedAll.value.isIndeterminate = false
      checkedAll.value.checked = true
    }
  } else {
    checkedAll.value.isIndeterminate = false
    checkedAll.value.checked = false
  }
  selectChangeEvent()
}
/**
 * å¤šé€‰
 */
const setCheckboxRow = (rows: Record<string, any>[], checked: boolean) => {
  const $table = tableRef.value
  if (props.radio) {
    radioValue.value = rows[0][props.id || 'id']
  } else {
    $table?.setCheckboxRow(rows, checked)
    const records = $table.getCheckboxRecords()
    selectionHandle(rows, checked)
    if (records.length > 0) {
      checkedAll.value.isIndeterminate = true
      if (records.length === dataSource.value.length) {
        checkedAll.value.isIndeterminate = false
        checkedAll.value.checked = true
      }
    } else {
      checkedAll.value.isIndeterminate = false
      checkedAll.value.checked = false
    }
    selectChangeEvent()
  }
}
/**
 * å•选选中
 */
const setRadioRow = (row = {}) => {
  if (props.radio) {
    radioValue.value = row[props.id || 'id']
  }
}
/**
 * å•选选中
 * @param id
 */
const setRadioRowKey = (id) => {
  if (props.radio) {
    radioValue.value = id
  }
}
/**
 * å…¨é€‰äº‹ä»¶
 */
const getRecords = () => {
  const records = []
  dataSource.value.forEach((item: Record<string, any>) => {
    if (selectionMap.value[item[props.id || 'id']]) {
      records.push(item)
    }
  })
  return records
}
const selectChangeEvent = () => {
  const $table = tableRef.value
  if ($table) {
    const records = getRecords()
    emit('check', records, cloneDeep(selectionMap.value))
  }
}
const onRowClick = (row: Record<string, any>, event: Event) => {
  if (props.cancelRowCheck) return
  if (props.isStop) {
    event?.stopPropagation()
  }
  if (props.radio) {
    return onRadioChange(row)
  }
  if (props.isChecked) {
    const dom = event.target as HTMLInputElement
    if (dom.getAttribute('slot-type') === 'native') {
      const $table = tableRef.value
      const checked = $table.isCheckedByCheckboxRow(row)
      selectionMap.value[row[props.id || 'id']] = !checked
      currentCheckedEvent(!checked, row)
    }
  }
}
const selectionLength = computed(() => {
  let l = 0
  Object.entries(selectionMap.value).forEach(([key, value]: any[]) => {
    if (value) {
      l++
    }
  })
  return l
})
/**
 * è§¦å‘复选框
 */
const emitCheckboxChange = () => {
  const records = tableRef.value?.getCheckboxRecords()
  emit('check', records, cloneDeep(selectionMap.value))
}
const handleDataSelection = () => {
  if (!dataSource.value.length) {
    checkedAll.value.isIndeterminate = false
    checkedAll.value.checked = false
    selectionMap.value = {}
  } else {
    const data: any = {}
    if (!selectionLength.value) {
      data.isIndeterminate = false
      data.checked = false
    } else {
      data.isIndeterminate = true
      data.checked = selectionLength.value === dataSource.value.length
    }
    checkedAll.value = data
  }
  emitCheckboxChange()
}
if (!props.cancelEmitCheck) {
  watch(
    () => dataSource.value.length,
    (v: number, oldV: number) => {
      if (v !== oldV) {
        const currentSelectedKeys: Record<string, boolean> = {}
        dataSource.value.forEach((item: Record<string, any>) => {
          const key = item[props.id || 'id']
          if (selectionMap.value.hasOwnProperty(key)) {
            currentSelectedKeys[key] = selectionMap.value[key]
          }
        })
        Object.entries(selectionMap.value).forEach(([key, value]: any[]) => {
          if (!currentSelectedKeys.hasOwnProperty(key)) {
            currentSelectedKeys[key] = false
          }
        })
        selectionMap.value = currentSelectedKeys
        handleDataSelection()
      }
    }
  )
}
defineExpose({
  clearSelection,
  setCheckboxRow,
  selectChangeAllEvent,
  setRadioRowKey,
  setRadioRow,
})
</script>
<style lang="scss">
@import url('../index.scss');
</style>
<style lang="scss" scoped>
@import url('../index.module.scss');
</style>