- 任务详情上传支持"本批素材无条码"开关,跳过条码必填校验 - SKU卡片/主图网格从4列改5列,图片高度压缩,去掉冗余标签和提示 - 详情图/PSD上传区去掉大占位框,改成紧凑按钮+标签 - 补充历史导入相关脚本和工具文件 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
77 lines
2.4 KiB
JavaScript
77 lines
2.4 KiB
JavaScript
const fs = require('fs')
|
||
const path = require('path')
|
||
|
||
const inputPath = process.argv[2] || path.join('output', 'tianlan-history-sku-import-scan-full.json')
|
||
const outputDir = process.argv[3] || path.join('output')
|
||
const baseName = process.argv[4] || 'tianlan-history-sku-import'
|
||
|
||
function escapeCsvCell(value) {
|
||
const text = String(value ?? '')
|
||
if (/[",\r\n]/.test(text)) {
|
||
return `"${text.replace(/"/g, '""')}"`
|
||
}
|
||
return text
|
||
}
|
||
|
||
function writeCsv(filePath, rows) {
|
||
const headers = [
|
||
'status(状态)',
|
||
'spu(SPU)',
|
||
'store(店铺)',
|
||
'product_category(产品品类)',
|
||
'author(作者)',
|
||
'main_count(主图数量)',
|
||
'sku_count(SKU图数量)',
|
||
'detail_count(详情图数量)',
|
||
'skip_reason(跳过或复核原因)',
|
||
'source_path(来源完整路径)',
|
||
'relative_source_path(来源相对路径)',
|
||
]
|
||
const lines = [
|
||
headers.join(','),
|
||
...rows.map((record) =>
|
||
[
|
||
record.status,
|
||
record.spu,
|
||
record.guessedStore,
|
||
record.productCategoryName,
|
||
record.authorName,
|
||
record.mainCount,
|
||
record.skuCount,
|
||
record.detailCount,
|
||
record.skipReason || '',
|
||
record.sourcePath,
|
||
record.relativeSourcePath,
|
||
]
|
||
.map(escapeCsvCell)
|
||
.join(','),
|
||
),
|
||
]
|
||
fs.writeFileSync(filePath, `\uFEFF${lines.join('\r\n')}`, 'utf8')
|
||
}
|
||
|
||
function countBy(records, picker) {
|
||
return records.reduce((acc, record) => {
|
||
const key = picker(record) || '<empty>'
|
||
acc[key] = (acc[key] || 0) + 1
|
||
return acc
|
||
}, {})
|
||
}
|
||
|
||
const records = JSON.parse(fs.readFileSync(inputPath, 'utf8'))
|
||
fs.mkdirSync(outputDir, { recursive: true })
|
||
|
||
writeCsv(path.join(outputDir, `${baseName}-ready.csv`), records.filter((record) => record.status === 'ready'))
|
||
writeCsv(path.join(outputDir, `${baseName}-needs-review.csv`), records.filter((record) => record.status === 'needs_review'))
|
||
writeCsv(path.join(outputDir, `${baseName}-skipped.csv`), records.filter((record) => record.status === 'skipped'))
|
||
|
||
const summary = {
|
||
total: records.length,
|
||
status: countBy(records, (record) => record.status),
|
||
store: countBy(records, (record) => record.guessedStore),
|
||
productCategory: countBy(records, (record) => record.productCategoryName),
|
||
}
|
||
|
||
fs.writeFileSync(path.join(outputDir, `${baseName}-summary.json`), JSON.stringify(summary, null, 2), 'utf8')
|
||
console.log(JSON.stringify(summary, null, 2))
|