+
{columnNames.map((col) => {
const sample = rowEditorDisplayRef.current?.[col] ?? '';
const placeholder = rowEditorNullColsRef.current?.has(col) ? '(NULL)' : undefined;
@@ -1527,19 +1559,21 @@ const DataGrid: React.FC
= ({
- {/* Cell Context Menu */}
- {cellContextMenu.visible && (
+ {/* Cell Context Menu - 使用 Portal 渲染到 body,避免 backdropFilter 影响 fixed 定位 */}
+ {cellContextMenu.visible && createPortal(
0 ? `blur(${blur}px)` : undefined,
+ border: darkMode ? '1px solid #303030' : '1px solid #d9d9d9',
borderRadius: 4,
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
- minWidth: 120,
+ minWidth: 160,
+ color: darkMode ? '#fff' : 'rgba(0, 0, 0, 0.88)'
}}
onClick={(e) => e.stopPropagation()}
>
@@ -1549,18 +1583,133 @@ const DataGrid: React.FC = ({
cursor: 'pointer',
transition: 'background 0.2s',
}}
- onMouseEnter={(e) => e.currentTarget.style.background = '#f5f5f5'}
+ onMouseEnter={(e) => e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
onClick={handleCellSetNull}
>
设置为 NULL
-
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleCopyInsert(cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 复制为 INSERT
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleCopyJson(cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 复制为 JSON
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleCopyCsv(cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 复制为 CSV
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) {
+ const records = getTargets(cellContextMenu.record);
+ const lines = records.map((r: any) => {
+ const { [GONAVI_ROW_KEY]: _rowKey, ...vals } = r;
+ return `| ${Object.values(vals).join(' | ')} |`;
+ });
+ copyToClipboard(lines.join('\n'));
+ }
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 复制为 Markdown
+
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleExportSelected('csv', cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 导出为 CSV
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleExportSelected('xlsx', cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 导出为 Excel
+
+
e.currentTarget.style.background = darkMode ? '#303030' : '#f5f5f5'}
+ onMouseLeave={(e) => e.currentTarget.style.background = 'transparent'}
+ onClick={() => {
+ if (cellContextMenu.record) handleExportSelected('json', cellContextMenu.record);
+ setCellContextMenu(prev => ({ ...prev, visible: false }));
+ }}
+ >
+ 导出为 JSON
+
+
,
+ document.body
)}