/** * SECTL KV 存储管理组件 * 基于 SECTL-One-Stop SDK 规范实现 */ import React, { useState, useEffect } from "react" import { Card, Table, Button, Space, Modal, Input, message, Popconfirm, Typography, Tag, Form, Switch, InputNumber, Tooltip, } from "antd" import { DatabaseOutlined, PlusOutlined, DeleteOutlined, EditOutlined, ReloadOutlined, CodeOutlined, FieldStringOutlined, } from "@ant-design/icons" import { sectlKVStorage, KVItem, ListKVOptions } from "../services/sectlKVStorage" import { useSectl } from "../contexts/SectlContext" const { Title, Text } = Typography const { TextArea } = Input export const SectlKVStorageManager: React.FC = () => { const { isAuthenticated } = useSectl() const [kvList, setKvList] = useState([]) const [loading, setLoading] = useState(false) const [total, setTotal] = useState(0) const [hasMore, setHasMore] = useState(false) const [offset, setOffset] = useState(0) const [prefix, setPrefix] = useState("") // 编辑/创建对话框状态 const [editModal, setEditModal] = useState<{ visible: boolean isEdit: boolean key?: string }>({ visible: false, isEdit: false }) // 查看详情对话框状态 const [detailModal, setDetailModal] = useState<{ visible: boolean item?: KVItem }>({ visible: false }) // 表单 const [form] = Form.useForm() // 加载 KV 列表 const loadKVList = async (resetOffset = false) => { if (!isAuthenticated) return try { setLoading(true) const currentOffset = resetOffset ? 0 : offset const options: ListKVOptions = { limit: 20, offset: currentOffset, } if (prefix) { options.prefix = prefix } const result = await sectlKVStorage.listKV(options) setKvList(resetOffset ? result.kv_list : [...kvList, ...result.kv_list]) setTotal(result.total) setHasMore(result.has_more) if (resetOffset) { setOffset(20) } else { setOffset(currentOffset + 20) } } catch (error: any) { message.error(`加载键值对列表失败:${error.message}`) } finally { setLoading(false) } } // 初始加载 useEffect(() => { if (isAuthenticated) { loadKVList(true) } }, [isAuthenticated, prefix]) // 处理创建/更新 const handleSave = async (values: { key: string value: string isJson: boolean ttl?: number }) => { try { let parsedValue: unknown if (values.isJson) { try { parsedValue = JSON.parse(values.value) } catch { message.error("JSON 格式无效") return } } else { parsedValue = values.value } await sectlKVStorage.setKV(values.key, parsedValue, values.ttl) message.success(editModal.isEdit ? "键值对已更新" : "键值对已创建") setEditModal({ visible: false, isEdit: false }) form.resetFields() loadKVList(true) } catch (error: any) { message.error(`保存失败:${error.message}`) } } // 处理删除 const handleDelete = async (key: string) => { try { await sectlKVStorage.deleteKV(key) message.success("键值对已删除") loadKVList(true) } catch (error: any) { message.error(`删除失败:${error.message}`) } } // 打开编辑对话框 const openEditModal = (item: KVItem) => { form.setFieldsValue({ key: item.key, value: item.is_json ? JSON.stringify(item.value, null, 2) : String(item.value), isJson: item.is_json, }) setEditModal({ visible: true, isEdit: true, key: item.key }) } // 打开创建对话框 const openCreateModal = () => { form.resetFields() form.setFieldsValue({ isJson: true }) setEditModal({ visible: true, isEdit: false }) } // 打开详情对话框 const openDetailModal = (item: KVItem) => { setDetailModal({ visible: true, item }) } // 格式化值显示 const formatValue = (value: unknown, isJson: boolean): string => { if (isJson) { return JSON.stringify(value).slice(0, 100) + (JSON.stringify(value).length > 100 ? "..." : "") } return String(value).slice(0, 100) + (String(value).length > 100 ? "..." : "") } // 表格列定义 const columns = [ { title: "键名", dataIndex: "key", key: "key", render: (text: string, record: KVItem) => ( openDetailModal(record)}>{text} ), }, { title: "类型", dataIndex: "is_json", key: "is_json", width: 100, render: (isJson: boolean) => isJson ? ( }> JSON ) : ( }>文本 ), }, { title: "大小", dataIndex: "size", key: "size", width: 100, render: (size: number) => { if (size < 1024) return `${size} B` if (size < 1024 * 1024) return `${(size / 1024).toFixed(2)} KB` return `${(size / (1024 * 1024)).toFixed(2)} MB` }, }, { title: "值预览", dataIndex: "value", key: "value", ellipsis: true, render: (_: unknown, record: KVItem) => ( {formatValue(record.value, record.is_json)} ), }, { title: "更新时间", dataIndex: "updated_at", key: "updated_at", width: 180, render: (text: string) => new Date(text).toLocaleString("zh-CN"), }, { title: "操作", key: "action", width: 150, render: (_: unknown, record: KVItem) => ( } > {/* 统计信息 */}
共 {total} 个键值对 {hasMore && `(已加载 ${kvList.length} 个)`}
{/* KV 列表 */} {/* 加载更多 */} {hasMore && (
)} {/* 创建/编辑对话框 */} form.submit()} onCancel={() => { setEditModal({ visible: false, isEdit: false }) form.resetFields() }} okText="保存" cancelText="取消" width={600} >