update
This commit is contained in:
@ -10,24 +10,24 @@ const { locale, t, getRelativeLocaleUrl } = getI18n(Astro)
|
||||
// 侧栏 filter 展示前 10 个(模板内 slice(0,10)),品牌弹窗 HOT 标签展示全部 30 个。
|
||||
// 接口不可用时回落假数据先看效果。
|
||||
const FAKE_HOT_BRANDS = [
|
||||
{ id: "hot-chanel", name: "Chanel", article_count: 342 },
|
||||
{ id: "hot-dior", name: "Dior", article_count: 318 },
|
||||
{ id: "hot-gucci", name: "Gucci", article_count: 287 },
|
||||
{ id: "hot-prada", name: "Prada", article_count: 254 },
|
||||
{ id: "hot-louisvuitton", name: "Louis Vuitton", article_count: 241 },
|
||||
{ id: "hot-saintlaurent", name: "Saint Laurent", article_count: 219 },
|
||||
{ id: "hot-balenciaga", name: "Balenciaga", article_count: 198 },
|
||||
{ id: "hot-valentino", name: "Valentino", article_count: 176 },
|
||||
{ id: "hot-givenchy", name: "Givenchy", article_count: 162 },
|
||||
{ id: "hot-burberry", name: "Burberry", article_count: 153 },
|
||||
{ id: "hot-fendi", name: "Fendi", article_count: 141 },
|
||||
{ id: "hot-bottega", name: "Bottega Veneta", article_count: 128 },
|
||||
{ id: "hot-chanel", name: "Chanel" },
|
||||
{ id: "hot-dior", name: "Dior" },
|
||||
{ id: "hot-gucci", name: "Gucci" },
|
||||
{ id: "hot-prada", name: "Prada" },
|
||||
{ id: "hot-louisvuitton", name: "Louis Vuitton" },
|
||||
{ id: "hot-saintlaurent", name: "Saint Laurent" },
|
||||
{ id: "hot-balenciaga", name: "Balenciaga" },
|
||||
{ id: "hot-valentino", name: "Valentino" },
|
||||
{ id: "hot-givenchy", name: "Givenchy" },
|
||||
{ id: "hot-burberry", name: "Burberry" },
|
||||
{ id: "hot-fendi", name: "Fendi" },
|
||||
{ id: "hot-bottega", name: "Bottega Veneta" },
|
||||
]
|
||||
let hotBrands: { id: string; name: string; article_count: number }[] = []
|
||||
let hotBrands: { id: string; name: string }[] = []
|
||||
try {
|
||||
hotBrands = (await getSsgHotBrands())
|
||||
.filter((b) => b.id && b.name)
|
||||
.map((b) => ({ id: String(b.id), name: b.name, article_count: b.article_count }))
|
||||
.map((b) => ({ id: String(b.id), name: b.name }))
|
||||
} catch {}
|
||||
if (!hotBrands.length) hotBrands = FAKE_HOT_BRANDS
|
||||
|
||||
@ -108,9 +108,8 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
<template x-for="b in hotBrands.slice(0, 10)" :key="b.id">
|
||||
<li>
|
||||
<label class="flex items-center gap-2 w-full cursor-pointer">
|
||||
<input type="checkbox" :value="b.id" :checked="selectedBrands.includes(b.id)" @change="toggleBrand(b.id)" />
|
||||
<input type="radio" name="designer" :value="b.id" :checked="selectedBrand === b.id" :class="{ 'is-checked': selectedBrand === b.id }" @click.prevent="toggleBrand(b.id)" />
|
||||
<span x-text="b.name"></span>
|
||||
<span class="ct" x-text="b.article_count ? b.article_count : ''"></span>
|
||||
</label>
|
||||
</li>
|
||||
</template>
|
||||
@ -130,7 +129,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
<div class="showsg-b" x-show="!collapsedGroups.collection">
|
||||
<ul class="showschecks showschecks--flat">
|
||||
{collectionOptions.map((o) => (
|
||||
<li><label><input type="checkbox" value={o.value} x-model="selectedCt" @change="refreshFilter()" /> <span>{o.label}</span></label></li>
|
||||
<li><label><input type="radio" name="collection" value={o.value} :checked={'selectedCt === "' + o.value + '"'} :class={`{'is-checked': selectedCt === '${o.value}'}`} @click.prevent={'selectedCt = selectedCt === "' + o.value + '" ? "" : "' + o.value + '"; refreshFilter()'} /> <span>{o.label}</span></label></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
@ -147,7 +146,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
<div class="showsg-b" x-show="!collapsedGroups.season">
|
||||
<ul class="showschecks showschecks--flat">
|
||||
{seasonOptions.map((o) => (
|
||||
<li><label><input type="checkbox" value={o.value} x-model="selectedSeason" @change="refreshFilter()" /> <span>{o.label}</span></label></li>
|
||||
<li><label><input type="radio" name="season" value={o.value} :checked={'selectedSeason === "' + o.value + '"'} :class={`{'is-checked': selectedSeason === '${o.value}'}`} @click.prevent={'selectedSeason = selectedSeason === "' + o.value + '" ? "" : "' + o.value + '"; refreshFilter()'} /> <span>{o.label}</span></label></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
@ -164,7 +163,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
<div class="showsg-b" x-show="!collapsedGroups.year">
|
||||
<ul class="showschecks showschecks--flat">
|
||||
{yearOptions.map((o) => (
|
||||
<li><label><input type="checkbox" value={String(o.value)} x-model="selectedYear" @change="refreshFilter()" /> <span>{o.label}</span></label></li>
|
||||
<li><label><input type="radio" name="year" value={String(o.value)} :checked={'selectedYear === "' + String(o.value) + '"'} :class={`{'is-checked': selectedYear === '${String(o.value)}'}`} @click.prevent={'selectedYear = selectedYear === "' + String(o.value) + '" ? "" : "' + String(o.value) + '"; refreshFilter()'} /> <span>{o.label}</span></label></li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
@ -235,9 +234,16 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="showsloadmore-wrap" x-show="items.length > 0">
|
||||
<button class="showsloadmore" type="button" :disabled="loadingMore || isEnd" @click="loadPage(page + 1, true)" x-text="loadBtnText"></button>
|
||||
</div>
|
||||
<nav class="showspager" x-show="lastPage > 1" aria-label="Pagination">
|
||||
<button class="pg pg-prev" type="button" :disabled="page <= 1 || loading" @click="prevPage()">‹ Prev</button>
|
||||
<template x-for="(p, i) in pageList" :key="i">
|
||||
<span class="pg-wrap">
|
||||
<span class="pg-ellipsis" x-show="p === '...'" x-text="'…'"></span>
|
||||
<button class="pg" type="button" x-show="p !== '...'" :class="{ 'is-current': p === page }" :disabled="p === page" @click="goPage(p)" x-text="p"></button>
|
||||
</span>
|
||||
</template>
|
||||
<button class="pg pg-next" type="button" :disabled="page >= lastPage || loading" @click="nextPage()">Next ›</button>
|
||||
</nav>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@ -258,6 +264,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
placeholder="搜索品牌名…"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<p class="showsmodal-tip">最多展示 30 条结果,可用搜索或字母筛选缩小范围</p>
|
||||
</div>
|
||||
<div class="showsmodal-az">
|
||||
<button class="az az-hot" type="button" :class="{ active: modalLetter === 'HOT' }" @click="modalPickLetter('HOT')">HOT</button>
|
||||
@ -273,14 +280,13 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
<li>
|
||||
<label class="showsmodal-row">
|
||||
<input
|
||||
type="checkbox"
|
||||
type="radio"
|
||||
name="modalDesigner"
|
||||
:value="b.id"
|
||||
:checked="modalSelected.includes(b.id)"
|
||||
:disabled="!modalSelected.includes(b.id) && modalMaxReached"
|
||||
@change="toggleModalBrand(b.id)"
|
||||
x-model="modalSelected"
|
||||
:class="{ 'is-checked': modalSelected === b.id }"
|
||||
/>
|
||||
<span class="showsmodal-name" x-text="b.display || b.name"></span>
|
||||
<span class="showsmodal-ct" x-text="b.article_count ? b.article_count : ''"></span>
|
||||
</label>
|
||||
</li>
|
||||
</template>
|
||||
@ -368,7 +374,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
|
||||
|
||||
.showschecks {
|
||||
@apply list-none mt-[10px] mb-0 p-0 max-h-[280px] overflow-y-auto [scrollbar-width:thin] overscroll-contain;
|
||||
@apply list-none mt-[10px] mb-0 p-0 max-h-[280px] overflow-y-auto [scrollbar-width:thin];
|
||||
}
|
||||
.showschecks li {
|
||||
@apply flex items-center gap-2 py-[5px] text-[11.5px] uppercase tracking-[0.04em] text-black cursor-pointer select-none hover:bg-[#f6f6f6];
|
||||
@ -376,12 +382,14 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
.showschecks li.letter-head {
|
||||
@apply p-[8px_0_3px] text-[10px] tracking-[0.22em] text-[#8a8a8a] cursor-default pointer-events-none border-t border-dotted border-[#e6e6e6] first:border-t-0 first:pt-1;
|
||||
}
|
||||
.showschecks input[type="checkbox"] {
|
||||
@apply appearance-none w-3 h-3 border-[1.5px] border-black bg-white cursor-pointer shrink-0 rounded-none inline-grid place-content-center checked:bg-black;
|
||||
.showschecks input[type="radio"] {
|
||||
@apply appearance-none w-3.5 h-3.5 border-[1.5px] border-black bg-white cursor-pointer shrink-0 rounded-full;
|
||||
}
|
||||
.showschecks input[type="checkbox"]:checked::after {
|
||||
content: "";
|
||||
@apply w-[6px] h-[6px] bg-white;
|
||||
/* 选中态由 Alpine 状态驱动(.is-checked),不依赖原生 :checked 伪类,
|
||||
因为 @click.prevent 会阻止原生选中,伪类不可靠;实心黑圆一眼可辨 */
|
||||
.showschecks input[type="radio"].is-checked {
|
||||
@apply bg-white border-black;
|
||||
background-image: radial-gradient(circle, #000 36%, transparent 40%);
|
||||
}
|
||||
.showschecks .ct {
|
||||
@apply text-[#999] text-[10px] ml-auto;
|
||||
@ -493,11 +501,26 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
animation: hbxShim 1.6s ease infinite;
|
||||
}
|
||||
|
||||
.showsloadmore-wrap {
|
||||
@apply flex justify-center mt-[38px];
|
||||
.showspager {
|
||||
@apply flex flex-wrap items-center justify-center gap-2 mt-[38px];
|
||||
}
|
||||
.showsloadmore {
|
||||
@apply border-[1.5px] border-black bg-white text-black p-[13px_36px] text-[11px] uppercase tracking-[0.22em] font-bold cursor-pointer rounded-none transition-all duration-200 hover:bg-black hover:text-white disabled:opacity-40 disabled:cursor-default;
|
||||
.pg {
|
||||
@apply min-w-[40px] h-[40px] px-3 border-[1.5px] border-black bg-white text-black text-[11px] uppercase tracking-[0.12em] font-bold cursor-pointer rounded-none transition-colors duration-200;
|
||||
}
|
||||
.pg.is-current {
|
||||
@apply bg-black text-white cursor-default;
|
||||
}
|
||||
.pg:disabled {
|
||||
@apply opacity-30 cursor-default;
|
||||
}
|
||||
.pg.enabled:hover {
|
||||
@apply bg-black text-white;
|
||||
}
|
||||
.pg-ellipsis {
|
||||
@apply px-1 text-[#999] select-none text-[11px];
|
||||
}
|
||||
.pg-wrap {
|
||||
@apply inline-flex items-center;
|
||||
}
|
||||
|
||||
.showsempty {
|
||||
@ -546,6 +569,9 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
.showsmodal-input {
|
||||
@apply w-full border border-black/15 px-3 py-2 text-[13px] outline-none bg-[#fafafa] focus:bg-white focus:border-black;
|
||||
}
|
||||
.showsmodal-tip {
|
||||
@apply mt-2 text-[11px] leading-snug text-black/45 font-mono;
|
||||
}
|
||||
.showsmodal-az {
|
||||
@apply flex flex-wrap gap-1 px-4 pb-3 shrink-0;
|
||||
}
|
||||
@ -573,6 +599,13 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
.showsmodal-row {
|
||||
@apply flex items-center gap-2 w-full cursor-pointer px-3 py-2 text-[13px] transition-colors hover:bg-black/5;
|
||||
}
|
||||
.showsmodal-row input[type="radio"] {
|
||||
@apply appearance-none w-4 h-4 border-[1.5px] border-black bg-white cursor-pointer shrink-0 rounded-full;
|
||||
}
|
||||
.showsmodal-row input[type="radio"].is-checked {
|
||||
@apply bg-white border-black;
|
||||
background-image: radial-gradient(circle, #000 36%, transparent 40%);
|
||||
}
|
||||
.showsmodal-name {
|
||||
@apply flex-1 truncate;
|
||||
}
|
||||
@ -616,10 +649,10 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
season: localStorage.getItem("showsg-season") === "1",
|
||||
year: localStorage.getItem("showsg-year") === "1",
|
||||
},
|
||||
selectedBrands: [] as number[],
|
||||
selectedCt: [] as string[],
|
||||
selectedSeason: [] as string[],
|
||||
selectedYear: [] as string[],
|
||||
selectedBrand: null as (string | null),
|
||||
selectedCt: "" as string,
|
||||
selectedSeason: "" as string,
|
||||
selectedYear: "" as string,
|
||||
|
||||
// 品牌筛选弹窗状态
|
||||
modalOpen: false,
|
||||
@ -627,91 +660,74 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
modalLetter: "",
|
||||
modalResults: [] as BrandEntry[],
|
||||
modalLoading: false,
|
||||
modalSelected: [] as number[],
|
||||
get modalMaxReached() {
|
||||
return this.modalSelected.length >= 1
|
||||
},
|
||||
modalSelected: null as (string | null),
|
||||
|
||||
items: [] as (ArticleItem & { _imgLoaded?: boolean })[],
|
||||
loading: false,
|
||||
loadingMore: false,
|
||||
page: 1,
|
||||
size: 24,
|
||||
total: 0,
|
||||
isEnd: false,
|
||||
lastPage: 1,
|
||||
reqToken: 0,
|
||||
|
||||
init() {
|
||||
this.locale = this.$el.dataset.locale || 'en';
|
||||
this.loadPage(1, false)
|
||||
this.loadPage(1)
|
||||
},
|
||||
|
||||
// 骨架屏数量与首屏 data 数量一致,保证「加载中」与「数据完成」两阶段的网格高度相同,消除抖动
|
||||
// 骨架屏数量与首屏每页条数一致(后端固定 ArticleFixedSize=24),消除加载抖动
|
||||
get skeletonCount() {
|
||||
return this.size
|
||||
return 24
|
||||
},
|
||||
|
||||
// 已筛选选项 Pills 视图数据
|
||||
get pills() {
|
||||
const list: { label: string; clear: () => void }[] = []
|
||||
if (this.selectedBrands.length) {
|
||||
if (this.selectedBrand) {
|
||||
const findName = (id: any) => this.hotBrands.find((b) => b.id === id)?.name ?? this.modalResults.find((b) => b.id === id)?.name ?? `#${id}`
|
||||
const labels = this.selectedBrands
|
||||
.map((id) => findName(id))
|
||||
.slice(0, 4)
|
||||
const more = this.selectedBrands.length > 4 ? ` +${this.selectedBrands.length - 4}` : ""
|
||||
const name = findName(this.selectedBrand)
|
||||
list.push({
|
||||
label: "DESIGNER · " + labels.join(", ") + more,
|
||||
clear: () => { this.selectedBrands = []; this.refreshFilter(); }
|
||||
label: "DESIGNER · " + name,
|
||||
clear: () => { this.selectedBrand = null; this.refreshFilter(); }
|
||||
})
|
||||
}
|
||||
if (this.selectedCt.length) {
|
||||
if (this.selectedCt) {
|
||||
list.push({
|
||||
label: "COLLECTION · " + this.selectedCt.join("+").toUpperCase(),
|
||||
clear: () => { this.selectedCt = []; this.refreshFilter(); }
|
||||
label: "COLLECTION · " + this.selectedCt.toUpperCase(),
|
||||
clear: () => { this.selectedCt = ""; this.refreshFilter(); }
|
||||
})
|
||||
}
|
||||
if (this.selectedSeason.length) {
|
||||
if (this.selectedSeason) {
|
||||
list.push({
|
||||
label: "SEASON · " + this.selectedSeason.join("+").toUpperCase(),
|
||||
clear: () => { this.selectedSeason = []; this.refreshFilter(); }
|
||||
label: "SEASON · " + this.selectedSeason.toUpperCase(),
|
||||
clear: () => { this.selectedSeason = ""; this.refreshFilter(); }
|
||||
})
|
||||
}
|
||||
if (this.selectedYear.length) {
|
||||
if (this.selectedYear) {
|
||||
list.push({
|
||||
label: "YEAR · " + [...this.selectedYear].sort().join("+"),
|
||||
clear: () => { this.selectedYear = []; this.refreshFilter(); }
|
||||
label: "YEAR · " + this.selectedYear,
|
||||
clear: () => { this.selectedYear = ""; this.refreshFilter(); }
|
||||
})
|
||||
}
|
||||
return list
|
||||
},
|
||||
|
||||
get loadBtnText() {
|
||||
if (this.loadingMore) return "Loading…"
|
||||
if (this.isEnd) return "— End of Archive —"
|
||||
return "Load More ↓"
|
||||
},
|
||||
|
||||
// 事件处理方法
|
||||
toggleGroup(group: "designers" | "collection" | "season" | "year") {
|
||||
this.collapsedGroups[group] = !this.collapsedGroups[group]
|
||||
localStorage.setItem(`showsg-${group}`, this.collapsedGroups[group] ? "1" : "")
|
||||
},
|
||||
|
||||
toggleBrand(id: number) {
|
||||
const idx = this.selectedBrands.indexOf(id)
|
||||
if (idx >= 0) this.selectedBrands.splice(idx, 1)
|
||||
else this.selectedBrands.push(id)
|
||||
this.refreshFilter()
|
||||
refreshFilter() {
|
||||
this.loadPage(1)
|
||||
},
|
||||
|
||||
refreshFilter() {
|
||||
this.loadPage(1, false)
|
||||
toggleBrand(id: string) {
|
||||
this.selectedBrand = this.selectedBrand === id ? null : id
|
||||
this.refreshFilter()
|
||||
},
|
||||
|
||||
// ===== 品牌筛选弹窗方法 =====
|
||||
async openBrandModal() {
|
||||
this.modalSelected = [...this.selectedBrands]
|
||||
this.modalSelected = this.selectedBrand
|
||||
this.modalKeyword = ""
|
||||
this.modalLetter = "HOT"
|
||||
this.modalOpen = true
|
||||
@ -730,7 +746,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
this.modalLetter = "HOT"
|
||||
this.modalLoading = true
|
||||
try {
|
||||
this.modalResults = this.hotBrands.map((b) => ({ id: b.id, name: b.name, article_count: b.article_count || 0 }))
|
||||
this.modalResults = this.hotBrands.map((b) => ({ id: b.id, name: b.name }))
|
||||
} finally {
|
||||
this.modalLoading = false
|
||||
}
|
||||
@ -742,7 +758,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
this.modalLoading = true
|
||||
this.modalResults = []
|
||||
try {
|
||||
this.modalResults = await getBrands({ keyword: kw, size: 60 })
|
||||
this.modalResults = await getBrands({ keyword: kw })
|
||||
} catch {
|
||||
this.modalResults = []
|
||||
} finally {
|
||||
@ -766,23 +782,15 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
this.modalResults = []
|
||||
try {
|
||||
const letter = l === "#" ? "OTHER" : l
|
||||
this.modalResults = await getBrands({ letter, size: 200 })
|
||||
this.modalResults = await getBrands({ letter })
|
||||
} catch {
|
||||
this.modalResults = []
|
||||
} finally {
|
||||
this.modalLoading = false
|
||||
}
|
||||
},
|
||||
toggleModalBrand(id: number) {
|
||||
const idx = this.modalSelected.indexOf(id)
|
||||
if (idx >= 0) this.modalSelected.splice(idx, 1)
|
||||
else {
|
||||
if (this.modalSelected.length >= 5) return
|
||||
this.modalSelected.push(id)
|
||||
}
|
||||
},
|
||||
confirmBrandModal() {
|
||||
this.selectedBrands = [...this.modalSelected]
|
||||
this.selectedBrand = this.modalSelected
|
||||
this.modalOpen = false
|
||||
document.documentElement.style.overflow = ""
|
||||
document.documentElement.style.paddingRight = ""
|
||||
@ -790,54 +798,73 @@ const seasonOptions: { value: string; label: string }[] = [
|
||||
},
|
||||
|
||||
clearAll() {
|
||||
this.selectedBrands = []
|
||||
this.selectedCt = []
|
||||
this.selectedSeason = []
|
||||
this.selectedYear = []
|
||||
this.selectedBrand = null
|
||||
this.selectedCt = ""
|
||||
this.selectedSeason = ""
|
||||
this.selectedYear = ""
|
||||
this.refreshFilter()
|
||||
},
|
||||
|
||||
// API 加载
|
||||
async loadPage(page: number, append = false) {
|
||||
// API 加载(分页:每次整页替换,不追加)
|
||||
async loadPage(page: number) {
|
||||
const myToken = ++this.reqToken
|
||||
if (append) {
|
||||
this.loadingMore = true
|
||||
} else {
|
||||
this.loading = true
|
||||
this.items = []
|
||||
}
|
||||
this.loading = true
|
||||
this.items = []
|
||||
|
||||
try {
|
||||
const json = await getArticles({
|
||||
page,
|
||||
size: this.size,
|
||||
sort: "newest",
|
||||
brand_ids: this.selectedBrands,
|
||||
collection_type: this.selectedCt,
|
||||
season: this.selectedSeason,
|
||||
year: this.selectedYear,
|
||||
with_images: 6,
|
||||
brandId: this.selectedBrand || undefined,
|
||||
collection: this.selectedCt || undefined,
|
||||
season: this.selectedSeason || undefined,
|
||||
year: this.selectedYear || undefined,
|
||||
})
|
||||
|
||||
if (myToken !== this.reqToken) return
|
||||
|
||||
const rows = json.data.map((item: ArticleItem) => ({ ...item, _imgLoaded: false }))
|
||||
this.total = json.total
|
||||
this.lastPage = json.last_page
|
||||
this.page = page
|
||||
this.items = append ? [...this.items, ...rows] : rows
|
||||
|
||||
this.isEnd = page >= json.last_page || rows.length < this.size
|
||||
this.items = rows
|
||||
} catch {
|
||||
if (myToken !== this.reqToken) return
|
||||
if (!append) this.items = []
|
||||
this.items = []
|
||||
} finally {
|
||||
if (myToken === this.reqToken) {
|
||||
this.loading = false
|
||||
this.loadingMore = false
|
||||
}
|
||||
if (myToken === this.reqToken) this.loading = false
|
||||
}
|
||||
},
|
||||
|
||||
// ===== 分页导航 =====
|
||||
// 生成窗口化的页码列表:始终含首尾页,当前页 ±1,其余用 "..." 折叠。
|
||||
get pageList() {
|
||||
const total = this.lastPage
|
||||
const cur = this.page
|
||||
if (total <= 7) {
|
||||
return Array.from({ length: total }, (_: any, i: number) => i + 1)
|
||||
}
|
||||
const pages: (number | "...")[] = [1]
|
||||
const start = Math.max(2, cur - 1)
|
||||
const end = Math.min(total - 1, cur + 1)
|
||||
if (start > 2) pages.push("...")
|
||||
for (let i = start; i <= end; i++) pages.push(i)
|
||||
if (end < total - 1) pages.push("...")
|
||||
pages.push(total)
|
||||
return pages
|
||||
},
|
||||
|
||||
goPage(p: number) {
|
||||
if (this.loading) return
|
||||
if (p < 1 || p > this.lastPage || p === this.page) return
|
||||
this.loadPage(p)
|
||||
const main = this.$el.querySelector(".showsmain")
|
||||
if (main) main.scrollIntoView({ behavior: "smooth", block: "start" })
|
||||
else window.scrollTo({ top: 0, behavior: "smooth" })
|
||||
},
|
||||
|
||||
prevPage() { this.goPage(this.page - 1) },
|
||||
nextPage() { this.goPage(this.page + 1) },
|
||||
|
||||
// 辅助辅助工具函数
|
||||
toAbsUrl(url: string) { return toAbs(url) },
|
||||
|
||||
|
||||
Reference in New Issue
Block a user