refactor: 统一中文文案并简化组件结构,提升代码可读性

移除 chrome.i18n 后,将各功能页面、布局与工具模块的 UI 文案改为内联中文;
删除冗余注释与过度抽象(如 ToolCard、StatCard),精简 props 与状态管理;
同步优化 JsonTools、StorageCleaner、Timestamp、TestDataGenerator、Dashboard、
Base64Converter、RightClickRestorer、TextStatistics、TopBar、RouterProvider、
ThemeModeProvider 及生成器库与 utils 工具文件。
This commit is contained in:
雨霖铃
2026-06-27 10:49:36 +08:00
parent f3a9838017
commit d38bafe237
85 changed files with 324 additions and 864 deletions
+1 -1
View File
@@ -41,7 +41,7 @@ export default function RouterContainer() {
<div className="flex h-12 w-12 items-center justify-center rounded-xl bg-destructive/10 text-destructive mb-4">
<AlertTriangle className="h-6 w-6" />
</div>
<h3 className="text-sm font-semibold text-foreground">{'页面未找到'}</h3>
<h3 className="text-sm font-semibold text-foreground"></h3>
<p className="text-xs text-muted-foreground mt-1 max-w-[240px]">
{`该功能在当前运行环境(${entryPointType})下不可用或已被移除。`}
</p>
+3 -8
View File
@@ -1,7 +1,7 @@
import React, { forwardRef, useCallback, useImperativeHandle, useRef, useState } from 'react';
import { X } from 'lucide-react';
import { cn } from '@/lib/utils';
import { toast } from 'sonner'; // 推荐使用 shadcn 的默认 Toast
import { toast } from 'sonner';
import { CopyButton } from '@/components/CopyButton';
import { Button } from './ui/button';
@@ -44,7 +44,6 @@ export interface TextInputAreaProps extends Omit<
onClear?: () => void;
}
// 提炼基础的 ActionButton,全面向 shadcn 核心 Button 样式对齐
function ActionButton({
action,
value,
@@ -126,7 +125,6 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
const textArea = internalRef.current;
if (!textArea) return;
// 重置高度计算
textArea.style.height = 'auto';
const computedMin = minRows * 24;
@@ -255,7 +253,6 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
{hasBottomBar && (
<div className="flex h-10 items-center justify-between px-4 bg-muted/30 border-t border-border/50">
{/* 左侧自定义动作 */}
<div className="flex items-center gap-1.5 min-w-0">
{bottomActions.map((action) => (
<ActionButton
@@ -268,16 +265,15 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
))}
</div>
{/* 右侧系统按钮组 */}
<div className="flex items-center gap-1.5 ml-auto shrink-0">
{allowCopy && value && (
<CopyButton text={value} tooltip={'复制内容'} size="sm" className="h-7 w-7 p-1" />
<CopyButton text={value} tooltip="复制内容" size="sm" className="h-7 w-7 p-1" />
)}
{showClear && value && !disabled && !readOnly && (
<Button
type="button"
onClick={handleClear}
aria-label={'清空'}
aria-label="清空"
variant="ghost"
size="icon"
className="h-7 w-7 text-muted-foreground hover:text-destructive hover:bg-destructive/10"
@@ -290,7 +286,6 @@ const TextInputArea = forwardRef<HTMLTextAreaElement, TextInputAreaProps>((props
)}
</div>
{/* 错误提示 */}
{displayError && (
<p className="text-xs font-medium text-destructive px-0.5 animate-in fade-in slide-in-from-top-1 duration-150">
{displayError}
+3 -19
View File
@@ -1,5 +1,5 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { act, render, screen, waitFor } from '@testing-library/react';
import { act, render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
// unmock the globally-mocked component so we test the real implementation
@@ -68,15 +68,10 @@ describe('CopyButton', () => {
expect(mockedToast.error).toHaveBeenCalled();
});
// ==================== 新增测试 ====================
it('初始渲染时显示 Copy 图标且无 emerald 样式', () => {
it('初始渲染时带有默认 aria-label', () => {
render(<CopyButton text="initial" />);
const button = screen.getByRole('button');
expect(button.className).not.toContain('text-emerald');
// 通过 aria-label 确认按钮存在,图标由 lucide 渲染为 svg
expect(button).toHaveAttribute('aria-label');
expect(screen.getByRole('button')).toHaveAttribute('aria-label', '复制');
});
it('自定义 tooltip 会覆盖默认 title 和 aria-label', () => {
@@ -119,15 +114,12 @@ describe('CopyButton', () => {
await user.click(screen.getByRole('button'));
// 在 1.5 秒超时到期前卸载组件
act(() => {
vi.advanceTimersByTime(500);
});
// 卸载不应抛出 "Can't perform a React state update on an unmounted component" 警告
expect(() => unmount()).not.toThrow();
// 前进剩余时间,确认没有异常
act(() => {
vi.advanceTimersByTime(2000);
});
@@ -141,23 +133,15 @@ describe('CopyButton', () => {
const button = screen.getByRole('button');
// 快速点击 3 次
await user.click(button);
await user.click(button);
await user.click(button);
// copyTextToClipboard 应该被调用 3 次(每次点击都执行)
expect(mockedCopy).toHaveBeenCalledTimes(3);
// 但 setTimeout 相关的 clearTimeout + setTimeout 组合应正常工作
// advance 1.5 秒后,copied 状态应恢复为 false
act(() => {
vi.advanceTimersByTime(1500);
});
await waitFor(() => {
expect(button.className).not.toContain('text-emerald');
});
});
it('其他 button props 通过 ...props 透传', () => {
@@ -4,13 +4,13 @@ import EmptyPlaceholder from '@/components/EmptyPlaceholder';
describe('EmptyPlaceholder 组件', () => {
it('应渲染字符串提示文本', () => {
render(<EmptyPlaceholder>{'请输入内容'}</EmptyPlaceholder>);
render(<EmptyPlaceholder></EmptyPlaceholder>);
expect(screen.getByText('请输入内容')).toBeInTheDocument();
});
it('应应用规范空状态容器样式', () => {
const { container } = render(<EmptyPlaceholder>{'提示'}</EmptyPlaceholder>);
const { container } = render(<EmptyPlaceholder></EmptyPlaceholder>);
const placeholder = container.firstChild;
expect(placeholder).toHaveClass(
@@ -24,14 +24,14 @@ describe('EmptyPlaceholder 组件', () => {
it('应支持 className 自定义容器样式', () => {
const { container } = render(
<EmptyPlaceholder className="flex-1 min-h-[320px]">{'提示'}</EmptyPlaceholder>,
<EmptyPlaceholder className="flex-1 min-h-[320px]"></EmptyPlaceholder>,
);
expect(container.firstChild).toHaveClass('flex-1', 'min-h-[320px]');
});
it('应支持 messageClassName 自定义文本样式', () => {
render(<EmptyPlaceholder messageClassName="text-sm max-w-none">{'提示'}</EmptyPlaceholder>);
render(<EmptyPlaceholder messageClassName="text-sm max-w-none"></EmptyPlaceholder>);
expect(screen.getByText('提示')).toHaveClass('text-sm', 'max-w-none');
});