diff --git a/packages/renderless/src/grid/body/index.ts b/packages/renderless/src/grid/body/index.ts index 8d57da7061..ff5e9879c1 100644 --- a/packages/renderless/src/grid/body/index.ts +++ b/packages/renderless/src/grid/body/index.ts @@ -34,7 +34,9 @@ export const scrollEvent = // 滚动处理: 如果存在列固定左侧,同步更新滚动状态;如果存在列固定右侧,同步更新滚动状态。 const $table = parent - let { $refs, lastScrollLeft, lastScrollTop, scrollXLoad, scrollYLoad, columnStore } = $table + let { $refs } = $table + const { lastScrollLeft, lastScrollTop, scrollXLoad, columnStorescrollXLoad, scrollYLoad, columnStore } = + $table.state let { leftList, rightList } = columnStore let { tableBody, tableFooter, tableHeader } = $refs @@ -53,9 +55,9 @@ export const scrollEvent = // 记录新的滚动位置和时间 $table.lastScrollTime = Date.now() - $table.lastScrollLeft = scrollLeft - $table.lastScrollTop = scrollTop - $table.scrollDirection = isX ? 'X' : 'Y' + $table.state.lastScrollLeft = scrollLeft + $table.state.lastScrollTop = scrollTop + $table.state.scrollDirection = isX ? 'X' : 'Y' // 同步滚动条状态,只同步表头(表尾)滚动条状态,冻结列已优化为sticky方式 syncHeaderAndFooterScroll({ bodyElem, footerElem, headerElem, isX }) diff --git a/packages/renderless/src/grid/body/vue.ts b/packages/renderless/src/grid/body/vue.ts index 6ed822ac4a..2dcaca94aa 100644 --- a/packages/renderless/src/grid/body/vue.ts +++ b/packages/renderless/src/grid/body/vue.ts @@ -15,7 +15,8 @@ export const renderless = (props, context: ISharedRenderlessParamHooks, { vm }): onMounted(() => { const { $el, $refs } = vm as any - const { elemStore, dropConfig } = $table + const { dropConfig } = $table + const { elemStore } = $table.state const keyPrefix = 'main-body-' // 表体第一层div,出现滚动条的dom元素 diff --git a/packages/renderless/src/grid/table/index.ts b/packages/renderless/src/grid/table/index.ts new file mode 100644 index 0000000000..9f4c404a77 --- /dev/null +++ b/packages/renderless/src/grid/table/index.ts @@ -0,0 +1,240 @@ +import { isEmptyObject, isObject } from '../../common/type' +import { hasChildrenList } from '../utils' +import GlobalConfig from '../../../../vue/src/grid/src/config' +import { extend } from '../../../common/object' + +/** + * some utils + */ +const run = (names, $table) => names.forEach((name) => $table[name].apply($table)) + +/** + * computed state + */ + +export const computedCtxMenuOpts = + ({ props }) => + () => + extend(true, {}, GlobalConfig.menu, props.contextMenu) + +export const computedBodyCtxMenu = + ({ state }) => + () => { + return state.ctxMenuOpts.body && state.ctxMenuOpts.body.options ? state.ctxMenuOpts.body.options : [] + } + +export const computedCtxMenuList = + ({ state }) => + () => { + let rest = [] + state.ctxMenuStore.list.forEach((list) => list.forEach((item) => rest.push(item))) + return rest + } + +export const computedHasFilter = + ({ state }) => + () => + state.tableColumn.some((column) => isObject(column.filter) && !isEmptyObject(column.filter)) + +export const computedHeaderCtxMenu = + ({ state }) => + () => + state.ctxMenuOpts.header && state.ctxMenuOpts.header.options ? state.ctxMenuOpts.header.options : [] + +export const computedIsCtxMenu = + ({ state }) => + () => + state.headerCtxMenu.length || state.bodyCtxMenu.length + +export const computedIsGroup = (state) => () => state.collectColumn.some((column) => hasChildrenList(column)) + +export const computedIsResizable = + ({ state, props }) => + () => + props.resizable || state.tableFullColumn.some((column) => column.resizable) + +export const computedOptimizeOpts = + ({ props }) => + () => + extend(true, {}, GlobalConfig.sortConfig, props.sortConfig) + +export const computedSortOpts = + ({ props }) => + () => + extend(true, {}, GlobalConfig.sortConfig, props.sortConfig) + +export const computedTooltipContentOpts = + ({ state, props }) => + () => { + return extend( + true, + { + content: props.tooltipContent, + pre: state.tooltipContentPre, // pre 元素可定义预格式化的文本 + placement: 'right', + type: props.tooltipConfig.effect ? undefined : 'normal' + }, + props.tooltipConfig + ) + } + +export const computedVSize = + ({ props, vm }) => + () => + props.size || (vm.$parent && vm.$parent.size) || (vm.$parent && vm.$parent.vSize) + +export const computedVaildTipOpts = + ({ sate, props }) => + () => { + return extend( + true, + { + isArrow: false, + placement: 'top', + type: 'error', + content: sate.validTipContent + }, + props.tooltipConfig + ) + } + +export const computedValidOpts = + ({ props, vm }) => + () => { + const config = Object.assign( + { message: 'tooltip' }, + GlobalConfig.validConfig, + vm.$grid?.designConfig?.validConfig, + props.validConfig + ) + + config.isMessageTooltip = config.message === 'tooltip' + config.isMessageDefault = config.message === 'default' + config.isMessageInline = config.message === 'inline' + + return config + } + +export const computedTableBodyHeight = + ({ state }) => + () => + state.tableBodyHeight === 0 ? 'calc(100% - 36px)' : `${state.tableBodyHeight}px` + +// TODO:修改 +export const computedIsThemeTiny = + ({ state }) => + () => + state.tinyTheme === 'tiny' + +export const computedIsThemeSaas = + ({ state }) => + () => + state.tinyTheme === 'saas' +export const computedIsViewDefault = + ({ props }) => + () => + props.view === 'default' + +export const computedIsShapeTable = + ({ state, props, vm }) => + () => + // 表格处于默认视图或mf视图大屏时显示为普通表格;其它视图都显示为多端形式 + state.isViewDefault || (props.viewType === V_MF && vm.$grid.currentBreakpoint !== 'default') + +export const computedColumnNames = + ({ props }) => + () => { + const customColumnNames = props.customColumnNames + const columnNames = [GlobalConfig.defaultColumnName] + + const pushIfNot = (columnName) => { + if (typeof columnName === 'string' && !columnNames.includes(columnName)) { + columnNames.push(columnName) + } + } + + if (Array.isArray(customColumnNames) && customColumnNames.length > 0) { + customColumnNames.forEach(pushIfNot) + } else if (typeof customColumnNames === 'string') { + pushIfNot(customColumnNames) + } + + return columnNames + } + +/** + * Component Methods + */ +export const getParentElem = + ({ vm }) => + () => { + const $el = vm.$grid ? vm.$grid.$el : vm.$el + return $el.parentNode + } + +// TODO:修改 +export const updateParentHeight = + ({ state, vm, api }) => + () => { + if (vm.$grid) { + vm.$grid.updateParentHeight() + } else { + state.parentHeight = api.getParentElem().clientHeight + } + } + +export const getParentHeight = + ({ state }) => + () => { + return state.parentHeight + } + +// TODO:修改 +export const clearAll = + ({ state, vm }) => + (silent) => { + const { fetchOption = {} } = vm.$grid + const { isReloadFilter } = fetchOption + + run(['clearScroll', 'clearSort', 'clearCurrentRow', 'clearCurrentColumn'], this) + run(['clearSelection', 'clearRowExpand', 'clearTreeExpand'], this) + + if (typeof isReloadFilter === 'undefined' ? TINYGrid._filter : !isReloadFilter) { + vm.clearFilter(silent) + } + + if (state.keyboardConfig || state.mouseConfig) { + run(['clearIndexChecked', 'clearHeaderChecked', 'clearChecked', 'clearSelected', 'clearCopyed'], this) + } + + return vm.clearActived() + } + +export const refreshData = + ({ state, api, vm }) => + (data) => { + const next = () => { + state.tableData = [] + return api.loadTableData(data || state.tableFullData) + } + return vm.$nextTick().then(next) + } + +export const refreshStyle = + ({ props, el, vm }) => + () => { + const $el = el + const rowSpan = props.rowSpan + const spanMethod = props.spanMethod + // 存在合并时才刷新样式 + if ($el && (rowSpan || spanMethod)) { + let transform = $el.style.transform + let restore = () => + setTimeout(() => { + $el.style.transform = transform + }) + $el.style.transform = 'scale(0.99999)' + return vm.$nextTick().then(restore) + } + return vm.$nextTick() + } diff --git a/packages/renderless/src/grid/table/vue.ts b/packages/renderless/src/grid/table/vue.ts new file mode 100644 index 0000000000..6283e6d3d1 --- /dev/null +++ b/packages/renderless/src/grid/table/vue.ts @@ -0,0 +1,137 @@ +import type { ISharedRenderlessParamHooks } from '@/types' +import GlobalConfig from '../../../../vue/src/grid/src/config' +import { extend } from '../../../common/object' + +export const api = ['state', 'scrollEvent'] + +const initState = ({ reactive, computed, props }) => { + return reactive({ + // 存储异步加载过的行\列数据 + asyncRenderMap: {}, + // 存放列相关的信息 + columnStore: { + // 自适应的列表集合 + autoList: [], + centerList: [], + // 左侧冻结列表集合 + leftList: [], + // 右侧冻结列表集合 + rightList: [], + // 固定像素宽度列表集合 + pxList: [], + // 设置了最小宽度列表集合 + pxMinList: [], + // 可调整列宽列表集合 + resizeList: [], + // 百分比宽度列表集合 + scaleList: [], + // 百分比最小宽度列表集合 + scaleMinList: [] + }, + // 存放快捷菜单的信息 + ctxMenuStore: { + list: [], + selectChild: null, + selected: null, + showChild: false, + style: null, + visible: false + }, + // 当前行 + currentRow: null, + // 存放可编辑相关信息 + editStore: { + editorAutoRefreshKey: 0, + // 激活 + actived: { column: null, row: null }, + // 所有选中 + checked: { columns: [], rows: [], tColumns: [], tRows: [] }, + // 已复制源 + copyed: { columns: [], cut: false, rows: [] }, + indexs: { columns: [] }, + insertList: [], + removeList: [], + // 选中源 + selected: { column: null, row: null }, + titles: { columns: [] } + }, + // 已展开的行 + expandeds: [], + // 当前选中的筛选列 + filterStore: { + column: null, + condition: { input: '', relation: 'equals' }, + id: '', + multi: false, + options: [], + visible: false + }, + // 表尾合计数据 + footerData: [], + // 所有列已禁用 + headerCheckDisabled: false, + // 是否全选 + isAllSelected: false, + // 多选属性,有选中且非全选状态 + isIndeterminate: false, + // 是否存在横向滚动条 + overflowX: false, + // 是否存在纵向滚动条 + overflowY: true, + // 存储滚动加载,上次滚动的位置 + scrollLoadStore: { bodyHeight: 0, scrollHeight: 0 }, + // 是否启用了横向 X 可视渲染方式加载 + scrollXLoad: false, + // 是否启用了纵向 Y 可视渲染方式加载 + scrollYLoad: false, + // 横向滚动条的高度 + scrollbarHeight: 0, + // 纵向滚动条的宽度 + scrollbarWidth: 0, + // 单选属性,选中行 + selectRow: null, + // 存放多选工具栏相关信息 + selectToolbarStore: { + layout: { height: 0, left: 0, top: 0, width: 0, zIndex: 1 }, + visible: false + }, + // 多选属性,已选中的列 + selection: [], + // 渲染中的数据 + tableData: [], + // tooltip提示内容 + tooltipContent: '', + // tooltip提示内容是否处理换行字符 + tooltipContentPre: false, + // 已展开树节点 + treeExpandeds: [], + // 树节点不确定状态的列表 + treeIndeterminates: [], + // 存放数据校验相关信息 + validStore: { + column: null, + content: '', + isArrow: false, + row: null, + rule: null, + visible: false + }, + // 校验tip提示内容 + validTipContent: '', + // 在编辑模式下 单元格在失去焦点验证的状态 + validatedMap: {}, + // 表尾边框线是否显示和位置 + showFooterBorder: false, + footerBorderBottom: 0, + tableBodyHeight: 0, + // 表格父容器的高度 + parentHeight: 0, + ctxMenuOpts: computed(() => extend(true, {}, GlobalConfig.menu, props.contextMenu)), + hasTip: computed(() => TINYGrid._tooltip) + }) +} + +export const renderless = (props, context: ISharedRenderlessParamHooks, { vm }): any => { + const api = {} as any + return api +} diff --git a/packages/vue/src/grid/src/body/src/pc.vue b/packages/vue/src/grid/src/body/src/pc.vue index c69301a312..e6e6d49635 100644 --- a/packages/vue/src/grid/src/body/src/pc.vue +++ b/packages/vue/src/grid/src/body/src/pc.vue @@ -18,7 +18,8 @@ import { getTreeChildrenKey, getTreeShowKey, handleRowGroupFold, isVirtualRow } // 滚动、拖动过程中不需要触发鼠标移入移出事件 const isOperateMouse = ($table) => - $table._isResize || ($table.lastScrollTime && Date.now() < $table.lastScrollTime + $table.optimizeOpts.delayHover) + $table._isResize || + ($table.lastScrollTime && Date.now() < $table.lastScrollTime + $table.state.optimizeOpts.delayHover) // 解决静态扫描驼峰变量问题 const classMap = { @@ -146,7 +147,7 @@ function buildColumnChildren(args) { }, // 调用column组件的renderCell渲染单元格内部的内容 // 如果不是表格形态,就只保留表格结构(到tiny-grid-cell),不渲染具体的内容 - $table.isShapeTable ? column.renderCell(h, params) : null + $table.state.isShapeTable ? column.renderCell(h, params) : null ), validNode ] @@ -198,7 +199,7 @@ function addListenerMouseleave({ $table, evntParams, showTip, showTooltip, table } if (showTip || showTooltip) { - $table.clostTooltip() + $table.closeTooltip() } evntParams.cell = event.currentTarget @@ -367,8 +368,8 @@ function renderColumn(args1) { let { $seq, $table, column, columnIndex } = args1 let { h, row } = args1 let { align: allAlign, cellClassName, columnKey, editConfig } = $table - let { editRules, editStore, rowId, rowSpan, height } = $table - let { tableData, validOpts, validStore, validatedMap, spanMethod, columnStore, dropConfig = {} } = $table + let { editRules, rowId, rowSpan, height, spanMethod, dropConfig = {} } = $table + let { tableData, validOpts, validStore, validatedMap, columnStore, editStore } = $table.state let { isDirty, attrs = { 'data-colid': column.id } } = {} let { isMessageDefault, isMessageInline } = validOpts let { actived } = editStore @@ -530,7 +531,8 @@ function renderRow(args) { let { $rowIndex, $seq, $table, _vm, editStore } = args let { h, row, rowActived } = args let { rowClassName, rowIndex, rowKey, rowLevel, rowid, rows } = args - let { selection, seq, tableColumn, trOn, treeConfig, isNotRenderRow } = args + let { seq, tableColumn, trOn, treeConfig, isNotRenderRow } = args + const { selection } = $table.state if (isNotRenderRow) { return @@ -663,9 +665,11 @@ function renderRowTree(args, renderRows) { } function renderRows({ h, _vm, $table, $seq, rowLevel, tableData, tableColumn, seqCount }) { - let { rowKey, rowClassName, treeConfig, treeExpandeds } = $table - let { groupData, scrollYLoad, scrollYStore, editConfig, editStore, expandConfig = {} } = $table - let { expandeds, selection, rowGroup, hasVirtualRow, afterFullData, visibleColumn } = $table + const { state } = $table + const hasVirtualRow = $table.hasVirtualRow + const { treeExpandeds, scrollYLoad, scrollYStore, editStore, expandeds, selection, afterFullData } = state + let { rowKey, rowClassName, treeConfig, rowGroup } = $table + let { editConfig, expandConfig = {} } = $table let rows = [] let expandMethod = expandConfig.activeMethod let startIndex = scrollYStore.startIndex @@ -814,8 +818,8 @@ export default defineComponent({ }, render() { let { $parent: $table } = this as any - let { $grid, isCenterEmpty, keyboardConfig = {}, mouseConfig = {}, renderEmpty } = $table - let { scrollLoad, tableColumn, tableData, tableLayout } = $table + let { $grid, isCenterEmpty, keyboardConfig = {}, mouseConfig = {}, renderEmpty, scrollLoad } = $table + let { tableColumn, tableData, tableLayout } = $table.state let $slots = $grid.slots let isCenterCls = isCenterEmpty ? 'is__center' : '' diff --git a/packages/vue/src/grid/src/table/src/pc.vue b/packages/vue/src/grid/src/table/src/pc.vue new file mode 100644 index 0000000000..2d85312856 --- /dev/null +++ b/packages/vue/src/grid/src/table/src/pc.vue @@ -0,0 +1,1003 @@ +