feat(需求): ①全站分页pageSize统一默认10+10/20/50/100选项(9页默认值15/20→10+6个兜底常量统一+撞款台账升级OldPagination+商品类目树顶层标准分页) ②修复6页OldPagination缺import分页器不渲染(不符合ASIN/查询ASIN/最低价ASIN/生成记录/店铺密钥/店铺管理)+查询ASIN操作列按钮间距+最低价范围筛选number类型报错 ③e2e断言对齐现状(顶栏h1/移除收起按钮后规格同步)+国家显示中文断言固化

This commit is contained in:
2026-09-07 02:36:35 +08:00
parent 056abcd473
commit 678605e9fb
44 changed files with 590 additions and 175 deletions
@@ -31,12 +31,12 @@ const scope = ref<Scope>('global')
const selectedGroup = ref('')
const shopGroups = ref<ShopGroups>({ members: {}, shopGroupNames: {} })
const LEDGER_PAGE_SIZE = 20
/** 台账每页条数(全站统一:10/20/50/100 可选,默认 10)。 */
const ledgerPageSize = ref(10)
const ledgerItems = ref<LedgerRow[]>([])
const ledgerTotal = ref(0)
const ledgerPage = ref(1)
const ledgerLoading = ref(false)
const jumpInput = ref('')
const sortState = reactive<{ key: LedgerSortKey | ''; dir: 'asc' | 'desc' }>({ key: 'earliest', dir: 'asc' })
import OldPagination from '@/components/OldPagination.vue'
@@ -143,7 +143,7 @@ const matrixRows = computed(() =>
dupRows.value.slice((matrixPage.value - 1) * matrixPageSize.value, matrixPage.value * matrixPageSize.value),
)
const dupCardPage = ref(1)
const dupCardPageSize = ref(6)
const dupCardPageSize = ref(10)
const dupCardRows = computed(() =>
dupRows.value.slice((dupCardPage.value - 1) * dupCardPageSize.value, dupCardPage.value * dupCardPageSize.value),
)
@@ -152,10 +152,17 @@ const drawerPageSize = ref(10)
const drawerRows = computed(() =>
drawerAggRows.value.slice((drawerPage.value - 1) * drawerPageSize.value, drawerPage.value * drawerPageSize.value),
)
watch([dupRows, drawerOccurrences], () => {
/** 分组说明抽屉:分组统计表客户端分页(10/20/50/100 可选)。 */
const groupStatsPage = ref(1)
const groupStatsPageSize = ref(10)
const pagedGroupStats = computed(() =>
groupStats.value.slice((groupStatsPage.value - 1) * groupStatsPageSize.value, groupStatsPage.value * groupStatsPageSize.value),
)
watch([dupRows, drawerOccurrences, groupStats], () => {
matrixPage.value = Math.min(matrixPage.value, Math.max(1, Math.ceil(dupRows.value.length / matrixPageSize.value)))
dupCardPage.value = Math.min(dupCardPage.value, Math.max(1, Math.ceil(dupRows.value.length / dupCardPageSize.value)))
drawerPage.value = 1
groupStatsPage.value = Math.min(groupStatsPage.value, Math.max(1, Math.ceil(groupStats.value.length / groupStatsPageSize.value)))
})
const matrixHint = computed(() =>
@@ -220,7 +227,7 @@ async function loadLedger(page: number, resetSort = false) {
sortState.key = 'earliest'
sortState.dir = 'asc'
}
const result = await fetchDuplicateLedger({ view: '', ...activeFilterParams() }, page, LEDGER_PAGE_SIZE, sortState.key, sortState.dir)
const result = await fetchDuplicateLedger({ view: '', ...activeFilterParams() }, page, ledgerPageSize.value, sortState.key, sortState.dir)
ledgerItems.value = result.items
ledgerTotal.value = result.total
ledgerPage.value = result.page
@@ -349,17 +356,14 @@ function onSort(key: LedgerSortKey) {
loadLedger(1)
}
const totalPages = computed(() => Math.max(1, Math.ceil(ledgerTotal.value / LEDGER_PAGE_SIZE)))
const totalPages = computed(() => Math.max(1, Math.ceil(ledgerTotal.value / ledgerPageSize.value)))
function goPage(page: number) {
if (page < 1 || page > totalPages.value) return
loadLedger(page)
}
function jumpPage() {
const value = parseInt(jumpInput.value, 10)
if (!Number.isNaN(value) && value >= 1 && value <= totalPages.value) {
goPage(value)
jumpInput.value = ''
}
function changeLedgerSize(size: number) {
ledgerPageSize.value = size
loadLedger(1)
}
async function exportXlsx() {
@@ -594,6 +598,7 @@ onMounted(() => {
</div>
</div>
<div v-else class="dup-empty">当前范围下没有撞款ASIN</div>
<OldPagination v-if="dupRows.length > dupCardPageSize" :total="dupRows.length" :page="dupCardPage" :page-size="dupCardPageSize" @change="dupCardPage = $event" @size-change="(s: number) => { dupCardPageSize = s; dupCardPage = 1 }" />
<div class="dup-total"> <b>{{ dupRows.length }}</b> {{ scope === 'group' && selectedGroup ? `分组「${selectedGroup}」组内撞款ASIN` : '全店撞款ASIN' }}</div>
</template>
@@ -631,12 +636,7 @@ onMounted(() => {
调整筛选条件或触发重新分析后再查看
</div>
</div>
<div v-if="ledgerTotal" class="pager">
<span class="total"> <b>{{ ledgerTotal.toLocaleString() }}</b> · {{ ledgerPage }}/{{ totalPages }} </span>
<button class="pg-btn" type="button" :disabled="ledgerPage <= 1" @click="goPage(ledgerPage - 1)">上一页</button>
<button class="pg-btn" type="button" :disabled="ledgerPage >= totalPages" @click="goPage(ledgerPage + 1)">下一页</button>
<span class="jump">跳至 <input type="number" min="1" :max="totalPages" v-model="jumpInput" @keyup.enter="jumpPage" /> 页 <button class="btn btn-ghost btn-sm" type="button" @click="jumpPage">跳转</button></span>
</div>
<OldPagination v-if="ledgerTotal" :total="ledgerTotal" :page="ledgerPage" :page-size="ledgerPageSize" @change="goPage" @size-change="changeLedgerSize" />
</template>
</template>
@@ -679,6 +679,7 @@ onMounted(() => {
</tbody>
</table>
<div v-else class="dup-empty">暂无上架时间明细</div>
<OldPagination v-if="drawerAggRows.length > drawerPageSize" :total="drawerAggRows.length" :page="drawerPage" :page-size="drawerPageSize" @change="drawerPage = $event" @size-change="(s: number) => { drawerPageSize = s; drawerPage = 1 }" />
</div>
</aside>
@@ -696,7 +697,7 @@ onMounted(() => {
<table class="d-table">
<thead><tr><th>分组</th><th>店铺数</th><th>成员店铺</th><th>组内撞款</th></tr></thead>
<tbody>
<tr v-for="group in groupStats" :key="group.name">
<tr v-for="group in pagedGroupStats" :key="group.name">
<td class="cell-strong">{{ group.name }}</td>
<td class="num-cell">{{ group.shopCount }}</td>
<td><template v-for="shop in (shopGroups.members[group.name] || [])" :key="shop"><span class="badge store">{{ shop }}</span></template></td>
@@ -705,6 +706,7 @@ onMounted(() => {
<tr v-if="!groupStats.length"><td colspan="4" class="empty-cell">暂无分组</td></tr>
</tbody>
</table>
<OldPagination v-if="groupStats.length > groupStatsPageSize" :total="groupStats.length" :page="groupStatsPage" :page-size="groupStatsPageSize" @change="groupStatsPage = $event" @size-change="(s: number) => { groupStatsPageSize = s; groupStatsPage = 1 }" />
</div>
</aside>
@@ -929,20 +931,7 @@ onMounted(() => {
.dup-ref .dt-cell { font-size: 11.5px; color: var(--text2); }
.dup-ref .site-chip { border-radius: 5px; padding: 1px 7px; font-size: 11.5px; margin-right: 4px; margin-bottom: 2px; font-weight: 600; display: inline-block; white-space: nowrap; }
/* ---------- 分页 ---------- */
.dup-ref .pager {
display: flex; align-items: center; justify-content: flex-end; gap: 12px; margin-top: 16px;
background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 16px;
box-shadow: var(--shadow);
}
.dup-ref .pager .total { color: var(--text2); font-size: 12.5px; margin-right: auto; }
.dup-ref .pager .total b { color: var(--text); font-size: 13px; }
.dup-ref .pager .pg-btn { height: 28px; padding: 0 12px; border: 1px solid var(--border-strong); border-radius: 6px; font-size: 12px; color: var(--text2); background: #fff; }
.dup-ref .pager .pg-btn:hover:not(:disabled) { border-color: var(--primary); color: var(--primary); }
.dup-ref .pager .pg-btn:disabled { opacity: 0.4; cursor: not-allowed; }
.dup-ref .pager .jump { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text2); }
.dup-ref .pager .jump input { width: 52px; height: 28px; border: 1px solid var(--border-strong); border-radius: 6px; text-align: center; }
.dup-ref .pager .jump input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(46, 91, 230, 0.12); }
/* ---------- 分页:台账/分布/矩阵/详情/抽屉统一共享 OldPagination(自带条数选择),无自绘 pager ---------- */
/* ---------- 空态 / 加载 ---------- */
.dup-ref .empty-state { padding: 48px 20px; text-align: center; color: var(--text3); font-size: 13px; }