diff --git a/src/pages/QrCode/hooks/useQrCode.ts b/src/pages/QrCode/hooks/useQrCode.ts index 66ab3d0..9c07a6d 100644 --- a/src/pages/QrCode/hooks/useQrCode.ts +++ b/src/pages/QrCode/hooks/useQrCode.ts @@ -6,10 +6,8 @@ import { useContextMenuData } from '@/utils/useContextMenuData'; import type { QrCodeContextValue } from '../contexts/QrCodeContext'; import type { QrCodeGeneratorState, QrCodeMode, QrCodeParserState } from '../types'; -/** 防抖延迟时间(毫秒) */ const DEBOUNCE_DELAY = 500; -/** 检测文本是否为URL格式 */ function isUrl(text: string): boolean { const trimmed = text.trim(); if (!trimmed) return false; @@ -99,7 +97,6 @@ export function useQrCode(): QrCodeContextValue { const debounceTimerRef = useRef | null>(null); - // 清除防抖定时器 useEffect(() => { return () => { if (debounceTimerRef.current) { @@ -108,7 +105,6 @@ export function useQrCode(): QrCodeContextValue { }; }, []); - /** 自动检测URL并生成二维码 */ const autoGenerateIfUrl = useCallback((text: string) => { if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); @@ -149,7 +145,6 @@ export function useQrCode(): QrCodeContextValue { [autoGenerateIfUrl], ); - /** 手动触发生成二维码(用于非URL文本) */ const confirmGenerate = useCallback(() => { const text = generatorState.textToEncode.trim(); @@ -159,10 +154,8 @@ export function useQrCode(): QrCodeContextValue { return; } - // 先设置 loading 状态,让 React 渲染加载动画 setGeneratorState((prev) => ({ ...prev, generating: true, inputError: '' })); - // 延迟到下一帧生成,确保 loading 状态先被渲染显示 setTimeout(() => { const qrCodeDataUrl = generateQrCodeDataUrl(text); @@ -186,7 +179,6 @@ export function useQrCode(): QrCodeContextValue { }, 0); }, [generatorState.textToEncode]); - /** 返回编辑态,保留上次输入内容 */ const backToEdit = useCallback(() => { if (debounceTimerRef.current) { clearTimeout(debounceTimerRef.current); @@ -200,7 +192,6 @@ export function useQrCode(): QrCodeContextValue { })); }, []); - /** 从图片URL解析二维码 */ const parseQrCodeFromUrl = useCallback(async (imageUrl: string) => { try { setParserState((prev) => ({ @@ -212,7 +203,6 @@ export function useQrCode(): QrCodeContextValue { selectedFile: null, })); - // 从URL获取图片并转换为File对象 const response = await fetch(imageUrl); const blob = await response.blob(); const file = new File([blob], 'qrcode-image.png', { type: blob.type }); @@ -239,35 +229,19 @@ export function useQrCode(): QrCodeContextValue { } }, []); - /** 右键菜单传入URL时,自动生成二维码或解析图片 */ const handleContextMenuData = useCallback( (payload: string) => { - // 检测是否为图片URL,如果是则切换到解析模式 if (isImageUrl(payload)) { setMode('parse'); void parseQrCodeFromUrl(payload); return; } - // 非图片URL,生成二维码 setMode('generate'); - // 直接生成二维码,无需等待 const qrCodeDataUrl = generateQrCodeDataUrl(payload); - if (qrCodeDataUrl) { - // 生成成功,直接跳转到预览态 - setGeneratorState((prev) => ({ - ...prev, - step: 'preview', - textToEncode: payload, - savedText: payload.trim(), - qrCodeDataUrl, - generating: false, - inputError: '', - })); - } else { - // 生成失败,停留在输入态,显示文本供用户编辑 + if (!qrCodeDataUrl) { setGeneratorState((prev) => ({ ...prev, step: 'input', @@ -284,7 +258,6 @@ export function useQrCode(): QrCodeContextValue { useContextMenuData({ featureKey: 'qrCode', onData: handleContextMenuData }); - // 反向活态解析二维码算法 const parseQrCode = useCallback(async (file: File) => { try { setParserState((prev) => ({ ...prev, parsing: true, parseError: '', decodedResult: '' })); @@ -354,7 +327,6 @@ export function useQrCode(): QrCodeContextValue { }; }); - // 触发解析安全的后台 Promise parseQrCode(file).catch((err) => { console.error('Parser standalone task thread exploded:', err); }); @@ -362,7 +334,6 @@ export function useQrCode(): QrCodeContextValue { [parseQrCode], ); - // 清除解析受控文件 const handleClearFile = useCallback(() => { setParserState((prev) => { if (prev.previewUrl) { diff --git a/src/pages/TestDataGenerator/components/DataPreview.tsx b/src/pages/TestDataGenerator/components/DataPreview.tsx index dbb10dd..5c09da4 100644 --- a/src/pages/TestDataGenerator/components/DataPreview.tsx +++ b/src/pages/TestDataGenerator/components/DataPreview.tsx @@ -1,6 +1,5 @@ /** * 数据预览组件 - * 展示一条示例数据,展示数据结构 */ import { useMemo } from 'react'; @@ -12,7 +11,6 @@ interface DataPreviewProps { fields: FieldConfig[]; } -/** JSON 语法高亮渲染 */ function JsonHighlight({ data }: { data: Record }) { const formatted = useMemo(() => { const lines: { indent: string; key?: string; value: string; isLast: boolean }[] = []; @@ -45,7 +43,6 @@ function JsonHighlight({ data }: { data: Record }) { ); } -/** 根据值类型返回颜色类名 */ function getValueColor(value: string): string { if (value === 'null') return 'text-muted-foreground'; if (value.startsWith('"')) return 'text-emerald-600 dark:text-emerald-400'; @@ -55,13 +52,11 @@ function getValueColor(value: string): string { } export default function DataPreview({ fields }: DataPreviewProps) { - // 生成一条示例数据 const sampleData = useMemo(() => { if (fields.length === 0) return null; const record: Record = {}; for (const field of fields) { - // 非必填字段预览显示 null if (!field.required) { record[field.name] = null; continue; @@ -87,7 +82,6 @@ export default function DataPreview({ fields }: DataPreviewProps) { return (
- {/* 示例标签 */}
@@ -100,7 +94,6 @@ export default function DataPreview({ fields }: DataPreviewProps) {
- {/* 示例数据展示 */}
diff --git a/src/pages/TestDataGenerator/components/FieldEditor.tsx b/src/pages/TestDataGenerator/components/FieldEditor.tsx index 1331b7f..a72af42 100644 --- a/src/pages/TestDataGenerator/components/FieldEditor.tsx +++ b/src/pages/TestDataGenerator/components/FieldEditor.tsx @@ -1,6 +1,5 @@ /** * 字段编辑器组件 - * 编辑单个字段的详细配置 */ import { useState, useCallback } from 'react'; @@ -16,7 +15,6 @@ import GeneratorConfig from './GeneratorConfig'; interface FieldEditorProps { field: FieldConfig; onChange: (field: FieldConfig) => void; - /** 所有字段名列表,用于检测重复 */ allFieldNames?: string[]; } @@ -46,7 +44,6 @@ export default function FieldEditor({ field, onChange, allFieldNames = [] }: Fie const handleNameChange = (name: string) => { onChange({ ...field, name }); - // 实时校验 const error = validateFieldName(name); setNameError(error); }; @@ -59,15 +56,12 @@ export default function FieldEditor({ field, onChange, allFieldNames = [] }: Fie onChange({ ...field, required, - // 切换为必填时清零,切换为非必填时默认 100% nullRate: required ? 0 : 100, }); }; const handleNullRateChange = (nullRate: number) => { - // 限制范围 0-100 const clampedRate = Math.max(0, Math.min(100, nullRate)); - // 空值率为 0 时自动设为必填 if (clampedRate === 0) { onChange({ ...field, nullRate: clampedRate, required: true }); } else { @@ -105,7 +99,6 @@ export default function FieldEditor({ field, onChange, allFieldNames = [] }: Fie return (
- {/* 基础配置 */}
@@ -139,7 +132,6 @@ export default function FieldEditor({ field, onChange, allFieldNames = [] }: Fie
- {/* 必填/选填配置 */}
@@ -187,19 +179,16 @@ export default function FieldEditor({ field, onChange, allFieldNames = [] }: Fie )}
- {/* 唯一性约束 */}
- {/* 生成器选择 */}
- {/* 生成器参数配置 */} {generator && (
diff --git a/src/pages/TextStatistics/useTextStatistics.ts b/src/pages/TextStatistics/useTextStatistics.ts index 2ca8339..4fd68cb 100644 --- a/src/pages/TextStatistics/useTextStatistics.ts +++ b/src/pages/TextStatistics/useTextStatistics.ts @@ -1,4 +1,4 @@ -import { useCallback, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import { getTextStats, type TextStats } from '@/utils/textStatistics'; import { useContextMenuData } from '@/utils/useContextMenuData'; @@ -10,6 +10,12 @@ export interface UseTextStatisticsReturn { export function useTextStatistics(): UseTextStatisticsReturn { const [text, setText] = useState(''); + const [debouncedText, setDebouncedText] = useState(''); + + useEffect(() => { + const handle = setTimeout(() => setDebouncedText(text), 200); + return () => clearTimeout(handle); + }, [text]); const handleContextMenuData = useCallback((payload: string) => { setText(payload); @@ -17,7 +23,7 @@ export function useTextStatistics(): UseTextStatisticsReturn { useContextMenuData({ featureKey: 'textStatistics', onData: handleContextMenuData }); - const stats = getTextStats(text); + const stats = useMemo(() => getTextStats(debouncedText), [debouncedText]); return { text, stats, setText }; } diff --git a/src/utils/diffEngine.ts b/src/utils/diffEngine.ts index 93c11b6..0736a8c 100644 --- a/src/utils/diffEngine.ts +++ b/src/utils/diffEngine.ts @@ -11,7 +11,7 @@ const isObject = (v: unknown): v is Record => const isArray = (v: unknown): v is unknown[] => Array.isArray(v); /** - * 健壮的 JSONPath 生成器:支持针对包含点号、空格或特殊字符的键名进行括号转义拦截 + * JSONPath 生成器 */ const buildPath = (parent: string, key: string, isArrayChild: boolean): string => { if (isArrayChild) { @@ -47,7 +47,7 @@ const diffNode = ( newValue: right, path, isLeaf: !isObject(right) && !isArray(right), - hasDiffInChildren: false, // 自身即是新增,子树无需向下检索 + hasDiffInChildren: false, }; } @@ -61,7 +61,7 @@ const diffNode = ( newValue: undefined, path, isLeaf: !isObject(left) && !isArray(left), - hasDiffInChildren: false, // 自身即是删除,子树无需向下检索 + hasDiffInChildren: false, }; } @@ -70,7 +70,6 @@ const diffNode = ( const leftArr = isArray(left); const rightArr = isArray(right); - // 分支 3:双对象深层递归 (容器状态) if (leftObj && rightObj) { const keySet = new Set(); const leftKeys = Object.keys(left); @@ -97,11 +96,10 @@ const diffNode = ( children, path, isLeaf: false, - hasDiffInChildren, // 完美注入预计算衍生状态 + hasDiffInChildren, }; } - // 分支 4:双数组深层按序递归 (容器状态) if (leftArr && rightArr) { const len = Math.max(left.length, right.length); const children: DiffNode[] = new Array(len); @@ -124,11 +122,10 @@ const diffNode = ( children, path, isLeaf: false, - hasDiffInChildren, // 完美注入预计算衍生状态 + hasDiffInChildren, }; } - // 分支 5:绝对类型安全防护大闸 (双基本基元比对) const leftIsContainer = leftObj || leftArr; const rightIsContainer = rightObj || rightArr; @@ -160,7 +157,6 @@ const diffNode = ( } } - // 类型完全发生突变错配,或者基本数值不相等 diffPaths.push(path); return { key, @@ -169,12 +165,12 @@ const diffNode = ( newValue: right, path, isLeaf: !leftIsContainer && !rightIsContainer, - hasDiffInChildren: false, // 变动在自身,后代无子树变动 + hasDiffInChildren: false, }; }; /** - * 比较两个 JSON 值的差异,返回安全的差异树及高精度差异路径列表。 + * 比较两个 JSON 值的差异 */ export const diffJson = (left: unknown, right: unknown): DiffResult => { const diffPaths: string[] = []; diff --git a/src/utils/format.ts b/src/utils/format.ts index cc83be5..5662759 100644 --- a/src/utils/format.ts +++ b/src/utils/format.ts @@ -2,7 +2,7 @@ * 格式化字节大小为易读字符串 * * @param bytes 字节数 - * @returns 格式化后的字符串,例如 "1.5 KB" 或 "100 B" + * @returns 格式化后的字符串 */ export function formatBytes(bytes: number): string { if (bytes === 0) return '0 B'; @@ -17,7 +17,6 @@ export function formatBytes(bytes: number): string { unitIndex++; } - // KB uses 1 decimal, MB/GB/TB use 2 decimals const decimals = unitIndex === 0 ? 1 : 2; return `${size.toFixed(decimals)} ${units[unitIndex]}`; diff --git a/src/utils/jsonToToml.ts b/src/utils/jsonToToml.ts index 78e24d3..7bb64ff 100644 --- a/src/utils/jsonToToml.ts +++ b/src/utils/jsonToToml.ts @@ -204,7 +204,7 @@ export function jsonToToml(text: string): JsonToTomlResult { // TOML 要求顶层是表 if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) { - throw new Error('TOML requires the top-level value to be an object'); + throw new Error('TOML 要求顶层值必须是一个对象'); } const lines: string[] = [];