-
+
+
-
-
{article ? : }
+
+
+
+{(article || snap) && (
+
+)}
+
+{/* 注意:不能用 define:vars 传 meta —— 它会把脚本包成内联 IIFE 并把 import 塞进函数体内,
+ 导致 SyntaxError(import 只能在模块顶层)。故改由 data-* 属性透传,脚本自行读取。 */}
+
diff --git a/src/pages/en/login.astro b/src/pages/en/login.astro
index abf4e9b..e4846a8 100644
--- a/src/pages/en/login.astro
+++ b/src/pages/en/login.astro
@@ -23,8 +23,17 @@ const failLabel = t('login failed')
const membersLabel = t('members')
const backLabel = t('back to site')
const homeUrl = href(locale, ROUTES.runwayLooks)
+const pageTitle = `${loginLabel} — Portfolio Studio`
---
-
+
+
+
+
+
+
+ {pageTitle}
+
+
-
-
+ window.location.href = clientHref(loc, ROUTES.account)
+ }
+ },
+
+ submit() {
+ this.error = ""
+ this.loading = true
+
+ login({ account: this.account, password: this.password })
+ .then(() => {
+ const params = new URLSearchParams(window.location.search)
+ const redirect = params.get("redirect")
+ const loc = this.$el.dataset.locale || "en"
+ window.location.href = redirect || clientHref(loc, ROUTES.account)
+ })
+ .catch(() => {
+ this.error = this.$el.dataset.failLabel || "login failed"
+ })
+ .finally(() => {
+ this.loading = false
+ })
+ },
+ }))
+ })
+
+
+
diff --git a/src/styles/look-grid.css b/src/styles/look-grid.css
index b7a5b1a..65a6c0f 100644
--- a/src/styles/look-grid.css
+++ b/src/styles/look-grid.css
@@ -255,3 +255,12 @@
@keyframes looksProg { 0% { left: -38%; } 100% { left: 100%; } }
@keyframes looksShim { 0%, 100% { opacity: .85; } 50% { opacity: .55; } }
+
+/* ====================== 卡片收藏按钮(图片收藏入口) ====================== */
+/* 白底细边、已收藏时仅实心黑心,尽量不使用黑底白字组件 */
+.showscard-wrap { @apply relative; }
+.fav-toggle {
+ @apply absolute right-2 top-2 z-20 grid h-9 w-9 place-items-center rounded-full border border-black/15 bg-white/85 text-black backdrop-blur-sm transition-transform duration-200 hover:scale-110 active:scale-95;
+}
+.fav-toggle svg { @apply h-[18px] w-[18px]; fill: none; stroke: currentColor; stroke-width: 1.6; }
+.fav-toggle.is-on svg { fill: #000; stroke: #000; }
-
-
- {pleaseLabel}
- {loginLabel} -
-
+
+
+
+
+
diff --git a/src/pages/en/item/[id].astro b/src/pages/en/item/[id].astro
index 6537ca4..1767fe0 100644
--- a/src/pages/en/item/[id].astro
+++ b/src/pages/en/item/[id].astro
@@ -3,6 +3,7 @@ import Layout from "@/layouts/Layout.astro"
import Item from "@/components/Item.astro"
import StreetSnap from "@/components/StreetSnap.astro"
import { getSsrArticle, getSsrStreetSnap } from "@/lib/api"
+import { getI18n } from "@/i18n/utils"
// 按需 SSR:走秀(runway)与街拍(street snap)详情统一收口到 /item/[id]。
// 对外 id 带类型前缀(r=runway / s=snap,见后端 hashid.EncodeTyped),据此直接查对应表,无歧义、无需回落。
@@ -11,6 +12,8 @@ export const prerender = false
const { id } = Astro.params
const isSnap = !!id && id[0] === "s"
+const { t } = getI18n(Astro)
+
let article = null
let articleErr = null
let snap = null
@@ -28,8 +31,93 @@ if (isSnap) {
article = data.article
articleErr = data.error
}
+
+// 供客户端「收藏 / 历史」使用的精简元信息(服务端已拿到,直接透传,避免前端再发请求)。
+const meta = isSnap
+ ? { id: id ?? "", type: "street", title: snap?.title ?? "", cover: snap?.cover ?? "", brand: "" }
+ : { id: id ?? "", type: "runway", title: article?.title ?? "", cover: article?.cover ?? "", brand: article?.brand_name ?? "" }
+const saveLabel = t("save")
+const savedLabel = t("saved")
---
-
-
- Account
--
-
- {accountLabel} - -
- {emailLabel} - -
-
-
-
-
-
- {t('View archive')}
-
-
-
-
-
-
-
-
+
+ {collectionsLabel}
-{soonLabel}
-{historyLabel}
-{soonLabel}
-
+
{t("member profile")}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {t("saved looks")}
+ {itemsLabel} +
+
+
+ {t("no saved looks yet")}
+ {t("browse the archive")} +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {historyLabel}
+
+ {itemsLabel}
+
+
+
+
+
+ {t("no history yet")}
+ {t("browse the archive")} +
+
+
+
+
+
+
+
+
+
+
+
+
+