mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-09 08:13:29 +08:00
feat(tdengine): 优化子表 TAG 值填写交互
- 超级表名改为下拉选择,自动从当前库拉取超级表列表 - 选择超级表后自动拉取 TAG 定义,逐行显示名称/类型/输入框 - 根据 TAG 类型自动处理引号拼接 TAGS 值 - 加载失败时降级为文本输入框兜底 - 新增 i18n 中/英/日/德/繁中翻译
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import Modal from './common/ResizableDraggableModal';
|
||||
import React, { useEffect, useState, useContext, useMemo, useRef, useCallback } from 'react';
|
||||
import { Table, Tabs, Button, message, Input, Checkbox, AutoComplete, Tooltip, Select, Empty, Space, Tag, Radio } from 'antd';
|
||||
import { Table, Tabs, Button, message, Input, Checkbox, AutoComplete, Tooltip, Select, Empty, Space, Tag, Radio, Spin } from 'antd';
|
||||
import { ReloadOutlined, SaveOutlined, PlusOutlined, DeleteOutlined, MenuOutlined, FileTextOutlined, EyeOutlined, EditOutlined, ExclamationCircleOutlined, CopyOutlined, TableOutlined } from '@ant-design/icons';
|
||||
import { DndContext, closestCenter, KeyboardSensor, PointerSensor, useSensor, useSensors, DragOverlay } from '@dnd-kit/core';
|
||||
import { arrayMove, SortableContext, sortableKeyboardCoordinates, verticalListSortingStrategy, useSortable } from '@dnd-kit/sortable';
|
||||
@@ -8,7 +8,7 @@ import { CSS } from '@dnd-kit/utilities';
|
||||
import Editor from './MonacoEditor';
|
||||
import { TabData, ColumnDefinition, IndexDefinition, ForeignKeyDefinition, TriggerDefinition } from '../types';
|
||||
import { useStore } from '../store';
|
||||
import { DBGetColumns, DBGetIndexes, DBQueryAudited, DBGetForeignKeys, DBGetTriggers, DBShowCreateTable } from '../../wailsjs/go/app/App';
|
||||
import { DBGetColumns, DBGetIndexes, DBQuery, DBQueryAudited, DBGetForeignKeys, DBGetTriggers, DBShowCreateTable } from '../../wailsjs/go/app/App';
|
||||
import { hasIndexFormChanged, normalizeIndexFormFromRow, resolveIndexMetadataResponse, shouldRestoreOriginalIndex, toggleIndexSelection as getNextIndexSelection, type IndexDisplaySnapshot } from './tableDesignerIndexUtils';
|
||||
import { buildIndexCreateSqlPreview } from './tableDesignerIndexSql';
|
||||
import { buildAlterTablePreviewSql, buildCreateTablePreviewSql, hasAlterTableDraftChanges, type StarRocksCreateTableOptions, type StarRocksDistributionType, type StarRocksKeyModel, type StarRocksTableKind, type TDengineCreateTableOptions, type TDengineTableKind, type TDengineTagDefinition } from './tableDesignerSchemaSql';
|
||||
@@ -456,6 +456,11 @@ const TableDesigner: React.FC<{ tab: TabData; embedded?: boolean }> = ({ tab, em
|
||||
{ _key: 'tag-1', name: 'location', type: 'BINARY(64)' },
|
||||
]);
|
||||
const [tdengineTagValues, setTdengineTagValues] = useState('');
|
||||
const [tdengineStableOptions, setTdengineStableOptions] = useState<{ label: string; value: string }[]>([]);
|
||||
const [tdengineStableOptionsLoading, setTdengineStableOptionsLoading] = useState(false);
|
||||
const [tdengineChildTagDefs, setTdengineChildTagDefs] = useState<TDengineTagDraft[]>([]);
|
||||
const [tdengineChildTagValues, setTdengineChildTagValues] = useState<Record<string, string>>({});
|
||||
const [tdengineChildTagDefsLoading, setTdengineChildTagDefsLoading] = useState(false);
|
||||
|
||||
const [columnsLoading, setColumnsLoading] = useState(false);
|
||||
const [indexesLoading, setIndexesLoading] = useState(false);
|
||||
@@ -1668,6 +1673,99 @@ ${selectedTrigger.statement}`;
|
||||
}
|
||||
}, [isTDengineNewTable, tab.initialTab]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTDengineChildNewTable) {
|
||||
setTdengineStableOptions([]);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
const fetchSuperTables = async () => {
|
||||
setTdengineStableOptionsLoading(true);
|
||||
try {
|
||||
const conn = connections.find(c => c.id === tab.connectionId);
|
||||
if (!conn) return;
|
||||
const config = {
|
||||
...conn.config,
|
||||
port: Number(conn.config.port),
|
||||
password: conn.config.password || "",
|
||||
database: conn.config.database || "",
|
||||
useSSH: conn.config.useSSH || false,
|
||||
ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
|
||||
};
|
||||
const rpcConfig = buildRpcConnectionConfig(config) as any;
|
||||
const dbName = tab.dbName || '';
|
||||
const res = await DBQuery(rpcConfig, dbName, `SHOW STABLES FROM \`${dbName}\``);
|
||||
if (cancelled) return;
|
||||
if (res.success && Array.isArray(res.data)) {
|
||||
const options = res.data.map((row: any) => {
|
||||
const name = row?.table_name || row?.Table || row?.name || row?.tablename || '';
|
||||
return { label: String(name), value: String(name) };
|
||||
}).filter(opt => opt.value);
|
||||
setTdengineStableOptions(options);
|
||||
} else {
|
||||
setTdengineStableOptions([]);
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) setTdengineStableOptions([]);
|
||||
} finally {
|
||||
if (!cancelled) setTdengineStableOptionsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchSuperTables();
|
||||
return () => { cancelled = true; };
|
||||
}, [isTDengineChildNewTable, tab.connectionId, tab.dbName, connections]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isTDengineChildNewTable || !tdengineStableName.trim()) {
|
||||
setTdengineChildTagDefs([]);
|
||||
setTdengineChildTagValues({});
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
const fetchTagDefs = async () => {
|
||||
setTdengineChildTagDefsLoading(true);
|
||||
try {
|
||||
const conn = connections.find(c => c.id === tab.connectionId);
|
||||
if (!conn) return;
|
||||
const config = {
|
||||
...conn.config,
|
||||
port: Number(conn.config.port),
|
||||
password: conn.config.password || "",
|
||||
database: conn.config.database || "",
|
||||
useSSH: conn.config.useSSH || false,
|
||||
ssh: conn.config.ssh || { host: "", port: 22, user: "", password: "", keyPath: "" }
|
||||
};
|
||||
const rpcConfig = buildRpcConnectionConfig(config) as any;
|
||||
const dbName = tab.dbName || '';
|
||||
const res = await DBGetColumns(rpcConfig, dbName, tdengineStableName.trim());
|
||||
if (cancelled) return;
|
||||
if (res.success && Array.isArray(res.data)) {
|
||||
const tagDefs: TDengineTagDraft[] = res.data
|
||||
.filter((col: any) => (col?.key || col?.Key || '') === 'TAG')
|
||||
.map((col: any, idx: number) => ({
|
||||
_key: `tagdef-${idx}`,
|
||||
name: String(col?.name || col?.Name || ''),
|
||||
type: String(col?.type || col?.Type || ''),
|
||||
}));
|
||||
setTdengineChildTagDefs(tagDefs);
|
||||
setTdengineChildTagValues({});
|
||||
} else {
|
||||
setTdengineChildTagDefs([]);
|
||||
setTdengineChildTagValues({});
|
||||
}
|
||||
} catch {
|
||||
if (!cancelled) {
|
||||
setTdengineChildTagDefs([]);
|
||||
setTdengineChildTagValues({});
|
||||
}
|
||||
} finally {
|
||||
if (!cancelled) setTdengineChildTagDefsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchTagDefs();
|
||||
return () => { cancelled = true; };
|
||||
}, [isTDengineChildNewTable, tdengineStableName, tab.connectionId, tab.dbName, connections]);
|
||||
|
||||
const parseStarRocksRollupOptions = (raw: string): StarRocksCreateTableOptions['rollups'] => (
|
||||
String(raw || '')
|
||||
.split(/\r?\n/)
|
||||
@@ -1705,11 +1803,23 @@ ${selectedTrigger.statement}`;
|
||||
|
||||
const buildTDengineCreateOptions = (): TDengineCreateTableOptions | undefined => {
|
||||
if (!isTDengineNewTable) return undefined;
|
||||
let tagValues = tdengineTagValues;
|
||||
if (tdengineTableKind === 'child' && tdengineChildTagDefs.length > 0) {
|
||||
tagValues = tdengineChildTagDefs
|
||||
.map(tag => {
|
||||
const val = tdengineChildTagValues[tag.name] ?? '';
|
||||
if (!val) return '';
|
||||
const needQuote = /^(BINARY|NCHAR|VARBINARY|TIMESTAMP)/i.test(tag.type);
|
||||
return needQuote ? `'${val.replace(/'/g, "\\'")}'` : val;
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join(', ');
|
||||
}
|
||||
return {
|
||||
tableKind: tdengineTableKind,
|
||||
stableName: tdengineStableName,
|
||||
tagDefinitions: tdengineTagDefinitions.map(({ _key, ...tag }) => tag),
|
||||
tagValues: tdengineTagValues,
|
||||
tagValues,
|
||||
};
|
||||
};
|
||||
|
||||
@@ -2509,7 +2619,13 @@ END;`;
|
||||
message.error(t('table_designer.message.tdengine_stable_name_required', undefined, i18nLanguage));
|
||||
return;
|
||||
}
|
||||
if (!tdengineTagValues.trim()) {
|
||||
if (tdengineChildTagDefs.length > 0) {
|
||||
const missing = tdengineChildTagDefs.filter(tag => !(tdengineChildTagValues[tag.name] ?? '').trim());
|
||||
if (missing.length > 0) {
|
||||
message.error(t('table_designer.message.tdengine_tag_values_required', undefined, i18nLanguage));
|
||||
return;
|
||||
}
|
||||
} else if (!tdengineTagValues.trim()) {
|
||||
message.error(t('table_designer.message.tdengine_tag_values_required', undefined, i18nLanguage));
|
||||
return;
|
||||
}
|
||||
@@ -2968,19 +3084,59 @@ END;`;
|
||||
|
||||
{tdengineTableKind === 'child' && (
|
||||
<Space direction="vertical" size={10} style={{ width: '100%' }}>
|
||||
<Input
|
||||
<Select
|
||||
{...noAutoCapInputProps}
|
||||
value={tdengineStableName}
|
||||
onChange={(event) => setTdengineStableName(event.target.value)}
|
||||
showSearch
|
||||
allowClear
|
||||
value={tdengineStableName || undefined}
|
||||
onChange={(value) => setTdengineStableName(value || '')}
|
||||
placeholder={t('table_designer.tdengine.placeholder.stable_name', undefined, i18nLanguage)}
|
||||
options={tdengineStableOptions}
|
||||
loading={tdengineStableOptionsLoading}
|
||||
style={{ width: '100%' }}
|
||||
filterOption={(input, option) =>
|
||||
(option?.label as string ?? '').toLowerCase().includes(input.toLowerCase())
|
||||
}
|
||||
notFoundContent={tdengineStableOptionsLoading ? null : t('table_designer.tdengine.message.no_stable_found', undefined, i18nLanguage)}
|
||||
/>
|
||||
<Input.TextArea
|
||||
{...noAutoCapInputProps}
|
||||
value={tdengineTagValues}
|
||||
onChange={(event) => setTdengineTagValues(event.target.value)}
|
||||
autoSize={{ minRows: 3, maxRows: 8 }}
|
||||
placeholder={t('table_designer.tdengine.placeholder.tag_values', undefined, i18nLanguage)}
|
||||
/>
|
||||
{tdengineChildTagDefsLoading || tdengineChildTagDefs.length > 0 ? (
|
||||
<Spin spinning={tdengineChildTagDefsLoading}>
|
||||
{tdengineChildTagDefs.length > 0 ? (
|
||||
<Space direction="vertical" size={8} style={{ width: '100%' }}>
|
||||
<Space size={8} style={{ width: '100%' }}>
|
||||
<span style={{ width: 160, color: '#888', fontSize: 12 }}>{t('table_designer.tdengine.tag.name', undefined, i18nLanguage)}</span>
|
||||
<span style={{ width: 160, color: '#888', fontSize: 12 }}>{t('table_designer.tdengine.tag.type', undefined, i18nLanguage)}</span>
|
||||
<span style={{ color: '#888', fontSize: 12 }}>{t('table_designer.tdengine.tag.value', undefined, i18nLanguage)}</span>
|
||||
</Space>
|
||||
{tdengineChildTagDefs.map((tag) => (
|
||||
<Space key={tag._key} align="start" wrap>
|
||||
<span style={{ width: 160, lineHeight: '32px', fontSize: 13, fontWeight: 500 }}>{tag.name}</span>
|
||||
<span style={{ width: 160, lineHeight: '32px', fontSize: 12, color: '#888' }}>{tag.type}</span>
|
||||
<Input
|
||||
{...noAutoCapInputProps}
|
||||
value={tdengineChildTagValues[tag.name] ?? ''}
|
||||
onChange={(event) => setTdengineChildTagValues(prev => ({ ...prev, [tag.name]: event.target.value }))}
|
||||
placeholder={t('table_designer.tdengine.placeholder.tag_value_input', undefined, i18nLanguage)}
|
||||
style={{ width: 280 }}
|
||||
/>
|
||||
</Space>
|
||||
))}
|
||||
</Space>
|
||||
) : (
|
||||
<div style={{ padding: '16px 0', textAlign: 'center', color: '#888' }}>
|
||||
{t('table_designer.tdengine.message.loading_tag_defs', undefined, i18nLanguage)}
|
||||
</div>
|
||||
)}
|
||||
</Spin>
|
||||
) : (
|
||||
<Input.TextArea
|
||||
{...noAutoCapInputProps}
|
||||
value={tdengineTagValues}
|
||||
onChange={(event) => setTdengineTagValues(event.target.value)}
|
||||
autoSize={{ minRows: 3, maxRows: 8 }}
|
||||
placeholder={t('table_designer.tdengine.placeholder.tag_values', undefined, i18nLanguage)}
|
||||
/>
|
||||
)}
|
||||
</Space>
|
||||
)}
|
||||
</Space>
|
||||
|
||||
@@ -8332,6 +8332,10 @@
|
||||
"table_designer.tdengine.table_kind.stable": "Supertabelle",
|
||||
"table_designer.tdengine.tag.name": "TAG-Name",
|
||||
"table_designer.tdengine.tag.type": "TAG-Typ",
|
||||
"table_designer.tdengine.tag.value": "TAG-Wert",
|
||||
"table_designer.tdengine.placeholder.tag_value_input": "TAG-Wert eingeben",
|
||||
"table_designer.tdengine.message.no_stable_found": "Keine Supertabelle gefunden, bitte erstellen Sie zuerst eine Supertabelle",
|
||||
"table_designer.tdengine.message.loading_tag_defs": "Lade TAG-Definitionen…",
|
||||
"table_designer.starrocks.bucket_mode.auto": "Eimeranzahl automatisch",
|
||||
"table_designer.starrocks.bucket_mode.number": "Feste Eimeranzahl",
|
||||
"table_designer.starrocks.distribution.hash": "Hash-Verteilung",
|
||||
|
||||
@@ -8332,6 +8332,10 @@
|
||||
"table_designer.tdengine.table_kind.stable": "Supertable",
|
||||
"table_designer.tdengine.tag.name": "TAG name",
|
||||
"table_designer.tdengine.tag.type": "TAG type",
|
||||
"table_designer.tdengine.tag.value": "TAG value",
|
||||
"table_designer.tdengine.placeholder.tag_value_input": "Enter TAG value",
|
||||
"table_designer.tdengine.message.no_stable_found": "No supertable found, please create a supertable first",
|
||||
"table_designer.tdengine.message.loading_tag_defs": "Loading TAG definitions…",
|
||||
"table_designer.starrocks.bucket_mode.auto": "Auto buckets",
|
||||
"table_designer.starrocks.bucket_mode.number": "Fixed bucket count",
|
||||
"table_designer.starrocks.distribution.hash": "Hash distribution",
|
||||
|
||||
@@ -8332,6 +8332,10 @@
|
||||
"table_designer.tdengine.table_kind.stable": "スーパーテーブル",
|
||||
"table_designer.tdengine.tag.name": "TAG 名",
|
||||
"table_designer.tdengine.tag.type": "TAG 型",
|
||||
"table_designer.tdengine.tag.value": "TAG 値",
|
||||
"table_designer.tdengine.placeholder.tag_value_input": "TAG の値を入力",
|
||||
"table_designer.tdengine.message.no_stable_found": "スーパーテーブルが見つかりません。最初にスーパーテーブルを作成してください",
|
||||
"table_designer.tdengine.message.loading_tag_defs": "TAG 定義を読み込み中…",
|
||||
"table_designer.starrocks.bucket_mode.auto": "バケット数を自動設定",
|
||||
"table_designer.starrocks.bucket_mode.number": "固定バケット数",
|
||||
"table_designer.starrocks.distribution.hash": "ハッシュ分散",
|
||||
|
||||
@@ -8332,6 +8332,10 @@
|
||||
"table_designer.tdengine.table_kind.stable": "超级表",
|
||||
"table_designer.tdengine.tag.name": "TAG 名称",
|
||||
"table_designer.tdengine.tag.type": "TAG 类型",
|
||||
"table_designer.tdengine.tag.value": "TAG 值",
|
||||
"table_designer.tdengine.placeholder.tag_value_input": "请输入 TAG 值",
|
||||
"table_designer.tdengine.message.no_stable_found": "未找到超级表,请先创建超级表",
|
||||
"table_designer.tdengine.message.loading_tag_defs": "正在加载 TAG 定义…",
|
||||
"table_designer.starrocks.bucket_mode.auto": "自动分桶数",
|
||||
"table_designer.starrocks.bucket_mode.number": "固定分桶数",
|
||||
"table_designer.starrocks.distribution.hash": "哈希分布",
|
||||
|
||||
@@ -8332,6 +8332,10 @@
|
||||
"table_designer.tdengine.table_kind.stable": "超級表",
|
||||
"table_designer.tdengine.tag.name": "TAG 名稱",
|
||||
"table_designer.tdengine.tag.type": "TAG 類型",
|
||||
"table_designer.tdengine.tag.value": "TAG 值",
|
||||
"table_designer.tdengine.placeholder.tag_value_input": "請輸入 TAG 值",
|
||||
"table_designer.tdengine.message.no_stable_found": "未找到超級表,請先創建超級表",
|
||||
"table_designer.tdengine.message.loading_tag_defs": "正在加載 TAG 定義…",
|
||||
"table_designer.starrocks.bucket_mode.auto": "自動分桶數",
|
||||
"table_designer.starrocks.bucket_mode.number": "固定分桶數",
|
||||
"table_designer.starrocks.distribution.hash": "雜湊分佈",
|
||||
|
||||
Reference in New Issue
Block a user