¶Ô±ÈÐÂÎļþ |
| | |
| | | <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> |