@@ -30,19 +30,6 @@ const progressStarted = ref(false)
const progressPercentage = ref ( 0 )
const totalProducts = ref ( 0 )
const processedProducts = ref ( 0 )
// 进度头部文案(展示在进度条上方)
const successCount = computed ( ( ) =>
allProducts . value . filter (
( p : any ) => p && p . mapRecognitionLink && String ( p . mapRecognitionLink ) . trim ( ) !== ''
) . length
)
const progressHeader = computed ( ( ) => {
if ( ! progressStarted . value ) return ''
if ( progressPercentage . value >= 100 ) {
return ` 数据获取完成(成功获取 ${ successCount . value } 个) 左侧操作栏点击“导出数据”按钮, 可导出为Excel文件 `
}
return '数据获取中'
} )
// 左侧步骤栏进度
const activeStep = computed ( ( ) => {
@@ -79,20 +66,33 @@ function openRakutenUpload() {
uploadInputRef . value ? . click ( )
}
function parseSkuPrices ( produc t: any ) {
if ( ! product . skuPrice ) return [ ]
function parseSkuPrices ( inpu t: any ) {
try {
let skuStr = product . skuPrice
if ( typeof skuStr === 'string ' ) {
skuStr = skuStr . replace ( /(\d+(?:\.\d+)?):"/g , '"$1":"' )
skuStr = JSON . parse ( skuStr )
let skuSource : any = input
if ( input && typeof input === 'object ' ) {
skuSource = input . skuPriceJson ? ? input . skuPrice
}
return Object . keys ( skuStr ) . map ( p => parseFloat ( p ) ) . filter ( n => ! isNaN ( n ) ) . sort ( ( a , b ) => a - b )
if ( ! skuSource ) return [ ]
if ( typeof skuSource === 'string' ) {
skuSource = skuSource . replace ( /(\d+(?:\.\d+)?):"/g , '"$1":"' )
skuSource = JSON . parse ( skuSource )
}
if ( ! skuSource || typeof skuSource !== 'object' ) return [ ]
return Object . keys ( skuSource )
. map ( p => parseFloat ( p ) )
. filter ( n => ! isNaN ( n ) )
. sort ( ( a , b ) => a - b )
} catch {
return [ ]
}
}
function needsSearch ( product : any ) {
const hasParsedPrices = Array . isArray ( product ? . skuPrices ) && product . skuPrices . length > 0
const hasRawPrices = ! ! ( product ? . skuPriceJson || product ? . skuPrice )
return ! ( hasParsedPrices || hasRawPrices )
}
async function loadLatest ( ) {
const resp = await rakutenApi . getLatestProducts ( )
allProducts . value = ( resp . products || [ ] ) . map ( p => ( { ... p , skuPrices : parseSkuPrices ( p ) } ) )
@@ -101,16 +101,18 @@ async function loadLatest() {
async function searchProductInternal ( product : any ) {
if ( ! product || ! product . imgUrl ) return
if ( product . mapRecognitionLink && String ( product . mapRecognitionLink ) . trim ( ) !== '' ) return
if ( ! needsSearch ( product ) ) return
const res = await rakutenApi . search1688 ( product . imgUrl , currentBatchId . value )
const data = res
const skuJson = ( data as any ) ? . skuPriceJson ? ? ( data as any ) ? . skuPrice
Object . assign ( product , {
mapRecognitionLink : data . mapRecognitionLink ,
freight : data . freight ,
median : data . median ,
weight : data . weight ,
skuPrice : data . skuPrice ,
skuPrices : parseSkuPrices ( data ) ,
skuPriceJson : skuJson ,
skuPrice : skuJson ,
skuPrices : parseSkuPrices ( { skuPriceJson : skuJson } ) ,
image1688Url : data . mapRecognitionLink ,
detailUrl1688 : data . mapRecognitionLink ,
} )
@@ -163,36 +165,9 @@ async function onDrop(e: DragEvent) {
await processFile ( file )
}
async function searchSingleShop ( ) {
const shop = singleShopName . value . trim ( )
if ( ! shop ) return
// 重置进度与状态
progressStarted . value = true
progressPercentage . value = 0
totalProducts . value = 0
processedProducts . value = 0
loading . value = true
tableLoading . value = true
currentBatchId . value = ` RAKUTEN_ ${ Date . now ( ) } `
try {
const resp = await rakutenApi . getProducts ( { shopName : shop , batchId : currentBatchId . value } )
allProducts . value = ( resp . products || [ ] ) . filter ( ( p : any ) => p . originalShopName === shop ) . map ( p => ( { ... p , skuPrices : parseSkuPrices ( p ) } ) )
statusType . value = 'info'
statusMessage . value = ` 店铺 ${ shop } 共 ${ allProducts . value . length } 条,点击“获取数据”开始识图 `
singleShopName . value = ''
} catch ( e : any ) {
statusMessage . value = e ? . message || '查询失败'
statusType . value = 'error'
} finally {
loading . value = false
tableLoading . value = false
}
}
// 点击“获取数据”触发识图
// 点击“获取数据
async function handleStartSearch ( ) {
// 如果存在待解析文件,先请求后端解析再进入识图
if ( pendingFile . value ) {
try {
loading . value = true
@@ -216,10 +191,19 @@ async function handleStartSearch() {
tableLoading . value = false
}
}
const items = allProducts . value . filter ( p => p && p . imgUrl && ! p . mapRecognitionLink )
const items = allProducts . value . filter ( p => p && p . imgUrl && needsSearch ( p ) )
if ( allProducts . value . length > 0 && items . length === 0 ) {
progressStarted . value = true
totalProducts . value = allProducts . value . length
processedProducts . value = totalProducts . value
progressPercentage . value = 100
statusType . value = 'success'
statusMessage . value = ''
return
}
if ( items . length === 0 ) {
statusType . value = 'warning'
statusMessage . value = '没有可识图 的商品,请先导入或查询店铺'
statusMessage . value = '没有可处理 的商品,请先导入或查询店铺'
return
}
await startBatch1688Search ( items )
@@ -235,7 +219,7 @@ function stopTask() {
}
async function startBatch1688Search ( products : any [ ] ) {
const items = ( products || [ ] ) . filter ( p => p && p . imgUrl && ! p . mapRecognitionLink )
const items = ( products || [ ] ) . filter ( p => p && p . imgUrl && needsSearch ( p ) )
if ( items . length === 0 ) {
progressPercentage . value = 100
statusType . value = 'success'
@@ -319,7 +303,7 @@ onMounted(loadLatest)
< div class = "body-layout" >
<!-- 左侧步骤栏 -- >
< aside class = "steps-sidebar" >
< div class = "steps-title" > 查询步骤 : < / div >
< div class = "steps-title" > 操作流程 : < / div >
< div class = "steps-flow" >
<!-- Step 1 导入乐天店铺 -- >
@@ -399,12 +383,8 @@ onMounted(loadLatest)
<!-- 数据显示区域 -- >
< div class = "table-container" >
< div class = "table-section" >
<!-- 表格上方进度条 ( 移动到表格容器内部 ) -- >
<!-- 表格上方进度条 -- >
< div v-if = "progressStarted" class="progress-head" >
< div class = "progress-title" >
< span v-if = "progressPercentage>=100" class="ok-badge" > ● < / span >
< span class = "title-text" > { { progressHeader } } < / span >
< / div >
< div class = "progress-section" >
< div class = "progress-box" >
< div class = "progress-container" >
@@ -519,7 +499,7 @@ onMounted(loadLatest)
. body - layout { display : flex ; gap : 12 px ; height : 100 % ; }
. steps - sidebar { width : 220 px ; background : # fff ; border : 1 px solid # ebeef5 ; border - radius : 6 px ; padding : 10 px ; height : 100 % ; flex - shrink : 0 ; }
. steps - title { font - size : 14 px ; font - weight : 600 ; color : # 303133 ; margin - bottom : 8 px ; }
. steps - title { font - size : 14 px ; font - weight : 600 ; color : # 303133 ; margin - bottom : 8 px ; text - align : left ; }
/* 卡片式步骤,与示例一致 */
. steps - flow { position : relative ; }
@@ -568,43 +548,12 @@ onMounted(loadLatest)
. btn - blue : disabled { background : # a6c8ff ; border - color : # a6c8ff ; color : # fff ; }
. w100 { width : 100 % ; }
. steps - sidebar : deep ( . el - button + . el - button ) { margin - left : 0 ; }
. import - section {
margin - bottom : 10 px ;
flex - shrink : 0 ;
}
. import - controls {
display : flex ;
align - items : flex - end ;
gap : 20 px ;
flex - wrap : wrap ;
margin - bottom : 8 px ;
}
. single - input {
display : flex ;
align - items : center ;
gap : 8 px ;
}
. action - buttons {
display : flex ;
gap : 10 px ;
flex - wrap : wrap ;
}
. progress - section { margin : 12 px 12 px 6 px 12 px ; }
. progress - section { margin : 0 px 12 px 0 px 12 px ; }
. progress - box { padding : 4 px 0 ; }
. progress - container { display : flex ; align - items : center ; gap : 8 px ; }
. progress - bar { flex : 1 ; height : 6 px ; background : # e3eeff ; border - radius : 999 px ; overflow : hidden ; }
. progress - fill { height : 100 % ; background : # 1677 FF ; border - radius : 999 px ; transition : width 0.3 s ease ; }
. progress - text { font - size : 13 px ; color : # 1677 FF ; font - weight : 500 ; min - width : 44 px ; text - align : right ; }
. progress - head { padding : 8 px 12 px 0 12 px ; }
. progress - title { display : flex ; align - items : center ; gap : 8 px ; color : # 606266 ; font - size : 13 px ; margin - bottom : 6 px ; }
. progress - title . ok - badge { color : # 52 c41a ; font - size : 12 px ; }
. progress - title . title - text { color : # 303133 ; font - weight : 600 ; }
. current - status {
font - size : 12 px ;