From d767cb424ab9cb409bdab06c5d42b048f1ddd6ef Mon Sep 17 00:00:00 2001 From: zs <zhousong@weben-smart.com> Date: 周三, 30 4月 2025 20:58:52 +0800 Subject: [PATCH] 前端初步改造 --- HIAWms/web/src/components/Table/components/custom-vxe-column.vue | 555 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 555 insertions(+), 0 deletions(-) diff --git a/HIAWms/web/src/components/Table/components/custom-vxe-column.vue b/HIAWms/web/src/components/Table/components/custom-vxe-column.vue new file mode 100644 index 0000000..1c4db54 --- /dev/null +++ b/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> -- Gitblit v1.9.3