refactor: 暂时移除图片二维码识别功能
- 删除 qrCode-image 右键菜单配置 - 删除 parseQrCodeFromUrl 函数 - 删除 QrCodeToUrlSection 中的 useContextMenuData 相关代码 - 更新相关测试用例
This commit is contained in:
@@ -42,14 +42,14 @@ describe('background 菜单注册与分流', () => {
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该创建图片二维码识别子菜单', () => {
|
it('应该创建网页链接转二维码子菜单', () => {
|
||||||
createAllContextMenus();
|
createAllContextMenus();
|
||||||
|
|
||||||
expect(chrome.contextMenus.create).toHaveBeenCalledWith(
|
expect(chrome.contextMenus.create).toHaveBeenCalledWith(
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
id: 'qrCode-image',
|
id: 'qrCode-page',
|
||||||
title: '🖼️ 识别图中的二维码',
|
title: '🔗 网页链接转二维码',
|
||||||
contexts: ['image'],
|
contexts: ['page'],
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@@ -79,35 +79,6 @@ describe('background 菜单注册与分流', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('当点击 qrCode-image 时应返回 qrCode 功能和 srcUrl', () => {
|
|
||||||
const info = createMockOnClickData({
|
|
||||||
menuItemId: 'qrCode-image',
|
|
||||||
srcUrl: 'https://example.com/image.png',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = parseContextMenuClick('qrCode-image', info);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: true,
|
|
||||||
data: { featureKey: 'qrCode', payload: 'https://example.com/image.png' },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('当图片为 Base64 内联图时应返回错误', () => {
|
|
||||||
const info = createMockOnClickData({
|
|
||||||
menuItemId: 'qrCode-image',
|
|
||||||
srcUrl:
|
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = parseContextMenuClick('qrCode-image', info);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: false,
|
|
||||||
error: '无法识别 Base64 内联图片,请使用图片文件 URL',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('当没有 selectionText 和 srcUrl 时应返回错误', () => {
|
it('当没有 selectionText 和 srcUrl 时应返回错误', () => {
|
||||||
const info = createMockOnClickData({
|
const info = createMockOnClickData({
|
||||||
menuItemId: 'unknown',
|
menuItemId: 'unknown',
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ import ClearIcon from '@mui/icons-material/Clear';
|
|||||||
import CopyButton from '@/components/CopyButton';
|
import CopyButton from '@/components/CopyButton';
|
||||||
import { qrCodePageStyles } from '@/config/pageTheme';
|
import { qrCodePageStyles } from '@/config/pageTheme';
|
||||||
import { useSnackbar } from '@/components/GlobalSnackbar';
|
import { useSnackbar } from '@/components/GlobalSnackbar';
|
||||||
import { parseQrCodeFromFile, parseQrCodeFromUrl } from '@/utils/qrCodeParser';
|
import { parseQrCodeFromFile } from '@/utils/qrCodeParser';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { useContextMenuData } from '@/utils/useContextMenuData';
|
|
||||||
|
|
||||||
interface QrCodeToUrlSectionProps {
|
interface QrCodeToUrlSectionProps {
|
||||||
expanded: boolean;
|
expanded: boolean;
|
||||||
@@ -47,39 +46,6 @@ const QrCodeToUrlSection = ({
|
|||||||
|
|
||||||
const fileInputRef = useRef<HTMLInputElement>(null);
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
// 处理右键菜单传递的图片 URL
|
|
||||||
const handleContextMenuData = useCallback(
|
|
||||||
async (imageUrl: string) => {
|
|
||||||
try {
|
|
||||||
setParsing(true);
|
|
||||||
setParseError('');
|
|
||||||
setParsedUrl('');
|
|
||||||
setPreviewUrl(imageUrl);
|
|
||||||
|
|
||||||
const result = await parseQrCodeFromUrl(imageUrl);
|
|
||||||
|
|
||||||
if (result.success && result.data) {
|
|
||||||
setParsedUrl(result.data);
|
|
||||||
showMessage(t('qrCode:parseSuccess'), { severity: 'success', autoHideDuration: 1000 });
|
|
||||||
} else {
|
|
||||||
setParseError(result.error || t('qrCode:noQrDetected'));
|
|
||||||
showMessage(result.error || t('qrCode:noQrDetected'), {
|
|
||||||
severity: 'error',
|
|
||||||
autoHideDuration: 1000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('解析二维码失败:', error);
|
|
||||||
showMessage(t('qrCode:parseError'), { severity: 'error', autoHideDuration: 300 });
|
|
||||||
} finally {
|
|
||||||
setParsing(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[showMessage, t],
|
|
||||||
);
|
|
||||||
|
|
||||||
useContextMenuData({ featureKey: 'qrCode', onData: handleContextMenuData });
|
|
||||||
|
|
||||||
// 清理预览 URL,防止内存泄漏
|
// 清理预览 URL,防止内存泄漏
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
|
|||||||
@@ -12,8 +12,8 @@ describe('contextMenu', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('CONTEXT_MENU_CONFIGS', () => {
|
describe('CONTEXT_MENU_CONFIGS', () => {
|
||||||
it('应该包含 8 个菜单项配置', () => {
|
it('应该包含 7 个菜单项配置', () => {
|
||||||
expect(CONTEXT_MENU_CONFIGS).toHaveLength(8);
|
expect(CONTEXT_MENU_CONFIGS).toHaveLength(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该有一个父级菜单项 Testing Tools', () => {
|
it('应该有一个父级菜单项 Testing Tools', () => {
|
||||||
@@ -36,14 +36,6 @@ describe('contextMenu', () => {
|
|||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该有 1 个 image 上下文的子菜单', () => {
|
|
||||||
const imageMenus = CONTEXT_MENU_CONFIGS.filter(
|
|
||||||
(c) => c.contexts[0] === 'image' && c.parentId === 'testing-tools-parent',
|
|
||||||
);
|
|
||||||
expect(imageMenus).toHaveLength(1);
|
|
||||||
expect(imageMenus[0].id).toBe('qrCode-image');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('应该有 2 个 page 上下文的子菜单', () => {
|
it('应该有 2 个 page 上下文的子菜单', () => {
|
||||||
const pageMenus = CONTEXT_MENU_CONFIGS.filter(
|
const pageMenus = CONTEXT_MENU_CONFIGS.filter(
|
||||||
(c) => c.contexts[0] === 'page' && c.parentId === 'testing-tools-parent',
|
(c) => c.contexts[0] === 'page' && c.parentId === 'testing-tools-parent',
|
||||||
@@ -57,7 +49,7 @@ describe('contextMenu', () => {
|
|||||||
it('应该为每个配置调用 chrome.contextMenus.create', () => {
|
it('应该为每个配置调用 chrome.contextMenus.create', () => {
|
||||||
createAllContextMenus();
|
createAllContextMenus();
|
||||||
|
|
||||||
expect(chrome.contextMenus.create).toHaveBeenCalledTimes(8);
|
expect(chrome.contextMenus.create).toHaveBeenCalledTimes(7);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('应该使用正确的参数创建菜单项', () => {
|
it('应该使用正确的参数创建菜单项', () => {
|
||||||
@@ -89,44 +81,6 @@ describe('contextMenu', () => {
|
|||||||
...overrides,
|
...overrides,
|
||||||
});
|
});
|
||||||
|
|
||||||
it('当点击 qrCode-image 菜单时应返回 qrCode 功能和 srcUrl', () => {
|
|
||||||
const info = createMockOnClickData({
|
|
||||||
srcUrl: 'https://example.com/image.png',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = parseContextMenuClick('qrCode-image', info);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: true,
|
|
||||||
data: { featureKey: 'qrCode', payload: 'https://example.com/image.png' },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('当图片为 Base64 内联图时应返回错误', () => {
|
|
||||||
const info = createMockOnClickData({
|
|
||||||
srcUrl:
|
|
||||||
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
|
|
||||||
});
|
|
||||||
|
|
||||||
const result = parseContextMenuClick('qrCode-image', info);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: false,
|
|
||||||
error: '无法识别 Base64 内联图片,请使用图片文件 URL',
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('当点击 qrCode-image 菜单但没有 srcUrl 时应返回成功', () => {
|
|
||||||
const info = createMockOnClickData({});
|
|
||||||
|
|
||||||
const result = parseContextMenuClick('qrCode-image', info);
|
|
||||||
|
|
||||||
expect(result).toEqual({
|
|
||||||
success: true,
|
|
||||||
data: { featureKey: 'qrCode', payload: '' },
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('当点击 qrCode-page 菜单时应返回 qrCode 功能和 pageUrl', () => {
|
it('当点击 qrCode-page 菜单时应返回 qrCode 功能和 pageUrl', () => {
|
||||||
const info = createMockOnClickData({
|
const info = createMockOnClickData({
|
||||||
pageUrl: 'https://example.com/page',
|
pageUrl: 'https://example.com/page',
|
||||||
|
|||||||
@@ -52,12 +52,6 @@ export const CONTEXT_MENU_CONFIGS: ContextMenuItemConfig[] = [
|
|||||||
contexts: [chrome.contextMenus.ContextType.SELECTION],
|
contexts: [chrome.contextMenus.ContextType.SELECTION],
|
||||||
parentId: PARENT_MENU_ID,
|
parentId: PARENT_MENU_ID,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
id: 'qrCode-image',
|
|
||||||
title: '🖼️ 识别图中的二维码',
|
|
||||||
contexts: [chrome.contextMenus.ContextType.IMAGE],
|
|
||||||
parentId: PARENT_MENU_ID,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: 'storageCleaner',
|
id: 'storageCleaner',
|
||||||
title: '🧹 清理当前网站存储',
|
title: '🧹 清理当前网站存储',
|
||||||
@@ -89,22 +83,6 @@ export function parseContextMenuClick(
|
|||||||
): ParseResult {
|
): ParseResult {
|
||||||
const featureKey = menuItemId as PageType;
|
const featureKey = menuItemId as PageType;
|
||||||
|
|
||||||
if (menuItemId === 'qrCode-image') {
|
|
||||||
const srcUrl = info.srcUrl || '';
|
|
||||||
|
|
||||||
if (srcUrl.startsWith('data:image/')) {
|
|
||||||
return {
|
|
||||||
success: false,
|
|
||||||
error: '无法识别 Base64 内联图片,请使用图片文件 URL',
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
success: true,
|
|
||||||
data: { featureKey: 'qrCode', payload: srcUrl },
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (menuItemId === 'qrCode-page') {
|
if (menuItemId === 'qrCode-page') {
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
|
|||||||
@@ -35,24 +35,3 @@ export async function parseQrCodeFromFile(file: File): Promise<QrCodeParseResult
|
|||||||
return { success: false, error: errorMsg };
|
return { success: false, error: errorMsg };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 从图片 URL 解析二维码
|
|
||||||
* 先下载图片,然后调用 parseQrCodeFromFile
|
|
||||||
*/
|
|
||||||
export async function parseQrCodeFromUrl(imageUrl: string): Promise<QrCodeParseResult> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(imageUrl);
|
|
||||||
if (!response.ok) {
|
|
||||||
return { success: false, error: '无法下载图片' };
|
|
||||||
}
|
|
||||||
|
|
||||||
const blob = await response.blob();
|
|
||||||
const file = new File([blob], 'qrcode.jpg', { type: blob.type || 'image/jpeg' });
|
|
||||||
|
|
||||||
return await parseQrCodeFromFile(file);
|
|
||||||
} catch (err) {
|
|
||||||
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
||||||
return { success: false, error: `下载图片失败: ${errorMsg}` };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
Reference in New Issue
Block a user