feat: 开发采集、采集箱和商品编辑功能
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
/**
|
||||
* 从 SSR JSON 构建 ScanResult
|
||||
*/
|
||||
import { toOriginalUrl } from './url';
|
||||
import type { SiteProfile } from '../profiles/types';
|
||||
import type { SSRData } from './ssr';
|
||||
|
||||
// 直接定义类型避免循环依赖
|
||||
interface TextMaterial {
|
||||
kind: 'title' | 'price' | 'params' | 'desc';
|
||||
content: string;
|
||||
pairs?: Array<{ key: string; value: string }>;
|
||||
}
|
||||
|
||||
interface ImageMaterial {
|
||||
key: string;
|
||||
groupKey: string;
|
||||
groupName: string;
|
||||
variantName?: string;
|
||||
url: string;
|
||||
thumbUrl: string;
|
||||
index: number;
|
||||
type: 'img' | 'video';
|
||||
width?: number;
|
||||
height?: number;
|
||||
}
|
||||
|
||||
interface ScanResult {
|
||||
platform: string;
|
||||
itemId: string | null;
|
||||
url: string;
|
||||
texts: TextMaterial[];
|
||||
images: ImageMaterial[];
|
||||
scannedAt: number;
|
||||
stats: Record<string, number>;
|
||||
warnings: string[];
|
||||
}
|
||||
|
||||
export function buildFromSSR(data: SSRData, profile: SiteProfile): ScanResult {
|
||||
const texts: TextMaterial[] = [];
|
||||
const images: ImageMaterial[] = [];
|
||||
|
||||
// 1. 标题(必需)
|
||||
texts.push({
|
||||
kind: 'title',
|
||||
content: data.item.title
|
||||
});
|
||||
|
||||
// 2. 价格
|
||||
if (data.price?.priceText) {
|
||||
texts.push({
|
||||
kind: 'price',
|
||||
content: `¥${data.price.priceText}`
|
||||
});
|
||||
}
|
||||
|
||||
// 3. 参数表
|
||||
const allParams = [
|
||||
...(data.params?.basicParamList || []),
|
||||
...(data.params?.enhanceParamList || [])
|
||||
];
|
||||
if (allParams.length > 0) {
|
||||
const pairs = allParams
|
||||
.filter(p => p.propertyName && p.valueName)
|
||||
.map(p => ({ key: p.propertyName, value: p.valueName }));
|
||||
|
||||
if (pairs.length > 0) {
|
||||
texts.push({
|
||||
kind: 'params',
|
||||
content: pairs.map(p => `${p.key}: ${p.value}`).join('\n'),
|
||||
pairs
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 主图(item.images)
|
||||
let idx = 0;
|
||||
(data.item.images || []).forEach((url, i) => {
|
||||
if (!url) return;
|
||||
const origUrl = toOriginalUrl(url);
|
||||
images.push({
|
||||
key: `main-${String(i + 1).padStart(3, '0')}`,
|
||||
groupKey: 'main',
|
||||
groupName: '主图',
|
||||
url: origUrl,
|
||||
thumbUrl: url,
|
||||
index: idx++,
|
||||
type: 'img'
|
||||
});
|
||||
});
|
||||
|
||||
// 5. SKU 图(skuBase.props[0].values)
|
||||
// 淘宝/天猫通常只有一个规格维度(颜色分类),取 props[0]
|
||||
const skuProp = data.skuBase?.props?.[0];
|
||||
if (skuProp?.values) {
|
||||
skuProp.values.forEach((v, i) => {
|
||||
if (!v.image) return; // 有些 SKU 没配图(如天猫那个 vid=43699206432)
|
||||
const origUrl = toOriginalUrl(v.image);
|
||||
images.push({
|
||||
key: `sku-${String(i + 1).padStart(3, '0')}`,
|
||||
groupKey: 'sku',
|
||||
groupName: 'SKU图片',
|
||||
variantName: v.name || undefined,
|
||||
url: origUrl,
|
||||
thumbUrl: v.image,
|
||||
index: idx++,
|
||||
type: 'img'
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// 6. 视频(item.videos)
|
||||
(data.item.videos || []).forEach((v, i) => {
|
||||
if (!v.url) return;
|
||||
images.push({
|
||||
key: `video-${String(i + 1).padStart(3, '0')}`,
|
||||
groupKey: 'video',
|
||||
groupName: '视频',
|
||||
url: v.url,
|
||||
thumbUrl: v.videoThumbnailURL || v.url,
|
||||
index: idx++,
|
||||
type: 'video'
|
||||
});
|
||||
});
|
||||
|
||||
// 统计各组数量
|
||||
const stats: Record<string, number> = {};
|
||||
for (const img of images) {
|
||||
stats[img.groupKey] = (stats[img.groupKey] ?? 0) + 1;
|
||||
}
|
||||
|
||||
// 生成警告
|
||||
const warnings: string[] = [];
|
||||
if (texts.length === 0) {
|
||||
warnings.push('未提取到任何文本');
|
||||
}
|
||||
if (images.length === 0) {
|
||||
warnings.push('未扫描到任何图片/视频');
|
||||
}
|
||||
// SSR 数据里没有详情图,需要 DOM 补充
|
||||
if (stats.detail === undefined) {
|
||||
warnings.push('详情图需 DOM 补充:请滚动到页面底部后重新采集');
|
||||
}
|
||||
|
||||
return {
|
||||
platform: profile.id,
|
||||
itemId: data.item.itemId,
|
||||
url: location.href,
|
||||
texts,
|
||||
images,
|
||||
scannedAt: Date.now(),
|
||||
stats,
|
||||
warnings
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user