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:
@@ -11,13 +11,14 @@ import { invalidAsinDeleteText, invalidAsinLockedGroupId, recordSourceLabel } fr
|
||||
import { createEmptyInvalidAsinForm, invalidAsinFormFromItem, validateInvalidAsinForm } from '../invalidasin/invalid-asin-form-model.ts'
|
||||
import { fetchShopManageGroups } from '../shop/shop-manage-api.ts'
|
||||
import type { ShopGroupOption } from '../shop/shop-dto.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const rows = ref<InvalidAsinItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const groups = ref<ShopGroupOption[]>([])
|
||||
const jumpPage = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
|
||||
|
||||
@@ -29,7 +29,7 @@ const loading = ref(false)
|
||||
const rows = ref<DedupeTotalItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const filter = reactive(createDedupeTotalFilterState())
|
||||
const groups = ref<DedupeGroupOption[]>([])
|
||||
const jumpPage = ref('')
|
||||
|
||||
@@ -58,16 +58,21 @@ const searching = computed(() => (keyword.value || '').trim().length > 0)
|
||||
const dialogTitle = computed(() => (editingId.value == null ? '新增商品类目' : '编辑商品类目'))
|
||||
const searchTotalPages = computed(() => Math.max(1, Math.ceil(searchTotal.value / searchPageSize.value)))
|
||||
|
||||
/** 拉取根级第一页(对齐 admin.js loadProductCategoryChildren(null) 懒加载根)。 */
|
||||
/** 顶层翻页(服务端分页,10/20/50/100 可选;子级保留懒加载+加载更多对齐旧版)。 */
|
||||
const rootPage = ref(1)
|
||||
const rootPageSize = ref(PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE)
|
||||
const rootTotal = ref(0)
|
||||
|
||||
/** 拉取根级当前页(对齐 admin.js loadProductCategoryChildren(null) 懒加载根)。 */
|
||||
async function loadTree(): Promise<void> {
|
||||
loading.value = true
|
||||
try {
|
||||
const result = await fetchProductCategoryChildren(null, 1, PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE)
|
||||
const result = await fetchProductCategoryChildren(null, rootPage.value, rootPageSize.value)
|
||||
const state = { page: result.page, total: result.total, hasMore: result.hasMore, loading: false }
|
||||
childrenState.set(ROOT_KEY, state)
|
||||
tree.value = result.hasMore
|
||||
? [...result.items, makeCategoryLoadMoreNode(null, result.items.length, result.total)]
|
||||
: result.items
|
||||
// 顶层当前页直接作为树根(不含合成「加载更多」节点,翻页由 OldPagination 承担)。
|
||||
tree.value = result.items
|
||||
rootTotal.value = result.total
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '类目加载失败')
|
||||
} finally {
|
||||
@@ -75,6 +80,17 @@ async function loadTree(): Promise<void> {
|
||||
}
|
||||
}
|
||||
|
||||
function changeRootPage(p: number) {
|
||||
rootPage.value = p
|
||||
void loadTree()
|
||||
}
|
||||
|
||||
function changeRootSize(size: number) {
|
||||
rootPageSize.value = size
|
||||
rootPage.value = 1
|
||||
void loadTree()
|
||||
}
|
||||
|
||||
/** 树形表格(对齐旧版 panel-product-categories):客户端展开状态 + 按展开扁平化行集。 */
|
||||
interface TreeRow {
|
||||
node: ProductCategoryNode
|
||||
@@ -420,6 +436,7 @@ onMounted(loadTree)
|
||||
</div>
|
||||
|
||||
<OldPagination v-if="searching" :total="searchTotal" :page="searchPage" :page-size="searchPageSize" @change="runSearch" @size-change="changeSearchSize" />
|
||||
<OldPagination v-else-if="rootTotal > 0" :total="rootTotal" :page="rootPage" :page-size="rootPageSize" @change="changeRootPage" @size-change="changeRootSize" />
|
||||
</section>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="560px">
|
||||
|
||||
@@ -14,12 +14,13 @@ import { fetchQueryAsinDeleteImportProgress, startQueryAsinDeleteImport } from '
|
||||
import { isAllowedExcelImportFile } from './import-progress-model.ts'
|
||||
import { fetchShopManageGroups } from '../shop/shop-manage-api.ts'
|
||||
import type { ShopGroupOption } from '../shop/shop-dto.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref<QueryAsinItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const groups = ref<ShopGroupOption[]>([])
|
||||
const filter = reactive<QueryAsinFilterState>(createQueryAsinFilterState())
|
||||
const jumpPage = ref('')
|
||||
@@ -690,7 +691,11 @@ h3 {
|
||||
color: #8b9aaa;
|
||||
}
|
||||
.asin-col-actions {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
background: #ffffff;
|
||||
box-shadow: -9px 0 12px -10px rgba(31, 48, 66, 0.45);
|
||||
}
|
||||
|
||||
@@ -14,12 +14,13 @@ import { isAllowedExcelImportFile } from './import-progress-model.ts'
|
||||
import { fetchShopNamesByGroup } from './query-asin-api.ts'
|
||||
import { fetchShopManageGroups } from '../shop/shop-manage-api.ts'
|
||||
import type { ShopGroupOption } from '../shop/shop-dto.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref<SkipPriceItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const groups = ref<ShopGroupOption[]>([])
|
||||
const filter = reactive<SkipPriceFilterState>(createSkipPriceFilterState())
|
||||
const jumpPage = ref('')
|
||||
@@ -748,7 +749,11 @@ h3 {
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.asin-col-actions {
|
||||
text-align: right;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: 8px;
|
||||
white-space: nowrap;
|
||||
background: #ffffff;
|
||||
box-shadow: -9px 0 12px -10px rgba(31, 48, 66, 0.45);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** ASIN 数据中心共享分页/筛选类型(任务 61):与 Java 各列表接口(snake)对齐,纯逻辑。 */
|
||||
|
||||
export const ASIN_PAGE_DEFAULT_SIZE = 15
|
||||
export const ASIN_PAGE_DEFAULT_SIZE = 10
|
||||
export const ASIN_PAGE_MIN_PAGE = 1
|
||||
export const ASIN_PAGE_MAX_SIZE = 200
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** 商品类目树/搜索模型(任务 78):解析 ProductCategoryListVo(嵌套 tree/path/level/childCount),纯逻辑。 */
|
||||
import { unwrap } from '../../api/envelope.ts'
|
||||
|
||||
export const PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE = 20
|
||||
export const PRODUCT_CATEGORY_DEFAULT_PAGE_SIZE = 10
|
||||
|
||||
export interface ProductCategoryNode {
|
||||
id: number
|
||||
|
||||
@@ -11,9 +11,9 @@ export interface SkipPriceFilterState {
|
||||
asin: string
|
||||
/** '' 表示不限;否则为 DE/UK/FR/IT/ES 之一。 */
|
||||
country: string
|
||||
/** 最低价区间输入原文;空串不限。 */
|
||||
minimumPriceFrom: string
|
||||
minimumPriceTo: string
|
||||
/** 最低价区间输入原文;空串不限(原生 number input v-model 会转 number,需兼容)。 */
|
||||
minimumPriceFrom: string | number
|
||||
minimumPriceTo: string | number
|
||||
}
|
||||
|
||||
export interface SkipPriceListParams {
|
||||
@@ -42,9 +42,9 @@ export function createSkipPriceFilterState(): SkipPriceFilterState {
|
||||
return { groupId: null, shopName: '', asin: '', country: '', minimumPriceFrom: '', minimumPriceTo: '' }
|
||||
}
|
||||
|
||||
/** 非负可解析价格 → number;空/非法/负数返回 null。 */
|
||||
export function normalizePriceInput(value: string): number | null {
|
||||
const trimmed = (value || '').trim()
|
||||
/** 非负可解析价格 → number;空/非法/负数返回 null。入参兼容 number(原生 number input 的 v-model 会转 number)。 */
|
||||
export function normalizePriceInput(value: string | number): number | null {
|
||||
const trimmed = String(value ?? '').trim()
|
||||
if (!trimmed) return null
|
||||
if (!/^\d+(\.\d+)?$/.test(trimmed)) return null
|
||||
const num = Number(trimmed)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
/** 数字人版本管理页 · 像素复刻旧版 admin.html panel-digital-human-version(自绘:面板头+上传、三态状态色、最新★、操作矩阵、数字页码分页)。
|
||||
* script 逻辑沿用现有 Vue 实现(确认文案/上传进度/下载取链接);确认走原生 confirm 对齐旧版。 */
|
||||
import { formatDateTime } from '@/utils/datetime'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import {
|
||||
deleteDigitalHumanVersion,
|
||||
@@ -28,7 +28,7 @@ const loading = ref(false)
|
||||
const items = ref<DigitalHumanVersion[]>([])
|
||||
|
||||
/** 分页(对齐 admin.js:6311 pageSize=20 + 数字页码 + 跳转;Java 列表接口不分页,前端分页呈现)。 */
|
||||
const pageSize = ref(20)
|
||||
const pageSize = ref(10)
|
||||
const page = ref(1)
|
||||
/** 版本号搜索(客户端过滤)。 */
|
||||
const versionKeyword = ref('')
|
||||
@@ -56,9 +56,7 @@ async function load() {
|
||||
loading.value = true
|
||||
try {
|
||||
items.value = (await fetchDigitalHumanVersions()).items
|
||||
// 数据收缩后页码越界回钳。
|
||||
const last = Math.max(Math.ceil(items.value.length / pageSize.value), 1)
|
||||
if (page.value > last) page.value = last
|
||||
// 页码越界回钳见下方 watch(items)。
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '版本列表加载失败')
|
||||
} finally {
|
||||
@@ -76,6 +74,12 @@ function changeSize(size: number) {
|
||||
page.value = 1
|
||||
}
|
||||
|
||||
// 版本号搜索导致数据收缩时回钳页码,避免停在空页。
|
||||
watch(items, () => {
|
||||
const last = Math.max(Math.ceil(filteredItems.value.length / pageSize.value), 1)
|
||||
if (page.value > last) page.value = last
|
||||
})
|
||||
|
||||
function goJump(): void {
|
||||
const n = Number.parseInt(jumpPage.value, 10)
|
||||
if (Number.isNaN(n)) {
|
||||
|
||||
@@ -9,13 +9,14 @@ import type { HistoryRecordItem } from './history-dto.ts'
|
||||
import { historyEmptyText, historyErrorText } from './history-feedback.ts'
|
||||
import { historyPanelTypeLabel } from './history-type.ts'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const userLoading = ref(false)
|
||||
const rows = ref<HistoryRecordItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const userOptions = ref<HistoryUserOption[]>([])
|
||||
const errorText = ref('')
|
||||
const filter = reactive({ userId: '', timeStart: '', timeEnd: '' })
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import { formatDateTime } from '@/utils/datetime'
|
||||
/** 软件版本管理页 · 像素复刻旧版 admin.html panel-version(自绘:面板头+上传新版本、版本列表、全量无分页同旧版)。
|
||||
* script 逻辑沿用现有 Vue 实现(上传弹窗成功留驻含链接)。 */
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { computed, onMounted, ref, watch } from 'vue'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { fetchSoftwareVersions, uploadSoftwareVersion } from './version-api.ts'
|
||||
@@ -24,6 +24,10 @@ const pageSize = ref(10)
|
||||
const pagedVersions = computed(() => filteredItems.value.slice((page.value - 1) * pageSize.value, page.value * pageSize.value))
|
||||
function changeVersionPage(p: number) { page.value = p }
|
||||
function changeVersionSize(size: number) { pageSize.value = size; page.value = 1 }
|
||||
// 版本号搜索导致数据收缩时回钳页码,避免停在空页。
|
||||
watch(filteredItems, () => {
|
||||
page.value = Math.min(page.value, Math.max(1, Math.ceil(filteredItems.value.length / pageSize.value)))
|
||||
})
|
||||
|
||||
const uploadVisible = ref(false)
|
||||
const uploading = ref(false)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** 历史生成记录 DTO(任务 121):列表筛选/分页归一与类型边界;与 Java ImageHistoryController
|
||||
* snake 契约对齐;历史页与后台版本/公开版本接口分离。纯逻辑。 */
|
||||
|
||||
export const HISTORY_DEFAULT_PAGE_SIZE = 15
|
||||
export const HISTORY_DEFAULT_PAGE_SIZE = 10
|
||||
export const HISTORY_MIN_PAGE = 1
|
||||
export const HISTORY_MAX_PAGE_SIZE = 200
|
||||
|
||||
|
||||
@@ -8,12 +8,13 @@ import { fetchShopKeyList, submitCreateShopKey, updateShopKey, deleteShopKey } f
|
||||
import { emptyShopKeyForm, normalizeZiniaoToken } from './shop-key-form-model.ts'
|
||||
import type { ShopKeyItem } from './shop-dto.ts'
|
||||
import { whitelistStatusMeta } from './shop-key-model.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const rows = ref<ShopKeyItem[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const jumpPage = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
|
||||
/** 筛选:备注名/紫鸟账号(用户要求店铺密钥列表补筛选)。 */
|
||||
|
||||
@@ -15,13 +15,14 @@ import {
|
||||
} from './shop-manage-api.ts'
|
||||
import { emptyShopManageForm } from './shop-manage-form-model.ts'
|
||||
import type { ShopCredential, ShopGroupOption, ShopSummary } from './shop-dto.ts'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const rows = ref<ShopSummary[]>([])
|
||||
const total = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(15)
|
||||
const pageSize = ref(10)
|
||||
const jumpPage = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
|
||||
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -18,13 +18,14 @@ import {
|
||||
import { grantedUserIds, type TaskPermissionItem } from './task-permission.ts'
|
||||
import { imageVideoDownloadFilename, videoKeysOf } from './image-video-view.ts'
|
||||
import { useAdminSessionStore } from '@/stores/admin-session'
|
||||
import OldPagination from '@/components/OldPagination.vue'
|
||||
|
||||
const session = useAdminSessionStore()
|
||||
const loading = ref(false)
|
||||
const tasks = ref<ImageVideoRow[]>([])
|
||||
const totalTasks = ref(0)
|
||||
const page = ref(1)
|
||||
const pageSize = ref(20)
|
||||
const pageSize = ref(10)
|
||||
const jumpPage = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(totalTasks.value / pageSize.value)))
|
||||
|
||||
@@ -39,6 +40,12 @@ const permissionVisible = ref(false)
|
||||
const permissionItems = ref<TaskPermissionItem[]>([])
|
||||
const permissionInitial = ref<number[]>([])
|
||||
const permissionSaving = ref(false)
|
||||
/** 权限弹窗用户表客户端分页(全量用户在表格内翻页,10/20/50/100 可选)。 */
|
||||
const permPage = ref(1)
|
||||
const permPageSize = ref(10)
|
||||
const pagedPermissionItems = computed(() =>
|
||||
permissionItems.value.slice((permPage.value - 1) * permPageSize.value, permPage.value * permPageSize.value),
|
||||
)
|
||||
|
||||
const downloading = ref(false)
|
||||
const downloadingKey = ref('')
|
||||
@@ -243,6 +250,7 @@ async function openPermission() {
|
||||
try {
|
||||
permissionItems.value = await fetchImageVideoPermissionUsers()
|
||||
permissionInitial.value = grantedUserIds(permissionItems.value)
|
||||
permPage.value = 1
|
||||
permissionVisible.value = true
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '权限用户加载失败')
|
||||
@@ -385,7 +393,7 @@ onMounted(load)
|
||||
|
||||
<el-dialog v-model="permissionVisible" title="视频任务记录权限配置" width="620px">
|
||||
<p class="dim">勾选允许查看视频任务记录的用户(仅数据范围)。当前已授权 {{ permissionGrantedCount }} 人。</p>
|
||||
<el-table :data="permissionItems" border size="small" max-height="420">
|
||||
<el-table :data="pagedPermissionItems" border size="small" max-height="420">
|
||||
<el-table-column prop="id" label="ID" min-width="80" />
|
||||
<el-table-column prop="username" label="用户名" min-width="180" />
|
||||
<el-table-column label="授权" min-width="90" align="center">
|
||||
@@ -394,6 +402,7 @@ onMounted(load)
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<OldPagination v-if="permissionItems.length > permPageSize" :total="permissionItems.length" :page="permPage" :page-size="permPageSize" @change="permPage = $event" @size-change="(s: number) => { permPageSize = s; permPage = 1 }" />
|
||||
<template #footer>
|
||||
<el-button @click="permissionVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="permissionSaving" @click="savePermission">保存</el-button>
|
||||
|
||||
@@ -32,10 +32,9 @@ const loading = ref(false)
|
||||
const groups = ref<ShopDataTaskGroup[]>([])
|
||||
const totalShops = ref(0)
|
||||
const page = ref(1)
|
||||
// 店铺级一次全载(>200 家店铺时后端按页截断,可上调或恢复底部店铺级分页)。
|
||||
const pageSize = ref(200)
|
||||
const pageJump = ref('')
|
||||
const totalPages = computed(() => Math.max(1, Math.ceil(totalShops.value / pageSize.value)))
|
||||
// 卡片宫格分页:一行 3 个、一页 9 张卡片(在店铺分页的结果文件内翻页)。
|
||||
// 卡片宫格分页:一行 3 个、一页 10 张卡片(在店铺分页的结果文件内翻页)。
|
||||
const cardPage = ref(1)
|
||||
const cardPageSize = ref(10)
|
||||
const cardJump = ref('')
|
||||
@@ -47,6 +46,12 @@ const selection = ref<Set<string>>(new Set())
|
||||
const permissionVisible = ref(false)
|
||||
const permissionItems = ref<TaskPermissionItem[]>([])
|
||||
const permissionSaving = ref(false)
|
||||
/** 权限弹窗用户表客户端分页(全量用户在表格内翻页,10/20/50/100 可选)。 */
|
||||
const permPage = ref(1)
|
||||
const permPageSize = ref(10)
|
||||
const pagedPermissionItems = computed(() =>
|
||||
permissionItems.value.slice((permPage.value - 1) * permPageSize.value, permPage.value * permPageSize.value),
|
||||
)
|
||||
|
||||
const busyKey = ref('')
|
||||
const deletingKey = ref('')
|
||||
@@ -115,30 +120,6 @@ function reset() {
|
||||
void load()
|
||||
}
|
||||
|
||||
function changePage(next: number) {
|
||||
if (next < 1 || next > totalPages.value) return
|
||||
page.value = next
|
||||
cardPage.value = 1
|
||||
selection.value = new Set()
|
||||
void load()
|
||||
}
|
||||
|
||||
function changeShopSize(size: number) {
|
||||
pageSize.value = size
|
||||
page.value = 1
|
||||
cardPage.value = 1
|
||||
void load()
|
||||
}
|
||||
|
||||
function goPageJump() {
|
||||
const n = Number.parseInt(pageJump.value, 10)
|
||||
if (Number.isNaN(n)) {
|
||||
ElMessage.warning('请输入页码')
|
||||
return
|
||||
}
|
||||
changePage(Math.min(Math.max(n, 1), totalPages.value))
|
||||
}
|
||||
|
||||
const allResultRows = computed(() => groups.value.flatMap((group) => group.results))
|
||||
const pagedResultRows = computed(() =>
|
||||
allResultRows.value.slice((cardPage.value - 1) * cardPageSize.value, cardPage.value * cardPageSize.value),
|
||||
@@ -261,6 +242,7 @@ function saveBlob(blob: Blob, filename: string) {
|
||||
async function openPermission() {
|
||||
try {
|
||||
permissionItems.value = await fetchShopDataTaskPermissionUsers()
|
||||
permPage.value = 1
|
||||
permissionVisible.value = true
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '权限用户加载失败')
|
||||
@@ -404,7 +386,7 @@ onMounted(load)
|
||||
|
||||
<el-dialog v-model="permissionVisible" title="店铺数据记录数据范围授权" width="620px">
|
||||
<p class="dim">勾选允许查看/操作店铺数据记录的用户(仅数据范围)。当前已授权 {{ grantedCount }} 人。</p>
|
||||
<el-table :data="permissionItems" border size="small" max-height="420">
|
||||
<el-table :data="pagedPermissionItems" border size="small" max-height="420">
|
||||
<el-table-column prop="id" label="ID" min-width="80" />
|
||||
<el-table-column prop="username" label="用户名" min-width="180" />
|
||||
<el-table-column label="授权" min-width="90" align="center">
|
||||
@@ -413,6 +395,7 @@ onMounted(load)
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<OldPagination v-if="permissionItems.length > permPageSize" :total="permissionItems.length" :page="permPage" :page-size="permPageSize" @change="permPage = $event" @size-change="(s: number) => { permPageSize = s; permPage = 1 }" />
|
||||
<template #footer>
|
||||
<el-button @click="permissionVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="permissionSaving" @click="savePermission">保存</el-button>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** 重复检查明细筛选与分页(任务 117):与 /duplicate-check-items 的 snake 参数契约对齐;纯逻辑。 */
|
||||
|
||||
export const DUPLICATE_DEFAULT_PAGE_SIZE = 20
|
||||
export const DUPLICATE_DEFAULT_PAGE_SIZE = 10
|
||||
export const DUPLICATE_MIN_PAGE_SIZE = 10
|
||||
export const DUPLICATE_MAX_PAGE_SIZE = 100
|
||||
export const DUPLICATE_PAGE_MIN = 1
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** 视频任务筛选与分页(任务 102):图生视频任务列表筛选/分页归一,与 Java AdminImageVideoTaskController
|
||||
* snake_case 查询契约对齐;纯逻辑。 */
|
||||
|
||||
export const IMAGE_VIDEO_DEFAULT_PAGE_SIZE = 20
|
||||
export const IMAGE_VIDEO_DEFAULT_PAGE_SIZE = 10
|
||||
export const IMAGE_VIDEO_MIN_PAGE_SIZE = 10
|
||||
export const IMAGE_VIDEO_MAX_PAGE_SIZE = 100
|
||||
export const IMAGE_VIDEO_PAGE_MIN = 1
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
/** 店铺数据任务筛选与分页(任务 109):与 Java AdminShopDataCrawlTasksController snake 契约对齐;纯逻辑。 */
|
||||
|
||||
export const SHOP_DATA_DEFAULT_PAGE_SIZE = 20
|
||||
export const SHOP_DATA_DEFAULT_PAGE_SIZE = 10
|
||||
export const SHOP_DATA_MIN_PAGE_SIZE = 10
|
||||
export const SHOP_DATA_MAX_PAGE_SIZE = 100
|
||||
export const SHOP_DATA_PAGE_MIN = 1
|
||||
|
||||
Reference in New Issue
Block a user