Refactor and enhance form recognition, mapping, and UI components (#18)
* docs: 添加组件文档注释和类型导入 refactor: 统一使用 SnackbarOptions 类型 style: 优化导入语句顺序和格式 * refactor: 简化假数据生成器中的faker导入和使用 Co-authored-by: Copilot <copilot@github.com> * feat(form-recognizer): 增强表单识别功能并优化UI交互 - 新增字段类型偏好设置功能,支持按域名保存字段类型 - 重构FieldList组件,改进字段选择和类型修改体验 - 添加字段定位闪烁功能,便于在页面上快速找到对应字段 - 优化表单填充逻辑,支持单个字段覆盖默认填充模式 - 移除独立的侧边栏页面,统一使用主页面组件 - 改进useStorageState钩子,增加加载状态管理和防抖处理 * refactor: 移除未使用的组件文件 * refactor(页面头部): 提取通用 PageHeader 组件并替换各页面头部实现 重构各页面头部为统一的 PageHeader 组件,提高代码复用性和维护性 * style(组件): 调整自动刷新开关和存储选项网格的样式 优化自动刷新开关的文本内边距,重构存储选项网格的布局结构,调整间距和边框样式 * style(ui): 调整时间戳页面和结果视图的样式 - 为时区选择器添加圆角 - 优化结果视图的布局和对齐方式 - 调整结果项的内边距和文本样式 * ci(workflow): 移除Firefox测试以简化CI流程 仅保留Chrome浏览器的构建步骤,减少CI运行时间和资源消耗 * refactor(qrcode): 重构二维码解析功能并提取为独立模块 * style(页面样式): 统一页面头部图标颜色并优化表单样式 更新各页面头部组件的图标颜色配置,移除冗余的表单标签属性,优化输入框样式和按钮悬停效果 * feat(formMapping): 添加表单映射功能 - 创建 FormMappingPage 页面组件 - 实现表单扫描器 scanner.ts - 实现高亮器 highlighter.ts - 更新路由配置 routes.ts - 更新内容脚本 content.ts - 更新类型定义 storage.d.ts - 添加 formMappingPageStyles 样式配置 * feat(表单映射): 添加配置导出功能及状态提示 添加配置导出为JSON文件的功能,包含导出按钮和错误处理。新增Snackbar组件用于显示导出成功和错误状态。优化页面布局结构,将状态提示移至全局容器外。 * feat(表单填充): 新增智能表单填充功能 添加智能表单填充功能,包括: 1. 新增表单填充页面和路由配置 2. 实现模糊匹配引擎和智能注入引擎 3. 添加Mock数据生成器和视觉反馈渲染器 4. 扩展消息接口支持填充操作 * ci: 精简触发CI的工作流分支 移除对develop分支及其变体的触发,仅保留main分支的触发 * test: 更新路由测试以匹配新增的路由数量 * test: 将测试文件中的描述和断言翻译为中文 * test(PageHeader): 添加组件测试用例验证渲染逻辑和样式 * refactor(theme): 重构主题样式并优化仪表盘卡片组件 将页面样式配置集中管理,移除各页面中硬编码的背景色 新增 DashboardCard 组件封装通用卡片逻辑 添加 dashboardCards 配置文件统一管理卡片数据 * refactor(Button): 统一按钮样式并移除重复样式定义 将按钮样式统一封装到 Button 组件中,移除各页面重复的样式定义 更新组件文档说明,提供更清晰的用法示例 * docs: 更新 README 文件中的目录结构说明 添加新组件和配置文件的说明,保持文档与代码同步 * refactor: 优化代码类型声明和UI布局 修复类型声明从any改为never以提高类型安全性 调整TimestampPage页面布局间距 移除DashboardPage中不必要的实时时钟状态 更新ESLint配置以使用推荐配置 添加选项页面打开的错误处理 * refactor(消息通信): 使用 @webext-core/messaging 重构消息处理逻辑 将原有的 chrome.runtime.onMessage 和 chrome.tabs.sendMessage 替换为类型安全的 @webext-core/messaging 实现 添加 ProtocolMap 类型定义确保消息类型安全 更新相关页面和后台脚本使用新的消息通信方式 * fix: 修复依赖项缺失导致的潜在问题 修复 useStorageState 中缺少 defaultValue 依赖的问题 重构 RouterProvider 的初始化逻辑,使用 useCallback 优化性能 * refactor: 重构表单识别和消息处理逻辑 将表单映射UI逻辑提取到独立文件 将消息处理器提取到独立文件 将表单识别页面逻辑提取到自定义hook 优化代码结构和可维护性 * feat(侧边栏): 添加侧边栏状态变化通知功能 - 在消息类型中新增侧边栏状态变化枚举和字段 - 侧边栏打开和关闭时发送状态通知 - 替换轮询检查方式为消息监听机制 * refactor: 优化高亮组件渲染逻辑并添加防抖处理 重构高亮组件渲染逻辑,使用 requestAnimationFrame 进行节流优化 在 useStorageCleaner 中添加防抖处理以避免频繁加载 * refactor: 统一组件导出方式为默认导出 * refactor: 重构样式系统并迁移至 MUI 主题 删除冗余的 CSS 文件,统一使用 MUI 主题管理样式 新增 useActiveTabDomain 和 useSidePanelState 自定义 Hook 优化各入口点的主题集成和布局处理 * docs: 更新项目文档以反映新增功能和技术栈细节 更新 AGENTS.md 文档,详细描述新增的功能模块(存储管理、URL 管理、二维码生成、表单工具套件等)和更新的技术栈信息 * feat: 添加错误边界组件以捕获子组件错误 在 popup、sidepanel 和 options 入口点添加 ErrorBoundary 组件,用于捕获并处理子组件中的 JavaScript 错误。当错误发生时,显示友好的错误界面并提供刷新功能。 * refactor(ui): 使用全局 snackbar 替换本地通知组件 重构表单页面中的通知系统,移除本地 Snackbar 和 Alert 组件,改用全局 GlobalSnackbar 组件统一管理通知 标准化主题配置中的 CSS 属性命名,移除带前缀的样式属性 * refactor(snackbar): 重构全局消息提示为 SnackbarProvider 组件 将原本分散在各页面的 GlobalSnackbar 组件重构为集中管理的 SnackbarProvider,通过 Context 提供统一的消息提示功能。主要变更包括: 1. 创建新的 SnackbarProvider 组件作为全局消息提示容器 2. 提供 useSnackbar hook 供子组件调用 3. 移除各页面中独立的 GlobalSnackbar 实例 4. 在 App 根组件中统一集成 SnackbarProvider 5. 优化消息提示样式和交互行为 * refactor(Snackbar): 重构消息提示组件并优化样式 重构 Snackbar 组件,将状态管理逻辑提取到 GlobalSnackbar 中复用 优化消息提示样式,调整阴影效果和最小尺寸 统一各页面使用 Snackbar 的方式,移除冗余配置 * refactor: 重构路由和功能配置系统 - 将路由配置和仪表盘卡片配置合并为统一的 features 配置 - 重构 RouterProvider 增加数据校验和本地快照功能 - 优化表单字段识别和填充逻辑,提取公共方法 - 更新相关组件和文档以适配新的配置系统 - 删除废弃的 dashboardCards 和 routes 配置文件
This commit is contained in:
@@ -0,0 +1,139 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
FEATURES,
|
||||
getFeatureByKey,
|
||||
getDefaultVisibleFeatureKeys,
|
||||
getAllFeatureKeys,
|
||||
getDefaultPageOrder,
|
||||
} from '../features';
|
||||
|
||||
describe('features', () => {
|
||||
describe('FEATURES', () => {
|
||||
it('should have 9 features defined', () => {
|
||||
expect(FEATURES).toHaveLength(9);
|
||||
});
|
||||
|
||||
it('should have all required properties for each feature', () => {
|
||||
FEATURES.forEach((feature) => {
|
||||
expect(feature).toHaveProperty('key');
|
||||
expect(feature).toHaveProperty('label');
|
||||
expect(feature).toHaveProperty('description');
|
||||
expect(feature).toHaveProperty('defaultVisible');
|
||||
expect(feature).toHaveProperty('components');
|
||||
expect(typeof feature.key).toBe('string');
|
||||
expect(typeof feature.label).toBe('string');
|
||||
expect(typeof feature.description).toBe('string');
|
||||
expect(typeof feature.defaultVisible).toBe('boolean');
|
||||
expect(typeof feature.components).toBe('object');
|
||||
expect(feature.components).toHaveProperty('popup');
|
||||
expect(feature.components).toHaveProperty('sidepanel');
|
||||
expect(feature.components).toHaveProperty('detached');
|
||||
|
||||
// Optional UI properties for non-hidden features
|
||||
if (feature.key !== 'dashboard' && feature.key !== 'openUrlViewer') {
|
||||
expect(feature).toHaveProperty('icon');
|
||||
expect(feature).toHaveProperty('themeColor');
|
||||
expect(typeof feature.themeColor).toBe('string');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it('should have unique keys for each feature', () => {
|
||||
const keys = FEATURES.map((f) => f.key);
|
||||
const uniqueKeys = new Set(keys);
|
||||
expect(uniqueKeys.size).toBe(keys.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getFeatureByKey', () => {
|
||||
it('should return dashboard feature', () => {
|
||||
const feature = getFeatureByKey('dashboard');
|
||||
expect(feature).toBeDefined();
|
||||
expect(feature?.key).toBe('dashboard');
|
||||
expect(feature?.label).toBe('Dashboard');
|
||||
});
|
||||
|
||||
it('should return timestamp feature', () => {
|
||||
const feature = getFeatureByKey('timestamp');
|
||||
expect(feature).toBeDefined();
|
||||
expect(feature?.key).toBe('timestamp');
|
||||
expect(feature?.label).toBe('时间戳');
|
||||
expect(feature?.themeColor).toBeDefined();
|
||||
});
|
||||
|
||||
it('should return storageCleaner feature', () => {
|
||||
const feature = getFeatureByKey('storageCleaner');
|
||||
expect(feature).toBeDefined();
|
||||
expect(feature?.key).toBe('storageCleaner');
|
||||
expect(feature?.label).toBe('存储清理');
|
||||
});
|
||||
|
||||
it('should return undefined for invalid key', () => {
|
||||
const feature = getFeatureByKey('invalid' as any);
|
||||
expect(feature).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultVisibleFeatureKeys', () => {
|
||||
it('should return only visible features', () => {
|
||||
const visibleKeys = getDefaultVisibleFeatureKeys();
|
||||
visibleKeys.forEach((key) => {
|
||||
const feature = getFeatureByKey(key);
|
||||
expect(feature?.defaultVisible).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should include dashboard, timestamp, storageCleaner, openUrl', () => {
|
||||
const visibleKeys = getDefaultVisibleFeatureKeys();
|
||||
expect(visibleKeys).toContain('dashboard');
|
||||
expect(visibleKeys).toContain('timestamp');
|
||||
expect(visibleKeys).toContain('storageCleaner');
|
||||
expect(visibleKeys).toContain('openUrl');
|
||||
});
|
||||
|
||||
it('should not include openUrlViewer (not visible by default)', () => {
|
||||
const visibleKeys = getDefaultVisibleFeatureKeys();
|
||||
expect(visibleKeys).not.toContain('openUrlViewer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllFeatureKeys', () => {
|
||||
it('should return all feature keys', () => {
|
||||
const allKeys = getAllFeatureKeys();
|
||||
expect(allKeys).toHaveLength(9);
|
||||
expect(allKeys).toContain('dashboard');
|
||||
expect(allKeys).toContain('timestamp');
|
||||
expect(allKeys).toContain('storageCleaner');
|
||||
expect(allKeys).toContain('openUrl');
|
||||
expect(allKeys).toContain('qrCode');
|
||||
expect(allKeys).toContain('formRecognizer');
|
||||
expect(allKeys).toContain('openUrlViewer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultPageOrder', () => {
|
||||
it('should exclude dashboard from page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).not.toContain('dashboard');
|
||||
});
|
||||
|
||||
it('should exclude openUrlViewer from page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).not.toContain('openUrlViewer');
|
||||
});
|
||||
|
||||
it('should include timestamp, storageCleaner, openUrl, qrCode, formRecognizer in page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).toContain('timestamp');
|
||||
expect(pageOrder).toContain('storageCleaner');
|
||||
expect(pageOrder).toContain('openUrl');
|
||||
expect(pageOrder).toContain('qrCode');
|
||||
expect(pageOrder).toContain('formRecognizer');
|
||||
});
|
||||
|
||||
it('should have 7 items in page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).toHaveLength(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,143 +0,0 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import {
|
||||
ROUTES,
|
||||
getRouteByKey,
|
||||
getDefaultVisibleRoutes,
|
||||
getAllRouteKeys,
|
||||
getDefaultPageOrder,
|
||||
} from '../routes';
|
||||
|
||||
describe('routes', () => {
|
||||
describe('ROUTES', () => {
|
||||
it('should have 9 routes defined', () => {
|
||||
expect(ROUTES).toHaveLength(9);
|
||||
});
|
||||
|
||||
it('should have all required properties for each route', () => {
|
||||
ROUTES.forEach((route) => {
|
||||
expect(route).toHaveProperty('key');
|
||||
expect(route).toHaveProperty('label');
|
||||
expect(route).toHaveProperty('defaultVisible');
|
||||
expect(route).toHaveProperty('components');
|
||||
expect(typeof route.key).toBe('string');
|
||||
expect(typeof route.label).toBe('string');
|
||||
expect(typeof route.defaultVisible).toBe('boolean');
|
||||
expect(typeof route.components).toBe('object');
|
||||
expect(route.components).toHaveProperty('popup');
|
||||
expect(route.components).toHaveProperty('sidepanel');
|
||||
expect(route.components).toHaveProperty('detached');
|
||||
});
|
||||
});
|
||||
|
||||
it('should have unique keys for each route', () => {
|
||||
const keys = ROUTES.map((route) => route.key);
|
||||
const uniqueKeys = new Set(keys);
|
||||
expect(uniqueKeys.size).toBe(keys.length);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRouteByKey', () => {
|
||||
it('should return dashboard route', () => {
|
||||
const route = getRouteByKey('dashboard');
|
||||
expect(route).toBeDefined();
|
||||
expect(route?.key).toBe('dashboard');
|
||||
expect(route?.label).toBe('Dashboard');
|
||||
});
|
||||
|
||||
it('should return timestamp route', () => {
|
||||
const route = getRouteByKey('timestamp');
|
||||
expect(route).toBeDefined();
|
||||
expect(route?.key).toBe('timestamp');
|
||||
expect(route?.label).toBe('时间戳');
|
||||
});
|
||||
|
||||
it('should return storageCleaner route', () => {
|
||||
const route = getRouteByKey('storageCleaner');
|
||||
expect(route).toBeDefined();
|
||||
expect(route?.key).toBe('storageCleaner');
|
||||
expect(route?.label).toBe('存储清理');
|
||||
});
|
||||
|
||||
it('should return openUrl route', () => {
|
||||
const route = getRouteByKey('openUrl');
|
||||
expect(route).toBeDefined();
|
||||
expect(route?.key).toBe('openUrl');
|
||||
expect(route?.label).toBe('Open Url');
|
||||
});
|
||||
|
||||
it('should return openUrlViewer route', () => {
|
||||
const route = getRouteByKey('openUrlViewer');
|
||||
expect(route).toBeDefined();
|
||||
expect(route?.key).toBe('openUrlViewer');
|
||||
expect(route?.label).toBe('查看');
|
||||
});
|
||||
|
||||
it('should return undefined for invalid key', () => {
|
||||
const route = getRouteByKey('invalid' as any);
|
||||
expect(route).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultVisibleRoutes', () => {
|
||||
it('should return only visible routes', () => {
|
||||
const visibleRoutes = getDefaultVisibleRoutes();
|
||||
visibleRoutes.forEach((key) => {
|
||||
const route = getRouteByKey(key);
|
||||
expect(route?.defaultVisible).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
it('should include dashboard, timestamp, storageCleaner, openUrl', () => {
|
||||
const visibleRoutes = getDefaultVisibleRoutes();
|
||||
expect(visibleRoutes).toContain('dashboard');
|
||||
expect(visibleRoutes).toContain('timestamp');
|
||||
expect(visibleRoutes).toContain('storageCleaner');
|
||||
expect(visibleRoutes).toContain('openUrl');
|
||||
});
|
||||
|
||||
it('should not include openUrlViewer (not visible by default)', () => {
|
||||
const visibleRoutes = getDefaultVisibleRoutes();
|
||||
expect(visibleRoutes).not.toContain('openUrlViewer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getAllRouteKeys', () => {
|
||||
it('should return all route keys', () => {
|
||||
const allKeys = getAllRouteKeys();
|
||||
expect(allKeys).toHaveLength(9);
|
||||
expect(allKeys).toContain('dashboard');
|
||||
expect(allKeys).toContain('timestamp');
|
||||
expect(allKeys).toContain('storageCleaner');
|
||||
expect(allKeys).toContain('openUrl');
|
||||
expect(allKeys).toContain('qrCode');
|
||||
expect(allKeys).toContain('formRecognizer');
|
||||
expect(allKeys).toContain('openUrlViewer');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getDefaultPageOrder', () => {
|
||||
it('should exclude dashboard from page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).not.toContain('dashboard');
|
||||
});
|
||||
|
||||
it('should exclude openUrlViewer from page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).not.toContain('openUrlViewer');
|
||||
});
|
||||
|
||||
it('should include timestamp, storageCleaner, openUrl, qrCode, formRecognizer in page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).toContain('timestamp');
|
||||
expect(pageOrder).toContain('storageCleaner');
|
||||
expect(pageOrder).toContain('openUrl');
|
||||
expect(pageOrder).toContain('qrCode');
|
||||
expect(pageOrder).toContain('formRecognizer');
|
||||
});
|
||||
|
||||
it('should have 7 items in page order', () => {
|
||||
const pageOrder = getDefaultPageOrder();
|
||||
expect(pageOrder).toHaveLength(7);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,4 +1,11 @@
|
||||
import React, { ReactNode } from 'react';
|
||||
import type { PageType } from '@/types/storage';
|
||||
import AccessTimeIcon from '@mui/icons-material/AccessTime';
|
||||
import StorageIcon from '@mui/icons-material/Storage';
|
||||
import LanguageIcon from '@mui/icons-material/Language';
|
||||
import QrCodeIcon from '@mui/icons-material/QrCode';
|
||||
import DescriptionIcon from '@mui/icons-material/Description';
|
||||
|
||||
import DashboardPage from '@/entrypoints/popup/pages/DashboardPage';
|
||||
import TimestampPage from '@/entrypoints/popup/pages/TimestampPage';
|
||||
import StorageCleanerPage from '@/entrypoints/popup/pages/StorageCleanerPage';
|
||||
@@ -9,21 +16,42 @@ import FormRecognizerPage from '@/entrypoints/popup/pages/FormRecognizerPage';
|
||||
import FormMappingPage from '@/entrypoints/popup/pages/FormMappingPage';
|
||||
import FormFillPage from '@/entrypoints/popup/pages/FormFillPage';
|
||||
|
||||
export interface RouteConfig {
|
||||
import { THEME_COLORS } from './pageTheme';
|
||||
|
||||
/**
|
||||
* 功能配置接口
|
||||
*
|
||||
* 整合了路由信息和仪表盘卡片元数据,作为功能的单一事实来源
|
||||
*/
|
||||
export interface FeatureConfig {
|
||||
/** 页面类型标识 */
|
||||
key: PageType;
|
||||
/** 功能名称(用于路由标签和卡片标题) */
|
||||
label: string;
|
||||
/** 功能描述(用于仪表盘卡片) */
|
||||
description: string;
|
||||
/** 主题颜色(用于仪表盘卡片) */
|
||||
themeColor?: string;
|
||||
/** 图标组件(用于仪表盘卡片) */
|
||||
icon?: ReactNode;
|
||||
/** 默认是否在仪表盘显示 */
|
||||
defaultVisible: boolean;
|
||||
/** 不同显示模式对应的组件 */
|
||||
components: {
|
||||
/** 弹窗模式组件 */
|
||||
popup: React.ComponentType;
|
||||
/** 侧边栏模式组件 */
|
||||
sidepanel: React.ComponentType;
|
||||
/** 独立窗口模式组件 */
|
||||
detached: React.ComponentType;
|
||||
};
|
||||
}
|
||||
|
||||
export const ROUTES: RouteConfig[] = [
|
||||
export const FEATURES: FeatureConfig[] = [
|
||||
{
|
||||
key: 'dashboard',
|
||||
label: 'Dashboard',
|
||||
description: '',
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: DashboardPage,
|
||||
@@ -34,6 +62,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'timestamp',
|
||||
label: '时间戳',
|
||||
description: 'Unix 毫秒数转换与格式化',
|
||||
themeColor: THEME_COLORS.primary,
|
||||
icon: <AccessTimeIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: TimestampPage,
|
||||
@@ -44,6 +75,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'storageCleaner',
|
||||
label: '存储清理',
|
||||
description: '清理缓存、Cookies 及本地存储',
|
||||
themeColor: THEME_COLORS.warning,
|
||||
icon: <StorageIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: StorageCleanerPage,
|
||||
@@ -54,6 +88,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'openUrl',
|
||||
label: 'Open Url',
|
||||
description: '打开当前选中的 URL',
|
||||
themeColor: THEME_COLORS.purple,
|
||||
icon: <LanguageIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: OpenUrlPage,
|
||||
@@ -64,6 +101,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'qrCode',
|
||||
label: '二维码工具',
|
||||
description: '生成当前选中的 URL 的二维码',
|
||||
themeColor: THEME_COLORS.success,
|
||||
icon: <QrCodeIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: QrCodePage,
|
||||
@@ -74,6 +114,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'formMapping',
|
||||
label: '表单映射',
|
||||
description: '智能识别表单指纹,自定义填充逻辑',
|
||||
themeColor: THEME_COLORS.primary,
|
||||
icon: <DescriptionIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: FormMappingPage,
|
||||
@@ -84,6 +127,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'formFill',
|
||||
label: '智能填充',
|
||||
description: '根据表单指纹填充表单数据',
|
||||
themeColor: THEME_COLORS.primary,
|
||||
icon: <DescriptionIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: FormFillPage,
|
||||
@@ -94,6 +140,9 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'formRecognizer',
|
||||
label: '表单识别',
|
||||
description: '智能识别表单指纹',
|
||||
themeColor: THEME_COLORS.primary,
|
||||
icon: <DescriptionIcon sx={{ fontSize: 20 }} />,
|
||||
defaultVisible: true,
|
||||
components: {
|
||||
popup: FormRecognizerPage,
|
||||
@@ -104,6 +153,7 @@ export const ROUTES: RouteConfig[] = [
|
||||
{
|
||||
key: 'openUrlViewer',
|
||||
label: '查看',
|
||||
description: '',
|
||||
defaultVisible: false,
|
||||
components: {
|
||||
popup: OpenUrlViewerPage,
|
||||
@@ -113,21 +163,21 @@ export const ROUTES: RouteConfig[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export function getRouteByKey(key: PageType): RouteConfig | undefined {
|
||||
return ROUTES.find((route) => route.key === key);
|
||||
export function getFeatureByKey(key: PageType): FeatureConfig | undefined {
|
||||
return FEATURES.find((f) => f.key === key);
|
||||
}
|
||||
|
||||
export function getDefaultVisibleRoutes(): PageType[] {
|
||||
return ROUTES.filter((route) => route.defaultVisible).map((route) => route.key);
|
||||
export function getDefaultVisibleFeatureKeys(): PageType[] {
|
||||
return FEATURES.filter((f) => f.defaultVisible).map((f) => f.key);
|
||||
}
|
||||
|
||||
export function getAllRouteKeys(): PageType[] {
|
||||
return ROUTES.map((route) => route.key);
|
||||
export function getAllFeatureKeys(): PageType[] {
|
||||
return FEATURES.map((f) => f.key);
|
||||
}
|
||||
|
||||
export function getDefaultPageOrder(): PageType[] {
|
||||
return ROUTES.filter((route) => route.key !== 'dashboard' && route.key !== 'openUrlViewer').map(
|
||||
(route) => route.key,
|
||||
return FEATURES.filter((f) => f.key !== 'dashboard' && f.key !== 'openUrlViewer').map(
|
||||
(f) => f.key,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -151,6 +151,13 @@ export const openUrlPageStyles = {
|
||||
errorBg: alpha(THEME_COLORS.error, 0.05),
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 查看 URL 页面样式
|
||||
*/
|
||||
export const openUrlViewerPageStyles = {
|
||||
backgroundColor: '#ffffff',
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* 存储清理页面样式
|
||||
*/
|
||||
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
import { createTheme } from '@mui/material/styles';
|
||||
import { THEME_COLORS } from './pageTheme';
|
||||
|
||||
/**
|
||||
* 统一的 MUI 主题配置
|
||||
* 整合了原有的外部 CSS 样式(滚动条、动画、基础重置)
|
||||
*/
|
||||
const theme = createTheme({
|
||||
palette: {
|
||||
primary: {
|
||||
main: THEME_COLORS.primary,
|
||||
dark: THEME_COLORS.primaryDark,
|
||||
light: THEME_COLORS.primaryLight,
|
||||
},
|
||||
success: {
|
||||
main: THEME_COLORS.success,
|
||||
dark: THEME_COLORS.successDark,
|
||||
light: THEME_COLORS.successLight,
|
||||
},
|
||||
warning: {
|
||||
main: THEME_COLORS.warning,
|
||||
dark: THEME_COLORS.warningDark,
|
||||
light: THEME_COLORS.warningLight,
|
||||
},
|
||||
error: {
|
||||
main: THEME_COLORS.error,
|
||||
dark: THEME_COLORS.errorDark,
|
||||
light: THEME_COLORS.errorLight,
|
||||
},
|
||||
secondary: {
|
||||
main: THEME_COLORS.purple,
|
||||
dark: THEME_COLORS.purpleDark,
|
||||
light: THEME_COLORS.purpleLight,
|
||||
},
|
||||
},
|
||||
typography: {
|
||||
fontFamily: [
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
'"Segoe UI"',
|
||||
'Roboto',
|
||||
'"Helvetica Neue"',
|
||||
'Arial',
|
||||
'sans-serif',
|
||||
'"Apple Color Emoji"',
|
||||
'"Segoe UI Emoji"',
|
||||
'"Segoe UI Symbol"',
|
||||
].join(','),
|
||||
},
|
||||
components: {
|
||||
MuiCssBaseline: {
|
||||
styleOverrides: {
|
||||
':root': {
|
||||
'--sb-width': '6px',
|
||||
'--sb-thumb-color': 'rgba(0, 0, 0, 0.1)',
|
||||
'--sb-thumb-hover': 'rgba(0, 0, 0, 0.2)',
|
||||
'--sb-track-color': 'transparent',
|
||||
},
|
||||
'html, body, #root': {
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
minWidth: '400px',
|
||||
minHeight: '600px',
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#f5f5f5',
|
||||
},
|
||||
// 针对 Popup 的特殊处理(如果需要固定宽高,可以在具体入口点或容器中处理,
|
||||
// 这里提供全局基础,具体尺寸在 App 容器中限制)
|
||||
body: {
|
||||
WebkitFontSmoothing: 'antialiased',
|
||||
MozOsxFontSmoothing: 'grayscale',
|
||||
},
|
||||
code: {
|
||||
fontFamily: 'source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace',
|
||||
},
|
||||
'h1, h2, h3, h4, h5, h6': {
|
||||
fontSize: 'inherit',
|
||||
fontWeight: 'inherit',
|
||||
},
|
||||
/* 全局极简滚动条定制 */
|
||||
'*::-webkit-scrollbar': {
|
||||
width: 'var(--sb-width)',
|
||||
},
|
||||
'*::-webkit-scrollbar-track': {
|
||||
background: 'var(--sb-track-color)',
|
||||
},
|
||||
'*::-webkit-scrollbar-thumb': {
|
||||
background: 'var(--sb-thumb-color)',
|
||||
borderRadius: '10px',
|
||||
backgroundClip: 'content-box',
|
||||
border: '1px solid transparent',
|
||||
},
|
||||
'*::-webkit-scrollbar-thumb:hover': {
|
||||
background: 'var(--sb-thumb-hover)',
|
||||
},
|
||||
/* Animations */
|
||||
'@keyframes slideInRight': {
|
||||
from: {
|
||||
transform: 'translateX(30px)',
|
||||
opacity: 0,
|
||||
},
|
||||
to: {
|
||||
transform: 'translateX(0)',
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
'@keyframes fadeIn': {
|
||||
from: {
|
||||
opacity: 0,
|
||||
},
|
||||
to: {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
'.page-transition-enter': {
|
||||
animation: 'slideInRight 0.3s cubic-bezier(0.25, 0.8, 0.25, 1) forwards',
|
||||
},
|
||||
'.page-transition-dashboard': {
|
||||
animation: 'fadeIn 0.3s ease-out forwards',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export default theme;
|
||||
Reference in New Issue
Block a user