diff --git a/frontend/src/pages/TicketCreatePage.tsx b/frontend/src/pages/TicketCreatePage.tsx index f41c8c3..0783537 100644 --- a/frontend/src/pages/TicketCreatePage.tsx +++ b/frontend/src/pages/TicketCreatePage.tsx @@ -667,7 +667,6 @@ export default function TicketCreatePage() { nodes.push(node); } if (i === parts.length - 1) { - // leaf — replace with leaf node node.fullKey = k.key; node.type = k.type; delete node.children; @@ -677,12 +676,9 @@ export default function TicketCreatePage() { } } } - // sort: folders first, then leaves, alphabetically const sortNodes = (arr: RedisTreeNode[]) => { arr.sort((a, b) => { - const aIsLeaf = !!a.fullKey; - const bIsLeaf = !!b.fullKey; - if (aIsLeaf !== bIsLeaf) return aIsLeaf ? 1 : -1; + if (!!a.fullKey !== !!b.fullKey) return a.fullKey ? 1 : -1; return a.name.localeCompare(b.name); }); arr.forEach(n => { if (n.children) sortNodes(n.children); }); @@ -691,7 +687,6 @@ export default function TicketCreatePage() { return root; }, []); - /* Redis expanded folders state */ const [redisExpanded, setRedisExpanded] = useState>(new Set()); const toggleRedisFolder = useCallback((path: string) => { @@ -702,7 +697,6 @@ export default function TicketCreatePage() { }); }, []); - /** Get Redis command for a key based on its type */ const redisCmdForKey = useCallback((key: string, type: string): string => { switch (type) { case 'string': return `GET ${key}`; @@ -714,29 +708,65 @@ export default function TicketCreatePage() { } }, []); - /** Recursive tree renderer */ + const keyTypeColor: Record = { + string: 'text-emerald-400', + hash: 'text-blue-400', + list: 'text-purple-400', + set: 'text-orange-400', + zset: 'text-rose-400', + }; + + /** Recursive tree renderer — single column with inline detail */ const renderRedisTreeNode = useCallback((node: RedisTreeNode, depth: number, parentPath: string): React.ReactNode => { const path = parentPath ? `${parentPath}:${node.name}` : node.name; const isLeaf = !!node.fullKey; const isExpanded = redisExpanded.has(path); + const indent = depth * 14; if (isLeaf) { + const isSelected = selectedRedisKey === node.fullKey; + const keyIconColor = keyTypeColor[node.type || 'string'] || 'text-amber-400'; return ( - +
+ + {/* Inline key detail */} + {isSelected && ( +
+ {loadingRedisDetail ? ( +
+ 加载... +
+ ) : redisKeyDetail ? ( +
+
+ {redisKeyDetail.type} + TTL: {formatTTL(redisKeyDetail.ttl)} + {redisKeyDetail.size && {formatBytes(redisKeyDetail.size)}} +
+
+                    {typeof redisKeyDetail.value === 'object'
+                      ? JSON.stringify(redisKeyDetail.value, null, 2)
+                      : String(redisKeyDetail.value)}
+                  
+
+ ) : null} +
+ )} +
); } @@ -745,30 +775,34 @@ export default function TicketCreatePage() { return (
- {isExpanded && node.children?.map(child => renderRedisTreeNode(child, depth + 1, path))} + {isExpanded && ( +
+ {node.children?.map(child => renderRedisTreeNode(child, depth + 1, path))} +
+ )}
); - }, [redisExpanded, selectedRedisKey, toggleRedisFolder, redisCmdForKey, handleSelectRedisKey]); + }, [redisExpanded, selectedRedisKey, toggleRedisFolder, redisCmdForKey, handleSelectRedisKey, loadingRedisDetail, redisKeyDetail]); const redisTree = useMemo(() => buildRedisTree(redisKeys), [redisKeys, buildRedisTree]); const renderRedisTab = () => ( -
+
{/* Search Bar */} -
+