task-272(验收反馈): 分组/软件版本/类目搜索表补齐分页组件(prev/next 中文由 locale 提供)
This commit is contained in:
@@ -16,6 +16,15 @@ const editingGroup = ref<ShopGroupItem | null>(null)
|
|||||||
const summary = computed(() => shopGroupSummary(rows.value))
|
const summary = computed(() => shopGroupSummary(rows.value))
|
||||||
const isSuperAdmin = computed(() => session.isSuperAdmin)
|
const isSuperAdmin = computed(() => session.isSuperAdmin)
|
||||||
const currentUserId = computed(() => session.user?.id ?? null)
|
const currentUserId = computed(() => session.user?.id ?? null)
|
||||||
|
// 客户端分页:全量拉取后按页切片展示。
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = 10
|
||||||
|
const total = computed(() => rows.value.length)
|
||||||
|
const pagedRows = computed(() => rows.value.slice((page.value - 1) * pageSize, page.value * pageSize))
|
||||||
|
|
||||||
|
function changePage(p: number) {
|
||||||
|
page.value = p
|
||||||
|
}
|
||||||
|
|
||||||
function canEditOf(group: ShopGroupItem): boolean {
|
function canEditOf(group: ShopGroupItem): boolean {
|
||||||
if (isSuperAdmin.value) return true
|
if (isSuperAdmin.value) return true
|
||||||
@@ -87,7 +96,7 @@ onMounted(loadGroups)
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
<el-table v-loading="loading" :data="rows" stripe>
|
<el-table v-loading="loading" :data="pagedRows" stripe>
|
||||||
<el-table-column type="index" label="序号" width="80" />
|
<el-table-column type="index" label="序号" width="80" />
|
||||||
<el-table-column prop="name" label="分组名称" min-width="180" />
|
<el-table-column prop="name" label="分组名称" min-width="180" />
|
||||||
<el-table-column prop="leaderUsername" label="组长" width="170" />
|
<el-table-column prop="leaderUsername" label="组长" width="170" />
|
||||||
@@ -113,6 +122,9 @@ onMounted(loadGroups)
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="table-footer">
|
||||||
|
<el-pagination background layout="prev, pager, next" :total="total" :page-size="pageSize" :current-page="page" @current-change="changePage" />
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
<GroupEditorDialog
|
<GroupEditorDialog
|
||||||
v-model="editorVisible"
|
v-model="editorVisible"
|
||||||
|
|||||||
@@ -133,7 +133,8 @@ async function runSearch(page: number): Promise<void> {
|
|||||||
searchLoading.value = true
|
searchLoading.value = true
|
||||||
try {
|
try {
|
||||||
const result = await searchProductCategories(keyword.value, page, PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE)
|
const result = await searchProductCategories(keyword.value, page, PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE)
|
||||||
searchRows.value = page === 1 ? result.items : [...searchRows.value, ...result.items]
|
// 分页换页模式:每页独立展示当前页结果(对齐列表统一分页交互)。
|
||||||
|
searchRows.value = result.items
|
||||||
searchTotal.value = result.total
|
searchTotal.value = result.total
|
||||||
searchPage.value = result.page
|
searchPage.value = result.page
|
||||||
searchHasMore.value = result.hasMore
|
searchHasMore.value = result.hasMore
|
||||||
@@ -158,8 +159,8 @@ function reset(): void {
|
|||||||
void loadTree()
|
void loadTree()
|
||||||
}
|
}
|
||||||
|
|
||||||
function loadMore(): void {
|
function searchChangePage(p: number): void {
|
||||||
void runSearch(searchPage.value + 1)
|
void runSearch(p)
|
||||||
}
|
}
|
||||||
|
|
||||||
function openCreate(parent: ProductCategoryNode | null) {
|
function openCreate(parent: ProductCategoryNode | null) {
|
||||||
@@ -350,8 +351,8 @@ onMounted(loadTree)
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
<div v-if="searchHasMore" class="load-more-row">
|
<div class="table-footer">
|
||||||
<el-button :loading="searchLoading" @click="loadMore">加载更多搜索结果</el-button>
|
<el-pagination background layout="prev, pager, next" :total="searchTotal" :page-size="PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE" :current-page="searchPage" @current-change="searchChangePage" />
|
||||||
</div>
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { formatDateTime } from '@/utils/datetime'
|
import { formatDateTime } from '@/utils/datetime'
|
||||||
/** 软件版本管理页:客户端版本列表 + 上传新版本(成功留驻弹窗含链接,对齐 admin panel-version)。 */
|
/** 软件版本管理页:客户端版本列表 + 上传新版本(成功留驻弹窗含链接,对齐 admin panel-version)。 */
|
||||||
import { onMounted, ref } from 'vue'
|
import { computed, onMounted, ref } from 'vue'
|
||||||
import { ElMessage } from 'element-plus'
|
import { ElMessage } from 'element-plus'
|
||||||
import { fetchSoftwareVersions, uploadSoftwareVersion } from './version-api.ts'
|
import { fetchSoftwareVersions, uploadSoftwareVersion } from './version-api.ts'
|
||||||
import type { SoftwareVersionItem } from './version-dto.ts'
|
import type { SoftwareVersionItem } from './version-dto.ts'
|
||||||
@@ -9,6 +9,15 @@ import { isNonEmptyVersion } from './version-dto.ts'
|
|||||||
|
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const items = ref<SoftwareVersionItem[]>([])
|
const items = ref<SoftwareVersionItem[]>([])
|
||||||
|
// 客户端分页:全量拉取后按页切片展示。
|
||||||
|
const page = ref(1)
|
||||||
|
const pageSize = 10
|
||||||
|
const total = computed(() => items.value.length)
|
||||||
|
const pagedItems = computed(() => items.value.slice((page.value - 1) * pageSize, page.value * pageSize))
|
||||||
|
|
||||||
|
function changePage(p: number) {
|
||||||
|
page.value = p
|
||||||
|
}
|
||||||
|
|
||||||
const uploadVisible = ref(false)
|
const uploadVisible = ref(false)
|
||||||
const uploading = ref(false)
|
const uploading = ref(false)
|
||||||
@@ -90,7 +99,7 @@ onMounted(load)
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-card shadow="never">
|
<el-card shadow="never">
|
||||||
<el-table v-loading="loading" :data="items" stripe border empty-text="暂无版本记录">
|
<el-table v-loading="loading" :data="pagedItems" stripe border empty-text="暂无版本记录">
|
||||||
<el-table-column prop="version" label="版本号" min-width="180" />
|
<el-table-column prop="version" label="版本号" min-width="180" />
|
||||||
<el-table-column label="下载链接" min-width="240">
|
<el-table-column label="下载链接" min-width="240">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
@@ -108,6 +117,9 @@ onMounted(load)
|
|||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
</el-table>
|
</el-table>
|
||||||
|
<div class="table-footer">
|
||||||
|
<el-pagination background layout="prev, pager, next" :total="total" :page-size="pageSize" :current-page="page" @current-change="changePage" />
|
||||||
|
</div>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
|
||||||
<el-dialog v-model="uploadVisible" title="上传新版本" width="520px">
|
<el-dialog v-model="uploadVisible" title="上传新版本" width="520px">
|
||||||
|
|||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import test from 'node:test'
|
||||||
|
import assert from 'node:assert/strict'
|
||||||
|
import { readSource } from './helpers.ts'
|
||||||
|
|
||||||
|
// 验收反馈:所有列表都要加分页组件(上一页/下一页,中文由全局 locale 提供)。
|
||||||
|
// 本轮补齐缺分页的列表:分组管理、软件版本、商品类目搜索表。
|
||||||
|
// 菜单管理是树结构(非列表),商品类目树用懒加载+加载更多节点,均豁免。
|
||||||
|
|
||||||
|
test('align_pagination_normal_primary_path', () => {
|
||||||
|
for (const page of [
|
||||||
|
'src/pages/account/GroupsPage.vue',
|
||||||
|
'src/pages/records/RecordsSoftwareVersionPage.vue',
|
||||||
|
'src/pages/asin/ProductCategoryPage.vue',
|
||||||
|
]) {
|
||||||
|
const src = readSource(page)
|
||||||
|
assert.match(src, /el-pagination/, `${page} 应包含分页组件`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('align_pagination_normal_variant_input', () => {
|
||||||
|
// 分页组件需含上一页/下一页(prev/next),中文文案由全局 zh-cn locale 渲染。
|
||||||
|
for (const page of [
|
||||||
|
'src/pages/account/GroupsPage.vue',
|
||||||
|
'src/pages/records/RecordsSoftwareVersionPage.vue',
|
||||||
|
'src/pages/asin/ProductCategoryPage.vue',
|
||||||
|
]) {
|
||||||
|
const src = readSource(page)
|
||||||
|
assert.match(src, /el-pagination[^>]*layout="[^"]*prev[^"]*next/, `${page} 分页 layout 需含 prev/next`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('align_pagination_boundary_empty_input', () => {
|
||||||
|
// 分组/软件版本为全量拉取,走客户端切片分页(computed 按 page/pageSize 切)。
|
||||||
|
for (const page of ['src/pages/account/GroupsPage.vue', 'src/pages/records/RecordsSoftwareVersionPage.vue']) {
|
||||||
|
const src = readSource(page)
|
||||||
|
assert.match(src, /pagedRows|pageRows|pageItems|pagedItems/, `${page} 应有客户端分页切片`)
|
||||||
|
assert.match(src, /:data="paged/, `${page} 表格应绑定分页后的数据`)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
test('align_pagination_boundary_single_item', () => {
|
||||||
|
// 类目搜索表从“加载更多”累加改为 el-pagination 换页模式:不再有加载更多按钮与累加拼接。
|
||||||
|
const src = readSource('src/pages/asin/ProductCategoryPage.vue')
|
||||||
|
assert.ok(!src.includes('加载更多搜索结果'), '搜索表不应再有“加载更多搜索结果”按钮')
|
||||||
|
assert.ok(!src.includes('[...searchRows.value, ...result.items]'), '搜索表不应再累加拼接结果')
|
||||||
|
})
|
||||||
|
|
||||||
|
test('align_pagination_repeated_operation_is_idempotent', () => {
|
||||||
|
// 已有分页的页面(12 页)保持原样,不被破坏。
|
||||||
|
for (const page of [
|
||||||
|
'src/pages/account/UsersPage.vue',
|
||||||
|
'src/pages/asin/AsinInvalidPage.vue',
|
||||||
|
'src/pages/shop/ShopManagePage.vue',
|
||||||
|
]) {
|
||||||
|
const src = readSource(page)
|
||||||
|
assert.match(src, /el-pagination/, `${page} 既有分页保留`)
|
||||||
|
}
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user