style(ui): 移除吉祥物插画,前台 hero 改为 Memphis 装饰元素

- 首页 hero 去掉 Mascot,换成漂浮的蜡笔色墨边方块(float/wiggle/bounce 动画)
- KPI 卡片轮流使用黄/粉/蓝/薄荷平涂色,能力卡图标、模型卡光晕统一 Memphis 调色
- 画廊跑马灯加粗黑上下边;About 页 hero 改为色块拼贴,文案不再以"薇薇"自称
- 空状态统一用旋转的 .empty-badge 黄色星标替代吉祥物
- 邀请页顶部卡片改为 ink-card + 蜡笔色统计块 + 橙色复制按钮
- 删除 Mascot.vue

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
2026-10-08 17:09:11 +08:00
co-authored by Devin
parent 313d54a5ec
commit b39173643d
8 changed files with 209 additions and 196 deletions
+66 -23
View File
@@ -1,59 +1,63 @@
<script setup>
import { computed } from 'vue'
import { site } from '../site'
import Mascot from '../components/Mascot.vue'
const contact = computed(() => site.contact || {})
const powers = [
{ icon: '🎨', title: '文字生图', desc: '一句话出图,画幅与分辨率随模型可选。', tint: 'from-fuchsia-400/20 to-pink-400/10' },
{ icon: '🖼️', title: '参考图生图', desc: '上传参考换风格、改构图、做变体。', tint: 'from-violet-400/20 to-indigo-400/10' },
{ icon: '🎬', title: '文字生视频', desc: '一段描述生成短片,时长分辨率可选。', tint: 'from-sky-400/20 to-cyan-400/10' },
{ icon: '✨', title: '图生视频', desc: '给张参考图,让静态画面动起来。', tint: 'from-amber-400/20 to-orange-400/10' },
{ icon: '🎨', title: '文字生图', desc: '一句话出图,画幅与分辨率随模型可选。', bg: 'var(--m-mint)' },
{ icon: '🖼️', title: '参考图生图', desc: '上传参考换风格、改构图、做变体。', bg: 'var(--m-pink)' },
{ icon: '🎬', title: '文字生视频', desc: '一段描述生成短片,时长分辨率可选。', bg: 'var(--m-blue)' },
{ icon: '✨', title: '图生视频', desc: '给张参考图,让静态画面动起来。', bg: 'var(--m-yellow)' },
]
</script>
<template>
<div class="space-y-16 pb-8">
<!-- ===== hero ===== -->
<section class="relative overflow-hidden rounded-[2rem] anime-card px-6 py-10 md:px-12 md:py-14">
<div class="anime-stars"></div>
<section class="relative overflow-hidden rounded-[1.4rem] anime-card px-6 py-10 md:px-12 md:py-14">
<div class="memphis-dots absolute inset-0 opacity-40 pointer-events-none"></div>
<div class="relative grid md:grid-cols-[1.1fr_0.9fr] gap-8 items-center">
<div>
<span class="sticker">✦ 关于我们</span>
<h1 class="mt-4 text-4xl md:text-5xl font-extrabold tracking-tight text-[color:var(--fg)]">
嗨,我是 <span class="anime-grad-text">薇薇</span>
<h1 class="mt-4 text-4xl md:text-5xl font-black tracking-tight text-[color:var(--fg)]">
把创作交给 <span class="anime-grad-text">{{ site.title }}</span>
</h1>
<p class="text-[color:var(--fg-2)] mt-5 text-base md:text-lg leading-relaxed max-w-xl">
{{ site.title }} 是一个聚合 GPT、Gemini、Firefly、Flux 等顶级模型的 AI 生图与生视频平台。
把脑海里的画面写成一句话,交给我替你变成图像与视频。
把脑海里的画面写成一句话,剩下的交给我们变成图像与视频。
</p>
<router-link to="/user" class="bubble-btn mt-7">开始创作 →</router-link>
</div>
<div class="grid place-items-center">
<Mascot :size="240" />
<!-- memphis tile cluster standing in for the old mascot illustration -->
<div class="ab-cluster" aria-hidden="true">
<span class="ab-tile ab-1">🎨</span>
<span class="ab-tile ab-2">🖼️</span>
<span class="ab-tile ab-3">🎬</span>
<span class="ab-tile ab-4">✨</span>
<span class="ab-star">✦</span>
</div>
</div>
</section>
<!-- ===== powers ===== -->
<section>
<div class="anime-eyebrow text-fuchsia-500 dark:text-fuchsia-300">能做什么</div>
<h2 class="mt-3 text-2xl md:text-3xl font-extrabold tracking-tight text-[color:var(--fg)]">四种方式,把想象变成画面</h2>
<div class="anime-eyebrow text-[var(--m-orange)]">能做什么</div>
<h2 class="mt-3 text-2xl md:text-3xl font-black tracking-tight text-[color:var(--fg)]">四种方式,把想象变成画面</h2>
<div class="mt-7 grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
<div v-for="p in powers" :key="p.title"
class="anime-card p-5 bg-gradient-to-br" :class="p.tint">
class="power-card p-5" :style="{ background: p.bg }">
<div class="text-3xl">{{ p.icon }}</div>
<div class="mt-3 text-base font-bold text-[color:var(--fg)]">{{ p.title }}</div>
<div class="mt-1.5 text-[13px] leading-relaxed text-[color:var(--fg-3)]">{{ p.desc }}</div>
<div class="mt-3 text-base font-extrabold text-[#111]">{{ p.title }}</div>
<div class="mt-1.5 text-[13px] font-medium leading-relaxed text-[#111]/60">{{ p.desc }}</div>
</div>
</div>
</section>
<!-- ===== contact ===== -->
<section>
<div class="anime-eyebrow text-sky-500 dark:text-sky-300">联系我们</div>
<h2 class="mt-3 text-2xl md:text-3xl font-extrabold tracking-tight text-[color:var(--fg)]">有问题?来找我们玩~</h2>
<div class="anime-eyebrow text-sky-600">联系我们</div>
<h2 class="mt-3 text-2xl md:text-3xl font-black tracking-tight text-[color:var(--fg)]">有问题?来找我们玩~</h2>
<p class="text-[color:var(--fg-3)] mt-2 mb-6">合作、反馈,或想加入交流群,随时招呼。</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 max-w-3xl">
@@ -61,7 +65,7 @@ const powers = [
class="group anime-card flex items-center justify-between gap-3 p-5">
<div>
<div class="sticker">🛍️ 商店</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 group-hover:text-fuchsia-400 transition-colors">前往商店</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 group-hover:text-[var(--m-orange)] transition-colors">前往商店</div>
</div>
<span class="text-[color:var(--fg-faint)] group-hover:translate-x-1 transition-transform">→</span>
</a>
@@ -69,14 +73,14 @@ const powers = [
<a v-if="contact.email" :href="`mailto:${contact.email}`"
class="group anime-card p-5">
<div class="sticker">✉️ 邮箱</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 break-all group-hover:text-sky-400 transition-colors">{{ contact.email }}</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 break-all group-hover:text-sky-600 transition-colors">{{ contact.email }}</div>
</a>
<a v-if="contact.qq && contact.qq_link" :href="contact.qq_link" target="_blank" rel="noopener"
class="group anime-card flex items-center justify-between gap-3 p-5">
<div>
<div class="sticker">💬 QQ</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 tabular-nums break-all group-hover:text-sky-400 transition-colors">{{ contact.qq }}</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 tabular-nums break-all group-hover:text-sky-600 transition-colors">{{ contact.qq }}</div>
</div>
<span class="text-[color:var(--fg-faint)] group-hover:translate-x-1 transition-transform">→</span>
</a>
@@ -89,7 +93,7 @@ const powers = [
class="group anime-card flex items-center justify-between gap-3 p-5">
<div>
<div class="sticker">🎀 QQ 群</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 tabular-nums break-all group-hover:text-emerald-400 transition-colors">{{ contact.qq_group }}</div>
<div class="text-base font-semibold text-[color:var(--fg)] mt-2 tabular-nums break-all group-hover:text-emerald-600 transition-colors">{{ contact.qq_group }}</div>
</div>
<span class="text-[color:var(--fg-faint)] group-hover:translate-x-1 transition-transform">→</span>
</a>
@@ -104,3 +108,42 @@ const powers = [
</section>
</div>
</template>
<style scoped>
/* Crayon tile cluster — a loose collage of rotated ink-outlined squares. */
.ab-cluster {
position: relative;
min-height: 240px;
}
.ab-tile {
position: absolute;
display: grid;
place-items: center;
width: 5.5rem;
height: 5.5rem;
font-size: 2.2rem;
border: 3px solid var(--ink);
border-radius: 1rem;
box-shadow: 6px 6px 0 var(--shadow-ink);
}
.ab-1 { left: 12%; top: 6%; background: var(--m-mint); transform: rotate(-9deg); animation: memphis-float 6s ease-in-out infinite; }
.ab-2 { right: 14%; top: 0; background: var(--m-pink); transform: rotate(7deg); animation: memphis-float 7s ease-in-out infinite reverse; }
.ab-3 { left: 26%; bottom: 4%; background: var(--m-blue); transform: rotate(5deg); animation: memphis-bounce 4.5s ease-in-out infinite; }
.ab-4 { right: 8%; bottom: 14%; background: var(--m-yellow); transform: rotate(-6deg); animation: memphis-float 5.5s ease-in-out infinite; }
.ab-star {
position: absolute;
left: 50%; top: 44%;
font-size: 2.6rem;
color: var(--m-orange);
animation: memphis-wiggle 3.5s ease-in-out infinite;
}
.power-card {
border: 2.5px solid var(--ink);
border-radius: 1rem;
box-shadow: 5px 5px 0 var(--shadow-ink);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.power-card:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--shadow-ink); }
@media (max-width: 768px) { .ab-cluster { display: none; } }
@media (prefers-reduced-motion: reduce) { .ab-tile, .ab-star { animation: none; } }
</style>
+105 -48
View File
@@ -4,8 +4,6 @@ import { useRouter } from 'vue-router'
import { api, generatedUrl } from '../api'
import { site } from '../site'
import { isAuthed, openLogin } from '../auth'
import Mascot from '../components/Mascot.vue'
const router = useRouter()
// Navigate to a signed-in page, or pop the login modal (remembering the
@@ -123,32 +121,32 @@ const caps = computed(() => {
{
key: 't2i', title: '文字生图', model: imageModels.value[0]?.id,
desc: '一句话出图,画幅与分辨率按所选模型的可用范围给出。',
tint: 'rgb(52 211 153 / 0.14)', color: 'rgb(52 211 153)',
tint: '#a7f3d0', color: '#065f46',
metas: [resRange.value, `${imageModels.value.length} 个模型`].filter(Boolean),
},
{
key: 'i2i', title: '参考图生图', model: i2iModels[0]?.id,
desc: '上传参考图换风格、改构图、做变体,支持多张参考。',
tint: 'rgb(217 70 239 / 0.14)', color: 'rgb(232 121 249)',
tint: '#ffc1cc', color: '#9f1239',
metas: ['多图参考', `${i2iModels.length} 个模型`],
},
{
key: 't2v', title: '文字生视频', model: videoModels.value[0]?.id,
desc: '一段描述生成短片,时长与分辨率按模型能力可选。',
tint: 'rgb(14 165 233 / 0.14)', color: 'rgb(56 189 248)',
tint: '#bae6fd', color: '#075985',
metas: [durationRange.value, videoRes.value, `${videoModels.value.length} 个模型`].filter(Boolean),
},
{
key: 'i2v', title: '图生视频', model: i2vModels[0]?.id,
desc: '给参考图或首帧,让静态画面动起来。',
tint: 'rgb(245 158 11 / 0.14)', color: 'rgb(251 191 36)',
tint: '#fef08a', color: '#92400e',
metas: ['参考图', `${i2vModels.length} 个模型`],
},
].filter((c) => c.model)
})
// ---- model cards: the admin-set alias wins, so no upstream naming leaks ----
const MODEL_TINT = ['#a78bfa', '#f0abfc', '#fb7185', '#fbbf24', '#38bdf8', '#34d399', '#94a3b8']
const MODEL_TINT = ['#ec5b13', '#ffc1cc', '#bae6fd', '#a7f3d0', '#fef08a', '#f472b6', '#94a3b8']
const modelCards = computed(() => managed.value.map((m, i) => {
const bits = []
if (m.resolutions?.length) bits.push(m.resolutions.join(' / '))
@@ -199,38 +197,43 @@ function useExample(ex) {
<template>
<div class="px-4 md:px-8">
<!-- ============ HERO (centered, mascot-forward) ============ -->
<section class="hero-band relative overflow-hidden pt-16 md:pt-24 pb-10">
<!-- ============ HERO (centered, memphis deco) ============ -->
<section class="hero-band relative overflow-hidden pt-14 md:pt-20 pb-12">
<div class="anime-stars"></div>
<div class="relative flex flex-col items-center text-center">
<!-- mascot centerpiece with soft aura -->
<div class="relative mb-6">
<span class="mascot-aura"></span>
<Mascot :size="184" class="relative" />
</div>
<!-- floating memphis shapes (desktop only) -->
<div aria-hidden="true" class="pointer-events-none absolute inset-0 hidden lg:block">
<span class="deco deco-a">🎨</span>
<span class="deco deco-b">⚡</span>
<span class="deco deco-c">4K</span>
<span class="deco deco-d">🎬</span>
<span class="deco deco-star">✦</span>
<span class="deco deco-star deco-star-2">✦</span>
</div>
<div class="relative flex flex-col items-center text-center">
<span class="sticker">
<span class="inline-block w-1.5 h-1.5 rounded-full bg-emerald-400 shadow-[0_0_8px_rgb(52_211_153)]"></span>
<span class="inline-block w-1.5 h-1.5 rounded-full bg-emerald-500 border border-[#111]"></span>
{{ modelCount }} 个模型在线 · 图像与视频一站生成
</span>
<h1 class="mt-5 font-extrabold tracking-tight leading-[1.05] text-[clamp(2.6rem,6vw,4.6rem)] text-[color:var(--fg)]">
<h1 class="mt-5 font-black tracking-tight leading-[1.05] text-[clamp(2.6rem,6vw,4.6rem)] text-[color:var(--fg)]">
把想象 <span class="anime-grad-text">说给 AI 听</span>
</h1>
<p class="mt-5 text-[15px] md:text-base text-[color:var(--fg-2)] max-w-xl leading-relaxed">
<p class="mt-5 text-[15px] md:text-base font-medium text-[color:var(--fg-2)] max-w-xl leading-relaxed">
写一句话,剩下的交给我们:自动选模型、自动排队,图像与视频都在同一个工作台里出片。
</p>
<!-- prompt composer — the shortest path into the workbench -->
<form class="composer mt-8 !max-w-2xl w-full" @submit.prevent="submitPrompt">
<form class="composer mt-9 !max-w-2xl w-full" @submit.prevent="submitPrompt">
<input v-model="prompt" class="composer-input" type="text"
placeholder="描述你想要的画面,比如:雨夜的东京小巷,霓虹倒映在湿滑路面…" />
<button type="submit" class="composer-go">✦ 生成</button>
</form>
<!-- quick starters come from the curated bento set -->
<div v-if="bento.length" class="mt-4 flex flex-wrap justify-center gap-2 max-w-2xl">
<div v-if="bento.length" class="mt-5 flex flex-wrap justify-center gap-2 max-w-2xl">
<button v-for="ex in bento.slice(0, 5)" :key="ex.id" type="button" class="chip"
@click="useExample(ex)">{{ ex.title }}</button>
</div>
@@ -238,7 +241,7 @@ function useExample(ex) {
</section>
<!-- ============ GALLERY MARQUEE (hero + bento imagery) ============ -->
<section v-if="gallery.length" class="mt-2 -mx-4 md:-mx-8 overflow-hidden marquee-mask">
<section v-if="gallery.length" class="mt-6 -mx-4 md:-mx-8 overflow-hidden marquee-mask border-y-[3px] border-y-[color:var(--ink)] bg-[var(--surface)] py-4">
<div class="marquee flex gap-4 px-4 md:px-8 w-max">
<button v-for="(card, i) in [...gallery, ...gallery]" :key="card.id + '-' + i" type="button"
class="gcard" :style="cardBg(card)" :aria-label="card.title" @click="useExample(card)">
@@ -254,23 +257,23 @@ function useExample(ex) {
<!-- ============ KPI STRIP ============ -->
<section class="mt-12 grid grid-cols-2 md:grid-cols-4 gap-3">
<div v-for="k in [
{ v: modelCount, l: '在线模型', e: '🧩' },
{ v: caps.length, l: '创作方式', e: '🎨' },
{ v: resRange, l: '输出分辨率', e: '🖼️' },
{ v: avgLabel, l: '平均出片 · 近 24h', e: '⚡' },
{ v: modelCount, l: '在线模型', e: '🧩', bg: 'var(--m-yellow)' },
{ v: caps.length, l: '创作方式', e: '🎨', bg: 'var(--m-pink)' },
{ v: resRange, l: '输出分辨率', e: '🖼️', bg: 'var(--m-blue)' },
{ v: avgLabel, l: '平均出片 · 近 24h', e: '⚡', bg: 'var(--m-mint)' },
]" :key="k.l"
class="anime-card px-5 py-4">
class="kpi-card px-5 py-4" :style="{ background: k.bg }">
<div class="flex items-center gap-2">
<span class="text-lg">{{ k.e }}</span>
<span class="text-[26px] font-bold tabular-nums text-[color:var(--fg)]">{{ k.v }}</span>
<span class="text-[26px] font-black tabular-nums text-[#111]">{{ k.v }}</span>
</div>
<div class="mt-1.5 text-[10px] uppercase tracking-[0.18em] text-[color:var(--fg-3)]">{{ k.l }}</div>
<div class="mt-1.5 text-[10px] font-bold uppercase tracking-[0.18em] text-[#111]/60">{{ k.l }}</div>
</div>
</section>
<!-- ============ CAPABILITIES ============ -->
<section class="mt-24">
<div class="anime-eyebrow text-violet-500 dark:text-violet-300/80">能力</div>
<div class="anime-eyebrow text-[var(--m-orange)]">能力</div>
<h2 class="mt-3 text-[clamp(1.65rem,3vw,2.3rem)] font-extrabold tracking-tight text-[color:var(--fg)]">
{{ caps.length }} 种方式开始创作
</h2>
@@ -299,7 +302,7 @@ function useExample(ex) {
<!-- ============ INSPIRATION (kind=bento) ============ -->
<section v-if="bento.length" id="bento" class="mt-24">
<div class="anime-eyebrow text-fuchsia-500 dark:text-fuchsia-300/80">灵感</div>
<div class="anime-eyebrow text-rose-500">灵感</div>
<h2 class="mt-3 text-[clamp(1.65rem,3vw,2.3rem)] font-extrabold tracking-tight text-[color:var(--fg)]">点一下,套用同款提示词</h2>
<p class="mt-2.5 text-[13.5px] text-[color:var(--fg-3)] max-w-lg leading-relaxed">
任选一张,自动进入画图工作台并预填提示词。
@@ -320,7 +323,7 @@ function useExample(ex) {
<!-- ============ WORKS (kind=work) ============ -->
<section v-if="works.length" class="mt-24">
<div class="anime-eyebrow text-sky-500 dark:text-sky-300/80">作品</div>
<div class="anime-eyebrow text-sky-600">作品</div>
<h2 class="mt-3 text-[clamp(1.65rem,3vw,2.3rem)] font-extrabold tracking-tight text-[color:var(--fg)]">大家都在生成什么</h2>
<p class="mt-2.5 text-[13.5px] text-[color:var(--fg-3)] max-w-lg leading-relaxed">
精选公开作品,点开即用同一条提示词继续创作。
@@ -340,7 +343,7 @@ function useExample(ex) {
<!-- ============ MODELS ============ -->
<section v-if="modelCards.length" class="mt-24">
<div class="anime-eyebrow text-emerald-500 dark:text-emerald-300/80">模型</div>
<div class="anime-eyebrow text-emerald-600">模型</div>
<h2 class="mt-3 text-[clamp(1.65rem,3vw,2.3rem)] font-extrabold tracking-tight text-[color:var(--fg)]">在线模型</h2>
<p class="mt-2.5 text-[13.5px] text-[color:var(--fg-3)] max-w-lg leading-relaxed">
每个模型的分辨率、时长与参考图能力都写在卡片上,选之前就知道能做什么。
@@ -668,27 +671,82 @@ html.dark .model-card { background: rgb(255 255 255 / 0.05); }
.model-tag.is-image { background: var(--m-mint); color: #065f46; }
.model-tag.is-video { background: var(--m-blue); color: #075985; }
/* ---------- hero band + mascot aura ---------- */
/* ---------- hero band + memphis deco ---------- */
.hero-band {
border-radius: 2rem;
}
.mascot-aura {
/* Floating crayon chips scattered around the headline — each is a small
ink-outlined tile with a hard shadow and its own idle animation. */
.deco {
position: absolute;
left: 50%;
top: 50%;
width: 260px;
height: 260px;
transform: translate(-50%, -50%);
border-radius: 50%;
background: radial-gradient(circle, rgb(254 240 138 / 0.9), rgb(236 91 19 / 0.2) 55%, transparent 72%);
filter: blur(14px);
animation: auraPulse 5s ease-in-out infinite;
pointer-events: none;
display: grid;
place-items: center;
border: 3px solid var(--ink);
box-shadow: 5px 5px 0 var(--shadow-ink);
font-weight: 900;
color: #111;
}
@keyframes auraPulse {
0%, 100% { opacity: 0.8; transform: translate(-50%, -50%) scale(1); }
50% { opacity: 1; transform: translate(-50%, -50%) scale(1.08); }
.deco-a {
left: 7%; top: 22%;
width: 3.6rem; height: 3.6rem;
border-radius: 0.9rem;
background: var(--m-yellow);
font-size: 1.5rem;
transform: rotate(-8deg);
animation: memphis-float 6s ease-in-out infinite;
}
.deco-b {
right: 8%; top: 16%;
width: 3rem; height: 3rem;
border-radius: 999px;
background: var(--m-pink);
font-size: 1.2rem;
transform: rotate(6deg);
animation: memphis-wiggle 3.5s ease-in-out infinite;
}
.deco-c {
left: 13%; bottom: 16%;
width: 3.2rem; height: 2.4rem;
border-radius: 0.6rem;
background: var(--m-blue);
font-size: 0.95rem;
letter-spacing: 0.05em;
transform: rotate(9deg);
animation: memphis-float 7s ease-in-out infinite reverse;
}
.deco-d {
right: 13%; bottom: 20%;
width: 3.4rem; height: 2.6rem;
border-radius: 0.7rem;
background: var(--m-mint);
font-size: 1.15rem;
transform: rotate(-7deg);
animation: memphis-bounce 4s ease-in-out infinite;
}
.deco-star {
left: 22%; top: 10%;
border: none;
box-shadow: none;
font-size: 1.7rem;
color: var(--m-orange);
animation: memphis-wiggle 4s ease-in-out infinite;
}
.deco-star-2 {
left: auto; right: 22%; top: auto; bottom: 10%;
font-size: 1.3rem;
color: var(--m-pink);
animation: memphis-wiggle 5s ease-in-out infinite reverse;
}
@media (prefers-reduced-motion: reduce) { .deco { animation: none; } }
/* KPI tiles — flat crayon fills, dark ink type (readable in both themes). */
.kpi-card {
border: 2.5px solid var(--ink);
border-radius: 1rem;
box-shadow: 5px 5px 0 var(--shadow-ink);
transition: transform 0.18s ease, box-shadow 0.18s ease;
}
.kpi-card:hover { transform: translate(-2px, -2px); box-shadow: 8px 8px 0 var(--shadow-ink); }
/* ---------- gallery marquee ---------- */
.marquee-mask {
@@ -724,6 +782,5 @@ html.dark .model-card { background: rgb(255 255 255 / 0.05); }
}
@media (prefers-reduced-motion: reduce) {
.marquee { animation: none; }
.mascot-aura { animation: none; }
}
</style>
+16 -17
View File
@@ -7,7 +7,6 @@ import { auth, refreshMe } from '../auth'
import { api } from '../api'
import { fmtIso } from '../utils/format'
import Icon from '../components/Icon.vue'
import Mascot from '../components/Mascot.vue'
// Reward per completed invite — comes from the backend (credits.invite_reward),
// falling back to 3 until the response lands.
@@ -86,34 +85,34 @@ function toast(m) {
<p class="text-white/45 mt-2">好友用你的链接注册,并完成首次生图后,你得 {{ INVITE_REWARD }} 积分。</p>
</header>
<section class="relative rounded-3xl ring-1 ring-white/[0.08] p-7 md:p-8 overflow-hidden"
style="background: radial-gradient(at 70% 30%,rgba(251,191,36,0.16) 0%, transparent 55%),linear-gradient(180deg,rgba(255,255,255,0.04),rgba(255,255,255,0.02))">
<div class="inline-grid w-10 h-10 rounded-xl bg-amber-500/15 ring-1 ring-amber-400/30 grid place-items-center text-amber-300">
<Icon name="accounts" class="w-4 h-4" />
<section class="relative rounded-2xl p-7 md:p-8 overflow-hidden ink-card">
<div class="memphis-dots absolute inset-0 opacity-30 pointer-events-none"></div>
<div class="relative inline-grid w-11 h-11 rounded-xl bg-[var(--m-yellow)] border-2 border-[color:var(--ink)] shadow-[3px_3px_0_var(--shadow-ink)] grid place-items-center text-[#111]">
<Icon name="accounts" class="w-5 h-5" />
</div>
<!-- summary -->
<div class="mt-6 grid grid-cols-2 gap-3">
<div class="rounded-xl bg-white/[0.04] ring-1 ring-white/[0.06] px-4 py-3">
<div class="text-2xl font-bold tabular-nums">{{ inviteCount }}</div>
<div class="text-[10px] text-white/40 mt-1 uppercase tracking-widest">已邀请</div>
<div class="relative mt-6 grid grid-cols-2 gap-3">
<div class="rounded-xl px-4 py-3 border-2 border-[color:var(--ink)] bg-[var(--m-mint)] shadow-[3px_3px_0_var(--shadow-ink)]">
<div class="text-2xl font-black tabular-nums text-[#111]">{{ inviteCount }}</div>
<div class="text-[10px] text-[#111]/55 mt-1 uppercase tracking-widest font-bold">已邀请</div>
</div>
<div class="rounded-xl bg-white/[0.04] ring-1 ring-white/[0.06] px-4 py-3">
<div class="text-2xl font-bold tabular-nums">{{ inviteEarned.toLocaleString('en-US') }}</div>
<div class="text-[10px] text-white/40 mt-1 uppercase tracking-widest">累计积分</div>
<div class="rounded-xl px-4 py-3 border-2 border-[color:var(--ink)] bg-[var(--m-blue)] shadow-[3px_3px_0_var(--shadow-ink)]">
<div class="text-2xl font-black tabular-nums text-[#111]">{{ inviteEarned.toLocaleString('en-US') }}</div>
<div class="text-[10px] text-[#111]/55 mt-1 uppercase tracking-widest font-bold">累计积分</div>
</div>
</div>
<!-- code -->
<div class="mt-6">
<label class="block text-xs text-white/50 mb-2">邀请码</label>
<div class="relative mt-6">
<label class="block text-xs font-bold text-white/50 mb-2">邀请码</label>
<div class="flex gap-2">
<button @click="copyCode"
class="flex-1 rounded-xl bg-white/[0.05] ring-1 ring-white/10 hover:ring-white/30 hover:bg-white/[0.08] px-4 py-3 text-sm font-mono text-left transition-all">
class="flex-1 rounded-xl border-2 border-[color:var(--ink)] bg-[var(--surface)] px-4 py-3 text-sm font-mono text-left shadow-[3px_3px_0_var(--shadow-ink)] hover:-translate-y-0.5 hover:shadow-[4px_4px_0_var(--shadow-ink)] transition-all">
{{ inviteCode || '—' }}
</button>
<button @click="copyInvite"
class="rounded-xl bg-white text-black hover:bg-white/90 px-5 py-3 text-sm font-semibold transition-colors">
class="rounded-xl border-[2.5px] border-[color:var(--ink)] bg-[var(--m-orange)] text-white px-5 py-3 text-sm font-extrabold shadow-[3px_3px_0_var(--shadow-ink)] hover:-translate-y-0.5 hover:shadow-[4px_4px_0_var(--shadow-ink)] active:translate-y-0.5 active:shadow-none transition-all">
复制链接
</button>
</div>
@@ -134,7 +133,7 @@ function toast(m) {
<div v-if="loading && !records.length" class="text-center text-sm text-white/40 py-16">加载中…</div>
<div v-else-if="!records.length" class="relative overflow-hidden flex flex-col items-center gap-3 text-white/40 py-14">
<div class="anime-stars"></div>
<Mascot :size="110" />
<span class="empty-badge">✦</span>
<span class="text-sm">还没有人通过你的链接注册~快去分享吧</span>
</div>
+1 -2
View File
@@ -8,7 +8,6 @@ import { api, jsonBody } from '../api'
import { openPayment } from '../payment'
import { refreshMe } from '../auth'
import Icon from '../components/Icon.vue'
import Mascot from '../components/Mascot.vue'
const router = useRouter()
const items = ref([])
@@ -118,7 +117,7 @@ async function cont(o) {
<div v-if="loading && !items.length" class="card text-center text-sm text-slate-400 py-24">加载中…</div>
<div v-else-if="!total" class="anime-card relative overflow-hidden flex flex-col items-center gap-3 text-[color:var(--fg-3)] py-20">
<div class="anime-stars"></div>
<Mascot :size="120" />
<span class="empty-badge">✦</span>
<span class="text-sm">还没有充值订单~</span>
</div>
+1 -2
View File
@@ -11,7 +11,6 @@ import { copyText } from '../utils/clipboard'
import { points } from '../credits'
import Icon from '../components/Icon.vue'
import MediaLightbox from '../components/MediaLightbox.vue'
import Mascot from '../components/Mascot.vue'
const router = useRouter()
const items = ref([]) // current server page
@@ -184,7 +183,7 @@ const params = (e) => {
<div v-if="loading && !items.length" class="card text-center text-sm text-slate-400 py-24">加载中…</div>
<div v-else-if="!total" class="anime-card relative overflow-hidden flex flex-col items-center gap-3 text-[color:var(--fg-3)] py-20">
<div class="anime-stars"></div>
<Mascot :size="120" />
<span class="empty-badge">✦</span>
<span class="text-sm">还没有生成日志~</span>
</div>
+1 -2
View File
@@ -7,7 +7,6 @@ import { copyText } from '../utils/clipboard'
import { zipSync } from 'fflate'
import Icon from '../components/Icon.vue'
import MediaLightbox from '../components/MediaLightbox.vue'
import Mascot from '../components/Mascot.vue'
const router = useRouter()
@@ -277,7 +276,7 @@ onUnmounted(() => {
<div v-else-if="!filtered.length"
class="anime-card relative overflow-hidden flex flex-col items-center gap-3 text-[color:var(--fg-3)] py-20">
<div class="anime-stars"></div>
<Mascot :size="120" />
<span class="empty-badge">✦</span>
<span class="text-sm">还没有创作记录,画一张试试吧~</span>
<button @click="router.push('/user')" class="bubble-btn mt-1">开始第一张 ✦</button>
</div>