个人页面更新
This commit is contained in:
12
src/App.vue
12
src/App.vue
@@ -1,6 +1,7 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { Monitor, Smartphone } from 'lucide-vue-next'
|
||||
import CommandPalette from './components/CommandPalette.vue'
|
||||
import PromptSkillFab from './components/PromptSkillFab.vue'
|
||||
|
||||
@@ -47,13 +48,13 @@ onBeforeUnmount(() => {
|
||||
title="桌面视图"
|
||||
aria-label="桌面视图"
|
||||
@click="togglePhone(false)"
|
||||
>🖥</button>
|
||||
><Monitor :size="17" :stroke-width="2" /></button>
|
||||
<button
|
||||
:class="{ active: phoneMode }"
|
||||
title="手机预览"
|
||||
aria-label="手机预览"
|
||||
@click="togglePhone(true)"
|
||||
>📱</button>
|
||||
><Smartphone :size="17" :stroke-width="2" /></button>
|
||||
</div>
|
||||
|
||||
<transition name="phone-pop">
|
||||
@@ -103,6 +104,7 @@ onBeforeUnmount(() => {
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: #d7dae6;
|
||||
font-size: 17px;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
@@ -111,6 +113,12 @@ onBeforeUnmount(() => {
|
||||
opacity: 0.55;
|
||||
transition: background 0.18s ease, opacity 0.18s ease, transform 0.18s ease;
|
||||
}
|
||||
.device-toggle button svg {
|
||||
display: block;
|
||||
}
|
||||
.device-toggle button.active {
|
||||
color: #fff;
|
||||
}
|
||||
.device-toggle button:hover {
|
||||
opacity: 1;
|
||||
transform: scale(1.08);
|
||||
|
||||
@@ -4,6 +4,18 @@ import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.ai
|
||||
|
||||
/* 模型卡 */
|
||||
const mcIdx = ref(-1)
|
||||
const mc = ref(null)
|
||||
function openMc(i) {
|
||||
mcIdx.value = i
|
||||
mc.value = c.cases[i]
|
||||
}
|
||||
function closeMc() {
|
||||
mcIdx.value = -1
|
||||
mc.value = null
|
||||
}
|
||||
|
||||
const question = '用一句话介绍深思大模型'
|
||||
const answer = '我是深思大模型:128K 上下文、40+ 行业方案,既能陪你头脑风暴,也能钻进产线找瑕疵 —— 大事小事,先深思,再回答。'
|
||||
const typedQ = ref('')
|
||||
@@ -148,7 +160,7 @@ onBeforeUnmount(clearAll)
|
||||
<h2 class="al-h2">已经在这些行业「上岗」</h2>
|
||||
</div>
|
||||
<div class="al-cases">
|
||||
<article v-for="(p, i) in c.cases" :key="p.name" class="al-case" :style="{ '--d': i * 0.07 + 's' }">
|
||||
<article v-for="(p, i) in c.cases" :key="p.name" class="al-case" :style="{ '--d': i * 0.07 + 's' }" @click="openMc(i)">
|
||||
<div class="al-case-media">
|
||||
<img :src="p.image" :alt="p.name" loading="lazy" />
|
||||
<span class="al-case-no">CASE 0{{ i + 1 }}</span>
|
||||
@@ -157,11 +169,53 @@ onBeforeUnmount(clearAll)
|
||||
<span class="al-case-tag">{{ p.tag }}</span>
|
||||
<h3>{{ p.name }}</h3>
|
||||
<p>{{ p.desc }}</p>
|
||||
<i class="al-case-more">✦ 查看模型卡</i>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 模型卡 -->
|
||||
<Transition name="al-card">
|
||||
<div v-if="mc" class="al-mask" @click.self="closeMc">
|
||||
<article class="al-modelcard">
|
||||
<button class="al-mc-x" aria-label="关闭" @click="closeMc">✕</button>
|
||||
<header class="al-mc-head">
|
||||
<p class="al-mc-path">deepthink / <b>{{ mc.name }}</b></p>
|
||||
<div class="al-mc-badges">
|
||||
<span>{{ mc.tag }}</span>
|
||||
<span class="ok">production</span>
|
||||
<span class="ctx">128K ctx</span>
|
||||
</div>
|
||||
</header>
|
||||
<img class="al-mc-img" :src="mc.image" :alt="mc.name" />
|
||||
<section class="al-mc-block">
|
||||
<b># Model Description</b>
|
||||
<p>{{ mc.story }}</p>
|
||||
</section>
|
||||
<section class="al-mc-block">
|
||||
<b># Capabilities</b>
|
||||
<ul>
|
||||
<li v-for="h in mc.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="al-mc-cols">
|
||||
<section class="al-mc-block">
|
||||
<b># Release Notes</b>
|
||||
<p v-for="t in mc.timeline" :key="t.at" class="al-mc-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</section>
|
||||
<section class="al-mc-block">
|
||||
<b># Evals</b>
|
||||
<div class="al-mc-mx">
|
||||
<div v-for="m in mc.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<footer class="al-mc-foot">License: 演示用途 · 模型可以很大,落地必须很轻 ✦</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="al-stats">
|
||||
<div v-for="s in c.stats" :key="s.label"><b>{{ s.value }}</b><span>{{ s.label }}</span></div>
|
||||
</div>
|
||||
@@ -779,4 +833,216 @@ onBeforeUnmount(clearAll)
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 模型卡 ---- */
|
||||
.al-case {
|
||||
cursor: pointer;
|
||||
}
|
||||
.al-case-more {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
font-style: normal;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
color: #7c6cff;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.al-case:hover .al-case-more {
|
||||
opacity: 1;
|
||||
}
|
||||
.al-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(4, 4, 10, 0.82);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.al-modelcard {
|
||||
position: relative;
|
||||
width: min(620px, 94vw);
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
background: #0c0c18;
|
||||
border: 1px solid rgba(124, 108, 255, 0.5);
|
||||
border-radius: 16px;
|
||||
color: #e8e5ff;
|
||||
box-shadow: 0 0 50px rgba(124, 108, 255, 0.2), 0 30px 80px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.al-mc-x {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 3;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid rgba(124, 108, 255, 0.45);
|
||||
border-radius: 50%;
|
||||
background: rgba(12, 12, 24, 0.8);
|
||||
color: #c9c2ff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
.al-mc-x:hover {
|
||||
border-color: #7c6cff;
|
||||
box-shadow: 0 0 16px rgba(124, 108, 255, 0.5);
|
||||
}
|
||||
.al-mc-head {
|
||||
padding: 22px 26px 14px;
|
||||
border-bottom: 1px solid rgba(124, 108, 255, 0.25);
|
||||
}
|
||||
.al-mc-path {
|
||||
margin: 0 0 10px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 14px;
|
||||
color: #8f88bd;
|
||||
}
|
||||
.al-mc-path b {
|
||||
color: #fff;
|
||||
font-size: 17px;
|
||||
}
|
||||
.al-mc-badges {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
.al-mc-badges span {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.06em;
|
||||
padding: 3px 12px;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(124, 108, 255, 0.45);
|
||||
color: #c9c2ff;
|
||||
}
|
||||
.al-mc-badges span.ok {
|
||||
border-color: rgba(77, 255, 176, 0.5);
|
||||
color: #4dffb0;
|
||||
}
|
||||
.al-mc-badges span.ctx {
|
||||
border-color: rgba(0, 224, 255, 0.5);
|
||||
color: #00e0ff;
|
||||
}
|
||||
.al-mc-img {
|
||||
display: block;
|
||||
width: calc(100% - 52px);
|
||||
margin: 16px 26px 0;
|
||||
aspect-ratio: 16 / 7;
|
||||
object-fit: cover;
|
||||
border-radius: 10px;
|
||||
border: 1px solid rgba(124, 108, 255, 0.35);
|
||||
}
|
||||
.al-mc-block {
|
||||
padding: 16px 26px 0;
|
||||
}
|
||||
.al-mc-block > b {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 12px;
|
||||
color: #7c6cff;
|
||||
}
|
||||
.al-mc-block p {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.95;
|
||||
color: #b5aede;
|
||||
}
|
||||
.al-mc-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.al-mc-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 20px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #d5d0f2;
|
||||
}
|
||||
.al-mc-block ul li::before {
|
||||
content: '✦';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #00e0ff;
|
||||
font-size: 10px;
|
||||
}
|
||||
.al-mc-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr 1fr;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
.al-mc-tl {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 6px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
.al-mc-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
color: #8f88bd;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.al-mc-mx {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.al-mc-mx > div {
|
||||
background: linear-gradient(160deg, rgba(124, 108, 255, 0.14), rgba(0, 224, 255, 0.05));
|
||||
border: 1px solid rgba(124, 108, 255, 0.35);
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.al-mc-mx b {
|
||||
font-size: 15.5px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 16px rgba(124, 108, 255, 0.7);
|
||||
}
|
||||
.al-mc-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #8f88bd;
|
||||
}
|
||||
.al-mc-foot {
|
||||
padding: 14px 26px 18px;
|
||||
border-top: 1px solid rgba(124, 108, 255, 0.25);
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.1em;
|
||||
color: #8f88bd;
|
||||
text-align: center;
|
||||
}
|
||||
.al-card-enter-active,
|
||||
.al-card-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.al-card-enter-active .al-modelcard,
|
||||
.al-card-leave-active .al-modelcard {
|
||||
transition: transform 0.32s cubic-bezier(0.25, 1.1, 0.4, 1);
|
||||
}
|
||||
.al-card-enter-from,
|
||||
.al-card-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.al-card-enter-from .al-modelcard,
|
||||
.al-card-leave-to .al-modelcard {
|
||||
transform: translateY(26px) scale(0.95);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.al-mc-cols {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -45,6 +45,19 @@ function useSample(s) {
|
||||
prompt.value = s
|
||||
generate()
|
||||
}
|
||||
|
||||
/* 画框详情 */
|
||||
const frameColors = ['#ff6b9d', '#4d96ff', '#6bcb77', '#ffb020']
|
||||
const frIdx = ref(-1)
|
||||
const fr = ref(null)
|
||||
function openFrame(i) {
|
||||
frIdx.value = i
|
||||
fr.value = c.cases[i]
|
||||
}
|
||||
function closeFrame() {
|
||||
frIdx.value = -1
|
||||
fr.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -110,6 +123,57 @@ function useSample(s) {
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 深思全家桶画展 -->
|
||||
<section class="ap-sec">
|
||||
<h2 class="ap-h2">深思全家桶 · 挂进画框看</h2>
|
||||
<div class="ap-frames">
|
||||
<figure
|
||||
v-for="(p, i) in c.cases"
|
||||
:key="p.name"
|
||||
class="ap-framed"
|
||||
:style="{ '--fc': frameColors[i], '--r': ((i % 2 ? 1 : -1) * 1.4) + 'deg' }"
|
||||
@click="openFrame(i)"
|
||||
>
|
||||
<span class="ap-framed-nail" aria-hidden="true"></span>
|
||||
<div class="ap-framed-art"><img :src="p.image" :alt="p.name" loading="lazy" /></div>
|
||||
<figcaption>《{{ p.name }}》<em>{{ p.tag }}</em></figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 画框详情 -->
|
||||
<Transition name="ap-hang">
|
||||
<div v-if="fr" class="ap-mask" @click.self="closeFrame">
|
||||
<article class="ap-easel" :style="{ '--fc': frameColors[frIdx] }">
|
||||
<button class="ap-easel-x" aria-label="关闭" @click="closeFrame">✕</button>
|
||||
<div class="ap-easel-frame">
|
||||
<img :src="fr.image" :alt="fr.name" />
|
||||
<span class="ap-easel-light" aria-hidden="true"></span>
|
||||
</div>
|
||||
<div class="ap-easel-plaque">
|
||||
<h3>《{{ fr.name }}》</h3>
|
||||
<p class="ap-easel-meta">{{ fr.tag }} · 深思智能 · prompt: 「让 AI 落进现实」</p>
|
||||
<p class="ap-easel-story">{{ fr.story }}</p>
|
||||
<div class="ap-easel-block">
|
||||
<b>🎨 亮点笔触</b>
|
||||
<ul>
|
||||
<li v-for="(h, i) in fr.highlights" :key="h" :style="{ '--lc': frameColors[i % 4] }">{{ h }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ap-easel-block">
|
||||
<b>🖌 创作过程</b>
|
||||
<p v-for="t in fr.timeline" :key="t.at" class="ap-easel-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</div>
|
||||
<div class="ap-easel-mx">
|
||||
<div v-for="(m, i) in fr.metrics" :key="m.label" :style="{ '--fc': frameColors[i % 4] }">
|
||||
<b>{{ m.value }}</b><span>{{ m.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="ap-stats">
|
||||
<div v-for="s in c.stats" :key="s.label"><b>{{ s.value }}</b><span>{{ s.label }}</span></div>
|
||||
</div>
|
||||
@@ -572,4 +636,252 @@ function useSample(s) {
|
||||
font-size: 11px !important;
|
||||
color: #6f6996 !important;
|
||||
}
|
||||
|
||||
/* ---- 深思全家桶画展 ---- */
|
||||
.ap-frames {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 22px;
|
||||
}
|
||||
.ap-framed {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding: 12px 12px 8px;
|
||||
background: #fff;
|
||||
border: 8px solid var(--fc, #ff6b9d);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 12px 26px rgba(30, 20, 60, 0.35);
|
||||
transform: rotate(var(--r, 0deg));
|
||||
cursor: pointer;
|
||||
transition: transform 0.22s ease, box-shadow 0.22s ease;
|
||||
}
|
||||
.ap-framed:hover {
|
||||
transform: rotate(0deg) translateY(-6px);
|
||||
box-shadow: 0 20px 40px rgba(30, 20, 60, 0.45);
|
||||
}
|
||||
.ap-framed-nail {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -18px;
|
||||
transform: translateX(-50%);
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background: #d8d3e8;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.ap-framed-art {
|
||||
overflow: hidden;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.ap-framed-art img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
object-fit: cover;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
.ap-framed:hover .ap-framed-art img {
|
||||
transform: scale(1.05);
|
||||
}
|
||||
.ap-framed figcaption {
|
||||
padding: 8px 2px 4px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: #3a3355;
|
||||
}
|
||||
.ap-framed figcaption em {
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
font-weight: 400;
|
||||
color: #8f88ad;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.ap-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(24, 16, 44, 0.76);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
.ap-easel {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
width: min(880px, 94vw);
|
||||
max-height: 88vh;
|
||||
background: #f7f5fc;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 32px 84px rgba(10, 5, 30, 0.6);
|
||||
}
|
||||
.ap-easel-x {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 3;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: rgba(58, 51, 85, 0.85);
|
||||
color: #fff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: transform 0.16s ease;
|
||||
}
|
||||
.ap-easel-x:hover {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
.ap-easel-frame {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 50% at 50% 0%, rgba(255, 255, 255, 0.16), transparent 65%),
|
||||
#241d3e;
|
||||
padding: clamp(22px, 4vw, 44px);
|
||||
}
|
||||
.ap-easel-frame img {
|
||||
width: 100%;
|
||||
max-height: 66vh;
|
||||
object-fit: cover;
|
||||
border: 12px solid var(--fc, #ff6b9d);
|
||||
border-radius: 4px;
|
||||
outline: 4px solid #fff;
|
||||
box-shadow: 0 18px 44px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
.ap-easel-light {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
transform: translateX(-50%);
|
||||
background: linear-gradient(180deg, rgba(255, 244, 214, 0.16), transparent 62%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.ap-easel-plaque {
|
||||
padding: clamp(22px, 3.4vw, 36px) clamp(20px, 3vw, 32px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ap-easel-plaque h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: clamp(21px, 2.8vw, 27px);
|
||||
font-weight: 900;
|
||||
color: #2c2547;
|
||||
}
|
||||
.ap-easel-meta {
|
||||
margin: 0 0 12px;
|
||||
font-size: 11.5px;
|
||||
color: #8f88ad;
|
||||
}
|
||||
.ap-easel-story {
|
||||
margin: 0 0 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.95;
|
||||
color: #4c4470;
|
||||
}
|
||||
.ap-easel-block {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.ap-easel-block > b {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 13px;
|
||||
color: #2c2547;
|
||||
}
|
||||
.ap-easel-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.ap-easel-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 20px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #4c4470;
|
||||
}
|
||||
.ap-easel-block ul li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 11px;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 3px;
|
||||
background: var(--lc, #ff6b9d);
|
||||
transform: rotate(20deg);
|
||||
}
|
||||
.ap-easel-tl {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 5px !important;
|
||||
}
|
||||
.ap-easel-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
color: #7c6cff;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ap-easel-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
}
|
||||
.ap-easel-mx > div {
|
||||
background: #fff;
|
||||
border: 2.5px solid var(--fc, #ff6b9d);
|
||||
border-radius: 12px;
|
||||
padding: 9px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
.ap-easel-mx b {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 900;
|
||||
color: #2c2547;
|
||||
}
|
||||
.ap-easel-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #8f88ad;
|
||||
}
|
||||
.ap-hang-enter-active,
|
||||
.ap-hang-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.ap-hang-enter-active .ap-easel {
|
||||
transition: transform 0.4s cubic-bezier(0.3, 1.3, 0.5, 1);
|
||||
transform-origin: 50% -40px;
|
||||
}
|
||||
.ap-hang-leave-active .ap-easel {
|
||||
transition: transform 0.22s ease;
|
||||
}
|
||||
.ap-hang-enter-from,
|
||||
.ap-hang-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ap-hang-enter-from .ap-easel {
|
||||
transform: rotate(-3deg) translateY(-22px);
|
||||
}
|
||||
.ap-hang-leave-to .ap-easel {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.ap-easel {
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ap-easel-frame img {
|
||||
max-height: 34vh;
|
||||
}
|
||||
.ap-easel-plaque {
|
||||
overflow-y: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.ai
|
||||
|
||||
/* 参数铭牌 */
|
||||
const plateIdx = ref(-1)
|
||||
const plate = ref(null)
|
||||
function openPlate(i) {
|
||||
plateIdx.value = i
|
||||
plate.value = c.cases[i]
|
||||
}
|
||||
function closePlate() {
|
||||
plateIdx.value = -1
|
||||
plate.value = null
|
||||
}
|
||||
|
||||
const specs = [
|
||||
{ k: '重复定位精度', v: '±0.02mm' },
|
||||
{ k: '最大负载', v: '35kg' },
|
||||
@@ -173,7 +187,7 @@ const scenes = [
|
||||
<section class="ar-sec">
|
||||
<h2 class="ar-h2">// 与深思大模型协同</h2>
|
||||
<div class="ar-cases">
|
||||
<article v-for="(p, i) in c.cases" :key="p.name" class="ar-case" :style="{ '--d': i * 0.06 + 's' }">
|
||||
<article v-for="(p, i) in c.cases" :key="p.name" class="ar-case" :style="{ '--d': i * 0.06 + 's' }" @click="openPlate(i)">
|
||||
<div class="ar-case-head">
|
||||
<span>COOP-FILE 0{{ i + 1 }} · {{ p.tag }}</span>
|
||||
<i class="ar-case-led" aria-hidden="true"></i>
|
||||
@@ -183,12 +197,58 @@ const scenes = [
|
||||
<div>
|
||||
<h3>{{ p.name }}</h3>
|
||||
<p>{{ p.desc }}</p>
|
||||
<i class="ar-case-more">READ NAMEPLATE ▸</i>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 参数铭牌 -->
|
||||
<Transition name="ar-bolt">
|
||||
<div v-if="plate" class="ar-mask" @click.self="closePlate">
|
||||
<article class="ar-plate">
|
||||
<i class="ar-rivet tl" aria-hidden="true"></i>
|
||||
<i class="ar-rivet tr" aria-hidden="true"></i>
|
||||
<i class="ar-rivet bl" aria-hidden="true"></i>
|
||||
<i class="ar-rivet br" aria-hidden="true"></i>
|
||||
<button class="ar-plate-x" aria-label="关闭" @click="closePlate">✕</button>
|
||||
<header class="ar-plate-head">
|
||||
<p class="ar-plate-kicker">DEEPTHINK ROBOTICS · NAMEPLATE</p>
|
||||
<h3>{{ plate.name }}</h3>
|
||||
<span class="ar-plate-model">MODEL DT-{{ 100 + plateIdx * 11 }} · {{ plate.tag }}</span>
|
||||
</header>
|
||||
<div class="ar-plate-img">
|
||||
<img :src="plate.image" :alt="plate.name" />
|
||||
<span class="ar-plate-warn" aria-hidden="true">⚠ 运行区域 · 请勿倚靠</span>
|
||||
</div>
|
||||
<section class="ar-plate-block">
|
||||
<b>// DESIGN LOG 研发志</b>
|
||||
<p>{{ plate.story }}</p>
|
||||
</section>
|
||||
<section class="ar-plate-block">
|
||||
<b>// RATED SPECS 额定参数</b>
|
||||
<ul>
|
||||
<li v-for="h in plate.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="ar-plate-cols">
|
||||
<section class="ar-plate-block">
|
||||
<b>// SERVICE RECORD 服役记录</b>
|
||||
<p v-for="t in plate.timeline" :key="t.at" class="ar-plate-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</section>
|
||||
<section class="ar-plate-block">
|
||||
<b>// GAUGES 表盘</b>
|
||||
<div class="ar-plate-mx">
|
||||
<div v-for="m in plate.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<footer class="ar-plate-foot">SN 2026-{{ String(plateIdx + 1).padStart(4, '0') }} · MADE BY DEEPTHINK · 铭牌为风格演示</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="ar-stats">
|
||||
<div v-for="s in c.stats" :key="s.label"><b>{{ s.value }}</b><span>{{ s.label }}</span></div>
|
||||
</div>
|
||||
@@ -718,4 +778,248 @@ const scenes = [
|
||||
grid-template-columns: 96px 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 参数铭牌 ---- */
|
||||
.ar-case {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ar-case-more {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #ffb020;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.ar-case:hover .ar-case-more {
|
||||
opacity: 1;
|
||||
}
|
||||
.ar-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(10, 11, 13, 0.82);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.ar-plate {
|
||||
position: relative;
|
||||
width: min(600px, 94vw);
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
background:
|
||||
repeating-linear-gradient(45deg, transparent 0 22px, rgba(255, 176, 32, 0.04) 22px 24px),
|
||||
linear-gradient(170deg, #26282e, #1b1d21);
|
||||
border: 2px solid #3a3d44;
|
||||
border-radius: 10px;
|
||||
color: #e8e9eb;
|
||||
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08), 0 30px 76px rgba(0, 0, 0, 0.7);
|
||||
}
|
||||
.ar-rivet {
|
||||
position: absolute;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 34% 30%, #6a6e77, #2c2e33 70%);
|
||||
box-shadow: inset 0 -1.5px 3px rgba(0, 0, 0, 0.6), 0 1px 1px rgba(255, 255, 255, 0.1);
|
||||
z-index: 2;
|
||||
}
|
||||
.ar-rivet.tl { left: 10px; top: 10px; }
|
||||
.ar-rivet.tr { right: 10px; top: 10px; }
|
||||
.ar-rivet.bl { left: 10px; bottom: 10px; }
|
||||
.ar-rivet.br { right: 10px; bottom: 10px; }
|
||||
.ar-plate-x {
|
||||
position: absolute;
|
||||
right: 30px;
|
||||
top: 16px;
|
||||
z-index: 3;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: 1.5px solid #4a4d55;
|
||||
border-radius: 6px;
|
||||
background: #26282e;
|
||||
color: #ffb020;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.18s ease;
|
||||
}
|
||||
.ar-plate-x:hover {
|
||||
border-color: #ffb020;
|
||||
}
|
||||
.ar-plate-head {
|
||||
padding: 24px 32px 12px;
|
||||
border-bottom: 2px solid #3a3d44;
|
||||
}
|
||||
.ar-plate-kicker {
|
||||
margin: 0 0 8px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.26em;
|
||||
color: #8d9099;
|
||||
}
|
||||
.ar-plate-head h3 {
|
||||
margin: 0 0 6px;
|
||||
font-size: clamp(21px, 2.8vw, 26px);
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.ar-plate-model {
|
||||
display: inline-block;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.14em;
|
||||
color: #ffb020;
|
||||
border: 1px solid rgba(255, 176, 32, 0.45);
|
||||
padding: 3px 12px;
|
||||
}
|
||||
.ar-plate-img {
|
||||
position: relative;
|
||||
margin: 16px 32px 0;
|
||||
border: 2px solid #3a3d44;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ar-plate-img img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 7;
|
||||
object-fit: cover;
|
||||
filter: saturate(0.8) contrast(1.05);
|
||||
}
|
||||
.ar-plate-warn {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
background: repeating-linear-gradient(-45deg, rgba(255, 176, 32, 0.9) 0 14px, #1b1d21 14px 28px);
|
||||
color: #1b1d21;
|
||||
font-size: 10px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.14em;
|
||||
text-align: center;
|
||||
padding: 3px 0;
|
||||
text-shadow: 0 0 4px rgba(255, 232, 178, 0.9);
|
||||
}
|
||||
.ar-plate-block {
|
||||
padding: 16px 32px 0;
|
||||
}
|
||||
.ar-plate-block > b {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #ffb020;
|
||||
}
|
||||
.ar-plate-block p {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.95;
|
||||
color: #b8bac0;
|
||||
}
|
||||
.ar-plate-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
border: 1px solid #3a3d44;
|
||||
}
|
||||
.ar-plate-block ul li {
|
||||
position: relative;
|
||||
padding: 8px 12px 8px 30px;
|
||||
font-size: 12.5px;
|
||||
color: #d5d7db;
|
||||
}
|
||||
.ar-plate-block ul li + li {
|
||||
border-top: 1px solid #2c2e33;
|
||||
}
|
||||
.ar-plate-block ul li::before {
|
||||
content: '▣';
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
color: #ffb020;
|
||||
font-size: 10px;
|
||||
}
|
||||
.ar-plate-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr 1fr;
|
||||
gap: 4px;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.ar-plate-tl {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 7px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
.ar-plate-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
color: #8d9099;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ar-plate-mx {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.ar-plate-mx > div {
|
||||
background: #1b1d21;
|
||||
border: 1px solid #3a3d44;
|
||||
border-left: 3px solid #ffb020;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.ar-plate-mx b {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
color: #ffb020;
|
||||
text-shadow: 0 0 12px rgba(255, 176, 32, 0.4);
|
||||
}
|
||||
.ar-plate-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #8d9099;
|
||||
}
|
||||
.ar-plate-foot {
|
||||
padding: 12px 32px 20px;
|
||||
border-top: 2px solid #3a3d44;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.18em;
|
||||
color: #8d9099;
|
||||
text-align: center;
|
||||
}
|
||||
.ar-bolt-enter-active,
|
||||
.ar-bolt-leave-active {
|
||||
transition: opacity 0.24s ease;
|
||||
}
|
||||
.ar-bolt-enter-active .ar-plate {
|
||||
transition: transform 0.3s cubic-bezier(0.2, 1.2, 0.4, 1);
|
||||
}
|
||||
.ar-bolt-leave-active .ar-plate {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.ar-bolt-enter-from,
|
||||
.ar-bolt-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ar-bolt-enter-from .ar-plate {
|
||||
transform: translateY(-24px) scale(1.03);
|
||||
}
|
||||
.ar-bolt-leave-to .ar-plate {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.ar-plate-cols {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.ai
|
||||
|
||||
/* 识别报告 */
|
||||
const rptIdx = ref(-1)
|
||||
const rpt = ref(null)
|
||||
function openRpt(i) {
|
||||
rptIdx.value = i
|
||||
rpt.value = c.cases[i]
|
||||
}
|
||||
function closeRpt() {
|
||||
rptIdx.value = -1
|
||||
rpt.value = null
|
||||
}
|
||||
|
||||
const detections = [
|
||||
{ label: '安全帽 ✓', conf: '99.2%', x: '12%', y: '18%', w: '30%', h: '38%' },
|
||||
{ label: '人员越界 ⚠', conf: '97.6%', x: '56%', y: '30%', w: '32%', h: '46%' },
|
||||
@@ -116,6 +130,69 @@ const events = [
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 识别档案库 -->
|
||||
<section class="av2-sec">
|
||||
<h2 class="av2-h2">// 识别档案库 · 深思产品矩阵</h2>
|
||||
<div class="av2-files">
|
||||
<button v-for="(p, i) in c.cases" :key="p.name" class="av2-file" :style="{ '--d': i * 0.06 + 's' }" @click="openRpt(i)">
|
||||
<div class="av2-file-cam">
|
||||
<img :src="p.image" :alt="p.name" loading="lazy" />
|
||||
<span class="av2-file-id">TARGET-0{{ i + 1 }}</span>
|
||||
<i class="av2-file-focus" aria-hidden="true"></i>
|
||||
</div>
|
||||
<b>{{ p.name }}</b>
|
||||
<span>{{ p.tag }} · 点击生成识别报告</span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 识别报告 -->
|
||||
<Transition name="av2-boot">
|
||||
<div v-if="rpt" class="av2-mask" @click.self="closeRpt">
|
||||
<article class="av2-report">
|
||||
<div class="av2-report-bar">
|
||||
<span><i class="av2-report-dot" aria-hidden="true"></i>RECOGNITION REPORT · TARGET-0{{ rptIdx + 1 }}</span>
|
||||
<button class="av2-report-x" aria-label="关闭" @click="closeRpt">✕</button>
|
||||
</div>
|
||||
<div class="av2-report-cam">
|
||||
<img :src="rpt.image" :alt="rpt.name" />
|
||||
<span class="av2-scan" aria-hidden="true"></span>
|
||||
<div class="av2-box av2-report-box">
|
||||
<b>{{ rpt.name }} ✓</b>
|
||||
<span>99.{{ 4 + rptIdx }}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<header class="av2-report-head">
|
||||
<h3>{{ rpt.name }}</h3>
|
||||
<span>{{ rpt.tag }} · CLASS: PRODUCT · STATUS: <em>VERIFIED</em></span>
|
||||
</header>
|
||||
<section class="av2-report-block">
|
||||
<b>// ANALYSIS 分析摘要</b>
|
||||
<p>{{ rpt.story }}</p>
|
||||
</section>
|
||||
<section class="av2-report-block">
|
||||
<b>// FEATURES 检出特征</b>
|
||||
<ul>
|
||||
<li v-for="(h, i) in rpt.highlights" :key="h"><em>F{{ i + 1 }}</em>{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="av2-report-cols">
|
||||
<section class="av2-report-block">
|
||||
<b>// EVENT LOG 事件日志</b>
|
||||
<p v-for="t in rpt.timeline" :key="t.at" class="av2-report-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</section>
|
||||
<section class="av2-report-block">
|
||||
<b>// CONFIDENCE 置信数据</b>
|
||||
<div class="av2-report-mx">
|
||||
<div v-for="m in rpt.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<footer class="av2-report-foot">REPORT SIGNED BY DEEPTHINK VISION · 数据为风格演示</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="av2-stats">
|
||||
<div v-for="s in c.stats" :key="s.label"><b>{{ s.value }}</b><span>{{ s.label }}</span></div>
|
||||
</div>
|
||||
@@ -699,4 +776,304 @@ const events = [
|
||||
.av2-feat-txt { grid-area: txt; }
|
||||
.av2-feat-status { grid-area: st; justify-self: end; }
|
||||
}
|
||||
|
||||
/* ---- 识别档案库 · 识别报告 ---- */
|
||||
.av2-files {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||
gap: 14px;
|
||||
}
|
||||
.av2-file {
|
||||
padding: 0 0 12px;
|
||||
background: #071a11;
|
||||
border: 1px solid rgba(0, 255, 170, 0.3);
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-family: inherit;
|
||||
color: inherit;
|
||||
animation: av2-file-in 0.5s ease both;
|
||||
animation-delay: var(--d, 0s);
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
@keyframes av2-file-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px);
|
||||
}
|
||||
}
|
||||
.av2-file:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: #00ffaa;
|
||||
box-shadow: 0 0 26px rgba(0, 255, 170, 0.18);
|
||||
}
|
||||
.av2-file-cam {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
.av2-file-cam img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 9;
|
||||
object-fit: cover;
|
||||
filter: saturate(0.75) hue-rotate(8deg);
|
||||
}
|
||||
.av2-file-id {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 8px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #00ffaa;
|
||||
background: rgba(4, 18, 12, 0.85);
|
||||
padding: 3px 8px;
|
||||
}
|
||||
.av2-file-focus {
|
||||
position: absolute;
|
||||
inset: 12px;
|
||||
border: 1px dashed rgba(0, 255, 170, 0.5);
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
.av2-file:hover .av2-file-focus {
|
||||
opacity: 1;
|
||||
animation: av2-focus 1.2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes av2-focus {
|
||||
50% { inset: 8px; }
|
||||
}
|
||||
.av2-file b {
|
||||
display: block;
|
||||
padding: 10px 14px 2px;
|
||||
font-size: 14px;
|
||||
color: #d9ffef;
|
||||
}
|
||||
.av2-file span {
|
||||
display: block;
|
||||
padding: 0 14px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10px;
|
||||
color: #4e8f74;
|
||||
letter-spacing: 0.05em;
|
||||
}
|
||||
.av2-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(1, 8, 5, 0.86);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.av2-report {
|
||||
position: relative;
|
||||
width: min(620px, 94vw);
|
||||
max-height: 88vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: #04120c;
|
||||
border: 1px solid rgba(0, 255, 170, 0.45);
|
||||
color: #d9ffef;
|
||||
box-shadow: 0 0 46px rgba(0, 255, 170, 0.14), 0 30px 80px rgba(0, 0, 0, 0.7);
|
||||
overflow: hidden;
|
||||
}
|
||||
.av2-report-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
padding: 10px 16px;
|
||||
background: rgba(0, 255, 170, 0.07);
|
||||
border-bottom: 1px solid rgba(0, 255, 170, 0.35);
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #00ffaa;
|
||||
}
|
||||
.av2-report-bar > span {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.av2-report-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ff5f56;
|
||||
animation: av2-rec-dot 1.2s ease-in-out infinite;
|
||||
}
|
||||
@keyframes av2-rec-dot {
|
||||
50% { opacity: 0.25; }
|
||||
}
|
||||
.av2-report-x {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid rgba(0, 255, 170, 0.4);
|
||||
background: transparent;
|
||||
color: #00ffaa;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.av2-report-x:hover {
|
||||
background: rgba(0, 255, 170, 0.15);
|
||||
}
|
||||
.av2-report-cam {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.av2-report-cam img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 6.5;
|
||||
object-fit: cover;
|
||||
filter: saturate(0.75) hue-rotate(8deg);
|
||||
}
|
||||
.av2-report-box {
|
||||
left: 10%;
|
||||
top: 16%;
|
||||
width: 34%;
|
||||
height: 58%;
|
||||
position: absolute;
|
||||
}
|
||||
.av2-report-head {
|
||||
padding: 14px 24px 0;
|
||||
}
|
||||
.av2-report-head h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: clamp(19px, 2.6vw, 24px);
|
||||
font-weight: 800;
|
||||
color: #eafff6;
|
||||
text-shadow: 0 0 18px rgba(0, 255, 170, 0.4);
|
||||
}
|
||||
.av2-report-head span {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.12em;
|
||||
color: #4e8f74;
|
||||
}
|
||||
.av2-report-head em {
|
||||
font-style: normal;
|
||||
color: #00ffaa;
|
||||
}
|
||||
.av2-report {
|
||||
overflow-y: auto;
|
||||
}
|
||||
.av2-report-block {
|
||||
padding: 14px 24px 0;
|
||||
}
|
||||
.av2-report-block > b {
|
||||
display: block;
|
||||
margin-bottom: 7px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #00ffaa;
|
||||
}
|
||||
.av2-report-block p {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.95;
|
||||
color: #a8d4c0;
|
||||
}
|
||||
.av2-report-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.av2-report-block ul li {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 4px 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #c6ecd9;
|
||||
}
|
||||
.av2-report-block ul li em {
|
||||
flex: 0 0 26px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
color: #4e8f74;
|
||||
}
|
||||
.av2-report-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1.25fr 1fr;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
.av2-report-tl {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 6px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
.av2-report-tl i {
|
||||
flex: 0 0 60px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
color: #4e8f74;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.av2-report-mx {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.av2-report-mx > div {
|
||||
border: 1px solid rgba(0, 255, 170, 0.3);
|
||||
background: rgba(0, 255, 170, 0.05);
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
}
|
||||
.av2-report-mx b {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 15px;
|
||||
color: #00ffaa;
|
||||
text-shadow: 0 0 12px rgba(0, 255, 170, 0.5);
|
||||
}
|
||||
.av2-report-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #4e8f74;
|
||||
}
|
||||
.av2-report-foot {
|
||||
padding: 12px 24px 18px;
|
||||
border-top: 1px solid rgba(0, 255, 170, 0.25);
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.16em;
|
||||
color: #4e8f74;
|
||||
text-align: center;
|
||||
}
|
||||
.av2-boot-enter-active,
|
||||
.av2-boot-leave-active {
|
||||
transition: opacity 0.22s ease;
|
||||
}
|
||||
.av2-boot-enter-active .av2-report {
|
||||
transition: transform 0.3s cubic-bezier(0.25, 1.1, 0.4, 1);
|
||||
}
|
||||
.av2-boot-leave-active .av2-report {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.av2-boot-enter-from,
|
||||
.av2-boot-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.av2-boot-enter-from .av2-report {
|
||||
transform: scaleY(0.5);
|
||||
}
|
||||
.av2-boot-leave-to .av2-report {
|
||||
transform: scale(0.97);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.av2-report-cols {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -35,6 +35,18 @@ const skills = [
|
||||
{ icon: '🎵', name: '氛围点唱', desc: '按心情选歌,支持哼唱识曲' },
|
||||
{ icon: '👵', name: '长辈模式', desc: '方言识别 + 大音量慢速播报' },
|
||||
]
|
||||
|
||||
/* 声纹卡 */
|
||||
const vpIdx = ref(-1)
|
||||
const vp = ref(null)
|
||||
function openVp(i) {
|
||||
vpIdx.value = i
|
||||
vp.value = c.cases[i]
|
||||
}
|
||||
function closeVp() {
|
||||
vpIdx.value = -1
|
||||
vp.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -106,6 +118,57 @@ const skills = [
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 深思全家桶 · 语音介绍 -->
|
||||
<section class="av-sec">
|
||||
<h2 class="av-h2">问它:「深思家还有什么?」</h2>
|
||||
<div class="av-vps">
|
||||
<button v-for="(p, i) in c.cases" :key="p.name" class="av-vp" :style="{ '--d': i * 0.07 + 's' }" @click="openVp(i)">
|
||||
<img :src="p.image" :alt="p.name" loading="lazy" />
|
||||
<span class="av-vp-txt">
|
||||
<b>{{ p.name }}</b>
|
||||
<em>{{ p.tag }} · 点击听语音介绍</em>
|
||||
</span>
|
||||
<span class="av-eq av-vp-eq" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i></span>
|
||||
</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 声纹卡 -->
|
||||
<Transition name="av-pop">
|
||||
<div v-if="vp" class="av-mask" @click.self="closeVp">
|
||||
<article class="av-voicecard">
|
||||
<button class="av-vc-x" aria-label="关闭" @click="closeVp">✕</button>
|
||||
<header class="av-vc-head">
|
||||
<span class="av-vc-orb" aria-hidden="true"><i></i><i></i><i></i></span>
|
||||
<h3>{{ vp.name }}</h3>
|
||||
<p>声纹卡 VOICEPRINT · {{ vp.tag }} · 0:{{ 32 + vpIdx * 9 }}″</p>
|
||||
</header>
|
||||
<div class="av-vc-wave" aria-hidden="true">
|
||||
<i v-for="n in 36" :key="n" :style="{ '--n': n }"></i>
|
||||
</div>
|
||||
<img class="av-vc-img" :src="vp.image" :alt="vp.name" />
|
||||
<section class="av-vc-block">
|
||||
<b>📝 语音转写</b>
|
||||
<p>「{{ vp.story }}」</p>
|
||||
</section>
|
||||
<section class="av-vc-block">
|
||||
<b>◉ 重点摘录</b>
|
||||
<ul>
|
||||
<li v-for="h in vp.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="av-vc-block">
|
||||
<b>🕐 时间轴书签</b>
|
||||
<p v-for="(t, i) in vp.timeline" :key="t.at" class="av-vc-tl"><i>0:{{ String(8 + i * 11).padStart(2, '0') }}″</i><em>{{ t.at }}</em>{{ t.event }}</p>
|
||||
</section>
|
||||
<div class="av-vc-mx">
|
||||
<div v-for="m in vp.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
<footer class="av-vc-foot">本条语音由深思语音合成(演示) · 声纹已脱敏</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<div class="av-stats">
|
||||
<div v-for="s in c.stats" :key="s.label"><b>{{ s.value }}</b><span>{{ s.label }}</span></div>
|
||||
</div>
|
||||
@@ -603,4 +666,276 @@ const skills = [
|
||||
font-size: 11px !important;
|
||||
color: #9bbdb7 !important;
|
||||
}
|
||||
|
||||
/* ---- 深思全家桶 · 声纹卡 ---- */
|
||||
.av-vps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
.av-vp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 12px 16px;
|
||||
background: #fff;
|
||||
border: 1.5px solid #d5eeea;
|
||||
border-radius: 999px;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
font-family: inherit;
|
||||
animation: av-vp-in 0.5s ease both;
|
||||
animation-delay: var(--d, 0s);
|
||||
transition: transform 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
@keyframes av-vp-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(12px);
|
||||
}
|
||||
}
|
||||
.av-vp:hover {
|
||||
transform: translateY(-3px);
|
||||
border-color: #2ec4b6;
|
||||
box-shadow: 0 12px 26px rgba(46, 196, 182, 0.16);
|
||||
}
|
||||
.av-vp img {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 50%;
|
||||
object-fit: cover;
|
||||
border: 2px solid #d5eeea;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.av-vp-txt {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
.av-vp-txt b {
|
||||
display: block;
|
||||
font-size: 13.5px;
|
||||
color: #1d4a44;
|
||||
}
|
||||
.av-vp-txt em {
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
color: #7ba8a1;
|
||||
}
|
||||
.av-vp-eq {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.av-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(16, 66, 60, 0.5);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
.av-voicecard {
|
||||
position: relative;
|
||||
width: min(460px, 94vw);
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
background: #f7fbfa;
|
||||
border-radius: 24px;
|
||||
color: #1d4a44;
|
||||
box-shadow: 0 28px 66px rgba(16, 66, 60, 0.4);
|
||||
}
|
||||
.av-vc-x {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
top: 12px;
|
||||
z-index: 3;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border: none;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
color: #fff;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.av-vc-x:hover {
|
||||
background: rgba(255, 255, 255, 0.48);
|
||||
}
|
||||
.av-vc-head {
|
||||
padding: 22px 24px 12px;
|
||||
background: linear-gradient(160deg, #2ec4b6, #1d9f92);
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
}
|
||||
.av-vc-orb {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.av-vc-orb i {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(255, 255, 255, 0.7);
|
||||
animation: av-vc-pulse 2s ease-out infinite;
|
||||
}
|
||||
.av-vc-orb i:nth-child(2) { animation-delay: 0.55s; }
|
||||
.av-vc-orb i:nth-child(3) { animation-delay: 1.1s; }
|
||||
@keyframes av-vc-pulse {
|
||||
from {
|
||||
transform: scale(0.45);
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
transform: scale(1.2);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.av-vc-head h3 {
|
||||
margin: 0 0 4px;
|
||||
font-size: 21px;
|
||||
font-weight: 800;
|
||||
}
|
||||
.av-vc-head p {
|
||||
margin: 0;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.14em;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.av-vc-wave {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 3px;
|
||||
height: 54px;
|
||||
padding: 0 24px;
|
||||
background: linear-gradient(160deg, #1d9f92, #17877c);
|
||||
}
|
||||
.av-vc-wave i {
|
||||
width: 4px;
|
||||
border-radius: 2px;
|
||||
background: rgba(255, 255, 255, 0.85);
|
||||
height: calc(8px + (var(--n) * 7 % 30) * 1px);
|
||||
animation: av-vc-bar 1s ease-in-out infinite;
|
||||
animation-delay: calc(var(--n) * -0.08s);
|
||||
}
|
||||
@keyframes av-vc-bar {
|
||||
50% { transform: scaleY(0.35); }
|
||||
}
|
||||
.av-vc-img {
|
||||
display: block;
|
||||
width: calc(100% - 48px);
|
||||
margin: 16px 24px 0;
|
||||
aspect-ratio: 16 / 7;
|
||||
object-fit: cover;
|
||||
border-radius: 14px;
|
||||
border: 2px solid #d5eeea;
|
||||
}
|
||||
.av-vc-block {
|
||||
padding: 14px 24px 0;
|
||||
}
|
||||
.av-vc-block > b {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 12.5px;
|
||||
color: #17877c;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.av-vc-block p {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.95;
|
||||
color: #40685f;
|
||||
}
|
||||
.av-vc-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.av-vc-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 20px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #40685f;
|
||||
}
|
||||
.av-vc-block ul li::before {
|
||||
content: '◉';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #2ec4b6;
|
||||
font-size: 10px;
|
||||
}
|
||||
.av-vc-tl {
|
||||
display: flex !important;
|
||||
gap: 8px;
|
||||
margin: 0 0 5px !important;
|
||||
align-items: baseline;
|
||||
}
|
||||
.av-vc-tl i {
|
||||
flex: 0 0 44px;
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
font-weight: 700;
|
||||
color: #2ec4b6;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.av-vc-tl em {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
color: #7ba8a1;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.av-vc-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
padding: 14px 24px 0;
|
||||
}
|
||||
.av-vc-mx > div {
|
||||
background: #fff;
|
||||
border: 2px solid #d5eeea;
|
||||
border-radius: 14px;
|
||||
padding: 9px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
.av-vc-mx b {
|
||||
display: block;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
color: #17877c;
|
||||
}
|
||||
.av-vc-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #7ba8a1;
|
||||
}
|
||||
.av-vc-foot {
|
||||
padding: 14px 24px 18px;
|
||||
text-align: center;
|
||||
font-size: 10.5px;
|
||||
color: #9bbdb7;
|
||||
}
|
||||
.av-pop-enter-active,
|
||||
.av-pop-leave-active {
|
||||
transition: opacity 0.24s ease;
|
||||
}
|
||||
.av-pop-enter-active .av-voicecard {
|
||||
transition: transform 0.36s cubic-bezier(0.3, 1.3, 0.5, 1);
|
||||
}
|
||||
.av-pop-leave-active .av-voicecard {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.av-pop-enter-from,
|
||||
.av-pop-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.av-pop-enter-from .av-voicecard {
|
||||
transform: translateY(34px) scale(0.94);
|
||||
}
|
||||
.av-pop-leave-to .av-voicecard {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -18,6 +18,18 @@ const fmt = (n) => '¥ ' + n.toLocaleString('zh-CN')
|
||||
const estOf = (i) => fmt(Math.round(basePrices[i] * 1.2 / 1000) * 1000) + ' – ' + fmt(Math.round(basePrices[i] * 1.8 / 1000) * 1000)
|
||||
const overHigh = (i) => bids.value[i].price >= basePrices[i] * 1.8
|
||||
|
||||
/* 拍卖图录页 */
|
||||
const catIdx = ref(-1)
|
||||
const cat = ref(null)
|
||||
function openCat(i) {
|
||||
catIdx.value = i
|
||||
cat.value = c.cases[i]
|
||||
}
|
||||
function closeCat() {
|
||||
catIdx.value = -1
|
||||
cat.value = null
|
||||
}
|
||||
|
||||
/* 开拍倒计时(演示:距今 3 天后 19:30) */
|
||||
const target = (() => {
|
||||
const d = new Date()
|
||||
@@ -84,9 +96,10 @@ onBeforeUnmount(() => clearInterval(timer))
|
||||
<h2 class="au-h2"><span>本场拍品 · LOTS</span></h2>
|
||||
<div class="au-lots">
|
||||
<article v-for="(w, i) in c.cases" :key="w.name" class="au-lot">
|
||||
<div class="au-lot-img">
|
||||
<div class="au-lot-img" role="button" tabindex="0" @click="openCat(i)" @keydown.enter="openCat(i)">
|
||||
<span class="au-lot-no">LOT {{ String(i + 1).padStart(3, '0') }}</span>
|
||||
<img :src="w.image" :alt="w.name" loading="lazy" />
|
||||
<span class="au-lot-open">翻开图录 ❦</span>
|
||||
</div>
|
||||
<div class="au-lot-info">
|
||||
<h3>《{{ w.name }}》</h3>
|
||||
@@ -103,9 +116,45 @@ onBeforeUnmount(() => clearInterval(timer))
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<p class="au-note">* 演示竞价,举牌不花钱,请放心过瘾</p>
|
||||
<p class="au-note">* 演示竞价,举牌不花钱,请放心过瘾 · 点击拍品大图可翻阅图录页</p>
|
||||
</section>
|
||||
|
||||
<!-- 拍卖图录页 -->
|
||||
<Transition name="au-page">
|
||||
<div v-if="cat" class="au-mask" @click.self="closeCat">
|
||||
<article class="au-cat">
|
||||
<button class="au-cat-x" aria-label="关闭" @click="closeCat">✕</button>
|
||||
<div class="au-cat-plate">
|
||||
<img :src="cat.image" :alt="cat.name" />
|
||||
<span>PLATE {{ catIdx + 1 }}</span>
|
||||
</div>
|
||||
<div class="au-cat-page">
|
||||
<p class="au-cat-kicker">LUMINA AUCTION · CATALOGUE</p>
|
||||
<p class="au-cat-lotno">LOT {{ String(catIdx + 1).padStart(3, '0') }}</p>
|
||||
<h3>《{{ cat.name }}》</h3>
|
||||
<p class="au-cat-meta">{{ cat.tag }} · {{ cat.year }} · 附馆藏证书与出版著录</p>
|
||||
<p class="au-cat-est">估价 {{ estOf(catIdx) }} <em :class="{ flash: bids[catIdx].flash }">现价 {{ fmt(bids[catIdx].price) }}</em></p>
|
||||
<div class="au-cat-rule" aria-hidden="true"><i>❦</i></div>
|
||||
<p class="au-cat-story">{{ cat.story }}</p>
|
||||
<div class="au-cat-block">
|
||||
<b>图录要点 · NOTES</b>
|
||||
<ul>
|
||||
<li v-for="h in cat.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="au-cat-block">
|
||||
<b>来源与著录 · PROVENANCE</b>
|
||||
<p v-for="t in cat.timeline" :key="t.at" class="au-cat-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</div>
|
||||
<div class="au-cat-mx">
|
||||
<div v-for="m in cat.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
<button class="au-cat-bid" @click="bid(catIdx)"><i class="au-paddle" aria-hidden="true">88</i>在图录页举牌 · {{ bids[catIdx].count }} 次出价</button>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 关于 -->
|
||||
<section id="house" class="au-sec">
|
||||
<h2 class="au-h2"><span>关于拍场 · THE HOUSE</span></h2>
|
||||
@@ -707,4 +756,279 @@ onBeforeUnmount(() => clearInterval(timer))
|
||||
grid-column: 2;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 拍卖图录页 ---- */
|
||||
.au-lot-img {
|
||||
cursor: pointer;
|
||||
}
|
||||
.au-lot-open {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 14px;
|
||||
transform: translateX(-50%) translateY(6px);
|
||||
z-index: 2;
|
||||
background: rgba(23, 19, 14, 0.92);
|
||||
border: 1px solid rgba(212, 175, 55, 0.65);
|
||||
color: #d4af37;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.18em;
|
||||
padding: 6px 16px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.22s ease, transform 0.22s ease;
|
||||
pointer-events: none;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.au-lot-img:hover .au-lot-open {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(0);
|
||||
}
|
||||
.au-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 22px;
|
||||
background: rgba(10, 8, 5, 0.8);
|
||||
backdrop-filter: blur(5px);
|
||||
}
|
||||
.au-cat {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 0.95fr 1.05fr;
|
||||
width: min(900px, 94vw);
|
||||
max-height: 88vh;
|
||||
background: #f4eee0;
|
||||
color: #2b2317;
|
||||
box-shadow: 0 34px 90px rgba(0, 0, 0, 0.65);
|
||||
}
|
||||
.au-cat::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
inset: 10px;
|
||||
border: 1px solid rgba(139, 115, 50, 0.4);
|
||||
pointer-events: none;
|
||||
}
|
||||
.au-cat-x {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 16px;
|
||||
z-index: 3;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid rgba(139, 115, 50, 0.5);
|
||||
border-radius: 50%;
|
||||
background: #f4eee0;
|
||||
color: #6b5a33;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
.au-cat-x:hover {
|
||||
background: #2b2317;
|
||||
color: #d4af37;
|
||||
}
|
||||
.au-cat-plate {
|
||||
position: relative;
|
||||
background: #17130e;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: clamp(18px, 3vw, 36px);
|
||||
}
|
||||
.au-cat-plate img {
|
||||
width: 100%;
|
||||
max-height: 72vh;
|
||||
object-fit: cover;
|
||||
border: 8px solid #2b2317;
|
||||
outline: 1px solid rgba(212, 175, 55, 0.5);
|
||||
box-shadow: 0 16px 44px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
.au-cat-plate span {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 10px;
|
||||
transform: translateX(-50%);
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.3em;
|
||||
color: rgba(212, 175, 55, 0.75);
|
||||
}
|
||||
.au-cat-page {
|
||||
padding: clamp(24px, 3.5vw, 42px) clamp(20px, 3vw, 38px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.au-cat-kicker {
|
||||
margin: 0 0 4px;
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.34em;
|
||||
color: #8b7332;
|
||||
}
|
||||
.au-cat-lotno {
|
||||
margin: 0 0 8px;
|
||||
font-family: 'Playfair Display', 'Noto Serif SC', serif;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.22em;
|
||||
color: #6b5a33;
|
||||
}
|
||||
.au-cat-page h3 {
|
||||
margin: 0 0 4px;
|
||||
font-family: 'Playfair Display', 'Noto Serif SC', serif;
|
||||
font-size: clamp(22px, 2.8vw, 30px);
|
||||
color: #17130e;
|
||||
}
|
||||
.au-cat-meta {
|
||||
margin: 0 0 8px;
|
||||
font-size: 11.5px;
|
||||
color: #6b5a33;
|
||||
}
|
||||
.au-cat-est {
|
||||
margin: 0 0 6px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #2b2317;
|
||||
}
|
||||
.au-cat-est em {
|
||||
margin-left: 10px;
|
||||
font-style: normal;
|
||||
font-size: 12px;
|
||||
color: #8b3122;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.au-cat-est em.flash {
|
||||
animation: au-cat-flash 0.6s ease;
|
||||
}
|
||||
@keyframes au-cat-flash {
|
||||
30% {
|
||||
background: rgba(212, 175, 55, 0.45);
|
||||
}
|
||||
}
|
||||
.au-cat-rule {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 12px 0 14px;
|
||||
color: #8b7332;
|
||||
}
|
||||
.au-cat-rule::before,
|
||||
.au-cat-rule::after {
|
||||
content: '';
|
||||
flex: 1;
|
||||
border-top: 1px solid rgba(139, 115, 50, 0.45);
|
||||
}
|
||||
.au-cat-story {
|
||||
margin: 0 0 16px;
|
||||
font-size: 13px;
|
||||
line-height: 2.05;
|
||||
color: #453a26;
|
||||
}
|
||||
.au-cat-block {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.au-cat-block > b {
|
||||
display: block;
|
||||
margin-bottom: 7px;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.26em;
|
||||
color: #8b7332;
|
||||
}
|
||||
.au-cat-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.au-cat-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 20px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #2b2317;
|
||||
}
|
||||
.au-cat-block ul li::before {
|
||||
content: '❧';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #8b7332;
|
||||
font-size: 11px;
|
||||
}
|
||||
.au-cat-tl {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin: 0 0 5px;
|
||||
font-size: 12px;
|
||||
color: #453a26;
|
||||
}
|
||||
.au-cat-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
color: #8b7332;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.au-cat-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1px;
|
||||
background: rgba(139, 115, 50, 0.45);
|
||||
border: 1px solid rgba(139, 115, 50, 0.45);
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.au-cat-mx > div {
|
||||
background: #f4eee0;
|
||||
padding: 10px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
.au-cat-mx b {
|
||||
display: block;
|
||||
font-family: 'Playfair Display', 'Noto Serif SC', serif;
|
||||
font-size: 18px;
|
||||
color: #17130e;
|
||||
}
|
||||
.au-cat-mx span {
|
||||
font-size: 10px;
|
||||
color: #6b5a33;
|
||||
}
|
||||
.au-cat-bid {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border: 1px solid #17130e;
|
||||
background: #17130e;
|
||||
color: #d4af37;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.14em;
|
||||
padding: 12px 22px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s ease, color 0.2s ease;
|
||||
}
|
||||
.au-cat-bid:hover {
|
||||
background: #d4af37;
|
||||
color: #17130e;
|
||||
}
|
||||
.au-page-enter-active,
|
||||
.au-page-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.au-page-enter-active .au-cat,
|
||||
.au-page-leave-active .au-cat {
|
||||
transition: transform 0.34s cubic-bezier(0.25, 1, 0.4, 1);
|
||||
transform-origin: left center;
|
||||
}
|
||||
.au-page-enter-from,
|
||||
.au-page-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.au-page-enter-from .au-cat,
|
||||
.au-page-leave-to .au-cat {
|
||||
transform: perspective(1400px) rotateY(-14deg) translateX(-16px);
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.au-cat {
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.au-cat-plate img {
|
||||
max-height: 36vh;
|
||||
}
|
||||
.au-cat-page {
|
||||
overflow-y: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.art
|
||||
|
||||
/* 先锋展览手册 */
|
||||
const zineIdx = ref(-1)
|
||||
const zine = ref(null)
|
||||
function openZine(i) {
|
||||
zineIdx.value = i
|
||||
zine.value = c.cases[i]
|
||||
}
|
||||
function closeZine() {
|
||||
zineIdx.value = -1
|
||||
zine.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -36,18 +49,60 @@ const c = corpProfiles.art
|
||||
<section id="shows" class="ag-sec">
|
||||
<h2 class="ag-h2">CURRENT<br />SHOWS ↓</h2>
|
||||
<div class="ag-shows">
|
||||
<article v-for="(w, i) in c.cases" :key="w.name" class="ag-show" :class="'s' + i">
|
||||
<article v-for="(w, i) in c.cases" :key="w.name" class="ag-show" :class="'s' + i" @click="openZine(i)">
|
||||
<span class="ag-show-no">{{ String(i + 1).padStart(2, '0') }}</span>
|
||||
<i class="ag-show-tape" aria-hidden="true"></i>
|
||||
<img :src="w.image" :alt="w.name" loading="lazy" />
|
||||
<div class="ag-show-info">
|
||||
<h3>{{ w.name }}</h3>
|
||||
<p>{{ w.tag }} / {{ w.year }} — {{ w.desc }}</p>
|
||||
<span class="ag-show-more">OPEN ZINE ↗ 翻手册</span>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 先锋展览手册 -->
|
||||
<Transition name="ag-slam">
|
||||
<div v-if="zine" class="ag-mask" @click.self="closeZine">
|
||||
<article class="ag-zine">
|
||||
<button class="ag-zine-x" aria-label="关闭" @click="closeZine">✕ CLOSE</button>
|
||||
<header class="ag-zine-head">
|
||||
<span class="ag-zine-no">ZINE {{ String(zineIdx + 1).padStart(2, '0') }}</span>
|
||||
<h3 class="ag-glitch" :data-text="zine.name">{{ zine.name }}</h3>
|
||||
<p class="ag-zine-meta">{{ zine.tag }} / {{ zine.year }} / FREE ENTRY</p>
|
||||
</header>
|
||||
<div class="ag-zine-img">
|
||||
<img :src="zine.image" :alt="zine.name" />
|
||||
<span aria-hidden="true">FIG.{{ zineIdx + 1 }} — INSTALLATION VIEW</span>
|
||||
</div>
|
||||
<section class="ag-zine-block">
|
||||
<b>STATEMENT 陈述</b>
|
||||
<p>{{ zine.story }}</p>
|
||||
</section>
|
||||
<section class="ag-zine-block">
|
||||
<b>WHY GO ↓ 看点</b>
|
||||
<ul>
|
||||
<li v-for="h in zine.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<div class="ag-zine-cols">
|
||||
<section class="ag-zine-block">
|
||||
<b>LOG 记录</b>
|
||||
<p v-for="t in zine.timeline" :key="t.at" class="ag-zine-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</section>
|
||||
<section class="ag-zine-block">
|
||||
<b>DATA 数据</b>
|
||||
<div class="ag-zine-mx">
|
||||
<div v-for="m in zine.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<footer class="ag-zine-foot">NO MEANING ALLOWED ✖ 手册免费带走 ✖ LUMINA*</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 宣言 -->
|
||||
<section id="manifesto" class="ag-sec ag-manifesto">
|
||||
<h2 class="ag-h2">MANIFESTO</h2>
|
||||
@@ -545,4 +600,233 @@ const c = corpProfiles.art
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 先锋展览手册 ---- */
|
||||
.ag-show {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ag-show-more {
|
||||
display: inline-block;
|
||||
margin-top: 8px;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.1em;
|
||||
color: #101010;
|
||||
background: #08d9d6;
|
||||
padding: 3px 10px;
|
||||
opacity: 0;
|
||||
transform: translateY(4px) rotate(-2deg);
|
||||
transition: opacity 0.18s ease, transform 0.18s ease;
|
||||
}
|
||||
.ag-show:hover .ag-show-more {
|
||||
opacity: 1;
|
||||
transform: translateY(0) rotate(-2deg);
|
||||
}
|
||||
.ag-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(16, 16, 16, 0.88);
|
||||
}
|
||||
.ag-zine {
|
||||
position: relative;
|
||||
width: min(640px, 94vw);
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
background: #101010;
|
||||
border: 3px solid #f4f4f4;
|
||||
box-shadow: 12px 12px 0 #ff2e63;
|
||||
color: #f4f4f4;
|
||||
}
|
||||
.ag-zine-x {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
left: 100%;
|
||||
z-index: 3;
|
||||
display: block;
|
||||
border: none;
|
||||
background: #ff2e63;
|
||||
color: #101010;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.1em;
|
||||
padding: 8px 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ag-zine-x:hover {
|
||||
background: #08d9d6;
|
||||
}
|
||||
.ag-zine-head {
|
||||
padding: 8px 26px 18px;
|
||||
border-bottom: 3px solid #f4f4f4;
|
||||
}
|
||||
.ag-zine-no {
|
||||
display: inline-block;
|
||||
font-size: 11px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.2em;
|
||||
background: #f4f4f4;
|
||||
color: #101010;
|
||||
padding: 2px 10px;
|
||||
transform: rotate(-1.5deg);
|
||||
}
|
||||
.ag-zine-head h3 {
|
||||
margin: 12px 0 6px;
|
||||
font-size: clamp(28px, 5vw, 44px);
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1.05;
|
||||
}
|
||||
.ag-zine-meta {
|
||||
margin: 0;
|
||||
font-size: 11.5px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.14em;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
.ag-zine-img {
|
||||
position: relative;
|
||||
border-bottom: 3px solid #f4f4f4;
|
||||
}
|
||||
.ag-zine-img img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 8;
|
||||
object-fit: cover;
|
||||
filter: grayscale(0.35) contrast(1.12);
|
||||
}
|
||||
.ag-zine-img span {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
background: #101010;
|
||||
color: #08d9d6;
|
||||
font-size: 9.5px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.18em;
|
||||
padding: 4px 12px;
|
||||
}
|
||||
.ag-zine-block {
|
||||
padding: 16px 26px;
|
||||
}
|
||||
.ag-zine-block > b {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.16em;
|
||||
color: #ff2e63;
|
||||
}
|
||||
.ag-zine-block p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.95;
|
||||
color: #cfcfcf;
|
||||
}
|
||||
.ag-zine-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.ag-zine-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 24px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #f4f4f4;
|
||||
font-weight: 700;
|
||||
}
|
||||
.ag-zine-block ul li::before {
|
||||
content: '✖';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: #08d9d6;
|
||||
}
|
||||
.ag-zine-cols {
|
||||
display: grid;
|
||||
grid-template-columns: 1.2fr 1fr;
|
||||
border-top: 1.5px dashed #4a4a4a;
|
||||
}
|
||||
.ag-zine-cols .ag-zine-block + .ag-zine-block {
|
||||
border-left: 1.5px dashed #4a4a4a;
|
||||
}
|
||||
.ag-zine-tl {
|
||||
margin: 0 0 7px !important;
|
||||
font-size: 12px !important;
|
||||
}
|
||||
.ag-zine-tl i {
|
||||
display: block;
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
color: #08d9d6;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ag-zine-mx {
|
||||
display: grid;
|
||||
gap: 8px;
|
||||
}
|
||||
.ag-zine-mx > div {
|
||||
border: 2px solid #f4f4f4;
|
||||
padding: 8px 10px;
|
||||
transform: rotate(-0.6deg);
|
||||
}
|
||||
.ag-zine-mx > div:nth-child(2) {
|
||||
transform: rotate(0.8deg);
|
||||
border-color: #ff2e63;
|
||||
}
|
||||
.ag-zine-mx > div:nth-child(3) {
|
||||
transform: rotate(-1deg);
|
||||
border-color: #08d9d6;
|
||||
}
|
||||
.ag-zine-mx b {
|
||||
display: block;
|
||||
font-size: 19px;
|
||||
font-weight: 900;
|
||||
}
|
||||
.ag-zine-mx span {
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
.ag-zine-foot {
|
||||
padding: 12px 26px 18px;
|
||||
border-top: 3px solid #f4f4f4;
|
||||
font-size: 10.5px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.2em;
|
||||
color: #8d8d8d;
|
||||
}
|
||||
.ag-slam-enter-active,
|
||||
.ag-slam-leave-active {
|
||||
transition: opacity 0.22s ease;
|
||||
}
|
||||
.ag-slam-enter-active .ag-zine {
|
||||
transition: transform 0.28s cubic-bezier(0.2, 1.4, 0.4, 1);
|
||||
}
|
||||
.ag-slam-leave-active .ag-zine {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.ag-slam-enter-from,
|
||||
.ag-slam-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ag-slam-enter-from .ag-zine {
|
||||
transform: scale(1.12) rotate(2deg);
|
||||
}
|
||||
.ag-slam-leave-to .ag-zine {
|
||||
transform: scale(0.94);
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.ag-zine-cols {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.ag-zine-cols .ag-zine-block + .ag-zine-block {
|
||||
border-left: none;
|
||||
border-top: 1.5px dashed #4a4a4a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.art
|
||||
@@ -7,6 +8,19 @@ const rarities = ['GENESIS', 'RARE', 'ULTRA', 'LIMITED']
|
||||
const hues = [265, 190, 320, 205]
|
||||
const tick = chains.map((h, i) => `${h}…${String(2600 + i * 137).slice(-4)} 已铸造`).join(' ◇ ') + ' ◇ '
|
||||
|
||||
/* 数字藏品档案 */
|
||||
const dossierIdx = ref(-1)
|
||||
const dossier = ref(null)
|
||||
function openDossier(i) {
|
||||
dossierIdx.value = i
|
||||
dossier.value = c.cases[i]
|
||||
}
|
||||
function closeDossier() {
|
||||
dossierIdx.value = -1
|
||||
dossier.value = null
|
||||
}
|
||||
const hashOf = (i) => `${chains[i]}…${String(2600 + i * 137).slice(-4)}`
|
||||
|
||||
/* 全息卡片:跟随光标的 3D 倾斜与眩光 */
|
||||
function tilt(e) {
|
||||
const el = e.currentTarget
|
||||
@@ -67,6 +81,7 @@ function untilt(e) {
|
||||
:style="{ '--hue': hues[i], '--d': (i * 90 + 80) + 'ms' }"
|
||||
@mousemove="tilt"
|
||||
@mouseleave="untilt"
|
||||
@click="openDossier(i)"
|
||||
>
|
||||
<div class="ad-card-img">
|
||||
<img :src="w.image" :alt="w.name" loading="lazy" />
|
||||
@@ -79,10 +94,50 @@ function untilt(e) {
|
||||
<span>限量 {{ (i + 2) * 50 }} 份</span>
|
||||
<b>{{ w.year }} 铸造</b>
|
||||
</div>
|
||||
<span class="ad-card-more">◈ 打开链上档案</span>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 数字藏品档案 -->
|
||||
<Transition name="ad-holo">
|
||||
<div v-if="dossier" class="ad-mask" @click.self="closeDossier">
|
||||
<article class="ad-dossier" :style="{ '--hue': hues[dossierIdx] }">
|
||||
<button class="ad-dossier-x" aria-label="关闭" @click="closeDossier">✕</button>
|
||||
<div class="ad-dossier-vis">
|
||||
<img :src="dossier.image" :alt="dossier.name" />
|
||||
<span class="ad-dossier-scan" aria-hidden="true"></span>
|
||||
<span class="ad-rare ad-dossier-rare">{{ rarities[dossierIdx] }}</span>
|
||||
</div>
|
||||
<div class="ad-dossier-info">
|
||||
<p class="ad-dossier-kicker">TOKEN DOSSIER · 链上档案</p>
|
||||
<h3>{{ dossier.name }} · 数字版</h3>
|
||||
<dl class="ad-dossier-meta">
|
||||
<div><dt>TOKEN</dt><dd>{{ hashOf(dossierIdx) }}</dd></div>
|
||||
<div><dt>EDITION</dt><dd>限量 {{ (dossierIdx + 2) * 50 }} 份</dd></div>
|
||||
<div><dt>MINTED</dt><dd>{{ dossier.year }}</dd></div>
|
||||
<div><dt>SOURCE</dt><dd>{{ dossier.tag }}</dd></div>
|
||||
</dl>
|
||||
<p class="ad-dossier-story">{{ dossier.story }}</p>
|
||||
<div class="ad-dossier-block">
|
||||
<b>// TRAITS 特征</b>
|
||||
<ul>
|
||||
<li v-for="h in dossier.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="ad-dossier-block">
|
||||
<b>// ON-CHAIN LOG 链上记录</b>
|
||||
<p v-for="t in dossier.timeline" :key="t.at" class="ad-dossier-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</div>
|
||||
<div class="ad-dossier-mx">
|
||||
<div v-for="m in dossier.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
<p class="ad-dossier-note">◇ 档案已验证 · 数字孪生与实体展品一一对应</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 实验室 -->
|
||||
<section id="lab" class="ad-sec">
|
||||
<h2 class="ad-h2"><span class="ad-h2-tag">// LAB_MODULES</span>数字馆实验室</h2>
|
||||
@@ -580,4 +635,248 @@ function untilt(e) {
|
||||
font-size: 11px !important;
|
||||
color: #55497e !important;
|
||||
}
|
||||
|
||||
/* ---- 数字藏品档案 ---- */
|
||||
.ad-card {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ad-card-more {
|
||||
display: block;
|
||||
margin: 8px 16px 14px;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.12em;
|
||||
color: #00e0ff;
|
||||
opacity: 0;
|
||||
transform: translateY(4px);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.ad-card:hover .ad-card-more {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
.ad-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(5, 3, 12, 0.82);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.ad-dossier {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 0.9fr 1.1fr;
|
||||
width: min(900px, 94vw);
|
||||
max-height: 88vh;
|
||||
background: #0d0920;
|
||||
border: 1px solid hsla(var(--hue, 265), 90%, 65%, 0.55);
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 0 60px hsla(var(--hue, 265), 90%, 55%, 0.28), 0 30px 80px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.ad-dossier-x {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 4;
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.28);
|
||||
border-radius: 50%;
|
||||
background: rgba(10, 7, 20, 0.7);
|
||||
color: #efeaff;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.18s ease, box-shadow 0.18s ease;
|
||||
}
|
||||
.ad-dossier-x:hover {
|
||||
border-color: #00e0ff;
|
||||
box-shadow: 0 0 14px rgba(0, 224, 255, 0.5);
|
||||
}
|
||||
.ad-dossier-vis {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
background: conic-gradient(from 220deg at 60% 40%, hsla(var(--hue, 265), 90%, 55%, 0.35), transparent 40%), #080514;
|
||||
}
|
||||
.ad-dossier-vis img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
min-height: 320px;
|
||||
object-fit: cover;
|
||||
opacity: 0.92;
|
||||
}
|
||||
.ad-dossier-scan {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(transparent 0 46%, hsla(var(--hue, 265), 100%, 75%, 0.22) 50%, transparent 54% 100%);
|
||||
background-size: 100% 260%;
|
||||
animation: ad-scan 3.4s linear infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes ad-scan {
|
||||
from { background-position: 0 130%; }
|
||||
to { background-position: 0 -130%; }
|
||||
}
|
||||
.ad-dossier-rare {
|
||||
position: absolute;
|
||||
left: 14px;
|
||||
top: 14px;
|
||||
}
|
||||
.ad-dossier-info {
|
||||
padding: clamp(22px, 3vw, 34px) clamp(20px, 2.6vw, 30px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ad-dossier-kicker {
|
||||
margin: 0 0 6px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.26em;
|
||||
color: hsla(var(--hue, 265), 90%, 75%, 0.9);
|
||||
}
|
||||
.ad-dossier-info h3 {
|
||||
margin: 0 0 14px;
|
||||
font-size: clamp(20px, 2.6vw, 26px);
|
||||
font-weight: 800;
|
||||
background: linear-gradient(90deg, #b56bff, #00e0ff);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.ad-dossier-meta {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 8px;
|
||||
margin: 0 0 14px;
|
||||
}
|
||||
.ad-dossier-meta > div {
|
||||
background: rgba(255, 255, 255, 0.04);
|
||||
border: 1px solid rgba(255, 255, 255, 0.12);
|
||||
border-radius: 9px;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
.ad-dossier-meta dt {
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 9px;
|
||||
letter-spacing: 0.2em;
|
||||
color: #7d6fae;
|
||||
}
|
||||
.ad-dossier-meta dd {
|
||||
margin: 3px 0 0;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #efeaff;
|
||||
font-family: Consolas, 'PingFang SC', monospace;
|
||||
}
|
||||
.ad-dossier-story {
|
||||
margin: 0 0 14px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.95;
|
||||
color: #b7abdd;
|
||||
}
|
||||
.ad-dossier-block {
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.ad-dossier-block > b {
|
||||
display: block;
|
||||
margin-bottom: 7px;
|
||||
font-family: Consolas, monospace;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.18em;
|
||||
color: #00e0ff;
|
||||
}
|
||||
.ad-dossier-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.ad-dossier-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 20px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #d9d2f2;
|
||||
}
|
||||
.ad-dossier-block ul li::before {
|
||||
content: '◈';
|
||||
position: absolute;
|
||||
left: 0;
|
||||
color: hsla(var(--hue, 265), 90%, 70%, 0.95);
|
||||
font-size: 11px;
|
||||
}
|
||||
.ad-dossier-tl {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin: 0 0 5px;
|
||||
font-size: 12px;
|
||||
color: #b7abdd;
|
||||
}
|
||||
.ad-dossier-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
font-family: Consolas, monospace;
|
||||
color: #7d6fae;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ad-dossier-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.ad-dossier-mx > div {
|
||||
background: linear-gradient(160deg, hsla(var(--hue, 265), 80%, 55%, 0.18), rgba(255, 255, 255, 0.03));
|
||||
border: 1px solid hsla(var(--hue, 265), 80%, 65%, 0.4);
|
||||
border-radius: 10px;
|
||||
padding: 9px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
.ad-dossier-mx b {
|
||||
display: block;
|
||||
font-size: 16.5px;
|
||||
font-weight: 800;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 14px hsla(var(--hue, 265), 90%, 70%, 0.7);
|
||||
}
|
||||
.ad-dossier-mx span {
|
||||
font-size: 10px;
|
||||
color: #9d90c8;
|
||||
}
|
||||
.ad-dossier-note {
|
||||
margin: 0;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.08em;
|
||||
color: #7d6fae;
|
||||
}
|
||||
.ad-holo-enter-active,
|
||||
.ad-holo-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.ad-holo-enter-active .ad-dossier,
|
||||
.ad-holo-leave-active .ad-dossier {
|
||||
transition: transform 0.34s cubic-bezier(0.25, 1.1, 0.4, 1);
|
||||
}
|
||||
.ad-holo-enter-from,
|
||||
.ad-holo-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ad-holo-enter-from .ad-dossier,
|
||||
.ad-holo-leave-to .ad-dossier {
|
||||
transform: perspective(1100px) rotateX(6deg) scale(0.92);
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.ad-dossier {
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.ad-dossier-vis img {
|
||||
min-height: 0;
|
||||
max-height: 34vh;
|
||||
}
|
||||
.ad-dossier-info {
|
||||
overflow-y: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,9 +1,22 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.art
|
||||
const crayons = ['#ff6b9d', '#ffb020', '#6bcb77', '#4d96ff']
|
||||
const rots = ['-1.8deg', '1.6deg', '-1.2deg', '2.2deg']
|
||||
|
||||
/* 儿童导览卡 */
|
||||
const cardIdx = ref(-1)
|
||||
const card = ref(null)
|
||||
function openCard(i) {
|
||||
cardIdx.value = i
|
||||
card.value = c.cases[i]
|
||||
}
|
||||
function closeCard() {
|
||||
cardIdx.value = -1
|
||||
card.value = null
|
||||
}
|
||||
const courses = [
|
||||
{ icon: '🖍️', name: '蜡笔涂鸦班', age: '3–6 岁', desc: '把墙上的想象力搬到纸上(合法的那种)' },
|
||||
{ icon: '✂️', name: '彩纸拼贴课', age: '5–9 岁', desc: '撕、剪、贴,一张彩纸变一个世界' },
|
||||
@@ -60,15 +73,52 @@ const courses = [
|
||||
<circle cx="995" cy="12" r="4.5" fill="#8c6f4a" />
|
||||
</svg>
|
||||
<div class="ak-works">
|
||||
<figure v-for="(w, i) in c.cases" :key="w.name" class="ak-work" :style="{ '--c': crayons[(i + 1) % 4], '--r': ((i % 2 ? 1 : -1) * 2) + 'deg' }">
|
||||
<figure v-for="(w, i) in c.cases" :key="w.name" class="ak-work" :style="{ '--c': crayons[(i + 1) % 4], '--r': ((i % 2 ? 1 : -1) * 2) + 'deg' }" @click="openCard(i)">
|
||||
<img :src="w.image" :alt="w.name" loading="lazy" />
|
||||
<figcaption>《{{ w.name }}》<span>灵感来自「{{ w.tag }}」展</span></figcaption>
|
||||
<figcaption>《{{ w.name }}》<span>灵感来自「{{ w.tag }}」展</span><em class="ak-work-more">🔍 领导览卡</em></figcaption>
|
||||
<span v-if="i % 2" class="ak-tape"></span>
|
||||
<span v-else class="ak-pin" aria-hidden="true"></span>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 儿童导览卡 -->
|
||||
<Transition name="ak-bounce">
|
||||
<div v-if="card" class="ak-mask" @click.self="closeCard">
|
||||
<article class="ak-guide" :style="{ '--c': crayons[cardIdx % 4] }">
|
||||
<button class="ak-guide-x" aria-label="关闭" @click="closeCard">✕</button>
|
||||
<header class="ak-guide-head">
|
||||
<span class="ak-guide-badge">小小导览卡 No.{{ cardIdx + 1 }}</span>
|
||||
<h3>「{{ card.name }}」展</h3>
|
||||
<p>{{ card.tag }} · {{ card.year }} 年 · 小朋友也能看懂的版本</p>
|
||||
</header>
|
||||
<div class="ak-guide-img">
|
||||
<img :src="card.image" :alt="card.name" />
|
||||
</div>
|
||||
<section class="ak-guide-block">
|
||||
<b>🗣️ 讲给你听</b>
|
||||
<p>{{ card.story }}</p>
|
||||
</section>
|
||||
<section class="ak-guide-block">
|
||||
<b>👀 看什么</b>
|
||||
<ul>
|
||||
<li v-for="(h, i) in card.highlights" :key="h" :style="{ '--lc': crayons[i % 4] }">{{ h }}</li>
|
||||
</ul>
|
||||
</section>
|
||||
<section class="ak-guide-block">
|
||||
<b>📅 展览小日历</b>
|
||||
<p v-for="t in card.timeline" :key="t.at" class="ak-guide-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</section>
|
||||
<div class="ak-guide-mx">
|
||||
<div v-for="(m, i) in card.metrics" :key="m.label" :style="{ '--c': crayons[i % 4] }">
|
||||
<b>{{ m.value }}</b><span>{{ m.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="ak-guide-foot">🌟 看完盖个章:今天也是小小艺术家!</footer>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 数据 -->
|
||||
<div class="ak-stats">
|
||||
<div v-for="(s, i) in c.stats" :key="s.label" :style="{ '--c': crayons[i] }">
|
||||
@@ -496,4 +546,202 @@ const courses = [
|
||||
margin-top: 12px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 儿童导览卡 ---- */
|
||||
.ak-work {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ak-work-more {
|
||||
display: block;
|
||||
margin-top: 4px;
|
||||
font-style: normal;
|
||||
font-size: 11px;
|
||||
color: #ff6b9d;
|
||||
opacity: 0;
|
||||
transform: scale(0.9);
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
}
|
||||
.ak-work:hover .ak-work-more {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
.ak-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 20px;
|
||||
background: rgba(74, 63, 53, 0.55);
|
||||
backdrop-filter: blur(4px);
|
||||
}
|
||||
.ak-guide {
|
||||
position: relative;
|
||||
width: min(520px, 94vw);
|
||||
max-height: 88vh;
|
||||
overflow-y: auto;
|
||||
background: #fffdf6;
|
||||
border: 4px solid var(--c, #ffb020);
|
||||
border-radius: 26px;
|
||||
box-shadow: 0 20px 54px rgba(74, 63, 53, 0.4), 0 0 0 8px rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
.ak-guide-x {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 3;
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
border: 3px solid #fff;
|
||||
border-radius: 50%;
|
||||
background: var(--c, #ffb020);
|
||||
color: #fff;
|
||||
font-size: 15px;
|
||||
font-weight: 900;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 4px 10px rgba(74, 63, 53, 0.3);
|
||||
transition: transform 0.18s ease;
|
||||
}
|
||||
.ak-guide-x:hover {
|
||||
transform: rotate(90deg) scale(1.08);
|
||||
}
|
||||
.ak-guide-head {
|
||||
padding: 22px 24px 12px;
|
||||
text-align: center;
|
||||
}
|
||||
.ak-guide-badge {
|
||||
display: inline-block;
|
||||
background: var(--c, #ffb020);
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
padding: 4px 16px;
|
||||
border-radius: 999px;
|
||||
transform: rotate(-2deg);
|
||||
box-shadow: 0 3px 0 rgba(74, 63, 53, 0.2);
|
||||
}
|
||||
.ak-guide-head h3 {
|
||||
margin: 12px 0 4px;
|
||||
font-size: 24px;
|
||||
color: #4a3f35;
|
||||
}
|
||||
.ak-guide-head p {
|
||||
margin: 0;
|
||||
font-size: 12px;
|
||||
color: #a08a72;
|
||||
}
|
||||
.ak-guide-img {
|
||||
padding: 0 24px;
|
||||
}
|
||||
.ak-guide-img img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 8;
|
||||
object-fit: cover;
|
||||
border-radius: 16px;
|
||||
border: 4px dashed var(--c, #ffb020);
|
||||
}
|
||||
.ak-guide-block {
|
||||
padding: 14px 26px 2px;
|
||||
}
|
||||
.ak-guide-block > b {
|
||||
display: inline-block;
|
||||
margin-bottom: 6px;
|
||||
font-size: 14.5px;
|
||||
color: #4a3f35;
|
||||
background: linear-gradient(transparent 62%, color-mix(in srgb, var(--c, #ffb020) 38%, transparent) 62%);
|
||||
padding: 0 4px;
|
||||
}
|
||||
.ak-guide-block p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
line-height: 1.95;
|
||||
color: #6d5c4c;
|
||||
}
|
||||
.ak-guide-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.ak-guide-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 24px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #6d5c4c;
|
||||
}
|
||||
.ak-guide-block ul li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 12px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 3px;
|
||||
background: var(--lc, #ffb020);
|
||||
transform: rotate(12deg);
|
||||
}
|
||||
.ak-guide-tl {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin: 0 0 5px !important;
|
||||
}
|
||||
.ak-guide-tl i {
|
||||
flex: 0 0 64px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
color: var(--c, #ffb020);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ak-guide-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 10px;
|
||||
padding: 14px 26px 4px;
|
||||
}
|
||||
.ak-guide-mx > div {
|
||||
background: #fff;
|
||||
border: 3px solid var(--c, #ffb020);
|
||||
border-radius: 14px;
|
||||
padding: 10px 6px;
|
||||
text-align: center;
|
||||
transform: rotate(-1deg);
|
||||
}
|
||||
.ak-guide-mx > div:nth-child(2) {
|
||||
transform: rotate(1.4deg);
|
||||
}
|
||||
.ak-guide-mx b {
|
||||
display: block;
|
||||
font-size: 17px;
|
||||
color: #4a3f35;
|
||||
}
|
||||
.ak-guide-mx span {
|
||||
font-size: 10.5px;
|
||||
color: #a08a72;
|
||||
}
|
||||
.ak-guide-foot {
|
||||
padding: 12px 26px 20px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: #8c6f4a;
|
||||
}
|
||||
.ak-bounce-enter-active,
|
||||
.ak-bounce-leave-active {
|
||||
transition: opacity 0.24s ease;
|
||||
}
|
||||
.ak-bounce-enter-active .ak-guide {
|
||||
transition: transform 0.4s cubic-bezier(0.3, 1.6, 0.5, 1);
|
||||
}
|
||||
.ak-bounce-leave-active .ak-guide {
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.ak-bounce-enter-from,
|
||||
.ak-bounce-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ak-bounce-enter-from .ak-guide {
|
||||
transform: scale(0.7) rotate(-4deg);
|
||||
}
|
||||
.ak-bounce-leave-to .ak-guide {
|
||||
transform: scale(0.92);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,7 +1,20 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { corpProfiles } from '../../data/corpProfiles'
|
||||
|
||||
const c = corpProfiles.art
|
||||
|
||||
/* 白盒展签详情 */
|
||||
const selIdx = ref(-1)
|
||||
const sel = ref(null)
|
||||
function openLabel(i) {
|
||||
selIdx.value = i
|
||||
sel.value = c.cases[i]
|
||||
}
|
||||
function closeLabel() {
|
||||
selIdx.value = -1
|
||||
sel.value = null
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -35,6 +48,7 @@ const c = corpProfiles.art
|
||||
class="aw-work"
|
||||
:class="'p' + i"
|
||||
:style="{ '--d': (i * 0.14) + 's' }"
|
||||
@click="openLabel(i)"
|
||||
>
|
||||
<i class="aw-wire" aria-hidden="true"></i>
|
||||
<div class="aw-frame">
|
||||
@@ -46,11 +60,45 @@ const c = corpProfiles.art
|
||||
<b>《{{ w.name }}》</b>
|
||||
<span>{{ w.tag }} · {{ w.year }}</span>
|
||||
<p>{{ w.desc }}</p>
|
||||
<i class="aw-plaque-more">走近看展签 →</i>
|
||||
</figcaption>
|
||||
</figure>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 白盒展签详情 -->
|
||||
<Transition name="aw-fade">
|
||||
<div v-if="sel" class="aw-mask" @click.self="closeLabel">
|
||||
<article class="aw-detail">
|
||||
<button class="aw-detail-x" aria-label="关闭" @click="closeLabel">✕</button>
|
||||
<div class="aw-detail-art">
|
||||
<img :src="sel.image" :alt="sel.name" />
|
||||
<i class="aw-detail-lamp" aria-hidden="true"></i>
|
||||
</div>
|
||||
<div class="aw-detail-label">
|
||||
<em>CAT. {{ String(selIdx + 1).padStart(3, '0') }} — {{ sel.tag }}</em>
|
||||
<h3>《{{ sel.name }}》</h3>
|
||||
<span class="aw-detail-year">{{ c.name }} · {{ sel.year }}</span>
|
||||
<p class="aw-detail-story">{{ sel.story }}</p>
|
||||
<div class="aw-detail-block">
|
||||
<b>看点</b>
|
||||
<ul>
|
||||
<li v-for="h in sel.highlights" :key="h">{{ h }}</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="aw-detail-block">
|
||||
<b>展览记录</b>
|
||||
<p v-for="t in sel.timeline" :key="t.at" class="aw-detail-tl"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
</div>
|
||||
<div class="aw-detail-mx">
|
||||
<div v-for="m in sel.metrics" :key="m.label"><b>{{ m.value }}</b><span>{{ m.label }}</span></div>
|
||||
</div>
|
||||
<p class="aw-detail-note">请勿触摸作品 · 欢迎触摸这张展签</p>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 关于:楼层索引式条目 -->
|
||||
<section id="about" class="aw-sec aw-about">
|
||||
<p class="aw-sec-label">ABOUT — 美术馆</p>
|
||||
@@ -592,4 +640,222 @@ const c = corpProfiles.art
|
||||
padding-left: clamp(48px, 14vw, 128px);
|
||||
}
|
||||
}
|
||||
|
||||
/* ---- 白盒展签详情 ---- */
|
||||
.aw-work {
|
||||
cursor: pointer;
|
||||
}
|
||||
.aw-plaque-more {
|
||||
display: block;
|
||||
margin-top: 6px;
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.12em;
|
||||
color: #1c1c1c;
|
||||
opacity: 0;
|
||||
transform: translateX(-4px);
|
||||
transition: opacity 0.22s ease, transform 0.22s ease;
|
||||
}
|
||||
.aw-work:hover .aw-plaque-more {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
.aw-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 520;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 22px;
|
||||
background: rgba(28, 28, 28, 0.55);
|
||||
backdrop-filter: blur(6px);
|
||||
}
|
||||
.aw-detail {
|
||||
position: relative;
|
||||
display: grid;
|
||||
grid-template-columns: 1.1fr 0.9fr;
|
||||
width: min(880px, 94vw);
|
||||
max-height: 88vh;
|
||||
background: #fcfcfa;
|
||||
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.aw-detail-x {
|
||||
position: absolute;
|
||||
right: 14px;
|
||||
top: 14px;
|
||||
z-index: 3;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border: 1px solid #d9d9d2;
|
||||
border-radius: 50%;
|
||||
background: #fcfcfa;
|
||||
color: #1c1c1c;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
transition: background 0.18s ease, color 0.18s ease;
|
||||
}
|
||||
.aw-detail-x:hover {
|
||||
background: #1c1c1c;
|
||||
color: #fcfcfa;
|
||||
border-color: #1c1c1c;
|
||||
}
|
||||
.aw-detail-art {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #f1f1ec;
|
||||
padding: clamp(20px, 4vw, 44px);
|
||||
overflow: hidden;
|
||||
}
|
||||
.aw-detail-art img {
|
||||
width: 100%;
|
||||
max-height: 70vh;
|
||||
object-fit: cover;
|
||||
border: 10px solid #20201e;
|
||||
outline: 1px solid #d9d9d2;
|
||||
box-shadow: 0 18px 40px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
.aw-detail-lamp {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 0;
|
||||
width: 240px;
|
||||
height: 150px;
|
||||
transform: translateX(-50%);
|
||||
background: radial-gradient(ellipse 50% 100% at 50% 0%, rgba(255, 250, 230, 0.75), transparent 70%);
|
||||
pointer-events: none;
|
||||
}
|
||||
.aw-detail-label {
|
||||
padding: clamp(22px, 3.5vw, 40px) clamp(20px, 3vw, 36px);
|
||||
overflow-y: auto;
|
||||
}
|
||||
.aw-detail-label em {
|
||||
font-style: normal;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.3em;
|
||||
color: #7a7a76;
|
||||
}
|
||||
.aw-detail-label h3 {
|
||||
margin: 10px 0 4px;
|
||||
font-size: clamp(20px, 2.6vw, 27px);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.aw-detail-year {
|
||||
display: block;
|
||||
font-size: 11.5px;
|
||||
color: #7a7a76;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.aw-detail-story {
|
||||
margin: 0 0 18px;
|
||||
padding-bottom: 16px;
|
||||
font-size: 13px;
|
||||
line-height: 2.05;
|
||||
color: #45443f;
|
||||
border-bottom: 1px solid #ecece7;
|
||||
}
|
||||
.aw-detail-block {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
.aw-detail-block > b {
|
||||
display: block;
|
||||
margin-bottom: 8px;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.3em;
|
||||
color: #7a7a76;
|
||||
font-weight: 700;
|
||||
}
|
||||
.aw-detail-block ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.aw-detail-block ul li {
|
||||
position: relative;
|
||||
padding: 4px 0 4px 18px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.8;
|
||||
color: #2c2c28;
|
||||
}
|
||||
.aw-detail-block ul li::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 2px;
|
||||
top: 13px;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border: 1px solid #1c1c1c;
|
||||
}
|
||||
.aw-detail-tl {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
margin: 0 0 5px;
|
||||
font-size: 12px;
|
||||
color: #45443f;
|
||||
}
|
||||
.aw-detail-tl i {
|
||||
flex: 0 0 62px;
|
||||
font-style: normal;
|
||||
color: #9c9c96;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.aw-detail-mx {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
border-top: 1px solid #1c1c1c;
|
||||
border-bottom: 1px solid #1c1c1c;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.aw-detail-mx > div {
|
||||
padding: 12px 6px;
|
||||
text-align: center;
|
||||
}
|
||||
.aw-detail-mx > div + div {
|
||||
border-left: 1px solid #ecece7;
|
||||
}
|
||||
.aw-detail-mx b {
|
||||
display: block;
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.aw-detail-mx span {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.08em;
|
||||
color: #7a7a76;
|
||||
}
|
||||
.aw-detail-note {
|
||||
margin: 0;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.14em;
|
||||
color: #9c9c96;
|
||||
}
|
||||
.aw-fade-enter-active,
|
||||
.aw-fade-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.aw-fade-enter-active .aw-detail,
|
||||
.aw-fade-leave-active .aw-detail {
|
||||
transition: transform 0.32s cubic-bezier(0.25, 1, 0.4, 1);
|
||||
}
|
||||
.aw-fade-enter-from,
|
||||
.aw-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.aw-fade-enter-from .aw-detail,
|
||||
.aw-fade-leave-to .aw-detail {
|
||||
transform: translateY(20px) scale(0.97);
|
||||
}
|
||||
@media (max-width: 760px) {
|
||||
.aw-detail {
|
||||
grid-template-columns: 1fr;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.aw-detail-art img {
|
||||
max-height: 38vh;
|
||||
}
|
||||
.aw-detail-label {
|
||||
overflow-y: visible;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -58,7 +58,7 @@ const fmt = (n) => n.toLocaleString('en-US')
|
||||
<span>SELECTED WORKS</span>
|
||||
</div>
|
||||
<div class="at-works">
|
||||
<router-link v-for="p in works" :key="p.id" to="/corp/furn-atelier/products" class="at-work">
|
||||
<router-link v-for="p in works" :key="p.id" :to="`/corp/furn-atelier/products#${p.id}`" class="at-work">
|
||||
<div class="at-work-img">
|
||||
<img :src="`https://picsum.photos/seed/${p.seed}/780/920`" :alt="p.name" loading="lazy" />
|
||||
<span class="at-work-view">VIEW INDEX ↗</span>
|
||||
|
||||
320
src/pages/lab/AsciiArt.vue
Normal file
320
src/pages/lab/AsciiArt.vue
Normal file
@@ -0,0 +1,320 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const output = ref('')
|
||||
const cols = ref(80)
|
||||
const charset = ref('classic')
|
||||
const invert = ref(false)
|
||||
const copied = ref(false)
|
||||
const fileName = ref('')
|
||||
const dragOver = ref(false)
|
||||
|
||||
const CHARSETS = {
|
||||
classic: '@%#*+=-:. ',
|
||||
blocks: '█▓▒░ ',
|
||||
simple: '#o+. ',
|
||||
digits: '9731. ',
|
||||
}
|
||||
|
||||
let lastImage = null
|
||||
|
||||
function handleFile(file) {
|
||||
if (!file || !file.type.startsWith('image/')) return
|
||||
fileName.value = file.name
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
lastImage = img
|
||||
render()
|
||||
URL.revokeObjectURL(img.src)
|
||||
}
|
||||
img.src = URL.createObjectURL(file)
|
||||
}
|
||||
|
||||
function onPick(e) {
|
||||
handleFile(e.target.files[0])
|
||||
e.target.value = ''
|
||||
}
|
||||
function onDrop(e) {
|
||||
dragOver.value = false
|
||||
handleFile(e.dataTransfer.files[0])
|
||||
}
|
||||
|
||||
function render() {
|
||||
if (!lastImage) return
|
||||
const img = lastImage
|
||||
const w = Math.min(160, Math.max(20, cols.value))
|
||||
// 字符高宽比约 2:1,压缩纵向
|
||||
const h = Math.max(4, Math.round((img.height / img.width) * w * 0.52))
|
||||
const cv = document.createElement('canvas')
|
||||
cv.width = w
|
||||
cv.height = h
|
||||
const ctx = cv.getContext('2d')
|
||||
ctx.drawImage(img, 0, 0, w, h)
|
||||
const data = ctx.getImageData(0, 0, w, h).data
|
||||
const chars = CHARSETS[charset.value]
|
||||
let out = ''
|
||||
for (let y = 0; y < h; y++) {
|
||||
for (let x = 0; x < w; x++) {
|
||||
const i = (y * w + x) * 4
|
||||
let lum = (0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]) / 255
|
||||
// 透明像素当白
|
||||
const alpha = data[i + 3] / 255
|
||||
lum = lum * alpha + (1 - alpha)
|
||||
if (invert.value) lum = 1 - lum
|
||||
const idx = Math.min(chars.length - 1, Math.floor(lum * chars.length))
|
||||
out += chars[idx]
|
||||
}
|
||||
out += '\n'
|
||||
}
|
||||
output.value = out
|
||||
}
|
||||
|
||||
function demo() {
|
||||
// 内置示例:canvas 画一只简笔猫再转字符
|
||||
const cv = document.createElement('canvas')
|
||||
cv.width = cv.height = 200
|
||||
const c = cv.getContext('2d')
|
||||
c.fillStyle = '#fff'
|
||||
c.fillRect(0, 0, 200, 200)
|
||||
c.fillStyle = '#111'
|
||||
c.beginPath()
|
||||
c.arc(100, 115, 62, 0, Math.PI * 2)
|
||||
c.fill()
|
||||
c.beginPath()
|
||||
c.moveTo(52, 80); c.lineTo(40, 28); c.lineTo(88, 58)
|
||||
c.moveTo(148, 80); c.lineTo(160, 28); c.lineTo(112, 58)
|
||||
c.fill()
|
||||
c.fillStyle = '#fff'
|
||||
c.beginPath()
|
||||
c.arc(78, 105, 10, 0, Math.PI * 2)
|
||||
c.arc(122, 105, 10, 0, Math.PI * 2)
|
||||
c.fill()
|
||||
const img = new Image()
|
||||
img.onload = () => {
|
||||
lastImage = img
|
||||
fileName.value = '内置示例 · 一只猫'
|
||||
render()
|
||||
}
|
||||
img.src = cv.toDataURL()
|
||||
}
|
||||
|
||||
async function copy() {
|
||||
if (!output.value) return
|
||||
try {
|
||||
await navigator.clipboard.writeText(output.value)
|
||||
copied.value = true
|
||||
setTimeout(() => (copied.value = false), 1400)
|
||||
} catch { /* 剪贴板不可用 */ }
|
||||
}
|
||||
function download() {
|
||||
if (!output.value) return
|
||||
const a = document.createElement('a')
|
||||
a.href = URL.createObjectURL(new Blob([output.value], { type: 'text/plain' }))
|
||||
a.download = 'ascii-art.txt'
|
||||
a.click()
|
||||
URL.revokeObjectURL(a.href)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="aa">
|
||||
<nav class="lab-nav">
|
||||
<router-link to="/lab" class="lab-back">← 实验室</router-link>
|
||||
<h1 class="lab-title">ASCII 字符画</h1>
|
||||
<span class="lab-badge">CANVAS</span>
|
||||
</nav>
|
||||
<p class="lab-desc">拖一张图进来,按亮度映射成字符画;可调宽度、字符集、反相,支持复制与下载 txt。图片只在本地处理,不上传。</p>
|
||||
|
||||
<div class="aa-controls">
|
||||
<label
|
||||
class="aa-drop"
|
||||
:class="{ over: dragOver }"
|
||||
@dragover.prevent="dragOver = true"
|
||||
@dragleave="dragOver = false"
|
||||
@drop.prevent="onDrop"
|
||||
>
|
||||
<input type="file" accept="image/*" @change="onPick" />
|
||||
{{ fileName || '点击选图 / 拖图到这里' }}
|
||||
</label>
|
||||
<button class="aa-demo" @click="demo">🐱 用示例图</button>
|
||||
<label class="aa-slider">
|
||||
宽 {{ cols }} 字符
|
||||
<input v-model.number="cols" type="range" min="30" max="160" @change="render" />
|
||||
</label>
|
||||
<div class="aa-sets">
|
||||
<button
|
||||
v-for="(v, k) in CHARSETS"
|
||||
:key="k"
|
||||
:class="{ on: charset === k }"
|
||||
@click="charset = k; render()"
|
||||
>
|
||||
{{ { classic: '经典', blocks: '色块', simple: '简洁', digits: '数字' }[k] }}
|
||||
</button>
|
||||
</div>
|
||||
<label class="aa-invert"><input v-model="invert" type="checkbox" @change="render" />反相</label>
|
||||
<button class="aa-act" :class="{ ok: copied }" :disabled="!output" @click="copy">{{ copied ? '✓ 已复制' : '复制' }}</button>
|
||||
<button class="aa-act" :disabled="!output" @click="download">下载 .txt</button>
|
||||
</div>
|
||||
|
||||
<pre v-if="output" class="aa-output">{{ output }}</pre>
|
||||
<div v-else class="aa-empty">
|
||||
<p>还没有图。选一张本地图片,或者点「用示例图」看效果。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.aa {
|
||||
min-height: 100vh;
|
||||
background: #0b0e14;
|
||||
color: #e8ecf4;
|
||||
font-family: 'Space Grotesk', 'Noto Serif SC', sans-serif;
|
||||
padding: 24px clamp(16px, 5vw, 64px) 60px;
|
||||
}
|
||||
.lab-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
max-width: 1060px;
|
||||
margin: 0 auto 10px;
|
||||
}
|
||||
.lab-back {
|
||||
color: #f59e0b;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
border: 1px solid rgba(245, 158, 11, 0.4);
|
||||
border-radius: 8px;
|
||||
padding: 7px 14px;
|
||||
}
|
||||
.lab-back:hover { background: rgba(245, 158, 11, 0.12); }
|
||||
.lab-title { margin: 0; font-size: 22px; font-weight: 700; }
|
||||
.lab-badge {
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.22em;
|
||||
color: #5eead4;
|
||||
border: 1px solid rgba(94, 234, 212, 0.4);
|
||||
border-radius: 99px;
|
||||
padding: 4px 10px;
|
||||
}
|
||||
.lab-desc {
|
||||
max-width: 1060px;
|
||||
margin: 0 auto 18px;
|
||||
font-size: 13.5px;
|
||||
color: rgba(232, 236, 244, 0.66);
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.aa-controls {
|
||||
max-width: 1060px;
|
||||
margin: 0 auto 16px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 10px 14px;
|
||||
}
|
||||
.aa-drop {
|
||||
position: relative;
|
||||
border: 1.5px dashed #2a2f3d;
|
||||
border-radius: 12px;
|
||||
padding: 11px 20px;
|
||||
font-size: 12.5px;
|
||||
color: rgba(232, 236, 244, 0.7);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
max-width: 260px;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.aa-drop.over,
|
||||
.aa-drop:hover {
|
||||
border-color: #f59e0b;
|
||||
color: #f59e0b;
|
||||
}
|
||||
.aa-drop input {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aa-demo {
|
||||
background: #13161f;
|
||||
border: 1px solid #2a2f3d;
|
||||
border-radius: 10px;
|
||||
color: rgba(232, 236, 244, 0.78);
|
||||
font-size: 12.5px;
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aa-demo:hover { border-color: #5eead4; color: #5eead4; }
|
||||
.aa-slider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 9px;
|
||||
font-size: 12px;
|
||||
color: rgba(232, 236, 244, 0.65);
|
||||
}
|
||||
.aa-slider input { accent-color: #f59e0b; }
|
||||
.aa-sets {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
}
|
||||
.aa-sets button {
|
||||
background: none;
|
||||
border: 1px solid #2a2f3d;
|
||||
border-radius: 99px;
|
||||
color: rgba(232, 236, 244, 0.7);
|
||||
font-size: 11.5px;
|
||||
padding: 7px 13px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aa-sets button.on { border-color: #5eead4; color: #5eead4; }
|
||||
.aa-invert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
font-size: 12px;
|
||||
color: rgba(232, 236, 244, 0.65);
|
||||
}
|
||||
.aa-invert input { accent-color: #f59e0b; }
|
||||
.aa-act {
|
||||
background: #13161f;
|
||||
border: 1px solid #2a2f3d;
|
||||
border-radius: 10px;
|
||||
color: rgba(232, 236, 244, 0.78);
|
||||
font-size: 12.5px;
|
||||
padding: 10px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.aa-act:disabled { opacity: 0.4; cursor: default; }
|
||||
.aa-act.ok { border-color: #5eead4; color: #5eead4; }
|
||||
|
||||
.aa-output {
|
||||
max-width: 1060px;
|
||||
margin: 0 auto;
|
||||
background: #0d1017;
|
||||
border: 1px solid #222736;
|
||||
border-radius: 14px;
|
||||
padding: 18px;
|
||||
overflow: auto;
|
||||
font-family: Consolas, 'Courier New', monospace;
|
||||
font-size: 9px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: 0.06em;
|
||||
color: #7dd487;
|
||||
max-height: 70vh;
|
||||
}
|
||||
.aa-empty {
|
||||
max-width: 1060px;
|
||||
margin: 0 auto;
|
||||
border: 1.5px dashed #222736;
|
||||
border-radius: 14px;
|
||||
padding: 70px 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.aa-empty p {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: rgba(232, 236, 244, 0.4);
|
||||
}
|
||||
</style>
|
||||
@@ -1,5 +1,9 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { profile } from '../../data/profile'
|
||||
|
||||
/* 玻璃详情对话框 */
|
||||
const dialog = ref(null)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -7,6 +11,7 @@ import { profile } from '../../data/profile'
|
||||
<!-- 吸顶导航:光泽玻璃条 -->
|
||||
<nav class="ae-nav">
|
||||
<router-link to="/" class="ae-back">← 返回合集</router-link>
|
||||
<router-link to="/style/aero/gadgets" class="ae-back">🧩 侧边栏小工具</router-link>
|
||||
<span class="ae-nav-title">niangao aero</span>
|
||||
<span class="ae-nav-time">2026 / 08 / 11</span>
|
||||
</nav>
|
||||
@@ -52,7 +57,16 @@ import { profile } from '../../data/profile'
|
||||
<section class="ae-sec">
|
||||
<h2 class="ae-h2">作品橱窗</h2>
|
||||
<div class="ae-works">
|
||||
<article v-for="p in profile.projects" :key="p.name" class="ae-work ae-glossy">
|
||||
<article
|
||||
v-for="p in profile.projects"
|
||||
:key="p.name"
|
||||
class="ae-work ae-glossy click"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="打开属性对话框"
|
||||
@click="dialog = p"
|
||||
@keyup.enter="dialog = p"
|
||||
>
|
||||
<div class="ae-work-img">
|
||||
<img :src="p.image" :alt="p.name" loading="lazy" />
|
||||
<span class="ae-shine"></span>
|
||||
@@ -61,7 +75,7 @@ import { profile } from '../../data/profile'
|
||||
<p class="ae-work-desc">{{ p.desc }}</p>
|
||||
<div class="ae-work-foot">
|
||||
<span v-for="t in p.tags" :key="t" class="ae-chip">{{ t }}</span>
|
||||
<b>{{ p.year }} · ⭐{{ p.stars }}</b>
|
||||
<b>{{ p.year }} · ⭐{{ p.stars }} · 详情 ›</b>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
@@ -74,10 +88,218 @@ import { profile } from '../../data/profile'
|
||||
</span>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<!-- 玻璃详情对话框 -->
|
||||
<Transition name="ae-dialog">
|
||||
<div v-if="dialog" class="ae-dlg-mask" @click.self="dialog = null">
|
||||
<div class="ae-dlg">
|
||||
<header class="ae-dlg-bar">
|
||||
<span class="ae-dlg-ic">💠</span>
|
||||
<b>{{ dialog.name }} — 属性</b>
|
||||
<button class="ae-dlg-x" title="关闭" @click="dialog = null">✕</button>
|
||||
</header>
|
||||
<div class="ae-dlg-body">
|
||||
<div class="ae-dlg-hero">
|
||||
<img :src="dialog.image" :alt="dialog.name" loading="lazy" />
|
||||
<span class="ae-shine"></span>
|
||||
</div>
|
||||
<p class="ae-dlg-sub">{{ dialog.alias }} · {{ dialog.year }} · ⭐{{ dialog.stars }}</p>
|
||||
<p class="ae-dlg-story">{{ dialog.story }}</p>
|
||||
<p class="ae-dlg-h">✦ 功能亮点</p>
|
||||
<p v-for="(h, hi) in dialog.highlights" :key="hi" class="ae-dlg-li"><i></i>{{ h }}</p>
|
||||
<p class="ae-dlg-h">✦ 版本历程</p>
|
||||
<p v-for="t in dialog.timeline" :key="t.at" class="ae-dlg-li tl"><b>{{ t.at }}</b>{{ t.event }}</p>
|
||||
<div class="ae-dlg-metrics">
|
||||
<span v-for="m in dialog.metrics" :key="m.label"><b>{{ m.value }}</b>{{ m.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="ae-dlg-foot">
|
||||
<span>{{ dialog.tags.join(' · ') }}</span>
|
||||
<button class="ae-dlg-ok" @click="dialog = null">确定</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ---- 玻璃详情对话框 ---- */
|
||||
.ae-work.click {
|
||||
cursor: pointer;
|
||||
}
|
||||
.ae-dlg-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 500;
|
||||
background: rgba(28, 62, 94, 0.4);
|
||||
backdrop-filter: blur(5px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 22px;
|
||||
}
|
||||
.ae-dlg {
|
||||
width: min(520px, 100%);
|
||||
max-height: 86vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(228, 244, 255, 0.94));
|
||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 0 0 4px rgba(120, 190, 240, 0.35), 0 30px 70px rgba(20, 50, 80, 0.5);
|
||||
}
|
||||
.ae-dlg-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 14px;
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(160, 214, 250, 0.75) 48%, rgba(108, 189, 242, 0.72) 52%, rgba(190, 230, 252, 0.85));
|
||||
border-bottom: 1px solid rgba(120, 180, 220, 0.5);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ae-dlg-bar b {
|
||||
flex: 1;
|
||||
font-size: 13.5px;
|
||||
color: #14406e;
|
||||
text-shadow: 0 1px 0 rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
.ae-dlg-x {
|
||||
width: 26px;
|
||||
height: 22px;
|
||||
border-radius: 5px;
|
||||
border: 1px solid rgba(160, 60, 60, 0.6);
|
||||
background: linear-gradient(180deg, #ff9f96, #e0574a);
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.ae-dlg-body {
|
||||
flex: 1;
|
||||
overflow-y: auto;
|
||||
padding: 14px 18px;
|
||||
}
|
||||
.ae-dlg-hero {
|
||||
position: relative;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.9);
|
||||
box-shadow: 0 8px 20px rgba(20, 64, 110, 0.2);
|
||||
}
|
||||
.ae-dlg-hero img {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 7;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
.ae-dlg-sub {
|
||||
margin: 0 0 8px;
|
||||
font-size: 11.5px;
|
||||
color: #557a99;
|
||||
}
|
||||
.ae-dlg-story {
|
||||
margin: 0 0 10px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
color: #1d3a52;
|
||||
}
|
||||
.ae-dlg-h {
|
||||
margin: 12px 0 6px;
|
||||
font-size: 12.5px;
|
||||
font-weight: 700;
|
||||
color: #1a6fb5;
|
||||
}
|
||||
.ae-dlg-li {
|
||||
margin: 0 0 6px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.7;
|
||||
color: #2c4f6e;
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: baseline;
|
||||
}
|
||||
.ae-dlg-li i {
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 32% 28%, #d6f2c8, #6bbf4e 65%, #3f8f2e);
|
||||
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.8);
|
||||
flex-shrink: 0;
|
||||
transform: translateY(1px);
|
||||
}
|
||||
.ae-dlg-li.tl b {
|
||||
color: #1a6fb5;
|
||||
flex-shrink: 0;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ae-dlg-metrics {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin: 10px 0 4px;
|
||||
}
|
||||
.ae-dlg-metrics span {
|
||||
background: linear-gradient(180deg, rgba(255, 255, 255, 0.95), rgba(214, 238, 252, 0.9));
|
||||
border: 1px solid rgba(150, 200, 235, 0.7);
|
||||
border-radius: 10px;
|
||||
padding: 8px 13px;
|
||||
font-size: 10.5px;
|
||||
color: #557a99;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
.ae-dlg-metrics b {
|
||||
font-size: 15px;
|
||||
color: #14406e;
|
||||
}
|
||||
.ae-dlg-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
padding: 10px 16px;
|
||||
border-top: 1px solid rgba(150, 200, 235, 0.55);
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
font-size: 11.5px;
|
||||
color: #557a99;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ae-dlg-ok {
|
||||
min-width: 88px;
|
||||
padding: 7px 18px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(60, 130, 60, 0.55);
|
||||
background: linear-gradient(180deg, #d9f2c8, #8fd47a 48%, #5cb548 52%, #a4e08c);
|
||||
color: #14421c;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.8), 0 3px 8px rgba(40, 100, 40, 0.25);
|
||||
}
|
||||
.ae-dlg-ok:hover {
|
||||
filter: brightness(1.06);
|
||||
}
|
||||
.ae-dialog-enter-active,
|
||||
.ae-dialog-leave-active {
|
||||
transition: opacity 0.22s ease;
|
||||
}
|
||||
.ae-dialog-enter-active .ae-dlg {
|
||||
transition: transform 0.26s cubic-bezier(0.2, 1.2, 0.4, 1);
|
||||
}
|
||||
.ae-dialog-enter-from,
|
||||
.ae-dialog-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.ae-dialog-enter-from .ae-dlg {
|
||||
transform: scale(0.82);
|
||||
}
|
||||
|
||||
.ae {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(180deg, #6cbdf2 0%, #a8ddfa 34%, #cdeefd 52%, #8fd47a 72%, #4faf3f 100%);
|
||||
|
||||
597
src/pages/styles/AndroidPage.vue
Normal file
597
src/pages/styles/AndroidPage.vue
Normal file
@@ -0,0 +1,597 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { profile } from '../../data/profile'
|
||||
|
||||
const drawer = ref(false)
|
||||
/* 作品详情底部抽屉(Material You bottom sheet) */
|
||||
const workSheet = ref(null)
|
||||
const apps = [
|
||||
{ icon: '☎', name: '电话' },
|
||||
{ icon: '✉', name: '短信' },
|
||||
{ icon: '⌘', name: '终端' },
|
||||
{ icon: '♫', name: '音乐' },
|
||||
{ icon: '⚙', name: '设置' },
|
||||
{ icon: '⌗', name: '相册' },
|
||||
{ icon: '✎', name: '备忘' },
|
||||
{ icon: '⏱', name: '时钟' },
|
||||
]
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="and">
|
||||
<div class="g-phone">
|
||||
<!-- 状态栏 -->
|
||||
<header class="g-status">
|
||||
<span>21:47</span>
|
||||
<b class="g-cam"></b>
|
||||
<span class="g-icons">◍ ▽ ▰</span>
|
||||
</header>
|
||||
|
||||
<!-- At a Glance -->
|
||||
<div class="g-glance">
|
||||
<p class="g-clock">21:47</p>
|
||||
<p class="g-date">8月12日周三 · 多云转写代码 26°</p>
|
||||
</div>
|
||||
|
||||
<!-- 大号资料小组件 -->
|
||||
<section class="g-widget hero">
|
||||
<img :src="profile.avatars.cute" alt="头像" class="g-avatar" />
|
||||
<div>
|
||||
<p class="g-widget-title">{{ profile.name }}</p>
|
||||
<p class="g-widget-sub">{{ profile.job }} · {{ profile.age }} 岁 · {{ profile.location }}</p>
|
||||
<p class="g-widget-body">{{ profile.bioShort }}</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 技能小组件:圆环 -->
|
||||
<section class="g-widget">
|
||||
<p class="g-widget-name">技能电量 · Material You</p>
|
||||
<div class="g-rings">
|
||||
<div v-for="(sk, i) in profile.skills" :key="sk.name" class="g-ring" :class="`r${i % 4}`">
|
||||
<i class="g-ring-fill" :style="{ '--pct': sk.level }"></i>
|
||||
<b>{{ sk.level }}</b>
|
||||
<span>{{ sk.name.split(' / ')[0] }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 作品通知卡 -->
|
||||
<section class="g-widget">
|
||||
<p class="g-widget-name">作品动态</p>
|
||||
<button v-for="p in profile.projects" :key="p.name" class="g-notif tap" @click="workSheet = p">
|
||||
<span class="g-notif-icon">▣</span>
|
||||
<div class="g-notif-body">
|
||||
<p class="g-notif-title">{{ p.name }} <small>{{ p.alias }}</small></p>
|
||||
<p class="g-notif-text">{{ p.desc }}</p>
|
||||
<p class="g-notif-meta">{{ p.year }} · ★{{ p.stars }} · {{ p.tags.join(' · ') }} · 轻点展开</p>
|
||||
</div>
|
||||
</button>
|
||||
</section>
|
||||
|
||||
<!-- 搜索药丸 -->
|
||||
<div class="g-search">
|
||||
<span>◎</span>
|
||||
<em>搜索联系方式…</em>
|
||||
<b @click="drawer = !drawer">⌕</b>
|
||||
</div>
|
||||
|
||||
<!-- dock -->
|
||||
<footer class="g-dock">
|
||||
<router-link to="/" class="g-app" title="返回合集"><i class="d0">⌂</i></router-link>
|
||||
<router-link to="/style/android-home/panel" class="g-app" title="通知中心"><i class="d0">🔔</i></router-link>
|
||||
<button class="g-app" @click="drawer = true"><i class="d1">☰</i></button>
|
||||
<a class="g-app" :href="`mailto:${profile.contacts[1].value}`"><i class="d2">✉</i></a>
|
||||
<button class="g-app" @click="drawer = true"><i class="d3">▦</i></button>
|
||||
</footer>
|
||||
|
||||
<!-- 应用抽屉(联系方式) -->
|
||||
<transition name="g-sheet">
|
||||
<div v-if="drawer" class="g-drawer" @click.self="drawer = false">
|
||||
<div class="g-sheet">
|
||||
<i class="g-handle"></i>
|
||||
<p class="g-sheet-title">联系方式</p>
|
||||
<div v-for="c in profile.contacts" :key="c.label" class="g-contact">
|
||||
<b>{{ c.label }}</b><span>{{ c.value }}</span>
|
||||
</div>
|
||||
<p class="g-sheet-title" style="margin-top: 22px">常用应用</p>
|
||||
<div class="g-grid">
|
||||
<div v-for="a in apps" :key="a.name" class="g-appcell">
|
||||
<i>{{ a.icon }}</i>
|
||||
<span>{{ a.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="g-motto">「{{ profile.motto }}」</p>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
|
||||
<!-- 作品详情 bottom sheet -->
|
||||
<transition name="g-sheet">
|
||||
<div v-if="workSheet" class="g-drawer" @click.self="workSheet = null">
|
||||
<div class="g-sheet g-work-sheet">
|
||||
<i class="g-handle"></i>
|
||||
<img :src="workSheet.image" :alt="workSheet.name" class="g-work-hero" loading="lazy" />
|
||||
<p class="g-sheet-title" style="margin-top: 12px">
|
||||
{{ workSheet.name }} <small class="g-work-sub">{{ workSheet.alias }} · {{ workSheet.year }} · ★{{ workSheet.stars }}</small>
|
||||
</p>
|
||||
<p class="g-work-story">{{ workSheet.story }}</p>
|
||||
<div class="g-work-chips">
|
||||
<span v-for="t in workSheet.tags" :key="t">{{ t }}</span>
|
||||
</div>
|
||||
<div class="g-work-metrics">
|
||||
<div v-for="m in workSheet.metrics" :key="m.label">
|
||||
<b>{{ m.value }}</b><span>{{ m.label }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<p class="g-work-label">亮点</p>
|
||||
<p v-for="(h, hi) in workSheet.highlights" :key="hi" class="g-work-li">• {{ h }}</p>
|
||||
<p class="g-work-label">时间线</p>
|
||||
<p v-for="t in workSheet.timeline" :key="t.at" class="g-work-li tl"><b>{{ t.at }}</b>{{ t.event }}</p>
|
||||
<button class="g-work-close" @click="workSheet = null">收起</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.and {
|
||||
min-height: 100vh;
|
||||
background: linear-gradient(170deg, #3a3f52, #23283a);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
padding: 30px 12px 40px;
|
||||
font-family: 'Space Grotesk', 'Noto Serif SC', Roboto, sans-serif;
|
||||
}
|
||||
|
||||
.g-phone {
|
||||
position: relative;
|
||||
width: min(430px, 100%);
|
||||
background:
|
||||
radial-gradient(ellipse 90% 40% at 80% -6%, rgba(255, 200, 170, 0.55), transparent 60%),
|
||||
radial-gradient(ellipse 80% 44% at 12% 106%, rgba(178, 200, 255, 0.5), transparent 62%),
|
||||
linear-gradient(170deg, #f4e5dc, #e8e0f4 55%, #dbe4f2);
|
||||
border-radius: 34px;
|
||||
border: 8px solid #14161f;
|
||||
padding: 12px 16px 90px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 30px 70px rgba(0, 0, 0, 0.5);
|
||||
color: #2f2a36;
|
||||
}
|
||||
|
||||
.g-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 12.5px;
|
||||
font-weight: 600;
|
||||
padding: 2px 6px 0;
|
||||
}
|
||||
.g-cam {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: #14161f;
|
||||
}
|
||||
.g-icons {
|
||||
letter-spacing: 0.14em;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
.g-glance {
|
||||
padding: 26px 10px 18px;
|
||||
}
|
||||
.g-clock {
|
||||
margin: 0;
|
||||
font-size: 56px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
letter-spacing: -0.02em;
|
||||
color: #4a3f66;
|
||||
}
|
||||
.g-date {
|
||||
margin: 8px 0 0;
|
||||
font-size: 13.5px;
|
||||
color: rgba(47, 42, 54, 0.7);
|
||||
}
|
||||
|
||||
.g-widget {
|
||||
background: rgba(255, 255, 255, 0.72);
|
||||
backdrop-filter: blur(6px);
|
||||
border-radius: 26px;
|
||||
padding: 16px 18px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.g-widget.hero {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
background: #4a3f66;
|
||||
color: #f4e5dc;
|
||||
}
|
||||
.g-avatar {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
background: #f4e5dc;
|
||||
}
|
||||
.g-widget-title {
|
||||
margin: 0;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.g-widget-sub {
|
||||
margin: 3px 0 6px;
|
||||
font-size: 12px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
.g-widget-body {
|
||||
margin: 0;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.7;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
opacity: 0.92;
|
||||
}
|
||||
.g-widget-name {
|
||||
margin: 0 0 14px;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
color: rgba(47, 42, 54, 0.6);
|
||||
}
|
||||
|
||||
/* 圆环 */
|
||||
.g-rings {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 16px 8px;
|
||||
}
|
||||
.g-ring {
|
||||
position: relative;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
text-align: center;
|
||||
padding-top: 4px;
|
||||
}
|
||||
.g-ring-fill {
|
||||
position: relative;
|
||||
width: 66px;
|
||||
height: 66px;
|
||||
border-radius: 50%;
|
||||
background: conic-gradient(var(--rc) calc(var(--pct) * 1%), rgba(0, 0, 0, 0.08) 0);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
}
|
||||
.g-ring-fill::after {
|
||||
content: '';
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
background: #fff;
|
||||
}
|
||||
.g-ring b {
|
||||
position: absolute;
|
||||
top: 26px;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.g-ring span {
|
||||
margin-top: 7px;
|
||||
font-size: 11px;
|
||||
color: rgba(47, 42, 54, 0.75);
|
||||
}
|
||||
.g-ring.r0 { --rc: #7c6cdb; }
|
||||
.g-ring.r1 { --rc: #d96a8b; }
|
||||
.g-ring.r2 { --rc: #4a9d7f; }
|
||||
.g-ring.r3 { --rc: #d9924a; }
|
||||
|
||||
/* 通知卡 */
|
||||
.g-notif {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 11px 4px;
|
||||
border-top: 1px solid rgba(47, 42, 54, 0.08);
|
||||
}
|
||||
.g-notif:first-of-type {
|
||||
border-top: none;
|
||||
}
|
||||
.g-notif-icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
flex-shrink: 0;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #e4dcf4;
|
||||
color: #4a3f66;
|
||||
border-radius: 14px;
|
||||
font-size: 17px;
|
||||
}
|
||||
.g-notif-body {
|
||||
min-width: 0;
|
||||
}
|
||||
.g-notif-title {
|
||||
margin: 0 0 3px;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.g-notif-title small {
|
||||
font-weight: 400;
|
||||
font-size: 11px;
|
||||
color: rgba(47, 42, 54, 0.55);
|
||||
margin-left: 5px;
|
||||
}
|
||||
.g-notif-text {
|
||||
margin: 0 0 4px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.7;
|
||||
color: rgba(47, 42, 54, 0.8);
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
.g-notif-meta {
|
||||
margin: 0;
|
||||
font-size: 11px;
|
||||
color: rgba(47, 42, 54, 0.5);
|
||||
}
|
||||
|
||||
.g-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: #e4dcf4;
|
||||
border-radius: 999px;
|
||||
padding: 13px 20px;
|
||||
font-size: 13.5px;
|
||||
color: rgba(47, 42, 54, 0.65);
|
||||
}
|
||||
.g-search em {
|
||||
flex: 1;
|
||||
font-style: normal;
|
||||
}
|
||||
.g-search b {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.g-dock {
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
right: 16px;
|
||||
bottom: 14px;
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
background: rgba(255, 255, 255, 0.66);
|
||||
backdrop-filter: blur(8px);
|
||||
border-radius: 30px;
|
||||
padding: 10px 8px;
|
||||
}
|
||||
.g-app {
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
}
|
||||
.g-app i {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
font-size: 21px;
|
||||
font-style: normal;
|
||||
transition: transform 0.16s ease;
|
||||
}
|
||||
.g-app:hover i {
|
||||
transform: scale(1.1);
|
||||
}
|
||||
.g-app .d0 { background: #4a3f66; color: #f4e5dc; }
|
||||
.g-app .d1 { background: #d96a8b; color: #fff; }
|
||||
.g-app .d2 { background: #4a9d7f; color: #fff; }
|
||||
.g-app .d3 { background: #d9924a; color: #fff; }
|
||||
|
||||
/* 抽屉 */
|
||||
.g-drawer {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(20, 22, 31, 0.4);
|
||||
backdrop-filter: blur(2px);
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
z-index: 10;
|
||||
}
|
||||
.g-sheet {
|
||||
width: 100%;
|
||||
max-height: 82%;
|
||||
overflow-y: auto;
|
||||
background: #f6f1fb;
|
||||
border-radius: 30px 30px 0 0;
|
||||
padding: 12px 22px 26px;
|
||||
}
|
||||
.g-handle {
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 4px;
|
||||
border-radius: 2px;
|
||||
background: rgba(47, 42, 54, 0.25);
|
||||
margin: 0 auto 16px;
|
||||
}
|
||||
.g-sheet-title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
color: rgba(47, 42, 54, 0.55);
|
||||
}
|
||||
.g-contact {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
background: #fff;
|
||||
border-radius: 16px;
|
||||
padding: 13px 16px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.g-contact b {
|
||||
color: #4a3f66;
|
||||
}
|
||||
.g-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 14px 8px;
|
||||
}
|
||||
.g-appcell {
|
||||
text-align: center;
|
||||
}
|
||||
.g-appcell i {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
margin: 0 auto;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
background: #e4dcf4;
|
||||
color: #4a3f66;
|
||||
border-radius: 18px;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
}
|
||||
.g-appcell span {
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
font-size: 11px;
|
||||
color: rgba(47, 42, 54, 0.7);
|
||||
}
|
||||
.g-motto {
|
||||
margin: 20px 0 0;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: rgba(47, 42, 54, 0.55);
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
|
||||
.g-sheet-enter-active,
|
||||
.g-sheet-leave-active {
|
||||
transition: opacity 0.25s ease;
|
||||
}
|
||||
.g-sheet-enter-active .g-sheet,
|
||||
.g-sheet-leave-active .g-sheet {
|
||||
transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
|
||||
}
|
||||
.g-sheet-enter-from,
|
||||
.g-sheet-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.g-sheet-enter-from .g-sheet,
|
||||
.g-sheet-leave-to .g-sheet {
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
/* ---- 作品详情 sheet ---- */
|
||||
.g-notif.tap {
|
||||
width: 100%;
|
||||
background: none;
|
||||
border-right: none;
|
||||
border-left: none;
|
||||
border-bottom: none;
|
||||
font: inherit;
|
||||
color: inherit;
|
||||
text-align: left;
|
||||
cursor: pointer;
|
||||
border-radius: 14px;
|
||||
transition: background 0.15s;
|
||||
}
|
||||
.g-notif.tap:hover {
|
||||
background: rgba(47, 42, 54, 0.05);
|
||||
}
|
||||
.g-work-hero {
|
||||
width: 100%;
|
||||
aspect-ratio: 16 / 8;
|
||||
object-fit: cover;
|
||||
border-radius: 22px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.g-work-sub {
|
||||
font-size: 11.5px;
|
||||
color: #7a7286;
|
||||
font-weight: 400;
|
||||
margin-left: 6px;
|
||||
}
|
||||
.g-work-story {
|
||||
margin: 8px 0 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.8;
|
||||
color: #4b4553;
|
||||
}
|
||||
.g-work-chips {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.g-work-chips span {
|
||||
background: #e3d7f3;
|
||||
color: #4f3d6b;
|
||||
border-radius: 10px;
|
||||
padding: 5px 12px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.g-work-metrics {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.g-work-metrics > div {
|
||||
flex: 1;
|
||||
background: #ece4f6;
|
||||
border-radius: 18px;
|
||||
padding: 12px 10px;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
}
|
||||
.g-work-metrics b {
|
||||
font-size: 17px;
|
||||
color: #2f2a36;
|
||||
}
|
||||
.g-work-metrics span {
|
||||
font-size: 10.5px;
|
||||
color: #7a7286;
|
||||
}
|
||||
.g-work-label {
|
||||
margin: 12px 0 6px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #6b5f7d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.g-work-li {
|
||||
margin: 0 0 5px;
|
||||
font-size: 12.5px;
|
||||
line-height: 1.7;
|
||||
color: #4b4553;
|
||||
}
|
||||
.g-work-li.tl b {
|
||||
color: #6247aa;
|
||||
margin-right: 8px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.g-work-close {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin-top: 16px;
|
||||
background: #6247aa;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 22px;
|
||||
padding: 12px;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
}
|
||||
.g-work-close:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
</style>
|
||||
645
src/pages/styles/ArcadePage.vue
Normal file
645
src/pages/styles/ArcadePage.vue
Normal file
@@ -0,0 +1,645 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { profile } from '../../data/profile'
|
||||
|
||||
const credits = ref(0)
|
||||
const picked = ref(-1)
|
||||
|
||||
function insertCoin() {
|
||||
credits.value++
|
||||
}
|
||||
function pick(i) {
|
||||
if (credits.value < 1) return
|
||||
picked.value = i
|
||||
}
|
||||
|
||||
/* 机台投币 → 看铭牌 */
|
||||
const plaque = ref(null)
|
||||
const shakeIdx = ref(-1)
|
||||
function playCab(p, i) {
|
||||
if (credits.value < 1) {
|
||||
shakeIdx.value = i
|
||||
setTimeout(() => (shakeIdx.value = -1), 500)
|
||||
return
|
||||
}
|
||||
credits.value--
|
||||
plaque.value = p
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="arc">
|
||||
<nav class="a-nav">
|
||||
<router-link to="/" class="a-back">← 离场</router-link>
|
||||
<router-link to="/style/arcade/breakout" class="a-back">▶ 打砖块机台</router-link>
|
||||
<span class="a-credit">CREDIT {{ String(credits).padStart(2, '0') }}</span>
|
||||
</nav>
|
||||
|
||||
<!-- 跑马灯招牌 -->
|
||||
<header class="a-marquee">
|
||||
<i v-for="n in 22" :key="n" class="a-bulb" :style="{ animationDelay: (n % 4) * 0.25 + 's' }"></i>
|
||||
<h1>NIANGAO ARCADE</h1>
|
||||
<p>{{ profile.name }} · {{ profile.job }} · SINCE 2001</p>
|
||||
</header>
|
||||
|
||||
<p class="a-insert" :class="{ ok: credits > 0 }" @click="insertCoin">
|
||||
{{ credits > 0 ? '▼ 选择你的技能 ▼' : '☞ INSERT COIN ☜(点这里投币)' }}
|
||||
</p>
|
||||
|
||||
<!-- 选人界面:技能就是可选角色 -->
|
||||
<section class="a-select">
|
||||
<div
|
||||
v-for="(sk, i) in profile.skills"
|
||||
:key="sk.name"
|
||||
class="a-fighter"
|
||||
:class="{ picked: picked === i, locked: credits < 1 }"
|
||||
@click="pick(i)"
|
||||
>
|
||||
<div class="a-portrait">
|
||||
<img :src="`https://api.dicebear.com/9.x/pixel-art/svg?seed=${sk.en}`" :alt="sk.name" />
|
||||
</div>
|
||||
<p class="a-fighter-name">{{ sk.name }}</p>
|
||||
<div class="a-power">
|
||||
<span>PWR</span>
|
||||
<div class="a-power-bar"><i :style="{ width: sk.level + '%' }"></i></div>
|
||||
<b>{{ sk.level }}</b>
|
||||
</div>
|
||||
<p v-if="picked === i" class="a-ready">READY!</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 机台一条街:作品 -->
|
||||
<section class="a-cabs-wrap">
|
||||
<h2 class="a-title">作品机台 · 投币试玩</h2>
|
||||
<div class="a-cabs">
|
||||
<article
|
||||
v-for="(p, i) in profile.projects"
|
||||
:key="p.name"
|
||||
class="a-cab"
|
||||
:class="[`c${i % 4}`, { shake: shakeIdx === i }]"
|
||||
>
|
||||
<div class="a-cab-marquee">{{ p.name }}</div>
|
||||
<div class="a-cab-screen">
|
||||
<p>{{ p.desc }}</p>
|
||||
</div>
|
||||
<div class="a-cab-panel">
|
||||
<i class="a-stick"></i>
|
||||
<b class="a-btn r"></b>
|
||||
<b class="a-btn y"></b>
|
||||
<button class="a-slot" :title="credits > 0 ? '投币 1 枚,查看机台铭牌' : '先在上面投币!'" @click="playCab(p, i)">
|
||||
<i></i>投币
|
||||
</button>
|
||||
</div>
|
||||
<footer>{{ p.year }} · HI-SCORE {{ p.stars }}</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="a-footer">
|
||||
<p class="a-rank">
|
||||
<span>名次</span><span>玩家</span><span>联系方式</span>
|
||||
</p>
|
||||
<p v-for="(c, i) in profile.contacts" :key="c.label" class="a-rank-row">
|
||||
<span>{{ ['1ST', '2ND', '3RD'][i] }}</span><span>{{ c.label.toUpperCase() }}</span><span>{{ c.value }}</span>
|
||||
</p>
|
||||
<p class="a-fin">{{ profile.motto }} — GAME OVER? CONTINUE!</p>
|
||||
</footer>
|
||||
|
||||
<!-- 机台铭牌 -->
|
||||
<Transition name="a-plaque">
|
||||
<div v-if="plaque" class="a-plaque-mask" @click.self="plaque = null">
|
||||
<div class="a-plaque">
|
||||
<header class="a-plaque-head">
|
||||
<span class="a-plaque-rivet"></span>
|
||||
<b>机台铭牌 · SPEC PLATE</b>
|
||||
<span class="a-plaque-rivet"></span>
|
||||
</header>
|
||||
<h3 class="a-plaque-title">{{ plaque.name }}</h3>
|
||||
<p class="a-plaque-serial">SERIAL NO. {{ plaque.slug.toUpperCase() }}-{{ plaque.year }} · {{ plaque.alias }}</p>
|
||||
<p class="a-plaque-story">{{ plaque.story }}</p>
|
||||
<div class="a-plaque-grid">
|
||||
<div class="a-plaque-block">
|
||||
<b>▸ 机能特性</b>
|
||||
<p v-for="(h, hi) in plaque.highlights" :key="hi">◆ {{ h }}</p>
|
||||
</div>
|
||||
<div class="a-plaque-block">
|
||||
<b>▸ 出厂记录</b>
|
||||
<p v-for="t in plaque.timeline" :key="t.at"><i>{{ t.at }}</i>{{ t.event }}</p>
|
||||
<b style="margin-top: 10px">▸ 性能参数</b>
|
||||
<p v-for="m in plaque.metrics" :key="m.label"><i>{{ m.label }}</i>{{ m.value }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="a-plaque-foot">
|
||||
<span>{{ plaque.tags.join(' / ') }}</span>
|
||||
<button @click="plaque = null">继续游戏 ▸</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.arc {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(ellipse at 50% -20%, rgba(155, 92, 255, 0.25), transparent 55%),
|
||||
repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.3) 0 2px, transparent 2px 4px),
|
||||
#120a1e;
|
||||
color: #f4f1ff;
|
||||
font-family: 'Press Start 2P', monospace;
|
||||
padding: 22px clamp(12px, 4vw, 60px) 60px;
|
||||
}
|
||||
|
||||
.a-nav {
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
z-index: 60;
|
||||
background: rgba(8, 8, 14, 0.62);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 920px;
|
||||
margin: 0 auto 28px;
|
||||
}
|
||||
.a-back {
|
||||
color: #ffd23f;
|
||||
text-decoration: none;
|
||||
font-size: 11px;
|
||||
border: 2px solid #ffd23f;
|
||||
padding: 8px 12px;
|
||||
box-shadow: 3px 3px 0 rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.a-back:hover {
|
||||
background: #ffd23f;
|
||||
color: #120a1e;
|
||||
}
|
||||
.a-credit {
|
||||
font-size: 11px;
|
||||
color: #5eead4;
|
||||
animation: a-blink 1.2s steps(2, end) infinite;
|
||||
}
|
||||
|
||||
.a-marquee {
|
||||
position: relative;
|
||||
max-width: 920px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
border: 4px solid #ff2bd6;
|
||||
border-radius: 14px;
|
||||
padding: 34px 16px 24px;
|
||||
background: linear-gradient(180deg, #2b1147, #1b0b33);
|
||||
box-shadow: 0 0 34px rgba(255, 43, 214, 0.35), inset 0 0 24px rgba(255, 43, 214, 0.14);
|
||||
overflow: hidden;
|
||||
}
|
||||
.a-bulb {
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: #ffd23f;
|
||||
box-shadow: 0 0 10px #ffd23f;
|
||||
animation: a-blink 1s steps(2, end) infinite;
|
||||
}
|
||||
.a-bulb:nth-child(-n + 11) { top: 7px; }
|
||||
.a-bulb:nth-child(n + 12) { bottom: 7px; }
|
||||
.a-bulb:nth-child(1), .a-bulb:nth-child(12) { left: 4%; }
|
||||
.a-bulb:nth-child(2), .a-bulb:nth-child(13) { left: 13%; }
|
||||
.a-bulb:nth-child(3), .a-bulb:nth-child(14) { left: 22%; }
|
||||
.a-bulb:nth-child(4), .a-bulb:nth-child(15) { left: 31%; }
|
||||
.a-bulb:nth-child(5), .a-bulb:nth-child(16) { left: 40%; }
|
||||
.a-bulb:nth-child(6), .a-bulb:nth-child(17) { left: 49%; }
|
||||
.a-bulb:nth-child(7), .a-bulb:nth-child(18) { left: 58%; }
|
||||
.a-bulb:nth-child(8), .a-bulb:nth-child(19) { left: 67%; }
|
||||
.a-bulb:nth-child(9), .a-bulb:nth-child(20) { left: 76%; }
|
||||
.a-bulb:nth-child(10), .a-bulb:nth-child(21) { left: 85%; }
|
||||
.a-bulb:nth-child(11), .a-bulb:nth-child(22) { left: 94%; }
|
||||
@keyframes a-blink {
|
||||
50% { opacity: 0.25; box-shadow: none; }
|
||||
}
|
||||
.a-marquee h1 {
|
||||
margin: 0 0 14px;
|
||||
font-size: clamp(20px, 4.4vw, 38px);
|
||||
color: #ffd23f;
|
||||
text-shadow: 0 0 18px rgba(255, 210, 63, 0.7), 4px 4px 0 #ff2bd6;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.a-marquee p {
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
color: #5eead4;
|
||||
line-height: 1.9;
|
||||
}
|
||||
|
||||
.a-insert {
|
||||
max-width: 920px;
|
||||
margin: 26px auto;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
color: #ffd23f;
|
||||
cursor: pointer;
|
||||
animation: a-blink 1.4s steps(2, end) infinite;
|
||||
user-select: none;
|
||||
}
|
||||
.a-insert.ok {
|
||||
color: #5eead4;
|
||||
animation: none;
|
||||
}
|
||||
.a-insert:active {
|
||||
transform: scale(0.96);
|
||||
}
|
||||
|
||||
/* 选人 */
|
||||
.a-select {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: 14px;
|
||||
max-width: 920px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.a-fighter {
|
||||
position: relative;
|
||||
border: 3px solid #3d2a66;
|
||||
background: #1b0f33;
|
||||
padding: 12px 10px 12px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
transition: border-color 0.15s ease, transform 0.15s ease;
|
||||
}
|
||||
.a-fighter:hover {
|
||||
border-color: #5eead4;
|
||||
transform: translateY(-3px);
|
||||
}
|
||||
.a-fighter.locked {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.75;
|
||||
}
|
||||
.a-fighter.picked {
|
||||
border-color: #ffd23f;
|
||||
box-shadow: 0 0 20px rgba(255, 210, 63, 0.4);
|
||||
animation: a-pick 0.3s steps(3, end);
|
||||
}
|
||||
@keyframes a-pick {
|
||||
from { transform: scale(0.9); }
|
||||
}
|
||||
.a-portrait {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
margin: 0 auto 10px;
|
||||
border: 3px solid #3d2a66;
|
||||
background: #2b1147;
|
||||
image-rendering: pixelated;
|
||||
}
|
||||
.a-portrait img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
.a-fighter-name {
|
||||
margin: 0 0 8px;
|
||||
font-size: 9.5px;
|
||||
line-height: 1.5;
|
||||
color: #f4f1ff;
|
||||
}
|
||||
.a-power {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
.a-power span {
|
||||
font-size: 8px;
|
||||
color: #ff2bd6;
|
||||
}
|
||||
.a-power-bar {
|
||||
flex: 1;
|
||||
height: 8px;
|
||||
border: 2px solid #3d2a66;
|
||||
background: #120a1e;
|
||||
}
|
||||
.a-power-bar i {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: repeating-linear-gradient(90deg, #5eead4 0 4px, #2b8f80 4px 6px);
|
||||
}
|
||||
.a-power b {
|
||||
font-size: 9px;
|
||||
color: #ffd23f;
|
||||
}
|
||||
.a-ready {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 34%;
|
||||
transform: translateX(-50%) rotate(-8deg);
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
color: #ffd23f;
|
||||
text-shadow: 2px 2px 0 #ff2bd6;
|
||||
animation: a-blink 0.5s steps(2, end) infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* 机台 */
|
||||
.a-cabs-wrap {
|
||||
max-width: 920px;
|
||||
margin: 52px auto 0;
|
||||
}
|
||||
.a-title {
|
||||
margin: 0 0 22px;
|
||||
font-size: 13px;
|
||||
color: #ff2bd6;
|
||||
text-shadow: 0 0 14px rgba(255, 43, 214, 0.6);
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.a-cabs {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
.a-cab {
|
||||
border: 3px solid;
|
||||
border-radius: 10px 10px 4px 4px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease;
|
||||
}
|
||||
.a-cab:hover {
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
.a-cab.c0 { border-color: #ff2bd6; background: #240f3d; }
|
||||
.a-cab.c1 { border-color: #5eead4; background: #0d2b30; }
|
||||
.a-cab.c2 { border-color: #ffd23f; background: #33270a; }
|
||||
.a-cab.c3 { border-color: #7a89f2; background: #131c3d; }
|
||||
.a-cab-marquee {
|
||||
padding: 9px 8px;
|
||||
font-size: 10px;
|
||||
text-align: center;
|
||||
color: #120a1e;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.c0 .a-cab-marquee { background: #ff2bd6; }
|
||||
.c1 .a-cab-marquee { background: #5eead4; }
|
||||
.c2 .a-cab-marquee { background: #ffd23f; }
|
||||
.c3 .a-cab-marquee { background: #7a89f2; }
|
||||
.a-cab-screen {
|
||||
margin: 10px;
|
||||
border: 2px solid rgba(255, 255, 255, 0.2);
|
||||
background: #050208;
|
||||
min-height: 108px;
|
||||
padding: 10px;
|
||||
}
|
||||
.a-cab-screen p {
|
||||
margin: 0;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
font-size: 12px;
|
||||
line-height: 1.85;
|
||||
color: rgba(244, 241, 255, 0.85);
|
||||
}
|
||||
.a-cab-panel {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 4px 14px 10px;
|
||||
}
|
||||
.a-slot {
|
||||
margin-left: auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
background: #14141f;
|
||||
border: 1px solid #3a3a52;
|
||||
color: #ffd75e;
|
||||
font-family: inherit;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.12em;
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
.a-slot i {
|
||||
width: 3px;
|
||||
height: 12px;
|
||||
background: #ffd75e;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 0 6px rgba(255, 215, 94, 0.8);
|
||||
}
|
||||
.a-slot:hover {
|
||||
background: #24243a;
|
||||
border-color: #ffd75e;
|
||||
}
|
||||
.a-cab.shake {
|
||||
animation: a-shake 0.4s ease;
|
||||
}
|
||||
@keyframes a-shake {
|
||||
20% { transform: translateX(-5px) rotate(-1deg); }
|
||||
40% { transform: translateX(5px) rotate(1deg); }
|
||||
60% { transform: translateX(-4px); }
|
||||
80% { transform: translateX(4px); }
|
||||
}
|
||||
/* ---- 机台铭牌 ---- */
|
||||
.a-plaque-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 500;
|
||||
background: rgba(6, 4, 18, 0.72);
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 20px;
|
||||
}
|
||||
.a-plaque {
|
||||
width: min(620px, 100%);
|
||||
max-height: 84vh;
|
||||
overflow-y: auto;
|
||||
background:
|
||||
repeating-linear-gradient(115deg, rgba(255, 255, 255, 0.045) 0 2px, transparent 2px 5px),
|
||||
linear-gradient(165deg, #3a3f4e, #262a36 60%, #30343f);
|
||||
border: 3px solid #5b6274;
|
||||
border-radius: 10px;
|
||||
padding: 18px 22px;
|
||||
color: #dfe4f0;
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.7), inset 0 1px 0 rgba(255, 255, 255, 0.25);
|
||||
}
|
||||
.a-plaque-head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.a-plaque-head b {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.28em;
|
||||
color: #9aa3ba;
|
||||
}
|
||||
.a-plaque-rivet {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 35% 35%, #cdd4e4, #6a7288 60%, #3c4252);
|
||||
box-shadow: inset 0 -1px 2px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.a-plaque-title {
|
||||
margin: 0 0 4px;
|
||||
font-size: 24px;
|
||||
color: #ffd75e;
|
||||
text-shadow: 0 0 14px rgba(255, 215, 94, 0.45);
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.a-plaque-serial {
|
||||
margin: 0 0 12px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.14em;
|
||||
color: #8b93a8;
|
||||
}
|
||||
.a-plaque-story {
|
||||
margin: 0 0 14px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
color: #c6cddd;
|
||||
border-left: 3px solid #ffd75e;
|
||||
padding-left: 12px;
|
||||
}
|
||||
.a-plaque-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 16px;
|
||||
}
|
||||
@media (max-width: 620px) {
|
||||
.a-plaque-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
.a-plaque-block b {
|
||||
display: block;
|
||||
font-size: 11.5px;
|
||||
letter-spacing: 0.1em;
|
||||
color: #7ee3ff;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
.a-plaque-block p {
|
||||
margin: 0 0 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.65;
|
||||
color: #b8c0d4;
|
||||
}
|
||||
.a-plaque-block p i {
|
||||
font-style: normal;
|
||||
color: #ffd75e;
|
||||
margin-right: 8px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.a-plaque-foot {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
margin-top: 16px;
|
||||
border-top: 1px dashed #4a5065;
|
||||
padding-top: 12px;
|
||||
font-size: 11px;
|
||||
color: #8b93a8;
|
||||
}
|
||||
.a-plaque-foot button {
|
||||
background: #ffd75e;
|
||||
border: none;
|
||||
color: #262a36;
|
||||
font-family: inherit;
|
||||
font-weight: 700;
|
||||
font-size: 12px;
|
||||
padding: 8px 16px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
letter-spacing: 0.08em;
|
||||
}
|
||||
.a-plaque-foot button:hover {
|
||||
filter: brightness(1.1);
|
||||
}
|
||||
.a-plaque-enter-active,
|
||||
.a-plaque-leave-active {
|
||||
transition: opacity 0.2s ease;
|
||||
}
|
||||
.a-plaque-enter-active .a-plaque {
|
||||
transition: transform 0.26s cubic-bezier(0.2, 1.2, 0.4, 1);
|
||||
}
|
||||
.a-plaque-enter-from,
|
||||
.a-plaque-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.a-plaque-enter-from .a-plaque {
|
||||
transform: translateY(40px) scale(0.92);
|
||||
}
|
||||
.a-stick {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 34% 30%, #ff6b6b, #b81f1f);
|
||||
position: relative;
|
||||
}
|
||||
.a-stick::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 5.5px;
|
||||
top: 12px;
|
||||
width: 3px;
|
||||
height: 9px;
|
||||
background: #55493c;
|
||||
}
|
||||
.a-btn {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 0 rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.a-btn.r { background: #ff4911; }
|
||||
.a-btn.y { background: #ffd23f; }
|
||||
.a-cab footer {
|
||||
padding: 0 14px 12px;
|
||||
font-size: 8.5px;
|
||||
color: rgba(244, 241, 255, 0.6);
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
|
||||
/* 排行榜页脚 */
|
||||
.a-footer {
|
||||
max-width: 640px;
|
||||
margin: 54px auto 0;
|
||||
}
|
||||
.a-rank,
|
||||
.a-rank-row {
|
||||
display: grid;
|
||||
grid-template-columns: 64px 96px 1fr;
|
||||
gap: 12px;
|
||||
margin: 0;
|
||||
padding: 8px 4px;
|
||||
font-size: 10px;
|
||||
}
|
||||
.a-rank {
|
||||
color: #ff2bd6;
|
||||
border-bottom: 2px solid #3d2a66;
|
||||
}
|
||||
.a-rank-row {
|
||||
color: #5eead4;
|
||||
border-bottom: 1px dashed rgba(61, 42, 102, 0.8);
|
||||
}
|
||||
.a-rank-row span:first-child {
|
||||
color: #ffd23f;
|
||||
}
|
||||
.a-rank-row span:last-child {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.a-fin {
|
||||
margin: 26px 0 0;
|
||||
text-align: center;
|
||||
font-size: 10px;
|
||||
line-height: 2;
|
||||
color: rgba(244, 241, 255, 0.66);
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
font-size: 12.5px;
|
||||
}
|
||||
</style>
|
||||
587
src/pages/styles/ArtDecoPage.vue
Normal file
587
src/pages/styles/ArtDecoPage.vue
Normal file
@@ -0,0 +1,587 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { profile } from '../../data/profile'
|
||||
|
||||
const deco = ['◆', '❖', '◇', '✦']
|
||||
|
||||
/* 烫金节目单 */
|
||||
const gala = ref(null)
|
||||
const galaIdx = ref(0)
|
||||
function openGala(p, i) {
|
||||
gala.value = p
|
||||
galaIdx.value = i
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="deco">
|
||||
<nav class="d-nav">
|
||||
<router-link to="/" class="d-back">← 返回合集</router-link>
|
||||
<router-link to="/style/art-deco/cabaret" class="d-back">✦ 歌舞厅节目单</router-link>
|
||||
<span class="d-nav-line"></span>
|
||||
<span class="d-nav-est">EST. MMI</span>
|
||||
</nav>
|
||||
|
||||
<!-- 日出扇形 hero -->
|
||||
<header class="d-hero">
|
||||
<div class="d-sunburst" aria-hidden="true">
|
||||
<i v-for="n in 13" :key="n" :style="{ transform: `rotate(${(n - 7) * 9}deg)` }"></i>
|
||||
</div>
|
||||
<p class="d-over">华丽年代 · LE MAGNIFIQUE</p>
|
||||
<h1 class="d-name">{{ profile.nameEn }}</h1>
|
||||
<p class="d-name-zh">{{ profile.name }}</p>
|
||||
<div class="d-divider"><i></i><b>❖</b><i></i></div>
|
||||
<p class="d-role">{{ profile.job }} · {{ profile.age }} 岁 · 杭州</p>
|
||||
<p class="d-bio">{{ profile.bio }}</p>
|
||||
</header>
|
||||
|
||||
<!-- 技能:鎏金阶梯表 -->
|
||||
<section class="d-section">
|
||||
<h2 class="d-title"><span>壹</span>技艺六品<em>LES TALENTS</em></h2>
|
||||
<div class="d-skills">
|
||||
<div v-for="(sk, i) in profile.skills" :key="sk.name" class="d-skill">
|
||||
<span class="d-skill-deco">{{ deco[i % 4] }}</span>
|
||||
<div class="d-skill-main">
|
||||
<div class="d-skill-head">
|
||||
<b>{{ sk.name }}</b>
|
||||
<span>{{ sk.level }}</span>
|
||||
</div>
|
||||
<div class="d-meter"><i :style="{ width: sk.level + '%' }"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 作品:对称金框展柜 -->
|
||||
<section class="d-section">
|
||||
<h2 class="d-title"><span>贰</span>作品陈列<em>LES ŒUVRES</em></h2>
|
||||
<div class="d-works">
|
||||
<article
|
||||
v-for="(p, i) in profile.projects"
|
||||
:key="p.name"
|
||||
class="d-work click"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="展开烫金节目单"
|
||||
@click="openGala(p, i)"
|
||||
@keyup.enter="openGala(p, i)"
|
||||
>
|
||||
<header class="d-work-top">
|
||||
<span class="d-work-no">N° {{ String(i + 1).padStart(2, '0') }}</span>
|
||||
<span class="d-work-star">★ {{ p.stars }}</span>
|
||||
</header>
|
||||
<h3>{{ p.name }}</h3>
|
||||
<p class="d-work-alias">— {{ p.alias }} —</p>
|
||||
<p class="d-work-desc">{{ p.desc }}</p>
|
||||
<footer class="d-work-tags">{{ p.tags.join(' · ') }}</footer>
|
||||
<i class="d-corner tl"></i><i class="d-corner tr"></i><i class="d-corner bl"></i><i class="d-corner br"></i>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="d-footer">
|
||||
<div class="d-divider"><i></i><b>◆</b><i></i></div>
|
||||
<p v-for="c in profile.contacts" :key="c.label" class="d-contact">
|
||||
<span>{{ c.label }}</span>{{ c.value }}
|
||||
</p>
|
||||
<p class="d-fin">« {{ profile.motto }} »</p>
|
||||
</footer>
|
||||
|
||||
<!-- 烫金节目单 -->
|
||||
<Transition name="d-gala">
|
||||
<div v-if="gala" class="d-gala-mask" @click.self="gala = null">
|
||||
<div class="d-gala">
|
||||
<i class="d-gala-sun" aria-hidden="true"></i>
|
||||
<button class="d-gala-x" title="合上节目单" @click="gala = null">✕</button>
|
||||
<p class="d-gala-no">PROGRAMME · N° {{ String(galaIdx + 1).padStart(2, '0') }}</p>
|
||||
<h3 class="d-gala-title">{{ gala.name }}</h3>
|
||||
<p class="d-gala-sub">— {{ gala.alias }} · {{ gala.year }} —</p>
|
||||
<div class="d-divider"><i></i><b>{{ deco[galaIdx % 4] }}</b><i></i></div>
|
||||
<p class="d-gala-story">{{ gala.story }}</p>
|
||||
<p class="d-gala-h">✦ ACTES · 华彩段落</p>
|
||||
<p v-for="(h, hi) in gala.highlights" :key="hi" class="d-gala-li">
|
||||
<span class="d-gala-li-no">{{ ['I', 'II', 'III', 'IV', 'V'][hi % 5] }}.</span>{{ h }}
|
||||
</p>
|
||||
<p class="d-gala-h">✦ CHRONIQUE · 编年</p>
|
||||
<p v-for="t in gala.timeline" :key="t.at" class="d-gala-li tl"><b>{{ t.at }}</b>{{ t.event }}</p>
|
||||
<div class="d-gala-metrics">
|
||||
<span v-for="m in gala.metrics" :key="m.label"><b>{{ m.value }}</b>{{ m.label }}</span>
|
||||
<span><b>★{{ gala.stars }}</b>ÉTOILES</span>
|
||||
</div>
|
||||
<p class="d-gala-foot">{{ gala.tags.join(' · ') }} · GRAND FOYER</p>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ---- 烫金节目单 ---- */
|
||||
.d-work.click {
|
||||
cursor: pointer;
|
||||
}
|
||||
.d-gala-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 500;
|
||||
background: rgba(8, 8, 12, 0.78);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 22px;
|
||||
}
|
||||
.d-gala {
|
||||
position: relative;
|
||||
width: min(480px, 100%);
|
||||
max-height: 86vh;
|
||||
overflow-y: auto;
|
||||
background: linear-gradient(175deg, #14141c, #0e0e14);
|
||||
border: 1px solid #c9a86a;
|
||||
outline: 1px solid #c9a86a;
|
||||
outline-offset: -8px;
|
||||
padding: 30px 32px;
|
||||
color: #e8dcc0;
|
||||
text-align: center;
|
||||
box-shadow: 0 0 60px rgba(201, 168, 106, 0.18), 0 34px 80px rgba(0, 0, 0, 0.7);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #c9a86a transparent;
|
||||
}
|
||||
.d-gala-sun {
|
||||
position: absolute;
|
||||
top: -1px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 130px;
|
||||
height: 65px;
|
||||
background:
|
||||
repeating-conic-gradient(from 180deg at 50% 100%, #c9a86a 0 3deg, transparent 3deg 12deg);
|
||||
border-radius: 0 0 130px 130px;
|
||||
opacity: 0.4;
|
||||
pointer-events: none;
|
||||
}
|
||||
.d-gala-x {
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
right: 14px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border: 1px solid #c9a86a;
|
||||
background: none;
|
||||
color: #c9a86a;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
}
|
||||
.d-gala-no {
|
||||
margin: 10px 0 6px;
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.5em;
|
||||
color: #a3854e;
|
||||
}
|
||||
.d-gala-title {
|
||||
margin: 0;
|
||||
font-size: 30px;
|
||||
letter-spacing: 0.16em;
|
||||
background: linear-gradient(180deg, #f2dfae, #c9a86a 55%, #f0d9a0);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
font-family: 'Noto Serif SC', Georgia, serif;
|
||||
}
|
||||
.d-gala-sub {
|
||||
margin: 6px 0 0;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.3em;
|
||||
color: #a3854e;
|
||||
}
|
||||
.d-gala-story {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12.5px;
|
||||
line-height: 2.1;
|
||||
color: #d8ccae;
|
||||
text-align: left;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
}
|
||||
.d-gala-h {
|
||||
margin: 14px 0 8px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.4em;
|
||||
color: #c9a86a;
|
||||
}
|
||||
.d-gala-li {
|
||||
margin: 0 0 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.9;
|
||||
color: #cfc3a2;
|
||||
text-align: left;
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
.d-gala-li-no {
|
||||
color: #c9a86a;
|
||||
flex-shrink: 0;
|
||||
min-width: 22px;
|
||||
text-align: right;
|
||||
}
|
||||
.d-gala-li.tl b {
|
||||
color: #c9a86a;
|
||||
flex-shrink: 0;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.d-gala-metrics {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin: 14px 0 10px;
|
||||
}
|
||||
.d-gala-metrics span {
|
||||
border: 1px solid #c9a86a;
|
||||
padding: 9px 15px;
|
||||
font-size: 9.5px;
|
||||
letter-spacing: 0.1em;
|
||||
color: #a3854e;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 3px;
|
||||
clip-path: polygon(10px 0, calc(100% - 10px) 0, 100% 50%, calc(100% - 10px) 100%, 10px 100%, 0 50%);
|
||||
background: rgba(201, 168, 106, 0.06);
|
||||
}
|
||||
.d-gala-metrics b {
|
||||
font-size: 15px;
|
||||
color: #f2dfae;
|
||||
}
|
||||
.d-gala-foot {
|
||||
margin: 0;
|
||||
font-size: 10px;
|
||||
letter-spacing: 0.24em;
|
||||
color: #a3854e;
|
||||
}
|
||||
.d-gala-enter-active,
|
||||
.d-gala-leave-active {
|
||||
transition: opacity 0.26s ease;
|
||||
}
|
||||
.d-gala-enter-active .d-gala {
|
||||
transition: transform 0.34s cubic-bezier(0.2, 1, 0.35, 1);
|
||||
}
|
||||
.d-gala-enter-from,
|
||||
.d-gala-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.d-gala-enter-from .d-gala {
|
||||
transform: translateY(34px) scale(0.94);
|
||||
}
|
||||
|
||||
.deco {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(ellipse 90% 40% at 50% -6%, rgba(212, 180, 106, 0.16), transparent 70%),
|
||||
repeating-linear-gradient(90deg, transparent 0 68px, rgba(212, 180, 106, 0.05) 68px 69px),
|
||||
linear-gradient(175deg, #101410, #0a0d0b 55%, #0c100d);
|
||||
color: #e8dcc0;
|
||||
font-family: 'Playfair Display', 'Noto Serif SC', serif;
|
||||
padding: 26px clamp(16px, 6vw, 90px) 60px;
|
||||
}
|
||||
|
||||
.d-nav {
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
z-index: 60;
|
||||
background: rgba(8, 8, 14, 0.62);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 18px;
|
||||
max-width: 980px;
|
||||
margin: 0 auto 40px;
|
||||
}
|
||||
.d-back {
|
||||
color: #d4b46a;
|
||||
text-decoration: none;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.3em;
|
||||
border: 1px solid rgba(212, 180, 106, 0.45);
|
||||
padding: 8px 18px;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.d-back:hover {
|
||||
background: rgba(212, 180, 106, 0.14);
|
||||
letter-spacing: 0.38em;
|
||||
}
|
||||
.d-nav-line {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, rgba(212, 180, 106, 0.5), transparent);
|
||||
}
|
||||
.d-nav-est {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.32em;
|
||||
color: rgba(212, 180, 106, 0.6);
|
||||
}
|
||||
|
||||
.d-hero {
|
||||
position: relative;
|
||||
max-width: 980px;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
padding: 84px 12px 46px;
|
||||
border: 1px solid rgba(212, 180, 106, 0.4);
|
||||
outline: 1px solid rgba(212, 180, 106, 0.16);
|
||||
outline-offset: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.d-sunburst {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: -12px;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
.d-sunburst i {
|
||||
position: absolute;
|
||||
left: -1.5px;
|
||||
top: 0;
|
||||
width: 3px;
|
||||
height: 74px;
|
||||
transform-origin: 50% 0;
|
||||
background: linear-gradient(rgba(212, 180, 106, 0.85), transparent);
|
||||
animation: d-ray 3.6s ease-in-out infinite alternate;
|
||||
}
|
||||
@keyframes d-ray {
|
||||
from { height: 60px; opacity: 0.65; }
|
||||
to { height: 84px; opacity: 1; }
|
||||
}
|
||||
.d-over {
|
||||
margin: 0 0 16px;
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.52em;
|
||||
color: rgba(232, 220, 192, 0.66);
|
||||
}
|
||||
.d-name {
|
||||
margin: 0;
|
||||
font-size: clamp(34px, 6.4vw, 68px);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
background: linear-gradient(178deg, #f4e6bd 20%, #d4b46a 58%, #9c7c3c);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.d-name-zh {
|
||||
margin: 8px 0 0;
|
||||
font-size: 17px;
|
||||
letter-spacing: 0.62em;
|
||||
text-indent: 0.62em;
|
||||
color: #d4b46a;
|
||||
}
|
||||
.d-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
max-width: 380px;
|
||||
margin: 26px auto;
|
||||
}
|
||||
.d-divider i {
|
||||
flex: 1;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(212, 180, 106, 0.75));
|
||||
}
|
||||
.d-divider i:last-child {
|
||||
background: linear-gradient(90deg, rgba(212, 180, 106, 0.75), transparent);
|
||||
}
|
||||
.d-divider b {
|
||||
color: #d4b46a;
|
||||
font-size: 13px;
|
||||
}
|
||||
.d-role {
|
||||
margin: 0 0 18px;
|
||||
font-size: 12.5px;
|
||||
letter-spacing: 0.34em;
|
||||
color: rgba(232, 220, 192, 0.72);
|
||||
}
|
||||
.d-bio {
|
||||
max-width: 560px;
|
||||
margin: 0 auto;
|
||||
font-size: 14.5px;
|
||||
line-height: 2;
|
||||
color: rgba(232, 220, 192, 0.85);
|
||||
}
|
||||
|
||||
.d-section {
|
||||
max-width: 980px;
|
||||
margin: 64px auto 0;
|
||||
}
|
||||
.d-title {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 16px;
|
||||
font-size: 21px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.3em;
|
||||
color: #f0e3bd;
|
||||
margin: 0 0 28px;
|
||||
}
|
||||
.d-title span {
|
||||
font-size: 14px;
|
||||
color: #d4b46a;
|
||||
border: 1px solid rgba(212, 180, 106, 0.55);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.d-title span {
|
||||
line-height: 1;
|
||||
}
|
||||
.d-title em {
|
||||
font-style: normal;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.5em;
|
||||
color: rgba(232, 220, 192, 0.5);
|
||||
}
|
||||
|
||||
.d-skills {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 16px 34px;
|
||||
}
|
||||
.d-skill {
|
||||
display: flex;
|
||||
gap: 14px;
|
||||
align-items: center;
|
||||
}
|
||||
.d-skill-deco {
|
||||
color: #d4b46a;
|
||||
font-size: 15px;
|
||||
}
|
||||
.d-skill-main {
|
||||
flex: 1;
|
||||
}
|
||||
.d-skill-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
margin-bottom: 7px;
|
||||
}
|
||||
.d-skill-head b {
|
||||
font-size: 14.5px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.06em;
|
||||
}
|
||||
.d-skill-head span {
|
||||
font-size: 12px;
|
||||
color: #d4b46a;
|
||||
letter-spacing: 0.14em;
|
||||
}
|
||||
.d-meter {
|
||||
height: 7px;
|
||||
border: 1px solid rgba(212, 180, 106, 0.45);
|
||||
padding: 1.5px;
|
||||
}
|
||||
.d-meter i {
|
||||
display: block;
|
||||
height: 100%;
|
||||
background: repeating-linear-gradient(105deg, #d4b46a 0 7px, #8f7136 7px 9px);
|
||||
animation: d-fill 1.1s cubic-bezier(0.22, 1, 0.36, 1) backwards;
|
||||
}
|
||||
@keyframes d-fill {
|
||||
from { width: 0 !important; }
|
||||
}
|
||||
|
||||
.d-works {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||
gap: 22px;
|
||||
}
|
||||
.d-work {
|
||||
position: relative;
|
||||
border: 1px solid rgba(212, 180, 106, 0.4);
|
||||
padding: 22px 22px 18px;
|
||||
background: linear-gradient(170deg, rgba(212, 180, 106, 0.07), transparent 40%);
|
||||
transition: transform 0.25s ease, border-color 0.25s ease;
|
||||
}
|
||||
.d-work:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: rgba(212, 180, 106, 0.85);
|
||||
}
|
||||
.d-work-top {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.22em;
|
||||
color: rgba(212, 180, 106, 0.85);
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
.d-work h3 {
|
||||
margin: 0;
|
||||
font-size: 21px;
|
||||
letter-spacing: 0.05em;
|
||||
color: #f0e3bd;
|
||||
}
|
||||
.d-work-alias {
|
||||
margin: 6px 0 12px;
|
||||
font-size: 11.5px;
|
||||
letter-spacing: 0.3em;
|
||||
color: rgba(232, 220, 192, 0.55);
|
||||
}
|
||||
.d-work-desc {
|
||||
margin: 0 0 16px;
|
||||
font-size: 13px;
|
||||
line-height: 1.9;
|
||||
color: rgba(232, 220, 192, 0.8);
|
||||
}
|
||||
.d-work-tags {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.18em;
|
||||
color: #d4b46a;
|
||||
border-top: 1px solid rgba(212, 180, 106, 0.28);
|
||||
padding-top: 12px;
|
||||
}
|
||||
.d-corner {
|
||||
position: absolute;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border: 1.5px solid #d4b46a;
|
||||
}
|
||||
.d-corner.tl { left: 4px; top: 4px; border-right: none; border-bottom: none; }
|
||||
.d-corner.tr { right: 4px; top: 4px; border-left: none; border-bottom: none; }
|
||||
.d-corner.bl { left: 4px; bottom: 4px; border-right: none; border-top: none; }
|
||||
.d-corner.br { right: 4px; bottom: 4px; border-left: none; border-top: none; }
|
||||
|
||||
.d-footer {
|
||||
max-width: 980px;
|
||||
margin: 70px auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
.d-contact {
|
||||
margin: 6px 0;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.12em;
|
||||
color: rgba(232, 220, 192, 0.8);
|
||||
}
|
||||
.d-contact span {
|
||||
display: inline-block;
|
||||
min-width: 88px;
|
||||
color: #d4b46a;
|
||||
letter-spacing: 0.3em;
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.d-fin {
|
||||
margin: 26px 0 0;
|
||||
font-size: 13.5px;
|
||||
font-style: italic;
|
||||
color: rgba(232, 220, 192, 0.6);
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
.d-hero { padding-top: 70px; }
|
||||
.d-title { flex-wrap: wrap; }
|
||||
}
|
||||
</style>
|
||||
594
src/pages/styles/AstrolabePage.vue
Normal file
594
src/pages/styles/AstrolabePage.vue
Normal file
@@ -0,0 +1,594 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { profile } from '../../data/profile'
|
||||
|
||||
/* 观测手记 */
|
||||
const obs = ref(null)
|
||||
const obsKind = ref('彗星过境')
|
||||
function openObs(p, i) {
|
||||
obs.value = p
|
||||
obsKind.value = ['彗星过境', '超新星爆发', '流星雨', '行星连珠'][i % 4]
|
||||
}
|
||||
|
||||
// 技能连成星座:圆盘上按等级分布
|
||||
function starPos(i, total) {
|
||||
const angle = (i / total) * Math.PI * 2 - Math.PI / 2
|
||||
const r = 34 + (i % 3) * 13
|
||||
return {
|
||||
left: 50 + Math.cos(angle) * r + '%',
|
||||
top: 50 + Math.sin(angle) * r + '%',
|
||||
}
|
||||
}
|
||||
const zodiac = ['♈', '♉', '♊', '♋', '♌', '♍', '♎', '♏', '♐', '♑', '♒', '♓']
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ast">
|
||||
<nav class="as-nav">
|
||||
<router-link to="/" class="as-back">✧ 收起星盘</router-link>
|
||||
<router-link to="/style/astrolabe/connect" class="as-back">✦ 连星成座</router-link>
|
||||
<span class="as-obs">年糕天文台 · 观测编号 No.25</span>
|
||||
</nav>
|
||||
|
||||
<header class="as-hero">
|
||||
<p class="as-over">天球星图 · 观测对象</p>
|
||||
<h1>{{ profile.name }}</h1>
|
||||
<p class="as-sub">{{ profile.job }}座 · {{ profile.age }}° · 升起于{{ profile.location }}</p>
|
||||
</header>
|
||||
|
||||
<!-- 星盘 -->
|
||||
<div class="as-chart">
|
||||
<i class="as-ring r1"></i>
|
||||
<i class="as-ring r2"></i>
|
||||
<i class="as-ring r3"></i>
|
||||
<!-- 黄道十二宫 -->
|
||||
<span
|
||||
v-for="(z, i) in zodiac"
|
||||
:key="z"
|
||||
class="as-zodiac"
|
||||
:style="{ transform: `rotate(${i * 30}deg) translateY(-158px) rotate(${-i * 30}deg)` }"
|
||||
>{{ z }}</span>
|
||||
<!-- 技能星 -->
|
||||
<div
|
||||
v-for="(sk, i) in profile.skills"
|
||||
:key="sk.name"
|
||||
class="as-star"
|
||||
:style="starPos(i, profile.skills.length)"
|
||||
>
|
||||
<i :style="{ '--m': sk.level / 100 }">✦</i>
|
||||
<span class="as-star-tip">
|
||||
<b>{{ sk.name }}</b>
|
||||
<em>星等 {{ (6 - sk.level / 20).toFixed(1) }} · 亮度 {{ sk.level }}</em>
|
||||
</span>
|
||||
</div>
|
||||
<!-- 中心 -->
|
||||
<div class="as-core">糕</div>
|
||||
<svg class="as-lines" viewBox="0 0 100 100" preserveAspectRatio="none" aria-hidden="true">
|
||||
<polygon
|
||||
points="50,9.6 85,29.4 92,50 74,84.6 26,84.6 15.7,29.4"
|
||||
fill="none"
|
||||
stroke="rgba(212,183,106,.4)"
|
||||
stroke-width="0.35"
|
||||
stroke-dasharray="1.4 1"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<p class="as-hint">悬停星点可读出技能星等 · 连线即「年糕座」主星群</p>
|
||||
|
||||
<p class="as-bio">{{ profile.bio }}</p>
|
||||
|
||||
<!-- 作品:天象记录 -->
|
||||
<section class="as-section">
|
||||
<h2 class="as-title">重要天象记录</h2>
|
||||
<div class="as-events">
|
||||
<article
|
||||
v-for="(p, i) in profile.projects"
|
||||
:key="p.name"
|
||||
class="as-event click"
|
||||
role="button"
|
||||
tabindex="0"
|
||||
title="翻开观测手记"
|
||||
@click="openObs(p, i)"
|
||||
@keyup.enter="openObs(p, i)"
|
||||
>
|
||||
<header>
|
||||
<span class="as-event-kind">{{ ['彗星过境', '超新星爆发', '流星雨', '行星连珠'][i % 4] }}</span>
|
||||
<span class="as-event-year">{{ p.year }} A.D.</span>
|
||||
</header>
|
||||
<h3>{{ p.name }}<small>{{ p.alias }}</small></h3>
|
||||
<p>{{ p.desc }}</p>
|
||||
<footer>观测亮度 ★{{ p.stars }} · 光谱型 {{ p.tags.join('-') }}</footer>
|
||||
</article>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="as-footer">
|
||||
<p class="as-obs-note">观测站通讯:</p>
|
||||
<p v-for="c in profile.contacts" :key="c.label"><b>{{ c.label }}</b>{{ c.value }}</p>
|
||||
<p class="as-fin">「{{ profile.motto }}」— 用蚀刻针刻于铜盘背面</p>
|
||||
</footer>
|
||||
|
||||
<!-- 观测手记 -->
|
||||
<Transition name="as-obs">
|
||||
<div v-if="obs" class="as-obs-mask" @click.self="obs = null">
|
||||
<div class="as-obs">
|
||||
<i class="as-obs-ring" aria-hidden="true"></i>
|
||||
<button class="as-obs-x" title="收起手记" @click="obs = null">✕</button>
|
||||
<p class="as-obs-kind">{{ obsKind }} · {{ obs.year }} A.D.</p>
|
||||
<h3 class="as-obs-title">{{ obs.name }}<small>{{ obs.alias }}</small></h3>
|
||||
<p class="as-obs-story">观测记录:{{ obs.story }}</p>
|
||||
<p class="as-obs-h">✦ 光谱分析</p>
|
||||
<p v-for="(h, hi) in obs.highlights" :key="hi" class="as-obs-li">✧ {{ h }}</p>
|
||||
<p class="as-obs-h">✦ 星历</p>
|
||||
<p v-for="t in obs.timeline" :key="t.at" class="as-obs-li eph"><b>{{ t.at }}</b>{{ t.event }}</p>
|
||||
<div class="as-obs-metrics">
|
||||
<span v-for="m in obs.metrics" :key="m.label"><b>{{ m.value }}</b>{{ m.label }}</span>
|
||||
<span><b>★{{ obs.stars }}</b>视星等</span>
|
||||
</div>
|
||||
<p class="as-obs-foot">仪器:{{ obs.tags.join(' · ') }} · 黄铜圆盘校准无误</p>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ---- 观测手记 ---- */
|
||||
.as-event.click {
|
||||
cursor: pointer;
|
||||
}
|
||||
.as-obs-mask {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 500;
|
||||
background: rgba(4, 8, 20, 0.82);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 22px;
|
||||
}
|
||||
.as-obs {
|
||||
position: relative;
|
||||
width: min(460px, 100%);
|
||||
max-height: 86vh;
|
||||
overflow-y: auto;
|
||||
background:
|
||||
radial-gradient(circle at 80% 12%, rgba(200, 170, 110, 0.12), transparent 40%),
|
||||
linear-gradient(175deg, #101a30, #0a1222);
|
||||
border: 1.5px solid #c8aa6e;
|
||||
border-radius: 12px;
|
||||
padding: 28px 30px;
|
||||
color: #d8e2f5;
|
||||
box-shadow: 0 0 46px rgba(200, 170, 110, 0.16);
|
||||
scrollbar-width: thin;
|
||||
scrollbar-color: #c8aa6e transparent;
|
||||
}
|
||||
.as-obs-ring {
|
||||
position: absolute;
|
||||
top: -40px;
|
||||
right: -40px;
|
||||
width: 120px;
|
||||
height: 120px;
|
||||
border: 1.5px dashed rgba(200, 170, 110, 0.4);
|
||||
border-radius: 50%;
|
||||
animation: as-rotate 22s linear infinite;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes as-rotate {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.as-obs-x {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(200, 170, 110, 0.6);
|
||||
background: none;
|
||||
color: #c8aa6e;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
}
|
||||
.as-obs-kind {
|
||||
margin: 0 0 8px;
|
||||
font-size: 10.5px;
|
||||
letter-spacing: 0.4em;
|
||||
color: #c8aa6e;
|
||||
}
|
||||
.as-obs-title {
|
||||
margin: 0 0 12px;
|
||||
font-size: 24px;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
color: #f0e6d2;
|
||||
}
|
||||
.as-obs-title small {
|
||||
font-size: 11.5px;
|
||||
color: #8fa3c8;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.as-obs-story {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12.5px;
|
||||
line-height: 2.05;
|
||||
color: #c2cfe8;
|
||||
}
|
||||
.as-obs-h {
|
||||
margin: 12px 0 6px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.4em;
|
||||
color: #c8aa6e;
|
||||
}
|
||||
.as-obs-li {
|
||||
margin: 0 0 6px;
|
||||
font-size: 12px;
|
||||
line-height: 1.95;
|
||||
color: #b0c0dd;
|
||||
}
|
||||
.as-obs-li.eph b {
|
||||
color: #8fd0ff;
|
||||
margin-right: 8px;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.as-obs-metrics {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
flex-wrap: wrap;
|
||||
margin: 12px 0;
|
||||
}
|
||||
.as-obs-metrics span {
|
||||
border: 1px solid rgba(200, 170, 110, 0.45);
|
||||
border-radius: 50%;
|
||||
width: 74px;
|
||||
height: 74px;
|
||||
font-size: 9px;
|
||||
color: #8fa3c8;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
.as-obs-metrics b {
|
||||
font-size: 13.5px;
|
||||
color: #f0e6d2;
|
||||
}
|
||||
.as-obs-foot {
|
||||
margin: 0;
|
||||
font-size: 10.5px;
|
||||
color: #6e7fa0;
|
||||
}
|
||||
.as-obs-enter-active,
|
||||
.as-obs-leave-active {
|
||||
transition: opacity 0.28s ease;
|
||||
}
|
||||
.as-obs-enter-active .as-obs {
|
||||
transition: transform 0.36s cubic-bezier(0.2, 1, 0.35, 1);
|
||||
}
|
||||
.as-obs-enter-from,
|
||||
.as-obs-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
.as-obs-enter-from .as-obs {
|
||||
transform: rotate(-3deg) scale(0.88);
|
||||
}
|
||||
|
||||
.ast {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(1.4px 1.4px at 12% 18%, rgba(255, 255, 255, 0.7), transparent),
|
||||
radial-gradient(1px 1px at 78% 8%, rgba(255, 255, 255, 0.5), transparent),
|
||||
radial-gradient(1.2px 1.2px at 90% 56%, rgba(212, 183, 106, 0.7), transparent),
|
||||
radial-gradient(1px 1px at 26% 74%, rgba(255, 255, 255, 0.45), transparent),
|
||||
radial-gradient(1.4px 1.4px at 58% 88%, rgba(212, 183, 106, 0.5), transparent),
|
||||
radial-gradient(ellipse at 50% 0%, #14203d 0%, #0b1226 55%, #070c1a 100%);
|
||||
color: #e6e9f4;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
padding: 26px clamp(16px, 5vw, 72px) 60px;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.as-nav {
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
z-index: 60;
|
||||
background: rgba(8, 8, 14, 0.62);
|
||||
backdrop-filter: blur(12px);
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
border-radius: 12px;
|
||||
padding: 8px 12px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
max-width: 900px;
|
||||
margin: 0 auto 40px;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
}
|
||||
.as-back {
|
||||
color: #d4b76a;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.18em;
|
||||
border: 1px solid rgba(212, 183, 106, 0.45);
|
||||
border-radius: 999px;
|
||||
padding: 8px 18px;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.as-back:hover {
|
||||
background: rgba(212, 183, 106, 0.12);
|
||||
box-shadow: 0 0 18px rgba(212, 183, 106, 0.25);
|
||||
}
|
||||
.as-obs {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.3em;
|
||||
color: rgba(230, 233, 244, 0.5);
|
||||
}
|
||||
|
||||
.as-hero {
|
||||
text-align: center;
|
||||
margin-bottom: 34px;
|
||||
}
|
||||
.as-over {
|
||||
margin: 0 0 10px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.5em;
|
||||
color: rgba(212, 183, 106, 0.75);
|
||||
}
|
||||
.as-hero h1 {
|
||||
margin: 0 0 8px;
|
||||
font-size: clamp(34px, 5.4vw, 52px);
|
||||
font-weight: 900;
|
||||
letter-spacing: 0.2em;
|
||||
background: linear-gradient(178deg, #f2ebd8, #d4b76a 75%);
|
||||
-webkit-background-clip: text;
|
||||
background-clip: text;
|
||||
color: transparent;
|
||||
}
|
||||
.as-sub {
|
||||
margin: 0;
|
||||
font-size: 13px;
|
||||
letter-spacing: 0.22em;
|
||||
color: rgba(230, 233, 244, 0.66);
|
||||
}
|
||||
|
||||
/* 星盘 */
|
||||
.as-chart {
|
||||
position: relative;
|
||||
width: min(380px, 88vw);
|
||||
aspect-ratio: 1;
|
||||
margin: 0 auto;
|
||||
}
|
||||
.as-ring {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(212, 183, 106, 0.4);
|
||||
}
|
||||
.as-ring.r1 {
|
||||
inset: 0;
|
||||
border-width: 1.5px;
|
||||
background:
|
||||
repeating-conic-gradient(rgba(212, 183, 106, 0.14) 0 0.4deg, transparent 0.4deg 30deg);
|
||||
animation: as-spin 90s linear infinite;
|
||||
}
|
||||
.as-ring.r2 {
|
||||
inset: 13%;
|
||||
border-style: dashed;
|
||||
animation: as-spin 60s linear infinite reverse;
|
||||
}
|
||||
.as-ring.r3 {
|
||||
inset: 27%;
|
||||
opacity: 0.6;
|
||||
}
|
||||
@keyframes as-spin {
|
||||
to { transform: rotate(1turn); }
|
||||
}
|
||||
.as-zodiac {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin: -11px 0 0 -11px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
font-size: 14px;
|
||||
color: rgba(212, 183, 106, 0.85);
|
||||
}
|
||||
@media (max-width: 460px) {
|
||||
.as-zodiac { display: none; }
|
||||
}
|
||||
.as-lines {
|
||||
position: absolute;
|
||||
inset: 12%;
|
||||
width: 76%;
|
||||
height: 76%;
|
||||
pointer-events: none;
|
||||
}
|
||||
.as-star {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 3;
|
||||
}
|
||||
.as-star > i {
|
||||
font-style: normal;
|
||||
font-size: calc(14px + var(--m) * 14px);
|
||||
color: #f2ebd8;
|
||||
text-shadow: 0 0 calc(8px + var(--m) * 18px) rgba(212, 183, 106, 0.95);
|
||||
cursor: default;
|
||||
animation: as-twinkle 2.6s ease-in-out infinite;
|
||||
display: inline-block;
|
||||
}
|
||||
.as-star:nth-child(odd) > i {
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
@keyframes as-twinkle {
|
||||
50% { opacity: 0.62; }
|
||||
}
|
||||
.as-star-tip {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: calc(100% + 8px);
|
||||
transform: translateX(-50%);
|
||||
white-space: nowrap;
|
||||
background: rgba(11, 18, 38, 0.94);
|
||||
border: 1px solid rgba(212, 183, 106, 0.55);
|
||||
border-radius: 8px;
|
||||
padding: 7px 12px;
|
||||
text-align: center;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
transition: opacity 0.2s ease, transform 0.2s ease;
|
||||
z-index: 9;
|
||||
}
|
||||
.as-star:hover .as-star-tip {
|
||||
opacity: 1;
|
||||
transform: translateX(-50%) translateY(-3px);
|
||||
}
|
||||
.as-star-tip b {
|
||||
display: block;
|
||||
font-size: 12.5px;
|
||||
color: #f2ebd8;
|
||||
}
|
||||
.as-star-tip em {
|
||||
font-style: normal;
|
||||
font-size: 10.5px;
|
||||
color: rgba(212, 183, 106, 0.9);
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.as-core {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle at 36% 30%, #f2ebd8, #d4b76a 65%);
|
||||
color: #14203d;
|
||||
font-family: 'Ma Shan Zheng', cursive;
|
||||
font-size: 26px;
|
||||
box-shadow: 0 0 44px rgba(212, 183, 106, 0.55);
|
||||
z-index: 4;
|
||||
}
|
||||
|
||||
.as-hint {
|
||||
text-align: center;
|
||||
margin: 20px 0 0;
|
||||
font-size: 11.5px;
|
||||
letter-spacing: 0.16em;
|
||||
color: rgba(230, 233, 244, 0.45);
|
||||
}
|
||||
.as-bio {
|
||||
max-width: 560px;
|
||||
margin: 30px auto 0;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
line-height: 2.05;
|
||||
color: rgba(230, 233, 244, 0.85);
|
||||
}
|
||||
|
||||
.as-section {
|
||||
max-width: 900px;
|
||||
margin: 60px auto 0;
|
||||
}
|
||||
.as-title {
|
||||
text-align: center;
|
||||
margin: 0 0 28px;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.3em;
|
||||
color: #d4b76a;
|
||||
}
|
||||
.as-events {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
.as-event {
|
||||
border: 1px solid rgba(212, 183, 106, 0.35);
|
||||
border-radius: 4px;
|
||||
background: rgba(20, 32, 61, 0.5);
|
||||
padding: 18px 20px 15px;
|
||||
transition: border-color 0.25s ease, box-shadow 0.25s ease, transform 0.25s ease;
|
||||
}
|
||||
.as-event:hover {
|
||||
border-color: rgba(212, 183, 106, 0.8);
|
||||
box-shadow: 0 0 30px rgba(212, 183, 106, 0.12);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
.as-event header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.18em;
|
||||
}
|
||||
.as-event-kind {
|
||||
color: #d4b76a;
|
||||
}
|
||||
.as-event-year {
|
||||
color: rgba(230, 233, 244, 0.5);
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.as-event h3 {
|
||||
margin: 0 0 8px;
|
||||
font-size: 19px;
|
||||
font-weight: 900;
|
||||
color: #f2ebd8;
|
||||
}
|
||||
.as-event h3 small {
|
||||
margin-left: 8px;
|
||||
font-size: 11.5px;
|
||||
font-weight: 400;
|
||||
color: rgba(230, 233, 244, 0.5);
|
||||
}
|
||||
.as-event p {
|
||||
margin: 0 0 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.9;
|
||||
color: rgba(230, 233, 244, 0.85);
|
||||
}
|
||||
.as-event footer {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.1em;
|
||||
color: rgba(212, 183, 106, 0.85);
|
||||
border-top: 1px dashed rgba(212, 183, 106, 0.3);
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.as-footer {
|
||||
max-width: 900px;
|
||||
margin: 56px auto 0;
|
||||
text-align: center;
|
||||
}
|
||||
.as-obs-note {
|
||||
font-size: 12px;
|
||||
letter-spacing: 0.3em;
|
||||
color: rgba(230, 233, 244, 0.5);
|
||||
margin: 0 0 12px;
|
||||
}
|
||||
.as-footer p {
|
||||
margin: 5px 0;
|
||||
font-size: 13.5px;
|
||||
}
|
||||
.as-footer b {
|
||||
display: inline-block;
|
||||
min-width: 80px;
|
||||
color: #d4b76a;
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.24em;
|
||||
text-transform: uppercase;
|
||||
font-family: 'Space Grotesk', sans-serif;
|
||||
}
|
||||
.as-fin {
|
||||
margin-top: 24px !important;
|
||||
font-size: 12px !important;
|
||||
color: rgba(230, 233, 244, 0.5);
|
||||
}
|
||||
</style>
|
||||
266
src/pages/styles/sub/AeroGadgetsPage.vue
Normal file
266
src/pages/styles/sub/AeroGadgetsPage.vue
Normal file
@@ -0,0 +1,266 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
import { profile } from '../../../data/profile'
|
||||
|
||||
const now = ref(new Date())
|
||||
let clock = null
|
||||
|
||||
/* 便签 */
|
||||
const NOTE_KEY = 'aero-gadget-note'
|
||||
const note = ref('')
|
||||
function saveNote() {
|
||||
localStorage.setItem(NOTE_KEY, note.value)
|
||||
}
|
||||
|
||||
/* 天气(本地随机模拟) */
|
||||
const weathers = [
|
||||
{ icon: '☀️', name: '晴', t: 31 },
|
||||
{ icon: '🌤️', name: '多云', t: 28 },
|
||||
{ icon: '🌧️', name: '小雨', t: 24 },
|
||||
{ icon: '⛈️', name: '雷阵雨', t: 26 },
|
||||
]
|
||||
const weather = ref(weathers[0])
|
||||
|
||||
/* CPU 仪表(模拟) */
|
||||
const cpu = ref(32)
|
||||
const ram = ref(58)
|
||||
let meter = null
|
||||
|
||||
/* 幻灯片 */
|
||||
const slides = ['🌉', '🏔️', '🌊', '🌸', '🌇']
|
||||
const slideIdx = ref(0)
|
||||
let slideTimer = null
|
||||
|
||||
onMounted(() => {
|
||||
note.value = localStorage.getItem(NOTE_KEY) || '双击便签开始记录…\n(内容会自动保存)'
|
||||
weather.value = weathers[Math.floor(Math.random() * weathers.length)]
|
||||
clock = setInterval(() => (now.value = new Date()), 1000)
|
||||
meter = setInterval(() => {
|
||||
cpu.value = Math.max(8, Math.min(96, cpu.value + Math.round(Math.random() * 18 - 9)))
|
||||
ram.value = Math.max(30, Math.min(90, ram.value + Math.round(Math.random() * 8 - 4)))
|
||||
}, 1400)
|
||||
slideTimer = setInterval(() => (slideIdx.value = (slideIdx.value + 1) % slides.length), 3000)
|
||||
})
|
||||
onBeforeUnmount(() => {
|
||||
clearInterval(clock)
|
||||
clearInterval(meter)
|
||||
clearInterval(slideTimer)
|
||||
})
|
||||
|
||||
const hh = () => String(now.value.getHours()).padStart(2, '0')
|
||||
const mm = () => String(now.value.getMinutes()).padStart(2, '0')
|
||||
const ss = () => String(now.value.getSeconds()).padStart(2, '0')
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ag">
|
||||
<nav class="ag-nav">
|
||||
<router-link to="/style/aero" class="ag-back">← 返回 Aero 桌面</router-link>
|
||||
<h1>侧边栏小工具</h1>
|
||||
</nav>
|
||||
|
||||
<div class="ag-desktop">
|
||||
<p class="ag-wallhint">Aero 桌面 · 小工具全部可用:便签能写字、时钟在走、仪表在跳</p>
|
||||
|
||||
<aside class="ag-sidebar">
|
||||
<!-- 时钟 -->
|
||||
<section class="ag-gadget">
|
||||
<p class="ag-g-title">🕐 时钟</p>
|
||||
<p class="ag-clock">{{ hh() }}:{{ mm() }}<small>:{{ ss() }}</small></p>
|
||||
<p class="ag-g-sub">{{ now.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'long' }) }}</p>
|
||||
</section>
|
||||
|
||||
<!-- 天气 -->
|
||||
<section class="ag-gadget">
|
||||
<p class="ag-g-title">🌈 天气 · {{ profile.location.split(' · ')[1] }}</p>
|
||||
<div class="ag-weather">
|
||||
<span class="ag-w-icon">{{ weather.icon }}</span>
|
||||
<div>
|
||||
<b>{{ weather.t }}°C</b>
|
||||
<span>{{ weather.name }} · 适合写码</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 便签 -->
|
||||
<section class="ag-gadget note">
|
||||
<p class="ag-g-title">📝 便签(自动保存)</p>
|
||||
<textarea v-model="note" rows="4" @input="saveNote"></textarea>
|
||||
</section>
|
||||
|
||||
<!-- 仪表 -->
|
||||
<section class="ag-gadget">
|
||||
<p class="ag-g-title">📊 系统仪表</p>
|
||||
<div class="ag-meter">
|
||||
<span>CPU</span>
|
||||
<div class="ag-bar"><i :style="{ width: cpu + '%' }" :class="{ hot: cpu > 80 }"></i></div>
|
||||
<b>{{ cpu }}%</b>
|
||||
</div>
|
||||
<div class="ag-meter">
|
||||
<span>内存</span>
|
||||
<div class="ag-bar"><i :style="{ width: ram + '%' }" :class="{ hot: ram > 80 }"></i></div>
|
||||
<b>{{ ram }}%</b>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 幻灯片 -->
|
||||
<section class="ag-gadget">
|
||||
<p class="ag-g-title">🖼️ 幻灯片</p>
|
||||
<div class="ag-slide">
|
||||
<transition name="ag-fade" mode="out-in">
|
||||
<span :key="slideIdx" class="ag-slide-pic">{{ slides[slideIdx] }}</span>
|
||||
</transition>
|
||||
</div>
|
||||
<div class="ag-dots">
|
||||
<i v-for="(s, i) in slides" :key="i" :class="{ on: slideIdx === i }" @click="slideIdx = i"></i>
|
||||
</div>
|
||||
</section>
|
||||
</aside>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ag {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 50% at 20% 0%, rgba(120, 200, 255, 0.35), transparent 60%),
|
||||
linear-gradient(160deg, #3a7ec4 0%, #2c5a94 45%, #1c3a64 100%);
|
||||
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||||
color: #fff;
|
||||
padding: 22px clamp(14px, 4vw, 48px) 40px;
|
||||
}
|
||||
.ag-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 18px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ag-back {
|
||||
color: #fff;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
background: rgba(255, 255, 255, 0.18);
|
||||
backdrop-filter: blur(8px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.35);
|
||||
border-radius: 8px;
|
||||
padding: 7px 16px;
|
||||
}
|
||||
.ag-back:hover { background: rgba(255, 255, 255, 0.3); }
|
||||
.ag-nav h1 { margin: 0; font-size: 20px; font-weight: 400; text-shadow: 0 2px 8px rgba(0, 30, 60, 0.5); }
|
||||
|
||||
.ag-desktop {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 20px;
|
||||
min-height: 70vh;
|
||||
}
|
||||
.ag-wallhint {
|
||||
align-self: center;
|
||||
max-width: 380px;
|
||||
font-size: 14px;
|
||||
line-height: 2;
|
||||
color: rgba(255, 255, 255, 0.75);
|
||||
text-shadow: 0 2px 6px rgba(0, 30, 60, 0.6);
|
||||
}
|
||||
@media (max-width: 720px) {
|
||||
.ag-desktop { flex-direction: column; }
|
||||
.ag-wallhint { max-width: none; }
|
||||
}
|
||||
|
||||
.ag-sidebar {
|
||||
width: 300px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
@media (max-width: 720px) { .ag-sidebar { width: 100%; } }
|
||||
.ag-gadget {
|
||||
background: linear-gradient(160deg, rgba(255, 255, 255, 0.32), rgba(255, 255, 255, 0.12));
|
||||
backdrop-filter: blur(14px);
|
||||
border: 1px solid rgba(255, 255, 255, 0.45);
|
||||
border-radius: 12px;
|
||||
padding: 13px 16px;
|
||||
box-shadow: 0 10px 30px rgba(0, 30, 60, 0.3), inset 0 1px 0 rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.ag-g-title { margin: 0 0 8px; font-size: 12px; color: rgba(255, 255, 255, 0.85); }
|
||||
.ag-clock {
|
||||
margin: 0;
|
||||
font-size: 38px;
|
||||
font-weight: 300;
|
||||
text-shadow: 0 2px 10px rgba(0, 30, 60, 0.5);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.ag-clock small { font-size: 18px; opacity: 0.7; }
|
||||
.ag-g-sub { margin: 2px 0 0; font-size: 12px; opacity: 0.75; }
|
||||
|
||||
.ag-weather { display: flex; align-items: center; gap: 14px; }
|
||||
.ag-w-icon { font-size: 42px; filter: drop-shadow(0 4px 10px rgba(0, 30, 60, 0.4)); }
|
||||
.ag-weather b { display: block; font-size: 24px; font-weight: 400; }
|
||||
.ag-weather span { font-size: 12px; opacity: 0.75; }
|
||||
|
||||
.ag-gadget.note textarea {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
background: rgba(255, 250, 200, 0.92);
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
padding: 10px 12px;
|
||||
font-family: inherit;
|
||||
font-size: 13px;
|
||||
color: #5a4c20;
|
||||
resize: vertical;
|
||||
outline: none;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.ag-meter {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin-bottom: 8px;
|
||||
font-size: 12px;
|
||||
}
|
||||
.ag-meter span { width: 36px; }
|
||||
.ag-bar {
|
||||
flex: 1;
|
||||
height: 9px;
|
||||
border-radius: 5px;
|
||||
background: rgba(0, 30, 60, 0.35);
|
||||
overflow: hidden;
|
||||
}
|
||||
.ag-bar i {
|
||||
display: block;
|
||||
height: 100%;
|
||||
border-radius: 5px;
|
||||
background: linear-gradient(90deg, #7ee787, #4cc2ff);
|
||||
transition: width 0.6s ease;
|
||||
}
|
||||
.ag-bar i.hot { background: linear-gradient(90deg, #ffb84c, #ff6a4c); }
|
||||
.ag-meter b { min-width: 36px; text-align: right; font-size: 11.5px; }
|
||||
|
||||
.ag-slide {
|
||||
height: 110px;
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 30, 60, 0.3);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
margin-bottom: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ag-slide-pic { font-size: 60px; filter: drop-shadow(0 6px 14px rgba(0, 30, 60, 0.5)); }
|
||||
.ag-fade-enter-active, .ag-fade-leave-active { transition: all 0.4s ease; }
|
||||
.ag-fade-enter-from { opacity: 0; transform: translateX(30px); }
|
||||
.ag-fade-leave-to { opacity: 0; transform: translateX(-30px); }
|
||||
.ag-dots { display: flex; justify-content: center; gap: 6px; }
|
||||
.ag-dots i {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
background: rgba(255, 255, 255, 0.4);
|
||||
cursor: pointer;
|
||||
}
|
||||
.ag-dots i.on { background: #fff; }
|
||||
</style>
|
||||
248
src/pages/styles/sub/AndroidPanelPage.vue
Normal file
248
src/pages/styles/sub/AndroidPanelPage.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { profile } from '../../../data/profile'
|
||||
|
||||
const wifi = ref(true)
|
||||
const bt = ref(false)
|
||||
const dnd = ref(false)
|
||||
const torch = ref(false)
|
||||
const dark = ref(true)
|
||||
const brightness = ref(72)
|
||||
|
||||
const notifications = ref([
|
||||
{ id: 1, app: '糯米终端', icon: '🖥️', time: '刚刚', title: '构建完成', body: 'niangao-theme v2.1 构建成功,用时 6.2s,全绿 ✅' },
|
||||
{ id: 2, app: '饭粒清单', icon: '✅', time: '8 分钟前', title: '今日待办 3/5', body: '「给杰伦钢琴房加延音」已完成,还剩 2 件小事' },
|
||||
{ id: 3, app: 'Git 信使', icon: '📦', time: '32 分钟前', title: 'CodeMochi 有新 Star', body: '⭐ 总数来到 2,301,有人 fork 了你的倔强' },
|
||||
{ id: 4, app: '像素厨房', icon: '🍳', time: '1 小时前', title: '每日挑战刷新', body: '今日菜谱:三分熟的正则表达式' },
|
||||
])
|
||||
function dismiss(n) {
|
||||
notifications.value = notifications.value.filter((x) => x.id !== n.id)
|
||||
}
|
||||
function clearAll() {
|
||||
notifications.value = []
|
||||
}
|
||||
const bgStyle = computed(() => ({
|
||||
filter: `brightness(${0.55 + (brightness.value / 100) * 0.55})`,
|
||||
}))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ap" :class="{ light: !dark }">
|
||||
<div class="ap-phone" :style="bgStyle">
|
||||
<!-- 状态栏 -->
|
||||
<header class="ap-status">
|
||||
<span>{{ new Date().toTimeString().slice(0, 5) }}</span>
|
||||
<span class="ap-status-r">{{ wifi ? '📶' : '' }}{{ dnd ? '🌙' : '' }} 🔋86%</span>
|
||||
</header>
|
||||
|
||||
<!-- 快捷开关 -->
|
||||
<section class="ap-quick">
|
||||
<button :class="{ on: wifi }" @click="wifi = !wifi"><i>📶</i>WLAN</button>
|
||||
<button :class="{ on: bt }" @click="bt = !bt"><i>🅱</i>蓝牙</button>
|
||||
<button :class="{ on: dnd }" @click="dnd = !dnd"><i>🌙</i>勿扰</button>
|
||||
<button :class="{ on: torch }" @click="torch = !torch"><i>🔦</i>手电筒</button>
|
||||
<button :class="{ on: dark }" @click="dark = !dark"><i>🌗</i>深色模式</button>
|
||||
<router-link to="/style/android-home" class="ap-exitq"><i>↩</i>回桌面</router-link>
|
||||
</section>
|
||||
|
||||
<!-- 亮度 -->
|
||||
<div class="ap-bright">
|
||||
<span>☀️</span>
|
||||
<input v-model.number="brightness" type="range" min="10" max="100" />
|
||||
<b>{{ brightness }}%</b>
|
||||
</div>
|
||||
|
||||
<!-- 通知 -->
|
||||
<section class="ap-notis">
|
||||
<header class="ap-notis-head">
|
||||
<span>通知中心</span>
|
||||
<button v-if="notifications.length" @click="clearAll">全部清除</button>
|
||||
</header>
|
||||
<TransitionGroup name="ap-slide">
|
||||
<article v-for="n in notifications" :key="n.id" class="ap-noti">
|
||||
<span class="ap-noti-icon">{{ n.icon }}</span>
|
||||
<div class="ap-noti-body">
|
||||
<p class="ap-noti-meta">{{ n.app }} · {{ n.time }}</p>
|
||||
<p class="ap-noti-title">{{ n.title }}</p>
|
||||
<p class="ap-noti-text">{{ n.body }}</p>
|
||||
</div>
|
||||
<button class="ap-noti-x" @click="dismiss(n)">✕</button>
|
||||
</article>
|
||||
</TransitionGroup>
|
||||
<p v-if="!notifications.length" class="ap-empty">🔕 没有新通知,清净</p>
|
||||
</section>
|
||||
|
||||
<!-- 小部件 -->
|
||||
<section class="ap-widgets">
|
||||
<div class="ap-widget clock">
|
||||
<b>{{ new Date().toTimeString().slice(0, 5) }}</b>
|
||||
<span>{{ profile.location }}</span>
|
||||
</div>
|
||||
<div class="ap-widget me">
|
||||
<img :src="profile.avatars.default" alt="" />
|
||||
<div>
|
||||
<b>{{ profile.name }}</b>
|
||||
<span>{{ profile.job }} · 在线</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 手电筒覆盖 -->
|
||||
<div v-if="torch" class="ap-torch" @click="torch = false">
|
||||
<p>🔦 手电筒已开启<br /><small>(点击关闭,费电)</small></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ap {
|
||||
min-height: 100vh;
|
||||
background: #101216;
|
||||
display: grid;
|
||||
place-items: center;
|
||||
padding: 26px 12px;
|
||||
font-family: 'Roboto', 'HarmonyOS Sans SC', 'Noto Sans SC', sans-serif;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
.ap.light { background: #c8d0dc; }
|
||||
.ap-phone {
|
||||
width: min(400px, 96vw);
|
||||
min-height: 760px;
|
||||
border-radius: 30px;
|
||||
background: linear-gradient(170deg, #1a2030, #12141c);
|
||||
border: 3px solid #2a3040;
|
||||
padding: 14px 14px 24px;
|
||||
color: #eef2f8;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.55);
|
||||
transition: filter 0.2s ease, background 0.3s ease;
|
||||
}
|
||||
.ap.light .ap-phone { background: linear-gradient(170deg, #eef2fa, #dce4f0); color: #1a2030; border-color: #b8c4d8; }
|
||||
|
||||
.ap-status {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 12.5px;
|
||||
padding: 4px 10px 12px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
.ap-quick {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 9px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.ap-quick button,
|
||||
.ap-exitq {
|
||||
border: none;
|
||||
border-radius: 16px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
color: inherit;
|
||||
font-family: inherit;
|
||||
font-size: 12px;
|
||||
padding: 12px 6px;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
text-decoration: none;
|
||||
transition: background 0.2s ease;
|
||||
}
|
||||
.ap.light .ap-quick button, .ap.light .ap-exitq { background: rgba(255, 255, 255, 0.65); }
|
||||
.ap-quick i { font-style: normal; font-size: 19px; }
|
||||
.ap-quick button.on { background: #3c6df0; color: #fff; }
|
||||
|
||||
.ap-bright {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 16px;
|
||||
padding: 10px 14px;
|
||||
margin-bottom: 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
.ap.light .ap-bright { background: rgba(255, 255, 255, 0.65); }
|
||||
.ap-bright input { flex: 1; accent-color: #3c6df0; }
|
||||
.ap-bright b { font-size: 11.5px; min-width: 36px; text-align: right; }
|
||||
|
||||
.ap-notis-head {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 12.5px;
|
||||
opacity: 0.7;
|
||||
padding: 0 6px 8px;
|
||||
}
|
||||
.ap-notis-head button {
|
||||
border: none;
|
||||
background: none;
|
||||
color: #3c9df0;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.ap-noti {
|
||||
display: flex;
|
||||
gap: 11px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 18px;
|
||||
padding: 13px 14px;
|
||||
margin-bottom: 8px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
.ap.light .ap-noti { background: rgba(255, 255, 255, 0.72); }
|
||||
.ap-noti-icon { font-size: 24px; }
|
||||
.ap-noti-body { flex: 1; min-width: 0; }
|
||||
.ap-noti-meta { margin: 0 0 3px; font-size: 10.5px; opacity: 0.55; }
|
||||
.ap-noti-title { margin: 0 0 3px; font-size: 13.5px; font-weight: 700; }
|
||||
.ap-noti-text { margin: 0; font-size: 12.5px; line-height: 1.6; opacity: 0.8; }
|
||||
.ap-noti-x {
|
||||
border: none;
|
||||
background: none;
|
||||
color: inherit;
|
||||
opacity: 0.4;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
}
|
||||
.ap-noti-x:hover { opacity: 1; }
|
||||
.ap-empty { text-align: center; font-size: 13px; opacity: 0.5; padding: 18px 0; }
|
||||
.ap-slide-leave-active { transition: all 0.28s ease; }
|
||||
.ap-slide-leave-to { opacity: 0; transform: translateX(60px); }
|
||||
.ap-slide-move { transition: transform 0.28s ease; }
|
||||
|
||||
.ap-widgets {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1.4fr;
|
||||
gap: 9px;
|
||||
margin-top: 14px;
|
||||
}
|
||||
.ap-widget {
|
||||
border-radius: 18px;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
padding: 14px;
|
||||
}
|
||||
.ap.light .ap-widget { background: rgba(255, 255, 255, 0.72); }
|
||||
.ap-widget.clock b { display: block; font-size: 26px; font-weight: 300; }
|
||||
.ap-widget.clock span { font-size: 11px; opacity: 0.6; }
|
||||
.ap-widget.me { display: flex; align-items: center; gap: 10px; }
|
||||
.ap-widget.me img { width: 40px; height: 40px; border-radius: 50%; background: #ffdfbf; }
|
||||
.ap-widget.me b { display: block; font-size: 13.5px; }
|
||||
.ap-widget.me span { font-size: 11px; opacity: 0.6; }
|
||||
|
||||
.ap-torch {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: rgba(255, 252, 235, 0.96);
|
||||
display: grid;
|
||||
place-items: center;
|
||||
color: #6a5c20;
|
||||
text-align: center;
|
||||
font-size: 17px;
|
||||
cursor: pointer;
|
||||
z-index: 10;
|
||||
line-height: 2;
|
||||
}
|
||||
</style>
|
||||
272
src/pages/styles/sub/ArcadeBreakoutPage.vue
Normal file
272
src/pages/styles/sub/ArcadeBreakoutPage.vue
Normal file
@@ -0,0 +1,272 @@
|
||||
<script setup>
|
||||
import { onBeforeUnmount, onMounted, ref } from 'vue'
|
||||
|
||||
const cvs = ref(null)
|
||||
const state = ref('ready') // ready | run | over | win
|
||||
const score = ref(0)
|
||||
const lives = ref(3)
|
||||
const best = ref(0)
|
||||
const KEY = 'arcade-breakout-best'
|
||||
|
||||
const W = 640
|
||||
const H = 420
|
||||
let ctx, raf
|
||||
let paddle, ball, bricks
|
||||
|
||||
const COLORS = ['#ff2d55', '#ff9500', '#ffe135', '#4cd964', '#5ac8fa', '#c86bff']
|
||||
|
||||
function buildBricks() {
|
||||
bricks = []
|
||||
for (let r = 0; r < 6; r++) {
|
||||
for (let c = 0; c < 10; c++) {
|
||||
bricks.push({ x: 24 + c * 60, y: 46 + r * 24, w: 52, h: 17, color: COLORS[r], alive: true, pt: (6 - r) * 10 })
|
||||
}
|
||||
}
|
||||
}
|
||||
function reset(full = true) {
|
||||
paddle = { x: W / 2, w: 92, h: 12 }
|
||||
ball = { x: W / 2, y: H - 70, vx: 3.2 * (Math.random() > 0.5 ? 1 : -1), vy: -4 }
|
||||
if (full) {
|
||||
buildBricks()
|
||||
score.value = 0
|
||||
lives.value = 3
|
||||
}
|
||||
}
|
||||
function start() {
|
||||
if (state.value === 'run') return
|
||||
if (state.value !== 'ready') reset(true)
|
||||
state.value = 'run'
|
||||
}
|
||||
|
||||
function loop() {
|
||||
raf = requestAnimationFrame(loop)
|
||||
// 背景
|
||||
ctx.fillStyle = '#0a0a14'
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
ctx.strokeStyle = 'rgba(90,200,250,.12)'
|
||||
for (let y = 0; y < H; y += 4) {
|
||||
ctx.beginPath()
|
||||
ctx.moveTo(0, y)
|
||||
ctx.lineTo(W, y)
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
if (state.value === 'run') {
|
||||
ball.x += ball.vx
|
||||
ball.y += ball.vy
|
||||
if (ball.x < 8 || ball.x > W - 8) ball.vx *= -1
|
||||
if (ball.y < 8) ball.vy *= -1
|
||||
// 板子
|
||||
if (ball.y > H - 34 && ball.y < H - 18 && Math.abs(ball.x - paddle.x) < paddle.w / 2 + 8 && ball.vy > 0) {
|
||||
ball.vy = -Math.abs(ball.vy) - 0.06
|
||||
ball.vx += ((ball.x - paddle.x) / (paddle.w / 2)) * 2.2
|
||||
ball.vx = Math.max(-6, Math.min(6, ball.vx))
|
||||
}
|
||||
// 掉落
|
||||
if (ball.y > H + 10) {
|
||||
lives.value--
|
||||
if (lives.value <= 0) {
|
||||
state.value = 'over'
|
||||
saveBest()
|
||||
} else {
|
||||
reset(false)
|
||||
}
|
||||
}
|
||||
// 砖块
|
||||
for (const b of bricks) {
|
||||
if (!b.alive) continue
|
||||
if (ball.x > b.x - 8 && ball.x < b.x + b.w + 8 && ball.y > b.y - 8 && ball.y < b.y + b.h + 8) {
|
||||
b.alive = false
|
||||
score.value += b.pt
|
||||
const fromSide = ball.x < b.x || ball.x > b.x + b.w
|
||||
if (fromSide) ball.vx *= -1
|
||||
else ball.vy *= -1
|
||||
break
|
||||
}
|
||||
}
|
||||
if (bricks.every((b) => !b.alive)) {
|
||||
state.value = 'win'
|
||||
saveBest()
|
||||
}
|
||||
}
|
||||
|
||||
// 砖
|
||||
bricks.forEach((b) => {
|
||||
if (!b.alive) return
|
||||
ctx.fillStyle = b.color
|
||||
ctx.fillRect(b.x, b.y, b.w, b.h)
|
||||
ctx.fillStyle = 'rgba(255,255,255,.25)'
|
||||
ctx.fillRect(b.x, b.y, b.w, 4)
|
||||
})
|
||||
// 板
|
||||
ctx.fillStyle = '#5ac8fa'
|
||||
ctx.fillRect(paddle.x - paddle.w / 2, H - 26, paddle.w, paddle.h)
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.fillRect(paddle.x - paddle.w / 2, H - 26, paddle.w, 3)
|
||||
// 球
|
||||
ctx.fillStyle = '#ffe135'
|
||||
ctx.beginPath()
|
||||
ctx.arc(ball.x, ball.y, 7, 0, Math.PI * 2)
|
||||
ctx.fill()
|
||||
|
||||
ctx.textAlign = 'center'
|
||||
if (state.value !== 'run') {
|
||||
ctx.fillStyle = 'rgba(0,0,0,.55)'
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
ctx.fillStyle = state.value === 'win' ? '#4cd964' : '#ffe135'
|
||||
ctx.font = 'bold 24px "Press Start 2P", monospace'
|
||||
ctx.fillText(state.value === 'win' ? 'YOU WIN!' : state.value === 'over' ? 'GAME OVER' : 'BREAKOUT', W / 2, 170)
|
||||
ctx.fillStyle = '#fff'
|
||||
ctx.font = '14px sans-serif'
|
||||
if (state.value === 'ready') ctx.fillText('移动鼠标控制挡板,点击发球', W / 2, 210)
|
||||
else ctx.fillText(`得分 ${score.value} · 点击再来一局`, W / 2, 210)
|
||||
}
|
||||
}
|
||||
|
||||
function saveBest() {
|
||||
if (score.value > best.value) {
|
||||
best.value = score.value
|
||||
localStorage.setItem(KEY, String(best.value))
|
||||
}
|
||||
}
|
||||
function onMove(e) {
|
||||
const rect = cvs.value.getBoundingClientRect()
|
||||
const x = ((e.clientX - rect.left) / rect.width) * W
|
||||
paddle.x = Math.max(paddle.w / 2, Math.min(W - paddle.w / 2, x))
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
best.value = Number(localStorage.getItem(KEY)) || 0
|
||||
ctx = cvs.value.getContext('2d')
|
||||
reset()
|
||||
loop()
|
||||
})
|
||||
onBeforeUnmount(() => cancelAnimationFrame(raf))
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ab">
|
||||
<nav class="ab-nav">
|
||||
<router-link to="/style/arcade" class="ab-back">← 离开机厅</router-link>
|
||||
<h1>打砖块 · 隐藏机台</h1>
|
||||
<span class="ab-best">HI-SCORE {{ best }}</span>
|
||||
</nav>
|
||||
|
||||
<div class="ab-cab">
|
||||
<div class="ab-marquee">★ BRICK BUSTER ★</div>
|
||||
<div class="ab-hud">
|
||||
<span>SCORE <b>{{ score }}</b></span>
|
||||
<span>LIVES <b>{{ '♥'.repeat(Math.max(0, lives)) || '—' }}</b></span>
|
||||
</div>
|
||||
<canvas ref="cvs" width="640" height="420" @pointermove="onMove" @pointerdown="start"></canvas>
|
||||
<div class="ab-panel">
|
||||
<i class="ab-stick"></i>
|
||||
<b class="ab-btn r"></b>
|
||||
<b class="ab-btn y"></b>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="ab-fin">鼠标左右移动挡板,点击开始 · 清光 60 块砖通关</p>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ab {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(ellipse 60% 40% at 50% 0%, rgba(90, 200, 250, 0.14), transparent 60%),
|
||||
#0d0716;
|
||||
color: #fff;
|
||||
font-family: 'Press Start 2P', 'Zpix', monospace;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 20px clamp(12px, 4vw, 48px) 44px;
|
||||
}
|
||||
.ab-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
width: min(700px, 100%);
|
||||
margin-bottom: 16px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ab-back {
|
||||
color: #5ac8fa;
|
||||
text-decoration: none;
|
||||
font-size: 11px;
|
||||
border: 2px solid #5ac8fa;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
.ab-back:hover { background: #5ac8fa; color: #0d0716; }
|
||||
.ab-nav h1 { margin: 0; font-size: 14px; }
|
||||
.ab-best { margin-left: auto; font-size: 10px; color: #ffe135; }
|
||||
|
||||
.ab-cab {
|
||||
width: min(700px, 100%);
|
||||
background: linear-gradient(#2a1a3e, #180e28);
|
||||
border: 3px solid #43305e;
|
||||
border-radius: 16px;
|
||||
padding: 14px 16px 18px;
|
||||
box-shadow: 0 24px 60px rgba(0, 0, 0, 0.6);
|
||||
}
|
||||
.ab-marquee {
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
color: #ffe135;
|
||||
text-shadow: 0 0 12px rgba(255, 225, 53, 0.7);
|
||||
padding: 8px 0 12px;
|
||||
animation: ab-flicker 2.4s steps(3, end) infinite;
|
||||
}
|
||||
@keyframes ab-flicker {
|
||||
92% { opacity: 1; }
|
||||
96% { opacity: 0.5; }
|
||||
}
|
||||
.ab-hud {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 10px;
|
||||
color: #9aa0b4;
|
||||
padding: 0 4px 8px;
|
||||
}
|
||||
.ab-hud b { color: #fff; margin-left: 6px; }
|
||||
.ab-cab canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: auto;
|
||||
border: 3px solid #0a0a14;
|
||||
border-radius: 8px;
|
||||
cursor: crosshair;
|
||||
touch-action: none;
|
||||
}
|
||||
.ab-panel {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 30px;
|
||||
padding-top: 16px;
|
||||
}
|
||||
.ab-stick {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #e04038;
|
||||
box-shadow: 0 -12px 0 -4px #999, 0 4px 10px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.ab-btn {
|
||||
width: 26px;
|
||||
height: 26px;
|
||||
border-radius: 50%;
|
||||
box-shadow: inset 0 -4px 0 rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
.ab-btn.r { background: #ff2d55; }
|
||||
.ab-btn.y { background: #ffe135; }
|
||||
|
||||
.ab-fin {
|
||||
margin: 16px 0 0;
|
||||
font-size: 9.5px;
|
||||
color: #9aa0b4;
|
||||
line-height: 1.8;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
170
src/pages/styles/sub/ArtDecoCabaretPage.vue
Normal file
170
src/pages/styles/sub/ArtDecoCabaretPage.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const ACTS = [
|
||||
{ time: '20:00', name: '开场 · 香槟塔金字塔', desc: '侍者以完美对称的阵型入场,倒香槟的弧线经过精确计算。', icon: '🥂' },
|
||||
{ time: '20:30', name: '爵士乐队 · 黑金四重奏', desc: '萨克斯的每个音符都镀了金边,鼓点像打字机一样规整。', icon: '🎷' },
|
||||
{ time: '21:15', name: '歌舞 · 孔雀扇之舞', desc: '十二把折扇同时展开,构成一幅完美的装饰派扇形图案。', icon: '💃' },
|
||||
{ time: '22:00', name: '魔术 · 消失的边框', desc: '魔术师让一整面金色边框凭空消失,设计师们掌声最响。', icon: '🎩' },
|
||||
{ time: '23:00', name: '压轴 · 年糕大乐队', desc: '据说主唱是位程序员,歌单里全是原创,谢幕曲叫《构建通过》。', icon: '🎤' },
|
||||
]
|
||||
const booked = ref(new Set())
|
||||
function toggle(i) {
|
||||
const s = new Set(booked.value)
|
||||
s.has(i) ? s.delete(i) : s.add(i)
|
||||
booked.value = s
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="ac">
|
||||
<nav class="ac-nav">
|
||||
<router-link to="/style/art-deco" class="ac-back">← 回大堂</router-link>
|
||||
<h1>金孔雀歌舞厅 · 今夜节目单</h1>
|
||||
<span class="ac-booked">已预定 {{ booked.size }} 场</span>
|
||||
</nav>
|
||||
|
||||
<main class="ac-bill">
|
||||
<header class="ac-bill-head">
|
||||
<div class="ac-fan"><i v-for="n in 11" :key="n" :style="{ transform: `rotate(${(n - 6) * 9}deg)` }"></i></div>
|
||||
<p class="ac-est">歌舞升平 · 夜夜笙歌</p>
|
||||
<h2>PROGRAMME</h2>
|
||||
<p class="ac-date">壹玖贰玖年 · 每晚八点</p>
|
||||
</header>
|
||||
|
||||
<div class="ac-divider"><i></i><b>❖</b><i></i></div>
|
||||
|
||||
<article
|
||||
v-for="(a, i) in ACTS"
|
||||
:key="a.name"
|
||||
class="ac-act"
|
||||
:class="{ on: booked.has(i) }"
|
||||
@click="toggle(i)"
|
||||
>
|
||||
<span class="ac-time">{{ a.time }}</span>
|
||||
<div class="ac-act-info">
|
||||
<h3><i>{{ a.icon }}</i>{{ a.name }}</h3>
|
||||
<p>{{ a.desc }}</p>
|
||||
</div>
|
||||
<span class="ac-book">{{ booked.has(i) ? '✓ 已订座' : '订座' }}</span>
|
||||
</article>
|
||||
|
||||
<footer class="ac-foot">
|
||||
<p>着装要求:金线,或至少心里有金线</p>
|
||||
<p class="ac-note">点击节目即可订座 · 座位无限,气派有限</p>
|
||||
</footer>
|
||||
</main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ac {
|
||||
min-height: 100vh;
|
||||
background:
|
||||
radial-gradient(ellipse 70% 40% at 50% 0%, rgba(217, 185, 106, 0.12), transparent 60%),
|
||||
#14100c;
|
||||
color: #e8dcc0;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 24px clamp(12px, 4vw, 48px) 46px;
|
||||
}
|
||||
.ac-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
width: min(640px, 100%);
|
||||
margin-bottom: 18px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.ac-back {
|
||||
color: #d9b96a;
|
||||
text-decoration: none;
|
||||
font-size: 12.5px;
|
||||
letter-spacing: 0.14em;
|
||||
border: 1px solid rgba(217, 185, 106, 0.5);
|
||||
padding: 7px 16px;
|
||||
}
|
||||
.ac-back:hover { background: rgba(217, 185, 106, 0.08); }
|
||||
.ac-nav h1 { margin: 0; font-size: 17px; font-weight: 400; letter-spacing: 0.2em; }
|
||||
.ac-booked { margin-left: auto; font-size: 12px; color: #d9b96a; }
|
||||
|
||||
.ac-bill {
|
||||
width: min(640px, 100%);
|
||||
background: #1c1610;
|
||||
border: 2px solid #d9b96a;
|
||||
outline: 1px solid rgba(217, 185, 106, 0.35);
|
||||
outline-offset: 5px;
|
||||
padding: 30px clamp(18px, 5vw, 44px) 24px;
|
||||
}
|
||||
.ac-bill-head { text-align: center; }
|
||||
.ac-fan {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.ac-fan i {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: 0;
|
||||
width: 2px;
|
||||
height: 38px;
|
||||
background: linear-gradient(to top, #d9b96a, transparent);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
.ac-est { margin: 0 0 6px; font-size: 11px; letter-spacing: 0.5em; color: rgba(232, 220, 192, 0.55); }
|
||||
.ac-bill-head h2 {
|
||||
margin: 0 0 6px;
|
||||
font-size: 30px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0.4em;
|
||||
color: #d9b96a;
|
||||
}
|
||||
.ac-date { margin: 0; font-size: 12px; letter-spacing: 0.24em; color: rgba(232, 220, 192, 0.5); }
|
||||
.ac-divider {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
margin: 20px 0;
|
||||
}
|
||||
.ac-divider i { flex: 1; height: 1px; background: rgba(217, 185, 106, 0.4); }
|
||||
.ac-divider b { color: #d9b96a; font-size: 13px; }
|
||||
|
||||
.ac-act {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
padding: 15px 12px;
|
||||
cursor: pointer;
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.ac-act:hover { background: rgba(217, 185, 106, 0.05); }
|
||||
.ac-act.on { border-color: rgba(217, 185, 106, 0.55); background: rgba(217, 185, 106, 0.07); }
|
||||
.ac-time {
|
||||
font-size: 13px;
|
||||
color: #d9b96a;
|
||||
letter-spacing: 0.1em;
|
||||
min-width: 48px;
|
||||
padding-top: 2px;
|
||||
}
|
||||
.ac-act-info { flex: 1; }
|
||||
.ac-act-info h3 { margin: 0 0 5px; font-size: 15.5px; font-weight: 700; letter-spacing: 0.06em; }
|
||||
.ac-act-info h3 i { font-style: normal; margin-right: 8px; }
|
||||
.ac-act-info p { margin: 0; font-size: 12px; line-height: 1.8; color: rgba(232, 220, 192, 0.6); }
|
||||
.ac-book {
|
||||
font-size: 11px;
|
||||
letter-spacing: 0.14em;
|
||||
color: rgba(232, 220, 192, 0.5);
|
||||
border: 1px solid rgba(232, 220, 192, 0.3);
|
||||
padding: 4px 12px;
|
||||
align-self: center;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.ac-act.on .ac-book { color: #14100c; background: #d9b96a; border-color: #d9b96a; }
|
||||
|
||||
.ac-foot { text-align: center; border-top: 1px solid rgba(217, 185, 106, 0.35); margin-top: 14px; padding-top: 14px; }
|
||||
.ac-foot p { margin: 0 0 5px; font-size: 12px; letter-spacing: 0.14em; }
|
||||
.ac-note { font-size: 10.5px !important; color: rgba(232, 220, 192, 0.45); }
|
||||
</style>
|
||||
213
src/pages/styles/sub/AstroConnectPage.vue
Normal file
213
src/pages/styles/sub/AstroConnectPage.vue
Normal file
@@ -0,0 +1,213 @@
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
|
||||
/* 点星连线:按顺序连出「码农座」 */
|
||||
const STARS = [
|
||||
{ id: 0, x: 22, y: 26, name: '启蒙星' },
|
||||
{ id: 1, x: 38, y: 18, name: '语法星' },
|
||||
{ id: 2, x: 55, y: 28, name: '框架星' },
|
||||
{ id: 3, x: 70, y: 20, name: '架构星' },
|
||||
{ id: 4, x: 62, y: 48, name: '调试星' },
|
||||
{ id: 5, x: 44, y: 56, name: '重构星' },
|
||||
{ id: 6, x: 28, y: 66, name: '开源星' },
|
||||
{ id: 7, x: 52, y: 76, name: '发版星' },
|
||||
]
|
||||
const path = ref([])
|
||||
const done = computed(() => path.value.length === STARS.length)
|
||||
|
||||
function tap(s) {
|
||||
if (done.value) return
|
||||
const expect = path.value.length
|
||||
if (s.id === expect) {
|
||||
path.value = [...path.value, s.id]
|
||||
}
|
||||
}
|
||||
function reset() {
|
||||
path.value = []
|
||||
}
|
||||
const lines = computed(() =>
|
||||
path.value.slice(1).map((id, i) => {
|
||||
const a = STARS[path.value[i]]
|
||||
const b = STARS[id]
|
||||
return { x1: a.x, y1: a.y, x2: b.x, y2: b.y }
|
||||
}),
|
||||
)
|
||||
const nextId = computed(() => path.value.length)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="asc">
|
||||
<nav class="asc-nav">
|
||||
<router-link to="/style/astrolabe" class="asc-back">← 收起星盘</router-link>
|
||||
<h1>连星成座 · 码农座</h1>
|
||||
<button class="asc-reset" @click="reset">↺ 重连</button>
|
||||
</nav>
|
||||
|
||||
<p class="asc-tip">
|
||||
{{ done ? '「码农座」连成!传说仰望它的人,构建从不失败。' : `按亮起的顺序连接星星(下一颗:${STARS[nextId]?.name})` }}
|
||||
</p>
|
||||
|
||||
<div class="asc-sky">
|
||||
<!-- 背景星尘 -->
|
||||
<i v-for="n in 40" :key="'d' + n" class="asc-dust" :style="{ left: ((n * 53) % 100) + '%', top: ((n * 37) % 100) + '%', animationDelay: (n % 7) * 0.4 + 's' }"></i>
|
||||
|
||||
<!-- 连线 -->
|
||||
<svg class="asc-lines" viewBox="0 0 100 100" preserveAspectRatio="none">
|
||||
<line v-for="(l, i) in lines" :key="i" :x1="l.x1" :y1="l.y1" :x2="l.x2" :y2="l.y2" />
|
||||
</svg>
|
||||
|
||||
<!-- 星星 -->
|
||||
<button
|
||||
v-for="s in STARS"
|
||||
:key="s.id"
|
||||
class="asc-star"
|
||||
:class="{ lit: path.includes(s.id), next: s.id === nextId && !done }"
|
||||
:style="{ left: s.x + '%', top: s.y + '%' }"
|
||||
@click="tap(s)"
|
||||
>
|
||||
<i></i>
|
||||
<span>{{ s.name }}</span>
|
||||
</button>
|
||||
|
||||
<!-- 完成光效 -->
|
||||
<div v-if="done" class="asc-glow"></div>
|
||||
</div>
|
||||
|
||||
<div v-if="done" class="asc-reading">
|
||||
<p class="asc-reading-title">☄ 星座解读</p>
|
||||
<p>码农座,冬夜可见(机房常年可见)。主星「调试星」光变周期不定,象征此座之人耐心极佳、好奇心旺盛,唯独在需求评审日光芒黯淡。</p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.asc {
|
||||
min-height: 100vh;
|
||||
background: radial-gradient(ellipse 80% 60% at 50% 100%, #1a2444, #0a0e20 70%);
|
||||
color: #dce4f8;
|
||||
font-family: 'Noto Serif SC', serif;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 22px clamp(12px, 4vw, 48px) 44px;
|
||||
}
|
||||
.asc-nav {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
width: min(720px, 100%);
|
||||
margin-bottom: 8px;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.asc-back {
|
||||
color: #ffd97a;
|
||||
text-decoration: none;
|
||||
font-size: 13px;
|
||||
border: 1px solid rgba(255, 217, 122, 0.45);
|
||||
border-radius: 99px;
|
||||
padding: 7px 16px;
|
||||
}
|
||||
.asc-back:hover { background: rgba(255, 217, 122, 0.08); }
|
||||
.asc-nav h1 { margin: 0; font-size: 19px; font-weight: 900; letter-spacing: 0.16em; }
|
||||
.asc-reset {
|
||||
margin-left: auto;
|
||||
border: 1px solid rgba(220, 228, 248, 0.3);
|
||||
background: none;
|
||||
color: #dce4f8;
|
||||
font-family: inherit;
|
||||
font-size: 12.5px;
|
||||
border-radius: 99px;
|
||||
padding: 7px 15px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.asc-tip { width: min(720px, 100%); margin: 0 0 12px; font-size: 13px; color: #ffd97a; min-height: 20px; }
|
||||
|
||||
.asc-sky {
|
||||
position: relative;
|
||||
width: min(720px, 100%);
|
||||
aspect-ratio: 16/10;
|
||||
border: 1px solid rgba(220, 228, 248, 0.15);
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: radial-gradient(circle at 70% 20%, rgba(122, 140, 220, 0.1), transparent 50%);
|
||||
}
|
||||
.asc-dust {
|
||||
position: absolute;
|
||||
width: 2px;
|
||||
height: 2px;
|
||||
border-radius: 50%;
|
||||
background: rgba(220, 228, 248, 0.5);
|
||||
animation: asc-tw 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes asc-tw {
|
||||
50% { opacity: 0.15; }
|
||||
}
|
||||
.asc-lines { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.asc-lines line {
|
||||
stroke: rgba(255, 217, 122, 0.75);
|
||||
stroke-width: 0.45;
|
||||
stroke-linecap: round;
|
||||
filter: drop-shadow(0 0 2px rgba(255, 217, 122, 0.7));
|
||||
animation: asc-draw 0.4s ease;
|
||||
}
|
||||
@keyframes asc-draw {
|
||||
from { opacity: 0; }
|
||||
}
|
||||
.asc-star {
|
||||
position: absolute;
|
||||
transform: translate(-50%, -50%);
|
||||
border: none;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
z-index: 3;
|
||||
}
|
||||
.asc-star i {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
border-radius: 50%;
|
||||
background: rgba(220, 228, 248, 0.4);
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
.asc-star span {
|
||||
font-size: 10.5px;
|
||||
color: rgba(220, 228, 248, 0.5);
|
||||
letter-spacing: 0.1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.asc-star.next i {
|
||||
background: #ffd97a;
|
||||
box-shadow: 0 0 18px rgba(255, 217, 122, 0.9);
|
||||
animation: asc-pulse 1s ease-in-out infinite;
|
||||
}
|
||||
@keyframes asc-pulse {
|
||||
50% { transform: scale(1.5); }
|
||||
}
|
||||
.asc-star.lit i { background: #ffd97a; box-shadow: 0 0 12px rgba(255, 217, 122, 0.8); }
|
||||
.asc-star.lit span { color: #ffd97a; }
|
||||
.asc-glow {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: radial-gradient(ellipse 46% 40% at 46% 44%, rgba(255, 217, 122, 0.1), transparent 65%);
|
||||
animation: asc-bloom 1.6s ease;
|
||||
pointer-events: none;
|
||||
}
|
||||
@keyframes asc-bloom {
|
||||
from { opacity: 0; }
|
||||
}
|
||||
|
||||
.asc-reading {
|
||||
width: min(720px, 100%);
|
||||
margin-top: 14px;
|
||||
background: rgba(255, 217, 122, 0.06);
|
||||
border: 1px dashed rgba(255, 217, 122, 0.4);
|
||||
border-radius: 12px;
|
||||
padding: 14px 18px;
|
||||
animation: asc-draw 0.5s ease;
|
||||
}
|
||||
.asc-reading-title { margin: 0 0 7px; font-size: 13px; color: #ffd97a; letter-spacing: 0.2em; }
|
||||
.asc-reading p:last-child { margin: 0; font-size: 13px; line-height: 2; color: rgba(220, 228, 248, 0.82); }
|
||||
</style>
|
||||
Reference in New Issue
Block a user