mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-06-02 20:49:48 +08:00
✨ feat(TableDesigner): 优化设计表滚动体验并支持索引批量删除
- 滚动优化:修复 Tab 切换时 ResizeObserver 高度归零导致表格异常滚动
- 零高度守卫:移除 activeKey 依赖,跳过 display:none 时的零高度观测
- 触发器统一:触发器 Tab 补充 scroll={{ y: tableHeight }} 与索引/外键保持一致
- 批量删除:handleDeleteIndex 支持多选索引批量生成 DROP SQL 合并执行
- 交互优化:删除确认弹窗展示选中索引数量和名称列表
- 状态清理:批量删除成功后自动清空 selectedIndexKeys
- refs #273
This commit is contained in:
@@ -292,17 +292,21 @@ const TableDesigner: React.FC<{ tab: TabData }> = ({ tab }) => {
|
||||
|
||||
// 透明 Monaco Editor 主题已在 main.tsx 全局注册(含 stickyScroll 不透明背景)
|
||||
|
||||
// 监听字段 Tab 容器高度,为所有 Tab 内表格计算 scroll.y
|
||||
// 当 Tab 切换时,字段 Tab 被 display:none 导致 height=0,跳过该次更新保持有效值
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
const resizeObserver = new ResizeObserver(entries => {
|
||||
for (let entry of entries) {
|
||||
const h = Math.max(200, entry.contentRect.height - 40);
|
||||
setTableHeight(h);
|
||||
const h = entry.contentRect.height;
|
||||
// 跳过零高度观测(Tab 面板被隐藏时)
|
||||
if (h <= 0) return;
|
||||
setTableHeight(Math.max(200, h - 40));
|
||||
}
|
||||
});
|
||||
resizeObserver.observe(containerRef.current);
|
||||
return () => resizeObserver.disconnect();
|
||||
}, [activeKey]); // Re-attach when tab switches
|
||||
}, []); // 不依赖 activeKey,仅挂载一次,通过零高度守卫避免 Tab 切换异常
|
||||
|
||||
// --- Resizable Columns State ---
|
||||
const [tableColumns, setTableColumns] = useState<any[]>([]);
|
||||
@@ -1687,28 +1691,44 @@ END;`;
|
||||
};
|
||||
|
||||
const handleDeleteIndex = () => {
|
||||
if (!selectedIndex) {
|
||||
message.warning('请先选择一个索引');
|
||||
if (selectedIndexKeys.length === 0) {
|
||||
message.warning('请先选择要删除的索引');
|
||||
return;
|
||||
}
|
||||
if (!supportsIndexSchemaOps()) {
|
||||
message.warning('当前数据库暂不支持在此维护索引');
|
||||
return;
|
||||
}
|
||||
// 根据选中的 key 找到对应的索引对象
|
||||
const toDelete = groupedIndexes.filter(idx => selectedIndexKeys.includes(idx.key));
|
||||
if (toDelete.length === 0) {
|
||||
message.warning('请先选择要删除的索引');
|
||||
return;
|
||||
}
|
||||
const names = toDelete.map(idx => `"${idx.name}"`).join('、');
|
||||
Modal.confirm({
|
||||
title: '确认删除索引',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: `确定删除索引 "${selectedIndex.name}" 吗?`,
|
||||
content: toDelete.length === 1
|
||||
? `确定删除索引 ${names} 吗?`
|
||||
: `确定删除以下 ${toDelete.length} 个索引吗?\n${names}`,
|
||||
okText: '删除',
|
||||
okType: 'danger',
|
||||
cancelText: '取消',
|
||||
onOk: async () => {
|
||||
const sql = buildIndexDropSql(selectedIndex.name);
|
||||
if (!sql) {
|
||||
message.warning('当前数据库暂不支持删除该索引');
|
||||
return;
|
||||
const sqls: string[] = [];
|
||||
for (const idx of toDelete) {
|
||||
const sql = buildIndexDropSql(idx.name);
|
||||
if (!sql) {
|
||||
message.warning(`当前数据库暂不支持删除索引 "${idx.name}"`);
|
||||
return;
|
||||
}
|
||||
sqls.push(sql);
|
||||
}
|
||||
const ok = await executeSchemaSql(sqls.join('\n'), toDelete.length === 1 ? '索引删除成功' : `${toDelete.length} 个索引删除成功`);
|
||||
if (ok) {
|
||||
setSelectedIndexKeys([]);
|
||||
}
|
||||
await executeSchemaSql(sql, '索引删除成功');
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -2538,6 +2558,7 @@ END;`;
|
||||
size="small"
|
||||
pagination={false}
|
||||
loading={loading}
|
||||
scroll={{ y: tableHeight }}
|
||||
locale={{ emptyText: <Empty description="该表暂无触发器" image={Empty.PRESENTED_IMAGE_SIMPLE} /> }}
|
||||
rowSelection={{
|
||||
type: 'radio',
|
||||
|
||||
Reference in New Issue
Block a user