1
This commit is contained in:
@@ -7,6 +7,7 @@ import { amazonApi } from '../../api/amazon'
|
||||
const loading = ref(false) // 主加载状态
|
||||
const tableLoading = ref(false) // 表格加载状态
|
||||
const progressPercentage = ref(0) // 进度百分比
|
||||
const progressVisible = ref(false) // 进度条是否显示(完成后仍保留)
|
||||
const localProductData = ref<any[]>([]) // 本地产品数据
|
||||
const currentAsin = ref('') // 当前处理的ASIN
|
||||
const genmaiLoading = ref(false) // Genmai Spirit加载状态
|
||||
@@ -43,7 +44,7 @@ const regionOptions = [
|
||||
]
|
||||
const pendingAsins = ref<string[]>([])
|
||||
|
||||
// 通用消息提示
|
||||
// 通用消息提示(Element Plus)
|
||||
function showMessage(message: string, type: 'success' | 'warning' | 'error' | 'info' = 'info') {
|
||||
ElMessage({ message, type })
|
||||
}
|
||||
@@ -55,6 +56,7 @@ async function processExcelFile(file: File) {
|
||||
tableLoading.value = true
|
||||
localProductData.value = []
|
||||
progressPercentage.value = 0
|
||||
progressVisible.value = false
|
||||
|
||||
const response = await amazonApi.importAsinFromExcel(file)
|
||||
const asinList = response.data.asinList
|
||||
@@ -63,10 +65,7 @@ async function processExcelFile(file: File) {
|
||||
showMessage('文件中未找到有效的ASIN数据', 'warning')
|
||||
return
|
||||
}
|
||||
|
||||
// 存入待采集队列,等待用户点击“获取数据”再开始
|
||||
pendingAsins.value = asinList
|
||||
showMessage(`成功解析 ${asinList.length} 个ASIN,点击“获取数据”开始采集`, 'success')
|
||||
} catch (error: any) {
|
||||
showMessage(error.message || '处理文件失败', 'error')
|
||||
} finally {
|
||||
@@ -90,8 +89,8 @@ async function onDrop(e: DragEvent) {
|
||||
dragActive.value = false
|
||||
const file = e.dataTransfer?.files?.[0]
|
||||
if (!file) return
|
||||
const ok = /(\.csv|\.txt|\.xls|\.xlsx)$/i.test(file.name)
|
||||
if (!ok) return showMessage('仅支持 .csv/.txt/.xls/.xlsx 文件', 'warning')
|
||||
const ok = /\.xlsx?$/i.test(file.name)
|
||||
if (!ok) return showMessage('仅支持 .xls/.xlsx 文件', 'warning')
|
||||
await processExcelFile(file)
|
||||
}
|
||||
|
||||
@@ -170,6 +169,7 @@ async function startQueuedFetch() {
|
||||
return
|
||||
}
|
||||
loading.value = true
|
||||
progressVisible.value = true
|
||||
tableLoading.value = true
|
||||
try {
|
||||
await batchGetProductInfo(pendingAsins.value)
|
||||
@@ -218,6 +218,13 @@ function getSellerShipperText(product: any) {
|
||||
return text
|
||||
}
|
||||
|
||||
// 判定无货(用于标红 ASIN)
|
||||
function isOutOfStock(product: any) {
|
||||
const sellerEmpty = !product?.seller || product.seller === '无货'
|
||||
const priceEmpty = !product?.price || product.price === '无货'
|
||||
return sellerEmpty || priceEmpty
|
||||
}
|
||||
|
||||
// 停止获取操作
|
||||
function stopFetch() {
|
||||
loading.value = false
|
||||
@@ -248,10 +255,26 @@ function handleCurrentChange(page: number) {
|
||||
}
|
||||
|
||||
// 使用 Element Plus 的 jumper,不再需要手动跳转函数
|
||||
|
||||
// 示例弹窗
|
||||
const amazonExampleVisible = ref(false)
|
||||
function openAmazonUpload() {
|
||||
amazonUpload.value?.click()
|
||||
}
|
||||
|
||||
function viewAmazonExample() { amazonExampleVisible.value = true }
|
||||
|
||||
function downloadAmazonTemplate() {
|
||||
const html = '<table><tr><th>ASIN</th></tr><tr><td>B0XXXXXXX1</td></tr><tr><td>B0XXXXXXX2</td></tr></table>'
|
||||
const blob = new Blob([html], { type: 'application/vnd.ms-excel' })
|
||||
const url = URL.createObjectURL(blob)
|
||||
const a = document.createElement('a')
|
||||
a.href = url
|
||||
a.download = 'amazon_asin_template.xls'
|
||||
document.body.appendChild(a)
|
||||
a.click()
|
||||
document.body.removeChild(a)
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
// 组件挂载时获取最新数据
|
||||
onMounted(async () => {
|
||||
try {
|
||||
@@ -276,18 +299,18 @@ onMounted(async () => {
|
||||
<div class="step-index">1</div>
|
||||
<div class="step-card">
|
||||
<div class="step-header"><div class="title">导入ASIN</div></div>
|
||||
<div class="desc">仅支持包含 ASIN 列的 CSV/Excel 文档</div>
|
||||
<div class="desc">仅支持包含 ASIN 列的 Excel 文档</div>
|
||||
<div class="links">
|
||||
<a class="link" @click.prevent>点击查看示例</a>
|
||||
<a class="link" @click.prevent="viewAmazonExample">点击查看示例</a>
|
||||
<span class="sep">|</span>
|
||||
<a class="link" @click.prevent>点击下载模板</a>
|
||||
<a class="link" @click.prevent="downloadAmazonTemplate">点击下载模板</a>
|
||||
</div>
|
||||
<div class="dropzone" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop="onDrop" @click="openAmazonUpload">
|
||||
<div class="dz-el-icon">📤</div>
|
||||
<div class="dz-text">点击或将文件拖拽到这里上传</div>
|
||||
<div class="dz-sub">支持 .csv .txt .xls .xlsx</div>
|
||||
<div class="dz-sub">支持 .xls .xlsx</div>
|
||||
</div>
|
||||
<input ref="amazonUpload" style="display:none" type="file" accept=".csv,.txt,.xls,.xlsx" @change="handleExcelUpload" :disabled="loading" />
|
||||
<input ref="amazonUpload" style="display:none" type="file" accept=".xls,.xlsx" @change="handleExcelUpload" :disabled="loading" />
|
||||
</div>
|
||||
</div>
|
||||
<!-- 2 网站地区 -->
|
||||
@@ -311,6 +334,16 @@ onMounted(async () => {
|
||||
<div class="desc">导入表格后,点击下方按钮开始获取ASIN数据</div>
|
||||
<el-button size="small" class="w100 btn-blue" :disabled="!pendingAsins.length || loading" @click="startQueuedFetch">{{ loading ? '处理中...' : '获取数据' }}</el-button>
|
||||
<div class="mini-hint" v-if="pendingAsins.length">已导入 {{ pendingAsins.length }} 个 ASIN</div>
|
||||
<div class="progress-section" v-if="progressVisible">
|
||||
<div class="progress-box">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: progressPercentage + '%' }"></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ progressPercentage }}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 左侧不再显示进度条 -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -337,17 +370,18 @@ onMounted(async () => {
|
||||
<!-- 数据显示区域 -->
|
||||
<div class="table-container">
|
||||
<div class="table-section">
|
||||
<!-- 表格上方进度条(与乐天一致) -->
|
||||
<div class="progress-section" v-if="loading">
|
||||
<div class="progress-box">
|
||||
<div class="progress-container">
|
||||
<div class="progress-bar">
|
||||
<div class="progress-fill" :style="{ width: progressPercentage + '%' }"></div>
|
||||
</div>
|
||||
<div class="progress-text">{{ progressPercentage }}%</div>
|
||||
</div>
|
||||
<el-dialog v-model="amazonExampleVisible" title="示例 - ASIN文档格式" width="480px">
|
||||
<div>
|
||||
<div style="margin:8px 0;color:#606266;font-size:13px;">Excel 示例:</div>
|
||||
<el-table :data="[{asin:'B0XXXXXXX1'},{asin:'B0XXXXXXX2'}]" size="small" border>
|
||||
<el-table-column prop="asin" label="ASIN" />
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button type="primary" class="btn-blue" @click="amazonExampleVisible = false">我知道了</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<div class="table-wrapper">
|
||||
<table class="table">
|
||||
<thead>
|
||||
@@ -359,7 +393,7 @@ onMounted(async () => {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="row in paginatedData" :key="row.asin">
|
||||
<td>{{ row.asin }}</td>
|
||||
<td><span :class="{ 'asin-out': isOutOfStock(row) }">{{ row.asin }}</span></td>
|
||||
<td>
|
||||
<div class="seller-info">
|
||||
<span class="seller">{{ row.seller || '无货' }}</span>
|
||||
@@ -419,6 +453,7 @@ onMounted(async () => {
|
||||
.step-header { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
|
||||
.title { font-size: 13px; font-weight: 600; color: #303133; text-align: left; }
|
||||
.desc { font-size: 12px; color: #909399; margin-bottom: 8px; text-align: left; }
|
||||
.mini-hint { font-size: 12px; color: #909399; margin-top: 6px; }
|
||||
.links { display: flex; align-items: center; gap: 6px; margin-bottom: 8px; }
|
||||
.link { color: #409EFF; cursor: pointer; font-size: 12px; }
|
||||
.sep { color: #dcdfe6; }
|
||||
@@ -452,7 +487,7 @@ onMounted(async () => {
|
||||
.text:focus { border-color: #409EFF; }
|
||||
.text:disabled { background: #f5f7fa; color: #c0c4cc; }
|
||||
.action-buttons { display: flex; gap: 10px; flex-wrap: wrap; }
|
||||
.progress-section { margin: 12px 12px 6px 12px; }
|
||||
.progress-section { margin: 0px 12px 0px 12px; }
|
||||
.progress-box { padding: 4px 0; }
|
||||
.progress-container { display: flex; align-items: center; gap: 8px; }
|
||||
.progress-bar { flex: 1; height: 6px; background: #e3eeff; border-radius: 999px; overflow: hidden; }
|
||||
@@ -474,6 +509,7 @@ onMounted(async () => {
|
||||
.table th:nth-child(1), .table td:nth-child(1) { width: 33.33%; }
|
||||
.table th:nth-child(2), .table td:nth-child(2) { width: 33.33%; }
|
||||
.table th:nth-child(3), .table td:nth-child(3) { width: 33.33%; }
|
||||
.asin-out { color: #f56c6c; font-weight: 600; }
|
||||
.seller-info { display: flex; align-items: center; gap: 4px; }
|
||||
.seller { color: #303133; font-weight: 500; }
|
||||
.shipper { color: #909399; font-size: 12px; }
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { zebraApi, type BanmaAccount } from '../../api/zebra'
|
||||
import { ElMessageBox, ElMessage } from 'element-plus'
|
||||
|
||||
type PlatformKey = 'zebra' | 'shopee' | 'rakuten' | 'amazon'
|
||||
|
||||
@@ -36,10 +37,12 @@ function formatDate(a: any) {
|
||||
|
||||
async function onDelete(a: any) {
|
||||
const id = a?.id
|
||||
const ok = confirm(`确定删除账号 “${a?.name || a?.username || id}” 吗?`)
|
||||
if (!ok) return
|
||||
await zebraApi.removeAccount(id)
|
||||
await load()
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除账号 “${a?.name || a?.username || id}” 吗?`, '提示', { type: 'warning' })
|
||||
} catch { return }
|
||||
await zebraApi.removeAccount(id)
|
||||
ElMessage({ message: '删除成功', type: 'success' })
|
||||
await load()
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -117,13 +120,12 @@ export default defineComponent({ name: 'AccountManager' })
|
||||
.dot { width:6px; height:6px; border-radius:50%; justify-self: center; }
|
||||
.dot.on { background:#52c41a; }
|
||||
.dot.off { background:#ff4d4f; }
|
||||
.user-info { display: flex; align-items: center; gap: 8px; }
|
||||
.user-info { display: flex; align-items: center; gap: 8px; min-width: 0; }
|
||||
.avatar { width:22px; height:22px; border-radius:50%; object-fit: cover; }
|
||||
.name { font-weight:500; font-size: 13px; color:#303133; }
|
||||
.name { font-weight:500; font-size: 13px; color:#303133; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
||||
.date { color:#999; font-size:11px; text-align: center; }
|
||||
.footer { display:flex; justify-content:center; padding-top: 10px; }
|
||||
.btn { width: 180px; height: 32px; font-size: 13px; }
|
||||
.el-button--danger.is-link { font-size: 11px; padding: 0; height: auto; }
|
||||
</style>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user