align(店铺数据记录): 改回每条任务一张卡片(店铺名头+任务号/状态+分组/最新/国家/文件行+下载文件/删除)、全选口径 file_ready&&有resultId 不再附加终态、空态文案对齐(对齐 admin.js renderShopDataRecordTable/syncShopDataSelectionUi)
This commit is contained in:
@@ -1,11 +1,13 @@
|
||||
<script setup lang="ts">
|
||||
import { formatDateTime } from '@/utils/datetime'
|
||||
/** 店铺数据任务/记录页:每店铺卡片网格(分组/任务状态/站点/文件 下载删除)+ 筛选 + 批量下载 + 数据范围授权。 */
|
||||
/** 店铺数据任务/记录页:每条任务一张卡片(店铺名/任务号/状态/分组/最新/国家/文件 + 下载文件/删除)
|
||||
* + 筛选 + 批量下载 + 数据范围授权(对齐 admin.js renderShopDataRecordTable)。 */
|
||||
import { computed, onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { asinCountryLabel } from '../asin/asin-country.ts'
|
||||
import { createShopDataFilter, type ShopDataFilter } from './shop-data-filter.ts'
|
||||
import type { ShopDataResultRow, ShopDataTaskGroup } from './shop-data-model.ts'
|
||||
import { isUsableShopDataResult } from './shop-data-model.ts'
|
||||
import {
|
||||
deleteShopDataResultHistory,
|
||||
fetchShopDataResultDownload,
|
||||
@@ -94,8 +96,9 @@ function resultFileCount(): number {
|
||||
return groups.value.reduce((sum, group) => sum + group.results.length, 0)
|
||||
}
|
||||
|
||||
/** 可勾选行(对齐 admin.js syncShopDataSelectionUi:file_ready && resultId>0,不附加终态约束)。 */
|
||||
function selectableRows(): ShopDataResultRow[] {
|
||||
return allResultRows.value.filter((row) => row.fileReady && isTerminalStatus(row.status))
|
||||
return allResultRows.value.filter((row) => isUsableShopDataResult(row))
|
||||
}
|
||||
|
||||
function toggleRow(row: ShopDataResultRow) {
|
||||
@@ -150,9 +153,9 @@ async function downloadBatch() {
|
||||
}
|
||||
}
|
||||
|
||||
async function removeRow(group: ShopDataTaskGroup, row: ShopDataResultRow) {
|
||||
async function removeRowByRow(row: ShopDataResultRow) {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确认删除店铺“${group.shopName}”的任务 ${row.taskNo || row.resultId} 及结果文件吗?`, '删除前确认', {
|
||||
await ElMessageBox.confirm(`确认删除店铺“${row.shopName}”的任务 ${row.taskNo || row.resultId} 及结果文件?`, '删除前确认', {
|
||||
confirmButtonText: '确认删除',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
@@ -275,63 +278,65 @@ onMounted(load)
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div v-if="groups.length" class="shop-card-grid">
|
||||
<section v-for="group in groups" :key="group.shopId || group.shopName" class="shop-card">
|
||||
<div v-if="allResultRows.length" class="shop-card-grid">
|
||||
<article v-for="row in allResultRows" :key="row.resultId || `${row.taskNo}-${row.filename}`" class="shop-card">
|
||||
<header class="shop-card-head">
|
||||
<span class="shop-name">{{ group.shopName }}</span>
|
||||
<el-tag v-if="group.groupName" size="small" effect="plain">{{ group.groupName }}</el-tag>
|
||||
<span v-if="group.latestCreatedAt" class="shop-latest">{{ formatDateTime(group.latestCreatedAt) }}</span>
|
||||
<span class="shop-name" :title="row.shopName || '-'">{{ row.shopName || '-' }}</span>
|
||||
</header>
|
||||
<div class="file-list">
|
||||
<div v-for="row in group.results" :key="row.resultId" class="file-row">
|
||||
<el-checkbox
|
||||
:model-value="rowSelected(row)"
|
||||
:disabled="!(row.fileReady && isTerminalStatus(row.status))"
|
||||
@change="toggleRow(row)"
|
||||
/>
|
||||
<div class="file-main">
|
||||
<div class="file-top">
|
||||
<span class="file-task">任务 {{ row.taskNo || row.resultId }}</span>
|
||||
<el-tag :type="statusType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
||||
<span v-for="code in row.countryCodes" :key="code" class="country-chip">{{ asinCountryLabel(code) }}</span>
|
||||
</div>
|
||||
<div class="file-name" :title="row.filename">{{ row.filename || '未命名文件' }}</div>
|
||||
<div class="file-meta">
|
||||
<span>{{ row.username || '—' }}</span>
|
||||
<span v-if="row.rowCount != null">· {{ row.rowCount }} 行</span>
|
||||
<span v-if="row.fileSize != null">· {{ row.fileSize }} 字节</span>
|
||||
<span>· {{ formatDateTime(row.createdAt) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-actions">
|
||||
<el-button
|
||||
text
|
||||
type="success"
|
||||
size="small"
|
||||
:loading="busyKey === row.resultId"
|
||||
:disabled="!row.fileReady"
|
||||
@click="downloadRow(row)"
|
||||
>
|
||||
下载
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="isTerminalStatus(row.status)"
|
||||
text
|
||||
type="danger"
|
||||
size="small"
|
||||
:loading="deletingKey === row.resultId"
|
||||
@click="removeRow(group, row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="!group.results.length" class="file-empty">暂无结果文件</div>
|
||||
<div class="shop-card-select-row">
|
||||
<el-checkbox
|
||||
:model-value="rowSelected(row)"
|
||||
:disabled="!isUsableShopDataResult(row)"
|
||||
@change="toggleRow(row)"
|
||||
/>
|
||||
<span class="shop-card-task-no">任务 {{ row.taskNo || row.resultId }}</span>
|
||||
<el-tag :type="statusType(row.status)" size="small">{{ statusLabel(row.status) }}</el-tag>
|
||||
</div>
|
||||
</section>
|
||||
<div class="shop-card-row">
|
||||
<span class="shop-card-label">分组</span>
|
||||
<span class="shop-card-value">{{ row.groupName || '-' }}</span>
|
||||
</div>
|
||||
<div class="shop-card-row">
|
||||
<span class="shop-card-label">最新</span>
|
||||
<span class="shop-card-value">{{ formatDateTime(row.updatedAt || row.createdAt) || '-' }}</span>
|
||||
</div>
|
||||
<div class="shop-card-row">
|
||||
<span class="shop-card-label">国家</span>
|
||||
<span class="shop-card-value">
|
||||
<span v-for="code in row.countryCodes" :key="code" class="country-chip">{{ asinCountryLabel(code) }}</span>
|
||||
<span v-if="!row.countryCodes.length" class="dim">-</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="shop-card-row">
|
||||
<span class="shop-card-label">文件</span>
|
||||
<span class="shop-card-value shop-card-file" :title="row.filename">{{ row.filename || '-' }}</span>
|
||||
</div>
|
||||
<div class="shop-card-actions">
|
||||
<el-button
|
||||
text
|
||||
type="success"
|
||||
size="small"
|
||||
:loading="busyKey === row.resultId"
|
||||
:disabled="!isUsableShopDataResult(row)"
|
||||
@click="downloadRow(row)"
|
||||
>
|
||||
下载文件
|
||||
</el-button>
|
||||
<el-button
|
||||
v-if="isTerminalStatus(row.status) && !!row.resultId"
|
||||
text
|
||||
type="danger"
|
||||
size="small"
|
||||
:loading="deletingKey === row.resultId"
|
||||
@click="removeRowByRow(row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
|
||||
<el-empty v-else-if="!loading" description="暂无店铺数据任务" />
|
||||
<el-empty v-else-if="!loading" description="暂无符合条件的店铺数据任务" />
|
||||
|
||||
<el-dialog v-model="permissionVisible" title="店铺数据任务数据范围授权" width="620px">
|
||||
<p class="dim">勾选允许查看/操作店铺数据任务的用户(仅数据范围)。当前已授权 {{ grantedCount }} 人。</p>
|
||||
@@ -365,17 +370,13 @@ onMounted(load)
|
||||
.shop-card { border: 1px solid var(--el-border-color); border-radius: 12px; background: #fff; box-shadow: 0 1px 2px rgba(39, 67, 94, 0.04), 0 14px 30px -24px rgba(39, 67, 94, 0.28); overflow: hidden; }
|
||||
.shop-card-head { display: flex; align-items: center; gap: 10px; padding: 12px 16px; border-bottom: 1px solid var(--el-border-color-lighter); }
|
||||
.shop-name { font-weight: 600; color: var(--el-text-color-primary); }
|
||||
.shop-latest { margin-left: auto; color: var(--el-text-color-secondary); font-size: 12.5px; }
|
||||
.file-list { padding: 4px 16px; }
|
||||
.file-row { display: flex; align-items: flex-start; gap: 12px; padding: 12px 0; border-bottom: 1px dashed var(--el-border-color-lighter); }
|
||||
.file-row:last-child { border-bottom: 0; }
|
||||
.file-main { flex: 1; min-width: 0; }
|
||||
.file-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
||||
.file-task { font-weight: 600; font-size: 13px; color: var(--el-text-color-primary); }
|
||||
.country-chip { padding: 1px 8px; border-radius: 999px; background: var(--el-fill-color-light); color: var(--el-text-color-regular); font-size: 12px; }
|
||||
.file-name { margin-top: 6px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--el-text-color-regular); font-size: 13px; }
|
||||
.file-meta { margin-top: 4px; color: var(--el-text-color-secondary); font-size: 12.5px; }
|
||||
.file-actions { display: flex; gap: 6px; }
|
||||
.file-empty { padding: 14px 0; color: var(--el-text-color-secondary); font-size: 13px; }
|
||||
.shop-card-select-row { display: flex; align-items: center; gap: 10px; padding: 10px 16px 0; }
|
||||
.shop-card-task-no { font-weight: 600; font-size: 13px; color: var(--el-text-color-primary); }
|
||||
.shop-card-row { display: flex; align-items: flex-start; gap: 10px; padding: 6px 16px; font-size: 13px; }
|
||||
.shop-card-label { flex: none; width: 52px; color: var(--el-text-color-secondary); }
|
||||
.shop-card-value { color: var(--el-text-color-regular); min-width: 0; }
|
||||
.shop-card-file { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
|
||||
.shop-card-actions { display: flex; gap: 6px; padding: 4px 16px 12px; }
|
||||
.country-chip { padding: 1px 8px; margin-right: 4px; border-radius: 999px; background: var(--el-fill-color-light); color: var(--el-text-color-regular); font-size: 12px; }
|
||||
.dim { color: var(--el-text-color-secondary); font-size: 12px; }
|
||||
</style>
|
||||
|
||||
@@ -135,3 +135,8 @@ export function parseShopDataTaskPage(payload: unknown): ShopDataTaskPageResult
|
||||
if (typeof rawSize === 'number' && rawSize >= 1) out.pageSize = Math.floor(rawSize)
|
||||
return out
|
||||
}
|
||||
|
||||
/** 结果是否可下载/勾选(对齐 admin.js:file_ready && resultId>0;Vue 中 resultId 为品牌串,非空即有效)。 */
|
||||
export function isUsableShopDataResult(row: Pick<ShopDataResultRow, 'fileReady' | 'resultId'>): boolean {
|
||||
return row.fileReady === true && (row.resultId || '').length > 0
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user