5
.vscode/extensions.json
vendored
5
.vscode/extensions.json
vendored
@ -1,4 +1,5 @@
|
|||||||
{
|
{
|
||||||
"recommendations": ["astro-build.astro-vscode"],
|
"recommendations": [
|
||||||
"unwantedRecommendations": []
|
"bradlc.vscode-tailwindcss"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"css.lint.unknownAtRules": "ignore",
|
||||||
|
"scss.lint.unknownAtRules": "ignore",
|
||||||
|
"tailwindCSS.experimental.configFile": "./src/styles/global.css",
|
||||||
|
"tailwindCSS.validate": true,
|
||||||
|
"files.associations": {
|
||||||
|
"*.css": "tailwindcss"
|
||||||
|
}
|
||||||
|
}
|
||||||
594
package-lock.json
generated
594
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -17,7 +17,7 @@
|
|||||||
"@tailwindcss/vite": "^4.3.3",
|
"@tailwindcss/vite": "^4.3.3",
|
||||||
"@types/alpinejs": "^3.13.11",
|
"@types/alpinejs": "^3.13.11",
|
||||||
"alpinejs": "^3.16.1",
|
"alpinejs": "^3.16.1",
|
||||||
"astro": "^7.2.4",
|
"astro": "^7.2.9",
|
||||||
"swiper": "^11.2.10",
|
"swiper": "^11.2.10",
|
||||||
"tailwindcss": "^4.3.3"
|
"tailwindcss": "^4.3.3"
|
||||||
}
|
}
|
||||||
|
|||||||
@ -502,6 +502,7 @@ const seasonOptions: { value: string; label: string }[] = [
|
|||||||
// 图片总数只用于末位遮罩文案,通过 moreLabel 回调传入。
|
// 图片总数只用于末位遮罩文案,通过 moreLabel 回调传入。
|
||||||
getCardSlots(it: ArticleItem) {
|
getCardSlots(it: ArticleItem) {
|
||||||
const total = it.image_count || 0
|
const total = it.image_count || 0
|
||||||
|
// 缩略图条带跳过主图(it.cover 即 gallery[0]),从第二张起,避免首张缩略图与主图重复。
|
||||||
return makeCardSlots(it.gallery || [], 5, {
|
return makeCardSlots(it.gallery || [], 5, {
|
||||||
moreLabel: () => (total > 30 ? "30+" : `${total}+`),
|
moreLabel: () => (total > 30 ? "30+" : `${total}+`),
|
||||||
})
|
})
|
||||||
|
|||||||
@ -297,6 +297,7 @@ const cityOptions: { value: string; label: string }[] = [
|
|||||||
// 卡片右侧缩略图条带:固定 5 个槽位,缺图留 placeholder
|
// 卡片右侧缩略图条带:固定 5 个槽位,缺图留 placeholder
|
||||||
// 街拍 images 是 { image, name }[](与 runway 的 string[] gallery 不同),先抽成纯 URL 数组
|
// 街拍 images 是 { image, name }[](与 runway 的 string[] gallery 不同),先抽成纯 URL 数组
|
||||||
getCardSlots(it: StreetSnapItem) {
|
getCardSlots(it: StreetSnapItem) {
|
||||||
|
// 缩略图条带跳过主图(it.cover 即 images[0]),从第二张起,避免首张缩略图与主图重复。
|
||||||
const imgs = (it.images || []).map((im: any) => (im && (im.image || im)) || "")
|
const imgs = (it.images || []).map((im: any) => (im && (im.image || im)) || "")
|
||||||
return makeCardSlots(imgs)
|
return makeCardSlots(imgs)
|
||||||
},
|
},
|
||||||
@ -341,7 +342,7 @@ const cityOptions: { value: string; label: string }[] = [
|
|||||||
nextPage() { this.goPage(this.page + 1) },
|
nextPage() { this.goPage(this.page + 1) },
|
||||||
|
|
||||||
toAbsUrl(url: string) { return toAbs(url) },
|
toAbsUrl(url: string) { return toAbs(url) },
|
||||||
// 街拍详情统一收口到 /item/[id](与服务端 /item 回落逻辑对应)
|
// 街拍详情与走秀统一收口 /item/[id],前端按编码前缀 r/s 分流(见后端 hashid.EncodeTyped)
|
||||||
itemHref(it: { id: string | number }) { return clientHref(this.locale, ROUTES.item(it.id)) },
|
itemHref(it: { id: string | number }) { return clientHref(this.locale, ROUTES.item(it.id)) },
|
||||||
|
|
||||||
// theimpression 标题含字面 \t:地点 \t 场次,拆成 [location, event]
|
// theimpression 标题含字面 \t:地点 \t 场次,拆成 [location, event]
|
||||||
|
|||||||
@ -141,10 +141,10 @@ const headers = {
|
|||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center gap-2" id="auth-slot" data-hi-label={hiLabel} data-logout-label={logoutLabel}>
|
<!-- <div class="flex items-center gap-2" id="auth-slot" data-hi-label={hiLabel} data-logout-label={logoutLabel}>
|
||||||
<a href={href(locale, ROUTES.login)} class="hover:opacity-60 transition-opacity duration-200">{t('Login')}</a>
|
<a href={href(locale, ROUTES.login)} class="hover:opacity-60 transition-opacity duration-200">{t('Login')}</a>
|
||||||
<a href={href(locale, ROUTES.register)} class="hover:opacity-60 transition-opacity duration-200">{t('Signup')}</a>
|
<a href={href(locale, ROUTES.register)} class="hover:opacity-60 transition-opacity duration-200">{t('Signup')}</a>
|
||||||
</div>
|
</div> -->
|
||||||
</div>
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
|||||||
@ -16,9 +16,13 @@ export const ROUTES = {
|
|||||||
runwayLooks: '/runway-looks',
|
runwayLooks: '/runway-looks',
|
||||||
// 街拍
|
// 街拍
|
||||||
streetSnaps: '/street-snaps',
|
streetSnaps: '/street-snaps',
|
||||||
// 动态路由(工厂函数)。所有详情(走秀 lookbook / 街拍)统一收口到 /item/[id],
|
// 动态路由(工厂函数)。
|
||||||
// 由 src/pages/{locale}/item/[id].astro 按 article→street 顺序回落渲染。
|
// 走秀 lookbook 详情统一收口到 /item/[id](src/pages/{locale}/item/[id].astro)。
|
||||||
|
// 街拍详情独立收口到 /street-snaps/[id](src/pages/{locale}/street-snaps/[id].astro),
|
||||||
|
// 与 runway 分路由是为了避免 HashID 类型无关导致两类 id 在同一 /item 路由里撞车
|
||||||
|
//(runway 与 street_snap 共用一套自增数字 id + 同一盐值,编码串不可区分类型)。
|
||||||
item: (id: string | number) => `/item/${id}`,
|
item: (id: string | number) => `/item/${id}`,
|
||||||
|
// 街拍详情与走秀详情统一收口到 /item/[id],由前端按编码前缀(r=runway/s=snap)分流。
|
||||||
streetSnap: (id: string | number) => `/item/${id}`,
|
streetSnap: (id: string | number) => `/item/${id}`,
|
||||||
} as const;
|
} as const;
|
||||||
|
|
||||||
|
|||||||
@ -4,19 +4,29 @@ import Item from "@/components/Item.astro"
|
|||||||
import StreetSnap from "@/components/StreetSnap.astro"
|
import StreetSnap from "@/components/StreetSnap.astro"
|
||||||
import { getSsrArticle, getSsrStreetSnap } from "@/lib/api"
|
import { getSsrArticle, getSsrStreetSnap } from "@/lib/api"
|
||||||
|
|
||||||
// 按需 SSR:走秀详情与街拍详情统一收口到 /item/[id]。
|
// 按需 SSR:走秀(runway)与街拍(street snap)详情统一收口到 /item/[id]。
|
||||||
// 先按 article 取数(runway lookbook);取不到再回落 street snap,实现「所有详情进 /item」。
|
// 对外 id 带类型前缀(r=runway / s=snap,见后端 hashid.EncodeTyped),据此直接查对应表,无歧义、无需回落。
|
||||||
export const prerender = false
|
export const prerender = false
|
||||||
|
|
||||||
const { id } = Astro.params
|
const { id } = Astro.params
|
||||||
const { article, error: articleErr } = await getSsrArticle(id, Astro.currentLocale)
|
const isSnap = !!id && id[0] === "s"
|
||||||
|
|
||||||
|
let article = null
|
||||||
|
let articleErr = null
|
||||||
let snap = null
|
let snap = null
|
||||||
let snapErr: "missing" | "failed" | null = null
|
let snapErr = null
|
||||||
if (!article) {
|
|
||||||
const res = await getSsrStreetSnap(id, Astro.currentLocale)
|
// await 必须位于 frontmatter 顶层(Astro 不支持块内顶层 await)
|
||||||
snap = res.snap
|
const data = isSnap
|
||||||
snapErr = res.error
|
? await getSsrStreetSnap(id, Astro.currentLocale)
|
||||||
|
: await getSsrArticle(id, Astro.currentLocale)
|
||||||
|
|
||||||
|
if (isSnap) {
|
||||||
|
snap = data.snap
|
||||||
|
snapErr = data.error
|
||||||
|
} else {
|
||||||
|
article = data.article
|
||||||
|
articleErr = data.error
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@ -4,19 +4,29 @@ import Item from "@/components/Item.astro"
|
|||||||
import StreetSnap from "@/components/StreetSnap.astro"
|
import StreetSnap from "@/components/StreetSnap.astro"
|
||||||
import { getSsrArticle, getSsrStreetSnap } from "@/lib/api"
|
import { getSsrArticle, getSsrStreetSnap } from "@/lib/api"
|
||||||
|
|
||||||
// 按需 SSR:走秀详情与街拍详情统一收口到 /item/[id]。
|
// 按需 SSR:走秀(runway)与街拍(street snap)详情统一收口到 /item/[id]。
|
||||||
// 先按 article 取数(runway lookbook);取不到再回落 street snap,实现「所有详情进 /item」。
|
// 对外 id 带类型前缀(r=runway / s=snap,见后端 hashid.EncodeTyped),据此直接查对应表,无歧义、无需回落。
|
||||||
export const prerender = false
|
export const prerender = false
|
||||||
|
|
||||||
const { id } = Astro.params
|
const { id } = Astro.params
|
||||||
const { article, error: articleErr } = await getSsrArticle(id, Astro.currentLocale)
|
const isSnap = !!id && id[0] === "s"
|
||||||
|
|
||||||
|
let article = null
|
||||||
|
let articleErr = null
|
||||||
let snap = null
|
let snap = null
|
||||||
let snapErr: "missing" | "failed" | null = null
|
let snapErr = null
|
||||||
if (!article) {
|
|
||||||
const res = await getSsrStreetSnap(id, Astro.currentLocale)
|
// await 必须位于 frontmatter 顶层(Astro 不支持块内顶层 await)
|
||||||
snap = res.snap
|
const data = isSnap
|
||||||
snapErr = res.error
|
? await getSsrStreetSnap(id, Astro.currentLocale)
|
||||||
|
: await getSsrArticle(id, Astro.currentLocale)
|
||||||
|
|
||||||
|
if (isSnap) {
|
||||||
|
snap = data.snap
|
||||||
|
snapErr = data.error
|
||||||
|
} else {
|
||||||
|
article = data.article
|
||||||
|
articleErr = data.error
|
||||||
}
|
}
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user