-
+
-
+
-
+
-
+
-
+
@@ -37,12 +37,12 @@
:style="resizeBarStyle"
/>
-
+
-
+
-
+
@@ -60,7 +60,7 @@
-
+
@@ -95,36 +95,48 @@ import { clearOnTableUnmount } from './strategy'
import methods from './methods'
import { useDrag, useRowGroup } from '../../composable'
+// 导入全局配置常量
const { themes, viewConfig, columnLevelKey, defaultColumnName } = GlobalConfig
const { TINY: T_TINY, SAAS: T_SAAS } = themes
const { DEFAULT: V_DEFAULT, MF: V_MF, CARD: V_CARD, LIST: V_LIST } = viewConfig
const { MF_SHOW_LIST: V_MF_LIST } = viewConfig
-const hiddenContainerClass = 'tiny-grid-hidden-column'
-
-// 校验插件是否被注册
+/**
+ * 校验必要的插件是否已注册
+ * @param {Object} _vm - 组件实例
+ */
function verifyConfig(_vm) {
+ // 校验是否设置了行主键
if (!getRowkey(_vm)) {
error('ui.grid.error.rowIdEmpty', true)
}
+ // 校验编辑插件
if (!TINYGrid._edit && _vm.editConfig) {
throw new Error(template(error('ui.grid.error.reqModule', true), { name: 'Edit' }))
}
+ // 校验验证插件
if (!TINYGrid._valid && _vm.editRules) {
throw new Error(template(error('ui.grid.error.reqModule', true), { name: 'Validator' }))
}
+ // 校验键盘操作插件
if (!TINYGrid._keyboard && (_vm.keyboardConfig || _vm.mouseConfig)) {
throw new Error(template(error('ui.grid.error.reqModule', true), { name: 'Keyboard' }))
}
+ // 校验自适应插件
if (!TINYGrid._resize && _vm.autoResize) {
throw new Error(template(error('ui.grid.error.reqModule', true), { name: 'Resize' }))
}
}
+/**
+ * 合并虚拟滚动配置
+ * @param {Object} scrollDir - 滚动方向配置
+ * @param {Object} scrollDirStore - 滚动存储对象
+ */
function mergeScrollDirStore(scrollDir, scrollDirStore) {
if (scrollDir) {
Object.assign(scrollDirStore, {
@@ -137,6 +149,10 @@ function mergeScrollDirStore(scrollDir, scrollDirStore) {
}
}
+/**
+ * 合并树形表格配置
+ * @param {Object} _vm - 组件实例
+ */
function mergeTreeConfig(_vm) {
if (_vm.treeConfig) {
const { ordered } = _vm.treeConfig
@@ -144,7 +160,11 @@ function mergeTreeConfig(_vm) {
}
}
-// 设置表格最外层元素类名
+/**
+ * 获取表格的样式类
+ * @param {Object} tableVm - 表格组件实例
+ * @returns {Object} 样式类对象
+ */
function getTableClasses(tableVm) {
const { isShapeTable, vSize, editConfig, showHeader, showFooter, overflowY, overflowX, showOverflow } = tableVm
const { showHeaderOverflow, highlightCell, optimizeOpts, stripe, border, isGroup } = tableVm
@@ -185,6 +205,11 @@ function getTableClasses(tableVm) {
}
}
+/**
+ * 获取表格的内联样式
+ * @param {Object} tableVm - 表格组件实例
+ * @returns {Object} 样式对象
+ */
function getTableStyles(tableVm) {
const { isShapeTable, maxHeight } = tableVm
const style = {}
@@ -197,6 +222,10 @@ function getTableStyles(tableVm) {
return style
}
+/**
+ * 绑定全局事件
+ * @param {Object} ctx - 组件上下文
+ */
const bindEvent = (ctx) => {
GlobalEvent.on(ctx, 'mousedown', ctx.handleGlobalMousedownEvent)
// 因为冒泡事件部分情况下被阻止继续传播,导致处理异常,因此注册 mousedown 捕获事件
@@ -208,6 +237,10 @@ const bindEvent = (ctx) => {
GlobalEvent.on(ctx, 'contextmenu', ctx.handleGlobalContextmenuEvent)
}
+/**
+ * 解绑全局事件
+ * @param {Object} table - 表格实例
+ */
const unbindEvent = (table) => {
GlobalEvent.off(table, 'mousedown')
GlobalEvent.off(table, 'mousedown', true)
@@ -218,6 +251,7 @@ const unbindEvent = (table) => {
GlobalEvent.off(table, 'contextmenu')
}
+// 导出表格组件
export default defineComponent({
name: `${$prefix}GridTable`,
components: {
@@ -230,6 +264,7 @@ export default defineComponent({
MfTable,
Tooltip
},
+ // 组件属性定义
props: {
// 所有的列对齐方式
align: { type: String, default: () => GlobalConfig.align },
@@ -425,24 +460,31 @@ export default defineComponent({
// 自定义列组件名称(列表)
customColumnNames: { type: [String, Array], default: defaultColumnName }
},
+
+ // 提供注入
provide() {
return {
$table: this,
- // 嵌套表格屏蔽父表格列
- $column: null
+ $column: null // 嵌套表格场景下屏蔽父表格列
}
},
+
+ // 注入依赖
inject: {
$grid: {
default: null
}
},
+
+ // 组件方法
methods: {
...methods,
viewCls(module) {
return (this as any).$grid.viewCls(module)
}
},
+
+ // 组件逻辑设置
setup(props, { slots, attrs, listeners }) {
// 获取实例
const instance = hooks.getCurrentInstance().proxy
@@ -834,103 +876,179 @@ export default defineComponent({
]
})
+ /**
+ * 判断表格是否有过滤功能
+ * 通过检查每一列的filter属性是否为非空对象来判断
+ * @returns {boolean} 是否有过滤功能
+ */
const hasFilter = computed(() => {
return tableColumn.value.some((column) => isObject(column.filter) && !isEmptyObject(column.filter))
})
+ /**
+ * 判断表格是否有tooltip功能
+ * 通过检查TINYGrid是否注册了tooltip插件来判断
+ * @returns {boolean} 是否有tooltip功能
+ */
const hasTip = computed(() => {
return TINYGrid._tooltip
})
+ /**
+ * 判断表格是否可调整列宽
+ * 当全局resizable为true或任一列的resizable为true时返回true
+ * @returns {boolean} 是否可调整列宽
+ */
const isResizable = computed(() => {
return props.resizable || tableFullColumn.value.some((column) => column.resizable)
})
+ /**
+ * 判断表格是否有右键菜单
+ * 通过检查右键菜单列表中是否有菜单项来判断
+ * @returns {boolean} 是否有右键菜单
+ */
const isCtxMenu = computed(() => {
return ctxMenuStore.value?.list?.some((item) => item.length > 0)
})
+ /**
+ * 获取排序配置
+ * 将全局排序配置与组件传入的排序配置深度合并
+ * @returns {Object} 合并后的排序配置对象
+ */
const sortOpts = computed(() => {
return extend(true, {}, GlobalConfig.sortConfig, props.sortConfig)
})
// 初始化列
const initColumns = () => {
- // 使用 useRelation 进行列关系处理
+ // 初始化表格实例的插槽
useInstanceSlots()
+ // 使用 useRelation 处理列与列之间的关系
+ // 主要用于处理多级表头、列分组等场景
useRelation({
+ // 关系标识,由列级别key和表格id组成,用于标识列之间的层级关系
relationKey: `${columnLevelKey}-${id.value}`,
+ // 子列的属性名称
childrenKey: 'childColumns',
- relationContainer: () => (!isServer ? instance.$el.querySelector(`.${hiddenContainerClass}`) : null),
+ // 获取隐藏列容器DOM元素
+ relationContainer: () => (!isServer ? instance.$el.querySelector('.tiny-grid-hidden-column') : null),
+ // 当列关系发生变化时的回调函数
onChange: () => {
+ // 计算列收集的key值
const collectKey = instance.computeCollectKey()
+ // 如果key值发生变化,说明列结构有更新
if (collectKey !== columnCollectKey.value) {
+ // 更新列收集key值
columnCollectKey.value = collectKey
+ // 重新组装列配置
instance.assembleColumns()
}
}
})
- // 拖拽相关初始化
+ // 初始化列拖拽功能
+ // 用于实现列的拖拽排序、拖拽调整宽度等功能
useDrag({
+ // 拖拽配置,响应式获取props中的dropConfig
dropConfig: computed(() => props.dropConfig),
+ // 收集的列配置
collectColumn,
+ // 表格当前显示的列
tableColumn
})
- // 行分组相关初始化
+ // 初始化行分组功能
+ // 用于实现表格数据的分组展示
useRowGroup({
+ // 行分组配置,响应式获取props中的rowGroup
rowGroup: computed(() => props.rowGroup),
+ // 当前可见的列
visibleColumn,
+ // 完整的表格列配置
tableFullColumn,
+ // 表格当前显示的列
tableColumn
})
}
// 监听数据变化
+ // 监听表格数据变化
watch(
+ // 监听props中的data属性
() => props.data,
+ // 当data发生变化时的回调函数
(newData) => {
+ // 判断新数据是否为数组类型
if (Array.isArray(newData)) {
+ // 1. 加载新的表格数据,第二个参数true表示重置表格状态
+ // 2. 处理默认行为,如默认选中、展开等
+ // 3. 处理表头选择框状态
instance.loadTableData(newData, true).then(instance.handleDefault).then(instance.handleSelectionHeader)
}
}
)
+ // 监听列配置变化
watch(
+ // 监听收集的列配置
() => collectColumn.value,
+ // 当列配置发生变化时的回调函数
(value) => {
+ // 调用实例方法处理列变化
+ // 主要用于:
+ // 1. 重新计算列宽
+ // 2. 更新固定列
+ // 3. 处理列的显示/隐藏状态
instance.watchColumn(value)
}
)
// 生命周期钩子
onBeforeUnmount(() => {
+ // 获取表格包装器DOM引用
const tableWrapper = instance.$refs.tableWrapper
+ // 如果表格包装器存在且有父节点,从DOM中移除表格包装器
if (tableWrapper && tableWrapper.parentNode) {
tableWrapper.parentNode.removeChild(tableWrapper)
}
+ // 如果启用了表格大小调整功能,解绑相关事件监听
if (TINYGrid._resize) {
instance.unbindResize()
}
+ // 关闭筛选面板
instance.closeFilter()
+ // 关闭右键菜单
instance.closeMenu()
+ // 解绑所有表格相关的事件监听器
unbindEvent(instance)
+ // 清理表格卸载时的状态和缓存
clearOnTableUnmount(instance)
})
onActivated(() => {
+ // 检查是否存在上次滚动位置的记录
if (lastScrollLeft.value || lastScrollTop.value) {
+ // 恢复表格到上次的滚动位置
instance.scrollTo(lastScrollLeft.value, lastScrollTop.value)
+
+ // 如果启用了横向虚拟滚动,触发横向滚动事件
+ // 用于加载新的列数据
scrollXLoad.value && instance.triggerScrollXEvent()
+
+ // 如果启用了纵向虚拟滚动,触发纵向滚动事件
+ // 传入上次的滚动位置,用于加载新的行数据
scrollYLoad.value && instance.triggerScrollYEvent({ target: { scrollTop: lastScrollTop.value } })
}
+ // 重新绑定表格相关的事件监听器
+ // 包括滚动、点击、键盘等事件
bindEvent(instance)
})
@@ -969,17 +1087,25 @@ export default defineComponent({
// 挂载后处理
nextTick().then(() => {
- // 初始化表格
+ // 调用初始化函数,完成表格的初始化配置和数据加载
initialize()
+ // 标记表格已完成挂载
afterMounted.value = true
+ // 如果开启了自动调整大小功能且已注册了resize插件
if (props.autoResize && TINYGrid._resize) {
+ // 绑定resize事件监听器,用于响应容器大小变化
instance.bindResize()
}
+ // 延迟执行表格底部边框相关的处理
setTimeout(() => {
+ // 获取表格底部组件实例
const tableFooter = instance.$refs.tableFooter
+ // 根据是否存在底部组件来设置是否显示底部边框
showFooterBorder.value = !!tableFooter
+ // 获取底部组件的实际高度,用于设置底部边框的位置
+ // 如果底部组件存在则获取其高度,否则为0
footerBorderBottom.value = tableFooter ? tableFooter.$el.getBoundingClientRect().height : 0
})
})
diff --git a/packages/vue/src/grid/src/table/src/utils/autoCellWidth.ts b/packages/vue/src/grid/src/table/src/utils/autoCellWidth.ts
index 13f4c720a9..addead7e9b 100644
--- a/packages/vue/src/grid/src/table/src/utils/autoCellWidth.ts
+++ b/packages/vue/src/grid/src/table/src/utils/autoCellWidth.ts
@@ -23,7 +23,15 @@
*
*/
-// 自适应
+/**
+ * 处理自适应列宽的函数
+ * @param autoArr - 需要自适应的列数组
+ * @param meanWidth - 平均宽度
+ * @param minCellWidth - 最小单元格宽度
+ * @param tableWidth - 表格总宽度
+ * @param fit - 是否需要填充满容器
+ * @param bodyWidth - 表格容器宽度
+ */
const adaptive = ({ autoArr, meanWidth, minCellWidth, tableWidth, fit, bodyWidth }) => {
autoArr.forEach((column, index) => {
let width = Math.max(meanWidth, minCellWidth)
@@ -45,75 +53,120 @@ const adaptive = ({ autoArr, meanWidth, minCellWidth, tableWidth, fit, bodyWidth
return tableWidth
}
-// 计算每一列的渲染宽度:renderWidth
+/**
+ * 初始化表格宽度,计算每一列的渲染宽度
+ * @param remainWidth - 剩余可用宽度
+ * @param columnStore - 列配置存储对象,包含不同类型的列数组
+ * @returns {Object} 返回计算后的表格总宽度和平均宽度
+ */
const initTableWidth = ({ remainWidth, columnStore }) => {
+ // 初始化表格总宽度
let tableWidth = 0
+
+ // 从columnStore中解构出不同类型的列数组:
+ // resizeArr - 用户手动调整过宽度的列
+ // pxMinArr - 设置了最小像素宽度(min-width="100px")的列
+ // pxArr - 设置了固定像素宽度(width="100px")的列
let { resizeList: resizeArr, pxMinList: pxMinArr, pxList: pxArr } = columnStore
+
+ // scaleArr - 设置了百分比宽度(width="20%")的列
+ // scaleMinArr - 设置了最小百分比宽度(min-width="20%")的列
let { scaleList: scaleArr, scaleMinList: scaleMinArr } = columnStore
- // 最小宽
+
+ // 1. 首先处理设置了最小像素宽度的列
+ // 这些列的宽度不能小于设定的最小宽度
pxMinArr.forEach((column) => {
let minWidth = parseInt(column.minWidth)
-
tableWidth += minWidth
column.renderWidth = minWidth
})
- // 最小百分比
+
+ // 计算1%宽度对应的像素值,用于处理百分比宽度
let meanWidth = remainWidth / 100
+ // 2. 处理设置了最小百分比宽度的列
+ // 将百分比转换为实际像素值
scaleMinArr.forEach((column) => {
let scaleWidth = Math.floor(parseInt(column.minWidth) * meanWidth)
-
tableWidth += scaleWidth
column.renderWidth = scaleWidth
})
- // 固定百分比
+
+ // 3. 处理设置了固定百分比宽度的列
scaleArr.forEach((column) => {
let scaleWidth = Math.floor(parseInt(column.width) * meanWidth)
-
tableWidth += scaleWidth
column.renderWidth = scaleWidth
})
- // 固定宽
+
+ // 4. 处理设置了固定像素宽度的列
pxArr.forEach((column) => {
let width = parseInt(column.width)
-
tableWidth += width
column.renderWidth = width
})
- // 调整了列宽
+
+ // 5. 最后处理用户手动调整过宽度的列
+ // 这些列的宽度优先级最高
resizeArr.forEach((column) => {
let width = parseInt(column.resizeWidth)
-
tableWidth += width
column.renderWidth = width
})
+ // 返回计算后的表格总宽度和平均宽度
+ // tableWidth用于后续的自适应计算
+ // meanWidth用于计算剩余空间的分配
return { tableWidth, meanWidth }
}
+/**
+ * 计算表格的总宽度,并处理列的自适应和填充逻辑
+ * @param bodyWidth - 表格容器的宽度
+ * @param columnStore - 列配置存储对象
+ * @param fit - 是否需要填充满容器
+ * @param minCellWidth - 最小单元格宽度
+ * @param remainWidth - 剩余可用宽度
+ * @returns {number} 返回计算后的表格总宽度
+ */
export const calcTableWidth = ({ bodyWidth, columnStore, fit, minCellWidth, remainWidth }) => {
+ // 初始化表格宽度和平均宽度
let { tableWidth, meanWidth } = initTableWidth({ remainWidth, columnStore })
+ // 获取最小像素宽度列、最小百分比宽度列和自适应列
let { pxMinList: pxMinArr, scaleMinList: scaleMinArr, autoList: autoArr } = columnStore
+ // 计算剩余可用宽度
remainWidth -= tableWidth
+ // 计算每列平均可分配宽度
+ // 如果有剩余宽度,则平均分配给最小宽度列和自适应列
+ // 如果没有剩余宽度,则为0
meanWidth = remainWidth > 0 ? Math.floor(remainWidth / (scaleMinArr.length + pxMinArr.length + autoArr.length)) : 0
+ // 如果需要填充满容器
if (fit) {
+ // 如果有剩余宽度
if (remainWidth > 0) {
+ // 将剩余宽度平均分配给最小百分比宽度列和最小像素宽度列
scaleMinArr.concat(pxMinArr).forEach((column) => {
tableWidth += meanWidth
column.renderWidth += meanWidth
})
}
} else {
+ // 如果不需要填充满容器,则使用最小单元格宽度
meanWidth = minCellWidth
}
- // 自适应修补一些列的宽度
+ // 处理自适应列的宽度
tableWidth = adaptive({ autoArr, meanWidth, minCellWidth, tableWidth, fit, bodyWidth })
+
+ // 计算处理完自适应列后与容器的剩余空间
const remainingSpace = bodyWidth - tableWidth
- // 如果还有空间剩余
+
+ // 如果需要填充满容器且还有剩余空间
if (fit && remainingSpace > 0) {
+ // 将剩余空间以1px为单位分配给最小百分比宽度列和最小像素宽度列
+ // 分配数量不超过剩余空间大小
scaleMinArr
.concat(pxMinArr)
.slice(0, remainingSpace)
@@ -125,6 +178,14 @@ export const calcTableWidth = ({ bodyWidth, columnStore, fit, minCellWidth, rema
return tableWidth
}
+/**
+ * 设置固定列的左侧或右侧定位位置
+ * @param columnList - 需要处理的列数组
+ * @param direction - 方向,'left' 或 'right'
+ * @param headerEl - 表头DOM元素
+ * @param bodyEl - 表体DOM元素
+ * @param scrollbarWidth - 滚动条宽度
+ */
const setLeftOrRightPosition = ({ columnList, direction, headerEl, bodyEl, scrollbarWidth }) => {
// 这里需要浅拷贝一份,避免改变原始数据的顺序
const colList = columnList.slice()
@@ -156,7 +217,12 @@ const setLeftOrRightPosition = ({ columnList, direction, headerEl, bodyEl, scrol
}, 0)
}
-// 设置分组父表头冻结列sticky布局的left和right值
+/**
+ * 设置分组表头中固定列的定位位置
+ * 处理多级表头情况下的固定列定位
+ * @param columnChart - 列层级关系图
+ * @param direction - 方向,'left' 或 'right'
+ */
const setGroupHeaderPosition = ({ columnChart, direction }) => {
// 这里需要浅拷贝一份,避免改变原始数据的顺序
const colChart = columnChart.slice()
@@ -191,7 +257,13 @@ const setGroupHeaderPosition = ({ columnChart, direction }) => {
})
}
-// 设置分组父表头冻结列是否左侧最后一项,或者是否右侧第一项
+/**
+ * 标记分组表头中的特殊固定列
+ * 设置左侧最后一个固定列和右侧第一个固定列的标记
+ * @param columnChart - 列层级关系图
+ * @param leftList - 左侧固定列数组
+ * @param rightList - 右侧固定列数组
+ */
const setGroupHeaderLastOrFirst = ({ columnChart, leftList, rightList }) => {
columnChart.forEach((columns) => {
const len = columns.length
@@ -207,6 +279,16 @@ const setGroupHeaderLastOrFirst = ({ columnChart, leftList, rightList }) => {
})
}
+/**
+ * 计算并设置固定列的粘性定位位置
+ * 主要处理固定列的位置计算和样式设置
+ * @param headerEl - 表头DOM元素
+ * @param bodyEl - 表体DOM元素
+ * @param columnStore - 列配置存储对象
+ * @param scrollbarWidth - 滚动条宽度
+ * @param columnChart - 列层级关系图
+ * @param isGroup - 是否是分组表头
+ */
export const calcFixedStickyPosition = ({ headerEl, bodyEl, columnStore, scrollbarWidth, columnChart, isGroup }) => {
// 获取左侧和右侧冻结列
const { leftList, rightList } = columnStore
From 3d4e0a91b4aba312ca8438c607e5bc3f01169216 Mon Sep 17 00:00:00 2001
From: ajaxzheng <894103554@qq.com>
Date: Mon, 31 Mar 2025 17:26:27 +0800
Subject: [PATCH 12/52] =?UTF-8?q?feat(grid):=20=E5=A2=9E=E5=8A=A0GridColum?=
=?UTF-8?q?n=E7=BB=84=E4=BB=B6=EF=BC=8C=E4=BC=98=E5=8C=96=E5=88=97?=
=?UTF-8?q?=E5=AE=BD=E5=BA=A6=E5=88=86=E6=9E=90=E9=80=BB=E8=BE=91=EF=BC=8C?=
=?UTF-8?q?=E5=A2=9E=E5=BC=BA=E8=A1=A8=E6=A0=BC=E6=95=B0=E6=8D=AE=E5=88=86?=
=?UTF-8?q?=E7=BB=84=E5=A4=84=E7=90=86=EF=BC=8C=E6=B7=BB=E5=8A=A0=E8=AF=A6?=
=?UTF-8?q?=E7=BB=86=E6=B3=A8=E9=87=8A=E4=BB=A5=E6=8F=90=E5=8D=87=E5=8F=AF?=
=?UTF-8?q?=E8=AF=BB=E6=80=A7?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/vue/src/grid/index.ts | 90 ++++++++---
.../vue/src/grid/src/column/src/Column.vue | 149 ++++++++++++++++++
packages/vue/src/grid/src/index.ts | 2 +-
packages/vue/src/grid/src/table/src/Table.vue | 24 +++
.../vue/src/grid/src/table/src/methods.ts | 43 ++++-
.../vue/src/grid/src/table/src/strategy.ts | 10 +-
6 files changed, 289 insertions(+), 29 deletions(-)
create mode 100644 packages/vue/src/grid/src/column/src/Column.vue
diff --git a/packages/vue/src/grid/index.ts b/packages/vue/src/grid/index.ts
index d0799e53cc..59674299fd 100644
--- a/packages/vue/src/grid/index.ts
+++ b/packages/vue/src/grid/index.ts
@@ -9,52 +9,88 @@
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
+// 导入国际化工具函数
import { t } from '@opentiny/vue-locale'
+
+// 导入grid组件的样式文件
import '@opentiny/vue-theme/grid/index.less'
+
+// 导入grid适配器,用于配置和扩展grid功能
import GridAdapter from './src/adapter'
+
+// 导入表格基础组件
import Table from './src/table'
+
+// 导入表格列组件
import GridColumn from './src/column'
+
+// 导入grid主组件
import Grid from './src'
+
+// 导入右键菜单插件
import Menu from './src/menu'
+
+// 导入编辑器插件
import Edit from './src/edit'
+
+// 导入导出功能插件
import Export from './src/export'
+
+// 导入键盘操作插件
import Keyboard from './src/keyboard'
+
+// 导入表单验证插件
import Validator from './src/validator'
+
+// 导入自适应大小插件
import Resize from './src/resize'
+
+// 导入过滤功能插件
import Filter from './src/filter'
+
+// 导入全局配置
import GridConfig from './src/config'
+
+// 导入单选功能组件
import GridRadio from './src/radio'
+
+// 导入数据获取插件
import FetchData from './src/fetch-data'
+
+// 导入分页器插件
import Pager from './src/pager'
+
+// 导入工具栏插件
import Toolbar from './src/toolbar'
+
+// 导入列锚点插件
import ColumnAnchor from './src/column-anchor'
+
+// 导入拖拽功能插件
import Dragger from './src/dragger'
+
+// 导入排序功能插件
import Sort from './src/sort'
+
+// 导入提示框插件
import Tooltip from './src/tooltip'
+
+// 导入多选功能插件
import Checkbox from './src/checkbox'
+
+// 导入树形表格插件
import Tree from './src/tree'
+
+// 导入工具函数集合
import * as GridTools from './src/tools'
+
+// 导入版本号
import { version } from './package.json'
+
+// 导入插件类型定义
import type { Plugin } from './src/types/index.type'
-/**
- * Menu 右键菜单
- * Edit 内置编辑器
- * Export 导出
- * Keyboard 键盘操作
- * Validator 校验
- * Resize 响应式改变表格宽高(auto-resize)
- * Filter 筛选
- * FetchData 远程数据处理
- * Pager 分页处理逻辑
- * Toolbar 工具栏处理逻辑
- * ColumnAnchor 表格列锚点
- * Dragger 拖拽相关逻辑
- * Sort 排序相关逻辑
- * Tooltip 提示相关逻辑
- * Checkbox 多选相关逻辑
- * Tree 树表相关逻辑
- */
+// 定义需要安装的插件列表
const plugins: Plugin[] = [
Menu,
Edit,
@@ -74,14 +110,18 @@ const plugins: Plugin[] = [
Tree
]
-// 设置全局参数,配置GlobalConfig,提供比如国际化方法
+// 设置全局参数,配置国际化方法
GridAdapter.setup({ i18n: t })
GridAdapter.t = t
-// 将每个插件的方法都合并回自己的宿主组件
+// 遍历安装所有插件,根据插件的host属性决定安装到Grid还是Table组件上
plugins.map((plugin) => plugin.install(plugin.host === 'grid' ? Grid : Table))
-// 让用户可以通过grid组件的方法间接调用内层table组件的方法
+/**
+ * 创建包装函数,用于代理table组件的方法到grid组件
+ * @param {string} name - 方法名
+ * @returns {Function} 返回包装后的函数
+ */
const getWrapFunc = (name) =>
function (...args) {
const tinyTable = this.$refs.tinyTable
@@ -90,20 +130,26 @@ const getWrapFunc = (name) =>
}
}
-// 将table组件的方法,传递给grid组件使用,this指向全部指向tinyTable
+// 将table组件的所有方法复制到grid组件中
Object.keys(Table.methods).forEach((name) => {
if (!Grid.methods[name]) {
Grid.methods[name] = getWrapFunc(name)
}
})
+// 设置组件版本号
Grid.version = version
+/**
+ * 安装方法,用于Vue.use()安装组件
+ * @param {Object} Vue - Vue构造函数
+ */
Grid.install = function (Vue) {
Vue.component(Grid.name, Grid)
}
/* istanbul ignore next */
+// 在运行时环境下自动安装组件
if (process.env.BUILD_TARGET === 'runtime') {
if (typeof window !== 'undefined' && window.Vue) {
Grid.install(window.Vue)
diff --git a/packages/vue/src/grid/src/column/src/Column.vue b/packages/vue/src/grid/src/column/src/Column.vue
new file mode 100644
index 0000000000..f77bb3589d
--- /dev/null
+++ b/packages/vue/src/grid/src/column/src/Column.vue
@@ -0,0 +1,149 @@
+
+
+
+
+
+
+
diff --git a/packages/vue/src/grid/src/index.ts b/packages/vue/src/grid/src/index.ts
index 28c3ded8ee..a00c79da4f 100644
--- a/packages/vue/src/grid/src/index.ts
+++ b/packages/vue/src/grid/src/index.ts
@@ -9,7 +9,7 @@
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
-import Grid from './grid/grid'
+import Grid from './grid/grid.vue'
Grid.install = function (Vue) {
Vue.component(Grid.name, Grid)
diff --git a/packages/vue/src/grid/src/table/src/Table.vue b/packages/vue/src/grid/src/table/src/Table.vue
index f16b96a4e0..e54df37a4e 100644
--- a/packages/vue/src/grid/src/table/src/Table.vue
+++ b/packages/vue/src/grid/src/table/src/Table.vue
@@ -991,6 +991,13 @@ export default defineComponent({
}
)
+ watch(
+ () => props.height,
+ () => {
+ instance.recalculate()
+ }
+ )
+
// 监听列配置变化
watch(
// 监听收集的列配置
@@ -1006,6 +1013,23 @@ export default defineComponent({
}
)
+ watch(
+ () => tableColumn.value,
+ () => {
+ // 对所有列的列宽进行分类:百分比/px
+ instance.analyColumnWidth()
+ // 处理空数据时表头是否禁用
+ instance.handleSelectionHeader()
+ }
+ )
+
+ watch(
+ () => parentHeight.value,
+ () => {
+ instance.recalculate()
+ }
+ )
+
// 生命周期钩子
onBeforeUnmount(() => {
// 获取表格包装器DOM引用
diff --git a/packages/vue/src/grid/src/table/src/methods.ts b/packages/vue/src/grid/src/table/src/methods.ts
index 1a72f85080..aacb22e588 100644
--- a/packages/vue/src/grid/src/table/src/methods.ts
+++ b/packages/vue/src/grid/src/table/src/methods.ts
@@ -1028,33 +1028,64 @@ const Methods = {
},
// 指定列宽的列进行拆分
analyColumnWidth() {
- let { columnMinWidth, columnStore, columnWidth, tableFullColumn } = this
- let [autoList, pxList, pxMinList, resizeList, scaleList, scaleMinList] = [[], [], [], [], [], []]
- let ruleChains = [
+ // 从实例中获取列相关的配置参数
+ const { columnMinWidth, columnStore, columnWidth, tableFullColumn } = this
+
+ // 初始化不同类型列的数组:
+ // autoList - 自适应宽度的列
+ // pxList - 固定像素宽度的列
+ // pxMinList - 最小像素宽度的列
+ // resizeList - 用户手动调整过宽度的列
+ // scaleList - 百分比宽度的列
+ // scaleMinList - 最小百分比宽度的列
+ const [autoList, pxList, pxMinList, resizeList, scaleList, scaleMinList] = [[], [], [], [], [], []]
+
+ // 定义列宽度规则链,按优先级从高到低排序
+ const ruleChains = [
{
+ // 规则1: 用户手动调整过宽度的列
match: (col) => col.resizeWidth,
action: (col) => resizeList.push(col)
},
- { match: (col) => isPx(col.width), action: (col) => pxList.push(col) },
{
+ // 规则2: 设置了固定像素宽度的列
+ match: (col) => isPx(col.width),
+ action: (col) => pxList.push(col)
+ },
+ {
+ // 规则3: 设置了百分比宽度的列
match: (col) => isScale(col.width),
action: (col) => scaleList.push(col)
},
{
+ // 规则4: 设置了最小像素宽度的列
match: (col) => isPx(col.minWidth),
action: (col) => pxMinList.push(col)
},
{
+ // 规则5: 设置了最小百分比宽度的列
match: (col) => isScale(col.minWidth),
action: (col) => scaleMinList.push(col)
},
- { match: () => true, action: (col) => autoList.push(col) }
+ {
+ // 规则6: 默认规则,将列加入自适应列表
+ match: () => true,
+ action: (col) => autoList.push(col)
+ }
]
+
+ // 遍历所有列,根据规则链分配到对应的列表中
for (let i = 0; i < tableFullColumn.length; i++) {
let column = tableFullColumn[i]
+
+ // 如果列没有设置宽度,使用全局columnWidth
columnWidth && !column.width && (column.width = columnWidth)
+ // 如果列没有设置最小宽度,使用全局columnMinWidth
columnMinWidth && !column.minWidth && (column.minWidth = columnMinWidth)
+
+ // 只处理可见的列
if (column.visible) {
+ // 遍历规则链,匹配第一个符合的规则
for (let j = 0; j < ruleChains.length; j++) {
let ruleChain = ruleChains[j]
if (ruleChain.match(column)) {
@@ -1064,6 +1095,8 @@ const Methods = {
}
}
}
+
+ // 更新columnStore中的列分类数据
Object.assign(columnStore, { autoList, pxList, pxMinList, resizeList, scaleList, scaleMinList })
},
diff --git a/packages/vue/src/grid/src/table/src/strategy.ts b/packages/vue/src/grid/src/table/src/strategy.ts
index 27767bf1b0..7fe565aaea 100644
--- a/packages/vue/src/grid/src/table/src/strategy.ts
+++ b/packages/vue/src/grid/src/table/src/strategy.ts
@@ -388,7 +388,15 @@ const setSliceColumnTree = (_vm) => _vm.isGroup && (_vm._sliceColumnTree = slice
/** 判断是否是虚拟行 */
const isVirtualRow = (row) => row && row[VIRTUAL_ROW_KEY]
-/** 普通表分组场景,按数据顺序对数据进行分组 */
+/**
+ * 对表格数据进行分组处理
+ * @param {Array} arr - 原始数据数组
+ * @param {string} key - 分组键
+ * @param {Function} equals - 比较函数
+ * @param {Function} active - 激活状态判断函数
+ * @param {string} rowKey - 行键
+ * @returns {Array} 分组后的数据数组
+ */
const orderingGroupBy = (arr, key, equals, active, rowKey) => {
const result = []
const virtualItems = []
From 7b9f53b80993f6e11c8325fb5b469486d12865da Mon Sep 17 00:00:00 2001
From: ajaxzheng <894103554@qq.com>
Date: Mon, 31 Mar 2025 19:15:29 +0800
Subject: [PATCH 13/52] =?UTF-8?q?fix(grid):=20=E6=9B=B4=E6=96=B0Column?=
=?UTF-8?q?=E7=BB=84=E4=BB=B6=E5=AF=BC=E5=85=A5=E8=B7=AF=E5=BE=84=EF=BC=8C?=
=?UTF-8?q?=E4=BF=AE=E6=AD=A3=E4=B8=BAColumn.vue=EF=BC=8C=E5=B9=B6?=
=?UTF-8?q?=E4=BC=98=E5=8C=96=E6=8F=92=E6=A7=BD=E5=A4=84=E7=90=86=E9=80=BB?=
=?UTF-8?q?=E8=BE=91=E4=BB=A5=E6=8F=90=E5=8D=87=E7=BB=84=E4=BB=B6=E6=80=A7?=
=?UTF-8?q?=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
packages/vue/src/grid/src/column/index.ts | 2 +-
.../vue/src/grid/src/column/src/Column.vue | 33 ++++++++++++++-----
2 files changed, 25 insertions(+), 10 deletions(-)
diff --git a/packages/vue/src/grid/src/column/index.ts b/packages/vue/src/grid/src/column/index.ts
index 78d407e533..a75958acc7 100644
--- a/packages/vue/src/grid/src/column/index.ts
+++ b/packages/vue/src/grid/src/column/index.ts
@@ -9,7 +9,7 @@
* A PARTICULAR PURPOSE. SEE THE APPLICABLE LICENSES FOR MORE DETAILS.
*
*/
-import Column from './src/column'
+import Column from './src/Column.vue'
Column.install = function (Vue) {
Vue.component(Column.name, Column)
diff --git a/packages/vue/src/grid/src/column/src/Column.vue b/packages/vue/src/grid/src/column/src/Column.vue
index f77bb3589d..b072d4bee0 100644
--- a/packages/vue/src/grid/src/column/src/Column.vue
+++ b/packages/vue/src/grid/src/column/src/Column.vue
@@ -5,6 +5,7 @@
From 8e812774a464b67484cd9597cacc758cb4293089 Mon Sep 17 00:00:00 2001
From: Gimmy <975402925@qq.com>
Date: Tue, 8 Apr 2025 16:58:42 +0800
Subject: [PATCH 17/52] fix: sync aui table optimization
---
packages/renderless/src/grid/utils/dom.ts | 4 +-
packages/theme-saas/src/grid/body.less | 6 +
packages/theme-saas/src/grid/header.less | 53 --
packages/theme-saas/src/grid/table.less | 122 +++-
packages/theme/src/grid/body.less | 6 +
packages/theme/src/grid/header.less | 2 -
packages/theme/src/grid/table.less | 91 ++-
packages/vue-locale/src/format.ts | 10 +
packages/vue-locale/src/vue2.7/index.ts | 4 +-
packages/vue-locale/src/vue2/index.ts | 4 +-
packages/vue-locale/src/vue3/index.ts | 4 +-
.../vue/src/grid/src/adapter/src/renderer.ts | 28 +-
packages/vue/src/grid/src/body/src/body.tsx | 4 -
packages/vue/src/grid/src/body/src/body.vue | 76 +--
.../vue/src/grid/src/body/src/useHeader.ts | 114 ++++
packages/vue/src/grid/src/body/src/usePool.ts | 82 +++
.../vue/src/grid/src/checkbox/src/methods.ts | 10 +-
packages/vue/src/grid/src/composable/index.ts | 1 +
.../src/grid/src/composable/useCellStatus.ts | 50 ++
.../src/grid/src/composable/useDrag/index.ts | 11 +
.../vue/src/grid/src/dragger/src/methods.ts | 2 +-
packages/vue/src/grid/src/edit/src/methods.ts | 9 +-
packages/vue/src/grid/src/footer/index.ts | 32 -
.../vue/src/grid/src/footer/src/footer.ts | 359 -----------
packages/vue/src/grid/src/header/index.ts | 32 -
.../vue/src/grid/src/header/src/header.ts | 579 ------------------
.../vue/src/grid/src/keyboard/src/methods.ts | 16 +-
.../vue/src/grid/src/mobile-first/index.vue | 3 +-
.../vue/src/grid/src/resize/src/methods.ts | 1 -
packages/vue/src/grid/src/table/src/Table.vue | 226 ++++---
.../vue/src/grid/src/table/src/methods.ts | 118 ++--
.../vue/src/grid/src/table/src/strategy.ts | 4 +-
.../src/table/src/utils/computeScrollLoad.ts | 2 +-
.../vue/src/grid/src/tooltip/src/methods.ts | 5 +-
34 files changed, 670 insertions(+), 1400 deletions(-)
create mode 100644 packages/vue/src/grid/src/body/src/useHeader.ts
create mode 100644 packages/vue/src/grid/src/body/src/usePool.ts
create mode 100644 packages/vue/src/grid/src/composable/useCellStatus.ts
delete mode 100644 packages/vue/src/grid/src/footer/index.ts
delete mode 100644 packages/vue/src/grid/src/footer/src/footer.ts
delete mode 100644 packages/vue/src/grid/src/header/index.ts
delete mode 100644 packages/vue/src/grid/src/header/src/header.ts
diff --git a/packages/renderless/src/grid/utils/dom.ts b/packages/renderless/src/grid/utils/dom.ts
index f8cd4fb266..f319dc4007 100644
--- a/packages/renderless/src/grid/utils/dom.ts
+++ b/packages/renderless/src/grid/utils/dom.ts
@@ -36,8 +36,8 @@ export const isPx = (val) => val && /^\d+(px)?$/.test(val)
export const isScale = (val) => val && /^\d+%$/.test(val)
-export const updateCellTitle = (event) => {
- const cellEl = event.currentTarget.querySelector(CELL_CLS)
+export const updateCellTitle = (event, td) => {
+ const cellEl = td ? td.querySelector(CELL_CLS) : event.currentTarget.querySelector(CELL_CLS)
const content = cellEl.innerText
if (cellEl.getAttribute('title') !== content) {
diff --git a/packages/theme-saas/src/grid/body.less b/packages/theme-saas/src/grid/body.less
index b14ed9fdd7..a45c2e2983 100644
--- a/packages/theme-saas/src/grid/body.less
+++ b/packages/theme-saas/src/grid/body.less
@@ -10,6 +10,12 @@
@apply border-b border-b-color-bg-3;
@apply overflow-y-auto;
@apply overflow-x-auto;
+
+ &.no-data {
+ @apply overflow-y-hidden;
+ @apply flex;
+ @apply flex-col;
+ }
}
.@{grid-prefix-cls}__borders {
diff --git a/packages/theme-saas/src/grid/header.less b/packages/theme-saas/src/grid/header.less
index cfa48b981e..ac0170ce15 100644
--- a/packages/theme-saas/src/grid/header.less
+++ b/packages/theme-saas/src/grid/header.less
@@ -4,8 +4,6 @@
@grid-header-prefix-cls: ~'@{css-prefix}grid-header';
@grid-cell-prefix-cls: ~'@{css-prefix}grid-cell';
@grid-checkbox-prefix-cls: ~'@{css-prefix}grid-checkbox';
-@header-suffix: ~'@{grid-prefix-cls}-cell__header-suffix';
-@cell-tooltip: ~'@{grid-prefix-cls}-cell__tooltip';
.@{grid-prefix-cls}__header-wrapper {
@apply bg-color-fill-8;
@@ -167,57 +165,6 @@
}
}
-.@{grid-prefix-cls}__header {
- .@{header-suffix} {
- @apply relative;
- min-height: 16px;
-
- .suffix-icon-1 {
- @apply absolute;
- @apply right-3;
- }
-
- .suffix-icon-0 {
- @apply absolute;
- @apply right-0;
- }
- }
-
- .col__ellipsis {
- &.is__editable.is__sortable.is__filter {
- .@{header-suffix}.@{cell-tooltip} {
- @apply pr-7;
- }
- }
-
- &.is__editable.is__sortable:not(.is__filter),
- &.is__editable.is__filter:not(.is__sortable) {
- .@{header-suffix}.@{cell-tooltip} {
- @apply ~'pr-3.5';
- }
- }
-
- &:not(.is__sortable):not(.is__filter) {
- .@{header-suffix}.@{cell-tooltip} {
- @apply pr-2;
- }
- }
-
- &.is__sortable.is__filter:not(.is__editable) {
- .@{header-suffix}.@{cell-tooltip} {
- padding-right: 26px;
- }
- }
-
- &.is__sortable:not(.is__filter):not(.is__editable),
- &.is__filter:not(.is__sortable):not(.is__editable) {
- .@{header-suffix}.@{cell-tooltip} {
- @apply pr-3;
- }
- }
- }
-}
-
.@{grid-prefix-cls} {
th.col__selection > .@{grid-cell-prefix-cls} {
@apply relative;
diff --git a/packages/theme-saas/src/grid/table.less b/packages/theme-saas/src/grid/table.less
index 43cc06ad16..6c21a20592 100644
--- a/packages/theme-saas/src/grid/table.less
+++ b/packages/theme-saas/src/grid/table.less
@@ -7,6 +7,8 @@
@input-prefix-cls: ~'@{css-prefix}input';
@select-prefix-cls: ~'@{css-prefix}select';
@pager-prefix-cls: ~'@{css-prefix}pager';
+@header-suffix: ~'@{grid-prefix-cls}-cell__header-suffix';
+@cell-tooltip: ~'@{grid-prefix-cls}-cell__tooltip';
// table
.@{grid-prefix-cls} {
@@ -372,7 +374,7 @@
}
&&__group-saas {
- .@{grid-prefix-cls}__header {
+ .@{grid-prefix-cls}__body thead {
@apply relative;
&::before {
@@ -418,7 +420,7 @@
}
&&__border-vertical {
- .@{grid-prefix-cls}__body {
+ .@{grid-prefix-cls}__body tbody {
@apply relative;
&::before {
@@ -861,37 +863,12 @@
}
& &__empty-block {
- @apply hidden;
- @apply opacity-0;
- @apply h-full;
- @apply ~"min-h-[theme('spacing.16')]";
- @apply py-16 px-0;
- @apply justify-center;
- @apply items-center;
- @apply text-center;
-
- &.is__visible {
- @apply flex;
- @apply flex-col;
- @apply opacity-100;
- &.is__center {
- @apply opacity-0;
- }
- }
- }
-
- .empty-center-block {
- @apply ~'z-[1]';
@apply flex;
@apply flex-col;
- @apply justify-center;
- @apply text-center;
- @apply absolute;
- @apply w-full;
-
- .@{grid-prefix-cls}__empty-text {
- @apply w-full;
- }
+ @apply flex-auto;
+ @apply items-center;
+ @apply sticky;
+ @apply left-0;
}
& &__empty-img {
@@ -904,8 +881,8 @@
& &__empty-text {
@apply block;
- @apply mt-2;
- @apply ~'w-1/2';
+ @apply w-full;
+ @apply text-center;
}
& &-body__column {
@@ -1012,12 +989,12 @@
& &__body-wrapper {
&.body__wrapper.is__scrollload {
@apply overflow-y-hidden;
- @apply static;
}
}
& .is__scrollload &-body__y-space {
@apply absolute;
@apply right-0;
+ @apply bottom-0;
@apply w-3;
@apply overflow-y-scroll;
@@ -1375,4 +1352,81 @@
}
}
}
+
+ .@{grid-prefix-cls}__body {
+ .tiny-grid-header__column {
+ @apply sticky;
+ /* --tiny-color-fill-8 真实对应 rgba(31, 85, 181, .05) */
+ background-color: var(--tiny-color-fill-8-solid, #f4f6fb);
+ }
+
+ .tiny-grid-header__column:last-child {
+ contain: layout;
+ }
+
+ .tiny-grid-header__column .tiny-grid-thead-partition,
+ .tiny-grid-header__column .tiny-grid-resizable {
+ transform: translateX(calc(50% - 1px));
+ }
+
+ .tiny-grid-custom-footer {
+ @apply w-full;
+ @apply sticky;
+ @apply bottom-0;
+ }
+
+ .tiny-grid-footer__column {
+ @apply sticky;
+ @apply bg-color-bg-1;
+ }
+
+ .@{header-suffix} {
+ @apply relative;
+ min-height: 16px;
+
+ .suffix-icon-1 {
+ @apply absolute;
+ @apply right-3;
+ }
+
+ .suffix-icon-0 {
+ @apply absolute;
+ @apply right-0;
+ }
+ }
+
+ .col__ellipsis {
+ &.is__editable.is__sortable.is__filter {
+ .@{header-suffix}.@{cell-tooltip} {
+ @apply pr-7;
+ }
+ }
+
+ &.is__editable.is__sortable:not(.is__filter),
+ &.is__editable.is__filter:not(.is__sortable) {
+ .@{header-suffix}.@{cell-tooltip} {
+ @apply ~'pr-3.5';
+ }
+ }
+
+ &:not(.is__sortable):not(.is__filter) {
+ .@{header-suffix}.@{cell-tooltip} {
+ @apply pr-2;
+ }
+ }
+
+ &.is__sortable.is__filter:not(.is__editable) {
+ .@{header-suffix}.@{cell-tooltip} {
+ padding-right: 26px;
+ }
+ }
+
+ &.is__sortable:not(.is__filter):not(.is__editable),
+ &.is__filter:not(.is__sortable):not(.is__editable) {
+ .@{header-suffix}.@{cell-tooltip} {
+ @apply pr-3;
+ }
+ }
+ }
+ }
}
diff --git a/packages/theme/src/grid/body.less b/packages/theme/src/grid/body.less
index df05526d6c..17dff090bd 100644
--- a/packages/theme/src/grid/body.less
+++ b/packages/theme/src/grid/body.less
@@ -21,6 +21,12 @@
.@{grid-prefix-cls}__fixed-right-body-wrapper {
overflow-y: auto;
overflow-x: auto;
+
+ &.no-data {
+ overflow-y: hidden;
+ display: flex;
+ flex-direction: column;
+ }
}
// 鼠标配置项开启后,选中单元格的边框样式(position:absolute)
diff --git a/packages/theme/src/grid/header.less b/packages/theme/src/grid/header.less
index 5044bd2296..746cb2ab70 100644
--- a/packages/theme/src/grid/header.less
+++ b/packages/theme/src/grid/header.less
@@ -16,8 +16,6 @@
@grid-header-prefix-cls: ~'@{css-prefix}grid-header';
@grid-cell-prefix-cls: ~'@{css-prefix}grid-cell';
@grid-checkbox-prefix-cls: ~'@{css-prefix}grid-checkbox';
-@header-suffix: ~'@{grid-prefix-cls}-cell__header-suffix';
-@cell-tooltip: ~'@{grid-prefix-cls}-cell__tooltip';
.@{grid-prefix-cls}__header-wrapper {
background-color: var(--tv-Grid-header-bg-color);
diff --git a/packages/theme/src/grid/table.less b/packages/theme/src/grid/table.less
index 46a9371c0e..80cc958d55 100644
--- a/packages/theme/src/grid/table.less
+++ b/packages/theme/src/grid/table.less
@@ -19,6 +19,7 @@
@input-prefix-cls: ~'@{css-prefix}input';
@select-prefix-cls: ~'@{css-prefix}select';
@pager-prefix-cls: ~'@{css-prefix}pager';
+@header-suffix: ~'@{grid-prefix-cls}-cell__header-suffix';
// table
.@{grid-prefix-cls} {
@@ -312,7 +313,11 @@
.@{grid-prefix-cls}-header__column,
.@{grid-prefix-cls}-body__column,
.@{grid-prefix-cls}-footer__column {
- background-image: linear-gradient(-90deg, var(--tv-Grid-border-color-divider), var(--tv-Grid-border-color-divider)),
+ background-image: linear-gradient(
+ -90deg,
+ var(--tv-Grid-border-color-divider),
+ var(--tv-Grid-border-color-divider)
+ ),
linear-gradient(-180deg, var(--tv-Grid-border-color-divider), var(--tv-Grid-border-color-divider));
background-repeat: no-repeat;
background-size:
@@ -708,38 +713,12 @@
// 暂无数据
& &__empty-block {
- display: none;
- opacity: 0;
- height: 100%;
- min-height: 60px;
- padding: 60px 0;
- justify-content: center;
- align-items: center;
- text-align: center;
-
- &.is__visible {
- display: flex;
- flex-flow: column wrap;
- opacity: 1;
- &.is__center {
- opacity: 0;
- }
- }
- }
-
- .empty-center-block {
- z-index: 1;
display: flex;
+ align-items: center;
+ position: sticky;
+ left: 0;
+ flex: auto;
flex-direction: column;
- justify-content: center;
- text-align: center;
- position: absolute;
- width: 100%;
- height: calc(100% - 60px);
-
- .@{grid-prefix-cls}__empty-text {
- width: 100%;
- }
}
// 表格无数据背景图
@@ -753,7 +732,8 @@
& &__empty-text {
display: block;
margin-top: 8px;
- width: 50%;
+ text-align: center;
+ width: 100%;
}
// 校验不通过
@@ -862,13 +842,13 @@
& &__body-wrapper {
&.body__wrapper.is__scrollload {
overflow-y: hidden;
- position: static;
}
}
& .is__scrollload &-body__y-space {
position: absolute;
right: 0;
+ bottom: 0;
width: 12px;
overflow-y: scroll;
@@ -1210,6 +1190,51 @@
}
}
}
+
+ .@{grid-prefix-cls}__body {
+ .tiny-grid-header__column {
+ background-color: var(--tv-Grid-header-bg-color);
+ position: sticky;
+ }
+
+ .tiny-grid-header__column:last-child {
+ contain: layout;
+ }
+
+ .tiny-grid-header__column .tiny-grid-thead-partition,
+ .tiny-grid-header__column .tiny-grid-resizable {
+ transform: translateX(calc(50% - 1px));
+ }
+
+ .tiny-grid-custom-footer {
+ width: 100%;
+ position: sticky;
+ bottom: 0;
+ }
+
+ .tiny-grid-footer__column {
+ position: sticky;
+ background-color: var(--tv-Grid-bg-color);
+ }
+
+ .@{grid-prefix-cls}-cell-text {
+ font-weight: var(--tv-Grid-header-font-weight);
+ }
+ .@{header-suffix} {
+ position: relative;
+ min-height: 16px;
+
+ .suffix-icon-1 {
+ position: absolute;
+ right: 12px;
+ }
+
+ .suffix-icon-0 {
+ position: absolute;
+ right: 0;
+ }
+ }
+ }
}
// 表格全屏样式
diff --git a/packages/vue-locale/src/format.ts b/packages/vue-locale/src/format.ts
index 0719fc5bf9..8d7f2b8edd 100644
--- a/packages/vue-locale/src/format.ts
+++ b/packages/vue-locale/src/format.ts
@@ -25,3 +25,13 @@ export default function (string, ...args) {
}
})
}
+
+export const memoize = (callback) => {
+ const cache = {}
+
+ return (key, ...args) => {
+ cache[key] = cache[key] || callback(key, ...args)
+
+ return cache[key]
+ }
+}
diff --git a/packages/vue-locale/src/vue2.7/index.ts b/packages/vue-locale/src/vue2.7/index.ts
index d4113c3745..cb9a62a458 100644
--- a/packages/vue-locale/src/vue2.7/index.ts
+++ b/packages/vue-locale/src/vue2.7/index.ts
@@ -1,6 +1,6 @@
import zhCN from '../lang/zh-CN'
import enUS from '../lang/en'
-import format from '../format'
+import format, { memoize } from '../format'
import { extend as _extend } from '@opentiny/utils'
let lang = zhCN
@@ -57,7 +57,7 @@ export const initI18n = ({ app, createI18n, messages = {}, i18n = {} as any, mer
messages: merge({ lang, i18n, messages })
})
- i18nHandler = (key, value) => vueI18n.global.t(key, value)
+ i18nHandler = memoize((key, value) => vueI18n.global.t(key, value))
return vueI18n
}
diff --git a/packages/vue-locale/src/vue2/index.ts b/packages/vue-locale/src/vue2/index.ts
index d24c481b24..19ff7172f2 100644
--- a/packages/vue-locale/src/vue2/index.ts
+++ b/packages/vue-locale/src/vue2/index.ts
@@ -1,7 +1,7 @@
import Vue from 'vue'
import zhCN from '../lang/zh-CN'
import enUS from '../lang/en'
-import format from '../format'
+import format, { memoize } from '../format'
import { extend as _extend } from '@opentiny/utils'
let lang = zhCN
@@ -70,7 +70,7 @@ export const initI18n = ({ VueI18n, messages = {}, i18n = {} as any, merge }) =>
messages: merge({ lang, i18n, messages })
})
- i18nHandler = (key, value) => vueI18n.t(key, value)
+ i18nHandler = memoize((key, value) => vueI18n.t(key, value))
return vueI18n
}
diff --git a/packages/vue-locale/src/vue3/index.ts b/packages/vue-locale/src/vue3/index.ts
index 76f7fbb14e..43e58971b8 100644
--- a/packages/vue-locale/src/vue3/index.ts
+++ b/packages/vue-locale/src/vue3/index.ts
@@ -1,6 +1,6 @@
import zhCN from '../lang/zh-CN'
import enUS from '../lang/en'
-import format from '../format'
+import format, { memoize } from '../format'
import { extend as _extend } from '@opentiny/utils'
let lang = zhCN
@@ -67,7 +67,7 @@ export const initI18n = ({ app, createI18n, messages = {}, i18n = {} as any, mer
messages: merge({ lang, i18n, messages })
})
- i18nHandler = (key, value) => vueI18n.global.t(key, value)
+ i18nHandler = memoize((key, value) => vueI18n.global.t(key, value))
return vueI18n
}
diff --git a/packages/vue/src/grid/src/adapter/src/renderer.ts b/packages/vue/src/grid/src/adapter/src/renderer.ts
index a06449e79a..b65322113f 100644
--- a/packages/vue/src/grid/src/adapter/src/renderer.ts
+++ b/packages/vue/src/grid/src/adapter/src/renderer.ts
@@ -22,8 +22,8 @@
* SOFTWARE.
*
*/
-import { set, assign, objectMap, get, each, isObject, isFunction } from '@opentiny/vue-renderless/grid/static/'
-import { getCellValue, setCellValue } from '@opentiny/vue-renderless/grid/utils'
+import { assign, objectMap, get, each, isObject, isFunction } from '@opentiny/vue-renderless/grid/static/'
+import { getCellValue, getRowid, setCellValue } from '@opentiny/vue-renderless/grid/utils'
import { hooks } from '@opentiny/vue-common'
/**
@@ -109,21 +109,17 @@ function getEvents(renderOpts, params, context) {
// - 其他组件直接使用event作为值
let cellValue = native ? event.target.value : event
- // 根据配置决定如何更新数据:
- // 1. 如果不需要始终验证且需要同步单元格,直接设置单元格值
- if (!renderOpts.isValidAlways && isSyncCell(renderOpts, params, context)) {
- setCellValue(row, column, cellValue)
- } else {
- // 2. 否则更新数据和状态:
- // - 非原生组件设置属性值
- // - 更新model状态
- // - 更新表格状态
- native || set(row, column.property, cellValue)
+ if (!isSyncCell(renderOpts, params, context)) {
model.update = true
model.value = cellValue
- $table.updateStatus(params, cellValue, renderOpts)
}
+ setCellValue(row, column, cellValue)
+
+ Promise.resolve().then(() => {
+ $table.updateStatus(params, cellValue, renderOpts)
+ })
+
// 对原生组件调用input和change回调
if (native) {
input && input.apply(null, [params].concat.apply(params, arguments))
@@ -341,7 +337,10 @@ function defaultFilterMethod({ option, row, column }) {
* @param context - 上下文
*/
function renderSelectEdit(h, renderOpts, params, context) {
+ const { column, $table, row } = params
+ const editorKey = `editor-${getRowid($table, row)}-${column.id}`
let props = {
+ ref: editorKey,
class: 'tiny-grid-default-select',
on: getEvents(renderOpts, params, context)
}
@@ -394,11 +393,14 @@ function defaultEditRender(h, renderOpts, params, context) {
// 如果是字符串组件使用value,否则使用modelValue或自定义的prop
let modelProps = typeof component === 'string' ? 'value' : editorModel.prop || 'modelValue'
+ const editorKey = `editor-${getRowid($table, row)}-${column.id}`
+
// 获取行的唯一标识作为key
const key = row[$table.rowId]
// 构建组件选项
let options = {
+ ref: editorKey,
key,
// 如果是原生标签则添加默认类名
class: isTag ? `tiny-grid-default-${component}` : '',
diff --git a/packages/vue/src/grid/src/body/src/body.tsx b/packages/vue/src/grid/src/body/src/body.tsx
index 7a406d072a..f7fddc0aa5 100644
--- a/packages/vue/src/grid/src/body/src/body.tsx
+++ b/packages/vue/src/grid/src/body/src/body.tsx
@@ -894,10 +894,6 @@ export default defineComponent({
beforeUnmount() {
this.rowSortable && this.rowSortable.destroy()
},
- updated() {
- const { $parent: $table, fixedType } = this
- !fixedType && $table.updateTableBodyHeight()
- },
setup(props, { slots }) {
hooks.onBeforeUnmount(() => {
const table = hooks.getCurrentInstance().proxy
diff --git a/packages/vue/src/grid/src/body/src/body.vue b/packages/vue/src/grid/src/body/src/body.vue
index 9fb75565be..f418bbfd7d 100644
--- a/packages/vue/src/grid/src/body/src/body.vue
+++ b/packages/vue/src/grid/src/body/src/body.vue
@@ -14,7 +14,15 @@
-