Files
ozon-seller-kit/extension-v1/entrypoints/sidepanel/App.tsx
T

184 lines
5.6 KiB
TypeScript

import { createRoot } from 'react-dom/client';
import { useState } from 'react';
import type { ScanResult } from '../../src/collector/scan';
function App() {
const [status, setStatus] = useState<string>('准备就绪');
const [result, setResult] = useState<ScanResult | null>(null);
const [error, setError] = useState<string>('');
const handleScan = async () => {
setStatus('采集中...');
setError('');
setResult(null);
try {
// 获取当前活跃 tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab?.id) {
setError('无法获取当前标签页');
setStatus('准备就绪');
return;
}
// 执行采集(调用 content script 暴露的全局函数)
const scanResult = await chrome.scripting.executeScript({
target: { tabId: tab.id },
func: () => (window as any).__SellerHelper?.scan()
});
const data = scanResult[0]?.result;
if (!data) {
setError('当前页面不支持采集(仅支持 1688/淘宝商品详情页)');
setStatus('准备就绪');
return;
}
setResult(data);
setStatus('采集完成');
} catch (err) {
setError(`采集失败: ${err instanceof Error ? err.message : String(err)}`);
setStatus('准备就绪');
}
};
return (
<div style={{ padding: '1rem', fontFamily: 'system-ui', width: '360px' }}>
<h2 style={{ margin: 0, fontSize: '1.25rem' }}>Seller Helper</h2>
<p style={{ margin: '0.25rem 0', fontSize: '0.875rem', color: '#666' }}>
1688/淘宝 商品采集
</p>
<div style={{
marginTop: '1rem',
padding: '0.5rem',
background: status === '采集完成' ? '#f0fff0' : '#f0f0f0',
borderRadius: '4px',
fontSize: '0.875rem'
}}>
状态: {status}
</div>
{error && (
<div style={{
marginTop: '0.5rem',
padding: '0.5rem',
background: '#fff0f0',
border: '1px solid #ffcccc',
borderRadius: '4px',
fontSize: '0.875rem',
color: '#cc0000'
}}>
{error}
</div>
)}
<button
style={{
width: '100%',
padding: '0.75rem',
marginTop: '1rem',
background: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
fontSize: '1rem',
fontWeight: 500
}}
onClick={handleScan}
disabled={status === '采集中...'}
>
{status === '采集中...' ? '采集中...' : '开始采集'}
</button>
{result && (
<div style={{ marginTop: '1rem', fontSize: '0.875rem' }}>
<div style={{
padding: '0.5rem',
background: '#fafafa',
borderRadius: '4px',
marginBottom: '0.5rem'
}}>
<div><strong>{result.platform}</strong> · {result.itemId}</div>
<div style={{ fontSize: '0.75rem', color: '#999', marginTop: '0.25rem' }}>
{new Date(result.scannedAt).toLocaleString()}
</div>
</div>
<div style={{ marginTop: '0.75rem' }}>
<h3 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem' }}>文本 ({result.texts.length})</h3>
{result.texts.map((t, i) => (
<div key={i} style={{
padding: '0.25rem 0.5rem',
background: '#f9f9f9',
borderLeft: '3px solid #1890ff',
marginBottom: '0.25rem',
fontSize: '0.75rem'
}}>
<strong>{t.kind}</strong>: {t.content.substring(0, 50)}...
</div>
))}
</div>
<div style={{ marginTop: '0.75rem' }}>
<h3 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem' }}>图片 ({result.images.length})</h3>
<div style={{ display: 'flex', gap: '0.5rem', flexWrap: 'wrap' }}>
{Object.entries(result.stats).map(([group, count]) => (
<div key={group} style={{
padding: '0.25rem 0.5rem',
background: '#e6f7ff',
borderRadius: '4px',
fontSize: '0.75rem'
}}>
{group}: {count}
</div>
))}
</div>
</div>
{result.warnings.length > 0 && (
<div style={{ marginTop: '0.75rem' }}>
<h3 style={{ margin: '0 0 0.5rem 0', fontSize: '0.875rem', color: '#ff6600' }}>
⚠️ 警告
</h3>
{result.warnings.map((w, i) => (
<div key={i} style={{
padding: '0.25rem 0.5rem',
background: '#fff7e6',
borderLeft: '3px solid #ff6600',
marginBottom: '0.25rem',
fontSize: '0.75rem'
}}>
{w}
</div>
))}
</div>
)}
</div>
)}
<div style={{
marginTop: '1rem',
padding: '0.5rem',
background: '#f9f9f9',
borderRadius: '4px',
fontSize: '0.75rem',
color: '#666'
}}>
<div>💡 使用说明:</div>
<ol style={{ margin: '0.25rem 0 0 1.25rem', padding: 0 }}>
<li>打开 1688 或淘宝商品详情页</li>
<li>滚动到页面底部(加载详情图)</li>
<li>点击"开始采集"按钮</li>
</ol>
</div>
</div>
);
}
const root = createRoot(document.getElementById('root')!);
root.render(<App />);
export default App;