import React, { useState, useEffect, useCallback } from 'react'; import { Card, Col, Row, message, Table, Button, Input, Form, Space } from 'antd'; import type { TableColumnsType } from 'antd'; import { ReloadOutlined, DeleteOutlined, KeyOutlined, FileTextOutlined, AppstoreOutlined } from '@ant-design/icons'; import { listCacheName, listCacheKey, getCacheValue, clearCacheName, clearCacheKey, clearCacheAll, } from '../../api/monitor/cache'; import type { CacheKeyPayload, CacheKeyRecord, CacheNamePayload, CacheNameRecord, } from '@/types/api'; import Permission from '@/components/Permission'; import { usePermission } from '@/contexts/PermissionContext'; import ReadonlyAction from '@/components/Permission/ReadonlyAction'; import './cache-list.css'; interface CacheForm { cacheName: string; cacheKey: string; cacheValue: string; } const defaultForm: CacheForm = { cacheName: '', cacheKey: '', cacheValue: '', }; const stringifyValue = (value: unknown): string => { if (value === undefined || value === null) { return ''; } if (typeof value === 'string') { return value; } try { return JSON.stringify(value, null, 2); } catch { return String(value); } }; const toCacheNameRecord = (item: string | CacheNameRecord): CacheNameRecord => { if (typeof item === 'string') { return { cacheName: item, remark: `缓存 ${item}` }; } return { cacheName: item.cacheName, remark: item.remark ?? `缓存 ${item.cacheName}`, }; }; const toCacheKeyString = (item: string | CacheKeyRecord): string => { return typeof item === 'string' ? item : item.cacheKey; }; const extractCacheNames = (payload: CacheNamePayload): CacheNameRecord[] => { if (!payload) { return []; } if (Array.isArray(payload)) { return payload.map((item) => toCacheNameRecord(item)); } if ('cacheNames' in payload && Array.isArray(payload.cacheNames)) { return payload.cacheNames.map((item) => toCacheNameRecord(item)); } return []; }; const extractCacheKeys = (payload: CacheKeyPayload): string[] => { if (!payload) { return []; } if (Array.isArray(payload)) { return payload.map((item) => toCacheKeyString(item)); } if ('cacheKeys' in payload && Array.isArray(payload.cacheKeys)) { return payload.cacheKeys.map((item) => toCacheKeyString(item)); } return []; }; const CacheListPage: React.FC = () => { const { hasPermi } = usePermission(); const [cacheNames, setCacheNames] = useState([]); const [cacheKeys, setCacheKeys] = useState([]); const [cacheForm, setCacheForm] = useState(defaultForm); const [loading, setLoading] = useState(false); const [subLoading, setSubLoading] = useState(false); const [nowCacheName, setNowCacheName] = useState(''); const [tableHeight, setTableHeight] = useState(window.innerHeight - 200); const canClearCache = hasPermi('monitor:cache:list'); const getCacheKeys = useCallback( async (cacheName?: string) => { const targetCacheName = cacheName ?? nowCacheName; if (!targetCacheName) { setCacheKeys([]); setCacheForm(defaultForm); return; } setSubLoading(true); try { const response = await listCacheKey(targetCacheName); const keys = extractCacheKeys(response); setCacheKeys(keys); setNowCacheName(targetCacheName); setCacheForm({ cacheName: targetCacheName, cacheKey: '', cacheValue: '', }); } catch (error: unknown) { console.error('Failed to fetch cache keys:', error); message.error('获取缓存键名列表失败'); } finally { setSubLoading(false); } }, [nowCacheName], ); const getCacheNames = useCallback(async () => { setLoading(true); try { const response = await listCacheName(); const names = extractCacheNames(response); setCacheNames(names); if (names.length === 0) { setNowCacheName(''); setCacheKeys([]); setCacheForm(defaultForm); return; } const targetCacheName = nowCacheName && names.some((item) => item.cacheName === nowCacheName) ? nowCacheName : names[0].cacheName; await getCacheKeys(targetCacheName); } catch (error: unknown) { console.error('Failed to fetch cache names:', error); message.error('获取缓存名称列表失败'); } finally { setLoading(false); } }, [getCacheKeys, nowCacheName]); const handleCacheValue = useCallback( async (fullCacheKey: string) => { if (!nowCacheName || !fullCacheKey) { return; } try { const response = await getCacheValue(nowCacheName, fullCacheKey); setCacheForm({ cacheName: nowCacheName, cacheKey: fullCacheKey, cacheValue: stringifyValue(response), }); } catch (error: unknown) { console.error('Failed to fetch cache value:', error); message.error('获取缓存内容失败'); } }, [nowCacheName], ); useEffect(() => { void getCacheNames(); const handleResize = () => { setTableHeight(window.innerHeight - 200); }; window.addEventListener('resize', handleResize); return () => window.removeEventListener('resize', handleResize); }, [getCacheNames]); const refreshCacheNames = async () => { await getCacheNames(); message.success('刷新缓存列表成功'); }; const handleClearCacheName = async (row: CacheNameRecord) => { if (!canClearCache) { return; } try { await clearCacheName(row.cacheName); message.success(`清理缓存名称[${row.cacheName}]成功`); await getCacheNames(); } catch { message.error('清理缓存名称失败'); } }; const refreshCacheKeys = async () => { await getCacheKeys(); message.success('刷新键名列表成功'); }; const handleClearCacheKey = async (fullCacheKey: string) => { if (!canClearCache) { return; } if (!nowCacheName) { return; } try { await clearCacheKey(fullCacheKey); message.success(`清理缓存键名[${fullCacheKey}]成功`); await getCacheKeys(nowCacheName); setCacheForm({ cacheName: nowCacheName, cacheKey: '', cacheValue: '' }); } catch { message.error('清理缓存键名失败'); } }; const handleClearCacheAll = async () => { if (!canClearCache) { return; } try { await clearCacheAll(); message.success('清理全部缓存成功'); await getCacheNames(); } catch { message.error('清理全部缓存失败'); } }; const nameFormatter = (cacheName: string) => { return cacheName.endsWith(':') ? cacheName.slice(0, -1) : cacheName; }; const keyFormatter = (fullCacheKey: string) => { const prefix = nowCacheName.endsWith(':') ? nowCacheName : `${nowCacheName}:`; return fullCacheKey.replace(prefix, ''); }; const cacheNamesColumns: TableColumnsType = [ { title: '序号', key: 'index', width: 60, render: (_text, _record, index) => index + 1, }, { title: '缓存名称', dataIndex: 'cacheName', ellipsis: true, render: (text) => nameFormatter(String(text ?? '')), }, { title: '备注', dataIndex: 'remark', ellipsis: true }, { title: '操作', key: 'operation', width: 60, render: (_text, record) => ( } danger>清理} >