Develop fill form (#10)

* feat(formRecognizer): 添加表单识别功能及相关组件

添加表单识别功能,包括以下内容:
1. 在路由配置中添加表单识别页面
2. 实现表单识别页面和侧边栏面板
3. 添加表单数据生成工具类
4. 实现与内容脚本的通信机制
5. 添加faker-js依赖用于生成测试数据
6. 支持不同入口点(popup/sidepanel)的组件渲染

* refactor(消息通信): 重构消息通信机制并集中管理消息协议

将分散的消息协议和通信逻辑集中到 utils/messages.ts 中
移除旧的 messages.tsx 文件并更新相关引用
添加消息动作枚举和类型定义,提高类型安全性
优化内容脚本注入失败时的处理逻辑
This commit is contained in:
LingandRX
2026-04-20 22:50:01 +08:00
committed by GitHub
parent be5e2f02ee
commit 0379f96e80
14 changed files with 1987 additions and 99 deletions
+13
View File
@@ -5,6 +5,7 @@ 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 type { PageType } from '@/types/storage';
import { useEffect, useState } from 'react';
import dayjs from '@/utils/dayjs';
@@ -88,6 +89,18 @@ export default function DashboardPage() {
cardBackgroundColor={dashboardPageStyles.cardBackgroundColor}
/>
);
case 'formRecognizer':
return (
<ToolCard
key={key}
title="表单识别"
description="识别标签页中的表单内容"
colorCode="#ff5722"
icon={<DescriptionIcon sx={{ fontSize: 20 }} />}
onClick={() => navigateTo('formRecognizer')}
cardBackgroundColor={dashboardPageStyles.cardBackgroundColor}
/>
);
default:
return null;
}
@@ -0,0 +1,193 @@
import { useState } from 'react';
import {
Box,
Typography,
Container,
Button,
Paper,
CircularProgress,
Stack,
Switch,
FormControlLabel,
} from '@mui/material';
import AutoAwesomeIcon from '@mui/icons-material/AutoAwesome';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import ClearAllIcon from '@mui/icons-material/ClearAll';
import GlobalSnackbar, { useSnackbar } from '@/components/GlobalSnackbar';
import { dashboardPageStyles } from '@/config/pageTheme';
const FormRecognizerPage = () => {
const { snackbarProps, showMessage } = useSnackbar({ autoHideDuration: 1500 });
const [loading, setLoading] = useState(false);
const [includeHidden, setIncludeHidden] = useState(false);
interface MessagePayload {
includeHidden?: boolean;
}
const sendMessageToContent = async (action: string, payload?: MessagePayload) => {
setLoading(true);
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab.id) {
showMessage('无法获取当前标签页', { severity: 'error' });
return;
}
const response = await chrome.tabs.sendMessage(tab.id, { action, ...payload });
if (response.success) {
showMessage(response.message, { severity: 'success' });
} else {
showMessage(response.message, { severity: 'error' });
}
} catch (error) {
console.error('发送消息失败:', error);
showMessage('请确保当前页面已加载完成', { severity: 'error' });
} finally {
setLoading(false);
}
};
const handleFillValidData = () => {
sendMessageToContent('fillValidData', { includeHidden });
};
const handleFillInvalidData = () => {
sendMessageToContent('fillInvalidData', { includeHidden });
};
const handleClearAllFields = () => {
sendMessageToContent('clearAllFields');
};
return (
<Box sx={{ bgcolor: dashboardPageStyles.backgroundColor, minHeight: '100%', pb: 4 }}>
<Container maxWidth="sm" sx={{ py: 3, px: 2 }}>
<Box sx={{ mb: 4 }}>
<Typography variant="h6" component="h1" sx={{ fontWeight: 700, mb: 1 }}>
Dummy Data Generator
</Typography>
<Typography variant="body2" color="text.secondary">
</Typography>
</Box>
{/* 主要操作按钮 */}
<Stack spacing={2} sx={{ mb: 4 }}>
<Button
variant="contained"
startIcon={
loading ? <CircularProgress size={16} color="inherit" /> : <AutoAwesomeIcon />
}
onClick={handleFillValidData}
disabled={loading}
fullWidth
sx={{
py: 1.2,
borderRadius: 3,
bgcolor: '#4caf50',
fontWeight: 700,
'&:hover': {
bgcolor: '#388e3c',
},
}}
>
{loading ? '填充中...' : '一键填充(有效数据)'}
</Button>
<Button
variant="contained"
startIcon={
loading ? <CircularProgress size={16} color="inherit" /> : <ErrorOutlineIcon />
}
onClick={handleFillInvalidData}
disabled={loading}
fullWidth
sx={{
py: 1.2,
borderRadius: 3,
bgcolor: '#ff9800',
fontWeight: 700,
'&:hover': {
bgcolor: '#f57c00',
},
}}
>
{loading ? '填充中...' : '一键填充(异常数据)'}
</Button>
<Button
variant="outlined"
startIcon={loading ? <CircularProgress size={16} color="inherit" /> : <ClearAllIcon />}
onClick={handleClearAllFields}
disabled={loading}
fullWidth
sx={{
py: 1.2,
borderRadius: 3,
borderColor: '#f44336',
color: '#f44336',
fontWeight: 700,
'&:hover': {
borderColor: '#d32f2f',
bgcolor: 'rgba(244, 67, 54, 0.05)',
},
}}
>
{loading ? '清空ing...' : '一键清空所有表单'}
</Button>
</Stack>
{/* 选项设置 */}
<Paper elevation={0} sx={{ borderRadius: 4, overflow: 'hidden', mb: 4 }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider', px: 2, py: 1.5 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
</Typography>
</Box>
<Box sx={{ p: 2 }}>
<FormControlLabel
control={
<Switch
checked={includeHidden}
onChange={(e) => setIncludeHidden(e.target.checked)}
color="primary"
/>
}
label="包含隐藏字段"
sx={{ width: '100%' }}
/>
</Box>
</Paper>
{/* 功能说明 */}
<Paper elevation={0} sx={{ borderRadius: 4, overflow: 'hidden' }}>
<Box sx={{ borderBottom: 1, borderColor: 'divider', px: 2, py: 1.5 }}>
<Typography variant="subtitle2" sx={{ fontWeight: 600 }}>
</Typography>
</Box>
<Box sx={{ p: 2 }}>
<Typography variant="body2" sx={{ mb: 1 }}>
<strong></strong>
</Typography>
<Typography variant="body2" sx={{ mb: 1 }}>
<strong></strong>
</Typography>
<Typography variant="body2" sx={{ mb: 1 }}>
<strong></strong>
</Typography>
<Typography variant="body2">
<strong></strong>
</Typography>
</Box>
</Paper>
<GlobalSnackbar {...snackbarProps} />
</Container>
</Box>
);
};
export default FormRecognizerPage;