import { h, ref, defineComponent, computed, reactive, provide } from 'vue' import { ElTable, ElTableColumn, ElRadio, ElTooltip } from 'element-plus' import { isNotEmpty, isEmpty, isArray, getUsingConfig, setFieldValue } from '@cip/utils/util' import CipTableHandler from '../cip-table-handler' import { useCipConfig } from '../hooks/use-cip-config' import { cipTableKey } from '../hooks/use-table' import { tableProps } from './table-props' import ColumnInput from './column-input' import { EmptyStatus } from './icons-vue' import { dateColumnWidthMap, handleColumnWidthMap } from './config' import './index.less' import { analyseData, getPropertyKeyByPath, calculateCurrentWidth } from './util' export default defineComponent({ name: 'CipTable', inheritAttrs: false, props: tableProps, emits: ['sort', 'update:data', 'update:selectColumns'], setup (props, context) { const cipConfig = useCipConfig() const cipTableRef = ref() const _size = computed(() => { // 考虑历史因素使用default作为默认值, 当前主要项目使用medium, 在修改表格size注意定宽项的折叠 return getUsingConfig(props.size, cipConfig.table.size, 'default') }) const calculateCurrentWidthFn = computed(() => { if (props.size) return (width) => width const { sizeStandard = 'default', size = 'default' } = cipConfig.table || {} return (width) => calculateCurrentWidth(size, sizeStandard, width) }) const cipTable = reactive({ size: _size }) provide(cipTableKey, cipTable) context.expose({ cipTableRef }) // table 数据更新 v-model:data const updateData = (val, index) => { // 数据索引 const dataIndexed = analyseData(props.data, props.treeProps) const path = dataIndexed[index] // 当存在tree是index与实际的不符合需要使用key进行唯一定位 或者遍历计数 const propertyKey = getPropertyKeyByPath(path, props.treeProps) const data = props.data setFieldValue(data, propertyKey, val) // data[index] = val context.emit('update:data', data) } // 触发table的排序事件 const onSortChange = ({ prop, order }) => { context.emit('sort', { prop, order }) } // 触发列的选中改变事件 const onSelectionChange = (val) => { context.emit('update:selectColumns', val) } // 原始的width 转换系数 const transformWidth = (widthStr) => { if (typeof widthStr === 'number') return Math.ceil(calculateCurrentWidthFn.value(widthStr)) if (widthStr.indexOf('px') > -1) return `${Math.ceil(calculateCurrentWidthFn.value(Number(widthStr.replace(/px$/, ''))))}px` return widthStr } // 渲染table的单个数据列 注意此处为Column const renderTableColumn = ({ key, config } = {}) => { const { children, type, formatter, ...tableColumnConfig } = config // date 类型 强行修改宽度 if (!tableColumnConfig.width) { if (config.type === 'date' && config.viewType === 'datetime') { tableColumnConfig.width = dateColumnWidthMap[_size.value] } } else { tableColumnConfig.width = transformWidth(tableColumnConfig.width) } const headerSlots = ({ column, $index }) => { const result = [h('span', {}, [config.label])] // 存在说明 if (config.description) { const descriptionComp = ( {{ content: () => config.description, default: () => }} ) result.push(descriptionComp) } // 必填标记 if (config.required === true && config.writable === true) { const requiredAsterisk = (*) result.unshift(requiredAsterisk) } return result } let dataIndexed if (props.rowKey) { dataIndexed = analyseData(props.data, props.treeProps) } return h(ElTableColumn, { prop: key, align: config.type === 'number' ? 'right' : '', // 针对数字类型进行居右优化 style: 'display: flex;', ...tableColumnConfig }, { header: headerSlots, default: ({ row, $index, column }) => { // if ($index === -1) return // 如果写上这个代码 children 将失效 if (isArray(config.children) && config.children.length > 0) { return renderTableColumns(config.children) } else { if ($index < 0) return null // 同时开放key值插槽及key+'Slot' 的插槽 // default/append/expand/_handler为table其他用途的插槽不可用于渲染column // 如果存在字段名为上述字段的,需要加Slot后缀,例: defaultSlot\appendSlot if (!['default', 'append', 'prepend', 'expand', '_handler', '$handler'].includes(key) && context.slots[key]) { return context.slots[key]({ row, $index, column }) } // 特殊字段插槽 ['default', 'append', 'expand', '_handler', '$handler'] if (context.slots[`${key}Slot`]) { return context.slots[`${key}Slot`]({ row, $index, column }) } let propertyKey = $index if (dataIndexed) { const path = dataIndexed[$index] if (path) { propertyKey = path?.length > 1 ? getPropertyKeyByPath(path, props.treeProps) : path[0] } } return h(ColumnInput, { config, fieldKey: props.fieldKey, index: $index, model: row, key, tableRuleKey: props.ruleKey, propertyKey: propertyKey, columnKey: key, tableDependOnValues: props.dependOnValues, tableData: props.data, updateData }) } } }) } // 渲染table的所有数据列 注意此处为Columns const renderTableColumns = (columns = []) => { if (!isArray(columns)) { throw new Error('function renderTableColumns param columns must be array') } return columns.filter(column => !column.config.hideItem).map(column => renderTableColumn(column)) } // 渲染table所有列 操作、选中、序号等 const TableColumns = () => { // table字段渲染 const slots = renderTableColumns(props.columns) // 序号渲染 if (isNotEmpty(props.offset) && props.offset > -1 && !props.hideIndex) { const indexColumn = h(ElTableColumn, { label: '序号', fixed: props.indexFixed ? 'left' : '', width: transformWidth(isEmpty(props.rowKey) ? 55 : 75) }, { default: ({ $index }) => `${$index + 1 + props.offset}` }) slots.unshift(indexColumn) } // 复选框 if (props.selectType === 'checkbox') { const option = { type: 'selection', width: transformWidth(45), fixed: 'left' } if (isNotEmpty(props.selectable)) { // 选择 option.selectable = (row, index) => props.selectable(row || {}, index) } const selectionColumn = h(ElTableColumn, option) slots.unshift(selectionColumn) } // 单选框 if (props.selectType === 'radio') { const selectionColumn = h(ElTableColumn, { width: transformWidth(45), fixed: 'left' }, { default: ({ row }) => h(ElRadio, { label: row[props.selectLabel] ?? row.id, modelValue: props.selectRadio, disabled: props.selectable && !props.selectable(row) }, { default: () => '' }) }) slots.unshift(selectionColumn) } // 展开 if (context.slots.expand) { const expendColumn = h(ElTableColumn, { type: 'expand', width: transformWidth(32), fixed: 'left' }, { default: ({ row, index }) => context.slots.expand({ row, index }) }) slots.unshift(expendColumn) } // jsx编译时有时候会去除_handler插槽 // 注意_handler即将废弃请使用$handler代替 if (props.withTableHandle && (context.slots._handler || context.slots.$handler)) { const handlerSlot = context.slots._handler || context.slots.$handler const handlerColumn = h(ElTableColumn, { label: '操作', fixed: 'right', width: props.handlerWidth ? transformWidth(props.handlerWidth) : handleColumnWidthMap[_size.value] }, { default: ({ row, $index }) => h(CipTableHandler, { limit: props.handlerLimit, row }, { default: () => handlerSlot({ row, $index }) }) }) slots.unshift(handlerColumn) // 内部组件必须使用cip-table-button } // el-table组件提供的默认插槽 if (context.slots.default) { slots.push(context.slots.default()) } // el-table组件提供的prepend插槽 if (context.slots.prepend) { const prependSlots = context.slots.prepend() if (isArray(prependSlots)) { slots.unshift(...prependSlots) } else { slots.unshift(prependSlots) } } // el-table组件提供的append插槽 if (context.slots.append) { const appendSlots = context.slots.append() if (isArray(appendSlots)) { slots.push(...appendSlots) } else { slots.push(appendSlots) } } // 给所有column加一个父亲 if (props.tableHeaderLabel) return h(ElTableColumn, { label: props.tableHeaderLabel, align: 'center' }, { default: () => slots }) return slots } // 数据为空时显示 const EmptyBlock = () => { return
暂无数据
} // 渲染table return () => {{ default: () => , empty: () => }} } })