mirror of
https://github.com/RhenCloud/Cloud-Home.git
synced 2026-01-22 17:39:07 +08:00
chore: 格式化代码
This commit is contained in:
19
README.md
19
README.md
@@ -117,7 +117,10 @@ const siteConfig: SiteConfig = {
|
|||||||
// 技能图标展示,详见https://github.com/tandpfun/skill-icons#icons-list
|
// 技能图标展示,详见https://github.com/tandpfun/skill-icons#icons-list
|
||||||
skills: [
|
skills: [
|
||||||
{ title: "前端", items: ["css", "html", "javascript", "typescript", "vue"] },
|
{ title: "前端", items: ["css", "html", "javascript", "typescript", "vue"] },
|
||||||
{ title: "后端 / 云", items: ["cpp", "cloudflare", "docker", "java", "mysql", "nodejs", "python", "vercel"] },
|
{
|
||||||
|
title: "后端 / 云",
|
||||||
|
items: ["cpp", "cloudflare", "docker", "java", "mysql", "nodejs", "python", "vercel"],
|
||||||
|
},
|
||||||
{ title: "工具", items: ["ae", "au", "git", "github", "md", "ps", "pr", "vscode"] },
|
{ title: "工具", items: ["ae", "au", "git", "github", "md", "ps", "pr", "vscode"] },
|
||||||
{ title: "操作系统", items: ["arch", "linux", "windows"] },
|
{ title: "操作系统", items: ["arch", "linux", "windows"] },
|
||||||
],
|
],
|
||||||
@@ -136,8 +139,16 @@ const siteConfig: SiteConfig = {
|
|||||||
],
|
],
|
||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
{ name: "Example Project 1", url: "https://github.com/ExampleUser/example-project-1", desc: "Example Project 1" },
|
{
|
||||||
{ name: "Example Project 2", url: "https://github.com/ExampleUser/example-project-2", desc: "Example Project 2" },
|
name: "Example Project 1",
|
||||||
|
url: "https://github.com/ExampleUser/example-project-1",
|
||||||
|
desc: "Example Project 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Example Project 2",
|
||||||
|
url: "https://github.com/ExampleUser/example-project-2",
|
||||||
|
desc: "Example Project 2",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
friends: [
|
friends: [
|
||||||
@@ -158,7 +169,7 @@ const siteConfig: SiteConfig = {
|
|||||||
footer: {
|
footer: {
|
||||||
beian: "备案号", // 备案号,留空则不显示
|
beian: "备案号", // 备案号,留空则不显示
|
||||||
beianLink: "https://beian.miit.gov.cn/", // 备案号链接,一般无需修改
|
beianLink: "https://beian.miit.gov.cn/", // 备案号链接,一般无需修改
|
||||||
customHtml: '', // 自定义 HTML 代码,如统计代码等
|
customHtml: "", // 自定义 HTML 代码,如统计代码等
|
||||||
hitokoto: {
|
hitokoto: {
|
||||||
enable: true, // 是否启用一言
|
enable: true, // 是否启用一言
|
||||||
type: "a&b&c&d&j", // 一言类型,详见 https://developer.hitokoto.cn/sentence/#%E5%8F%A5%E5%AD%90%E7%B1%BB%E5%9E%8B-%E5%8F%82%E6%95%B0
|
type: "a&b&c&d&j", // 一言类型,详见 https://developer.hitokoto.cn/sentence/#%E5%8F%A5%E5%AD%90%E7%B1%BB%E5%9E%8B-%E5%8F%82%E6%95%B0
|
||||||
|
|||||||
@@ -12,7 +12,9 @@
|
|||||||
<span class="text-xl leading-none">🎂</span>
|
<span class="text-xl leading-none">🎂</span>
|
||||||
<h3 class="m-0 text-sm font-semibold text-white/90">年龄</h3>
|
<h3 class="m-0 text-sm font-semibold text-white/90">年龄</h3>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60">
|
<p
|
||||||
|
class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60"
|
||||||
|
>
|
||||||
{{ age }} 岁
|
{{ age }} 岁
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
@@ -25,7 +27,9 @@
|
|||||||
<span class="text-xl leading-none">⚧️</span>
|
<span class="text-xl leading-none">⚧️</span>
|
||||||
<h3 class="m-0 text-sm font-semibold text-white/90">性别</h3>
|
<h3 class="m-0 text-sm font-semibold text-white/90">性别</h3>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60">
|
<p
|
||||||
|
class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60"
|
||||||
|
>
|
||||||
{{ profile.gender }}
|
{{ profile.gender }}
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
@@ -38,7 +42,9 @@
|
|||||||
<span class="text-xl leading-none">🗣️</span>
|
<span class="text-xl leading-none">🗣️</span>
|
||||||
<h3 class="m-0 text-sm font-semibold text-white/90">代词</h3>
|
<h3 class="m-0 text-sm font-semibold text-white/90">代词</h3>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60">
|
<p
|
||||||
|
class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60"
|
||||||
|
>
|
||||||
{{ profile.pronouns }}
|
{{ profile.pronouns }}
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
@@ -51,7 +57,9 @@
|
|||||||
<span class="text-xl leading-none">📍</span>
|
<span class="text-xl leading-none">📍</span>
|
||||||
<h3 class="m-0 text-sm font-semibold text-white/90">地区</h3>
|
<h3 class="m-0 text-sm font-semibold text-white/90">地区</h3>
|
||||||
</div>
|
</div>
|
||||||
<p class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60">
|
<p
|
||||||
|
class="text-text-muted text-xs m-0 text-right whitespace-nowrap font-medium text-white/60"
|
||||||
|
>
|
||||||
{{ profile.location }}
|
{{ profile.location }}
|
||||||
</p>
|
</p>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -73,7 +73,8 @@ const buildHitokotoUrl = () => {
|
|||||||
if (Array.isArray(type)) {
|
if (Array.isArray(type)) {
|
||||||
type.filter(Boolean).forEach((t) => url.searchParams.append("c", t));
|
type.filter(Boolean).forEach((t) => url.searchParams.append("c", t));
|
||||||
} else if (typeof type === "string") {
|
} else if (typeof type === "string") {
|
||||||
type.split("&")
|
type
|
||||||
|
.split("&")
|
||||||
.map((t) => t.trim())
|
.map((t) => t.trim())
|
||||||
.filter(Boolean)
|
.filter(Boolean)
|
||||||
.forEach((t) => url.searchParams.append("c", t));
|
.forEach((t) => url.searchParams.append("c", t));
|
||||||
@@ -107,11 +108,14 @@ const fetchStats = async () => {
|
|||||||
const endAt = Date.now();
|
const endAt = Date.now();
|
||||||
const startAt = new Date(siteConfig.siteMeta.startDate).getTime();
|
const startAt = new Date(siteConfig.siteMeta.startDate).getTime();
|
||||||
|
|
||||||
const resp = await fetch(`${apiBase}/v1/websites/${websiteId}/stats?startAt=${startAt}&endAt=${endAt}`, {
|
const resp = await fetch(
|
||||||
|
`${apiBase}/v1/websites/${websiteId}/stats?startAt=${startAt}&endAt=${endAt}`,
|
||||||
|
{
|
||||||
headers: {
|
headers: {
|
||||||
Authorization: `Bearer ${apiKey}`,
|
Authorization: `Bearer ${apiKey}`,
|
||||||
},
|
},
|
||||||
});
|
}
|
||||||
|
);
|
||||||
|
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
console.warn(`Stats API returned ${resp.status}`);
|
console.warn(`Stats API returned ${resp.status}`);
|
||||||
|
|||||||
@@ -4,18 +4,26 @@
|
|||||||
<p class="text-text-muted text-sm m-0 mb-3 block">欢迎互换友链 · Friends</p>
|
<p class="text-text-muted text-sm m-0 mb-3 block">欢迎互换友链 · Friends</p>
|
||||||
<div class="grid grid-cols-1 gap-4 w-full max-w-[1100px] mx-auto sm:grid-cols-2">
|
<div class="grid grid-cols-1 gap-4 w-full max-w-[1100px] mx-auto sm:grid-cols-2">
|
||||||
<article
|
<article
|
||||||
v-for="f in displayedFriends" :key="f.url"
|
v-for="f in displayedFriends"
|
||||||
class="rounded-[14px] border border-white/10 bg-gradient-to-br from-white/5 to-white/0 px-4 py-3.5 transition-all duration-200 hover:-translate-y-[3px] hover:border-pink-400/50 w-[290px] h-[145px] flex flex-col">
|
:key="f.url"
|
||||||
|
class="rounded-[14px] border border-white/10 bg-gradient-to-br from-white/5 to-white/0 px-4 py-3.5 transition-all duration-200 hover:-translate-y-[3px] hover:border-pink-400/50 w-[290px] h-[145px] flex flex-col"
|
||||||
|
>
|
||||||
<div class="flex items-center justify-between mb-1.5">
|
<div class="flex items-center justify-between mb-1.5">
|
||||||
<div class="flex items-center gap-2 min-w-0">
|
<div class="flex items-center gap-2 min-w-0">
|
||||||
<NuxtImg
|
<NuxtImg
|
||||||
v-if="f.avatar" :src="f.avatar" :alt="f.name" loading="lazy"
|
v-if="f.avatar"
|
||||||
class="w-12 h-12 rounded-full object-cover border border-white/15" />
|
:src="f.avatar"
|
||||||
|
:alt="f.name"
|
||||||
|
loading="lazy"
|
||||||
|
class="w-12 h-12 rounded-full object-cover border border-white/15"
|
||||||
|
/>
|
||||||
<h3 class="m-0 font-semibold text-base whitespace-nowrap overflow-hidden text-ellipsis">
|
<h3 class="m-0 font-semibold text-base whitespace-nowrap overflow-hidden text-ellipsis">
|
||||||
{{ f.name }}
|
{{ f.name }}
|
||||||
</h3>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<span class="rounded-full px-2.5 py-1 text-xs bg-purple-400/15 text-purple-300">友链</span>
|
<span class="rounded-full px-2.5 py-1 text-xs bg-purple-400/15 text-purple-300"
|
||||||
|
>友链</span
|
||||||
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-white/60 flex-1 overflow-hidden truncate-lines-2 mb-2">
|
<p class="text-sm text-white/60 flex-1 overflow-hidden truncate-lines-2 mb-2">
|
||||||
@@ -24,7 +32,8 @@ v-if="f.avatar" :src="f.avatar" :alt="f.name" loading="lazy"
|
|||||||
|
|
||||||
<NuxtLink
|
<NuxtLink
|
||||||
:to="f.url"
|
:to="f.url"
|
||||||
class="inline-flex items-center gap-1.5 mt-auto shrink-0 font-semibold text-pink-300 hover:text-pink-400 transition-all duration-200 hover:gap-2">
|
class="inline-flex items-center gap-1.5 mt-auto shrink-0 font-semibold text-pink-300 hover:text-pink-400 transition-all duration-200 hover:gap-2"
|
||||||
|
>
|
||||||
访问 →
|
访问 →
|
||||||
</NuxtLink>
|
</NuxtLink>
|
||||||
</article>
|
</article>
|
||||||
@@ -34,23 +43,28 @@ v-if="f.avatar" :src="f.avatar" :alt="f.name" loading="lazy"
|
|||||||
<div class="flex justify-center items-center align-center flex-wrap">
|
<div class="flex justify-center items-center align-center flex-wrap">
|
||||||
<button
|
<button
|
||||||
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/80 hover:shadow-lg hover:shadow-primary/25"
|
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/80 hover:shadow-lg hover:shadow-primary/25"
|
||||||
@click="openForm">
|
@click="openForm"
|
||||||
|
>
|
||||||
申请友链
|
申请友链
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<Teleport to="body">
|
<Teleport to="body">
|
||||||
<div
|
<div
|
||||||
v-if="showDialog" class="fixed inset-0 bg-black/45 backdrop-blur-sm flex items-center justify-center z-50"
|
v-if="showDialog"
|
||||||
@click.self="closeDialog">
|
class="fixed inset-0 bg-black/45 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
|
@click.self="closeDialog"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="min-w-[280px] max-w-[420px] bg-gradient-to-br from-pink-500/12 to-white/8 border border-white/15 rounded-2xl p-4 shadow-xl">
|
class="min-w-[280px] max-w-[420px] bg-gradient-to-br from-pink-500/12 to-white/8 border border-white/15 rounded-2xl p-4 shadow-xl"
|
||||||
|
>
|
||||||
<h3 class="m-0 mb-2">{{ dialogTitle }}</h3>
|
<h3 class="m-0 mb-2">{{ dialogTitle }}</h3>
|
||||||
<p class="text-text-muted text-sm mb-4">{{ dialogText }}</p>
|
<p class="text-text-muted text-sm mb-4">{{ dialogText }}</p>
|
||||||
<div class="flex justify-end">
|
<div class="flex justify-end">
|
||||||
<button
|
<button
|
||||||
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary cursor-pointer hover:bg-primary/20 transition-all"
|
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary cursor-pointer hover:bg-primary/20 transition-all"
|
||||||
@click="closeDialog">
|
@click="closeDialog"
|
||||||
|
>
|
||||||
好的
|
好的
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,9 +77,11 @@ class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-pr
|
|||||||
<div
|
<div
|
||||||
v-if="showFormModal"
|
v-if="showFormModal"
|
||||||
class="fixed inset-0 bg-black/45 backdrop-blur-sm flex items-center justify-center z-50"
|
class="fixed inset-0 bg-black/45 backdrop-blur-sm flex items-center justify-center z-50"
|
||||||
@click.self="showFormModal = false">
|
@click.self="showFormModal = false"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
class="w-[92%] max-w-[540px] bg-gradient-to-br from-white/8 to-primary/6 border border-white/15 rounded-2xl p-6 shadow-xl">
|
class="w-[92%] max-w-[540px] bg-gradient-to-br from-white/8 to-primary/6 border border-white/15 rounded-2xl p-6 shadow-xl"
|
||||||
|
>
|
||||||
<h3 class="m-0 mb-4 text-center">申请友链</h3>
|
<h3 class="m-0 mb-4 text-center">申请友链</h3>
|
||||||
|
|
||||||
<div class="mb-4 text-sm text-text-primary">
|
<div class="mb-4 text-sm text-text-primary">
|
||||||
@@ -79,56 +95,79 @@ v-if="showFormModal"
|
|||||||
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold sm:col-span-2">
|
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold sm:col-span-2">
|
||||||
网站名称 *
|
网站名称 *
|
||||||
<input
|
<input
|
||||||
v-model="form.name" required placeholder="网站名称"
|
v-model="form.name"
|
||||||
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none" >
|
required
|
||||||
|
placeholder="网站名称"
|
||||||
|
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- URL 与 Email 同行 -->
|
<!-- URL 与 Email 同行 -->
|
||||||
<label class="flex fl ex-col gap-1 text-sm text-text-primary font-semibold">
|
<label class="flex fl ex-col gap-1 text-sm text-text-primary font-semibold">
|
||||||
网站链接 *
|
网站链接 *
|
||||||
<input
|
<input
|
||||||
v-model="form.url" type="url" required placeholder="https://example.com"
|
v-model="form.url"
|
||||||
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none" >
|
type="url"
|
||||||
|
required
|
||||||
|
placeholder="https://example.com"
|
||||||
|
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
||||||
联系邮箱 *
|
联系邮箱 *
|
||||||
<input
|
<input
|
||||||
v-model="form.email" type="email" required placeholder="example@example.com"
|
v-model="form.email"
|
||||||
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none" >
|
type="email"
|
||||||
|
required
|
||||||
|
placeholder="example@example.com"
|
||||||
|
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<!-- 描述 与 头像 同行 -->
|
<!-- 描述 与 头像 同行 -->
|
||||||
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
||||||
网站描述
|
网站描述
|
||||||
<input
|
<input
|
||||||
v-model="form.desc" placeholder="可选"
|
v-model="form.desc"
|
||||||
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none" >
|
placeholder="可选"
|
||||||
|
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold">
|
||||||
头像链接
|
头像链接
|
||||||
<input
|
<input
|
||||||
v-model="form.avatar" type="url" placeholder="可选,展示头像"
|
v-model="form.avatar"
|
||||||
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none" >
|
type="url"
|
||||||
|
placeholder="可选,展示头像"
|
||||||
|
class="px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary focus:outline-none"
|
||||||
|
/>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold sm:col-span-2">
|
<label class="flex flex-col gap-1 text-sm text-text-primary font-semibold sm:col-span-2">
|
||||||
想说的话
|
想说的话
|
||||||
<div class="flex items-center gap-2">
|
<div class="flex items-center gap-2">
|
||||||
<textarea
|
<textarea
|
||||||
v-model="form.message" placeholder="可选,最多50字" maxlength="50"
|
v-model="form.message"
|
||||||
class="flex-1 px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary h-24 resize-none"/>
|
placeholder="可选,最多50字"
|
||||||
|
maxlength="50"
|
||||||
|
class="flex-1 px-2.5 py-2 rounded-xl border border-white/20 bg-white/8 text-text-primary h-24 resize-none"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div class="sm:col-span-2 flex items-center justify-center gap-3 mt-2">
|
<div class="sm:col-span-2 flex items-center justify-center gap-3 mt-2">
|
||||||
<button
|
<button
|
||||||
type="button" class="px-3 py-2 rounded-2xl border border-white/10 bg-white/6"
|
type="button"
|
||||||
@click="showFormModal = false">
|
class="px-3 py-2 rounded-2xl border border-white/10 bg-white/6"
|
||||||
|
@click="showFormModal = false"
|
||||||
|
>
|
||||||
取消
|
取消
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit" :disabled="loading"
|
type="submit"
|
||||||
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary disabled:opacity-50">
|
:disabled="loading"
|
||||||
|
class="px-3 py-2 rounded-2xl border border-primary/50 bg-primary/12 text-text-primary disabled:opacity-50"
|
||||||
|
>
|
||||||
{{ loading ? "提交中..." : "提交" }}
|
{{ loading ? "提交中..." : "提交" }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -197,7 +236,10 @@ const resolveUrl = (p) => {
|
|||||||
if (!p) return "";
|
if (!p) return "";
|
||||||
const s = String(p).trim();
|
const s = String(p).trim();
|
||||||
if (/^https?:\/\//i.test(s) || /^\/\//.test(s)) return s;
|
if (/^https?:\/\//i.test(s) || /^\/\//.test(s)) return s;
|
||||||
const base = (siteConfig.siteMeta && siteConfig.siteMeta.url) ? String(siteConfig.siteMeta.url).replace(/\/$/, "") : "";
|
const base =
|
||||||
|
siteConfig.siteMeta && siteConfig.siteMeta.url
|
||||||
|
? String(siteConfig.siteMeta.url).replace(/\/$/, "")
|
||||||
|
: "";
|
||||||
if (!base) return s;
|
if (!base) return s;
|
||||||
if (s.startsWith("/")) return base + s;
|
if (s.startsWith("/")) return base + s;
|
||||||
return base + "/" + s;
|
return base + "/" + s;
|
||||||
|
|||||||
@@ -29,7 +29,10 @@
|
|||||||
<span class="text-text-muted text-sm">{{ lang.percent }}%</span>
|
<span class="text-text-muted text-sm">{{ lang.percent }}%</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="h-2 rounded-full bg-white/5 overflow-hidden">
|
<div class="h-2 rounded-full bg-white/5 overflow-hidden">
|
||||||
<span class="block h-full rounded-full transition-all duration-300" :style="barStyle(lang)" />
|
<span
|
||||||
|
class="block h-full rounded-full transition-all duration-300"
|
||||||
|
:style="barStyle(lang)"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
@@ -51,7 +54,9 @@ const github = props.github;
|
|||||||
|
|
||||||
const palette = ["#7cc1ff", "#6bdba6", "#ffd166", "#f497da", "#9b8cfc", "#5ce1e6", "#ffa3a3"];
|
const palette = ["#7cc1ff", "#6bdba6", "#ffd166", "#f497da", "#9b8cfc", "#5ce1e6", "#ffa3a3"];
|
||||||
|
|
||||||
const topLanguages = computed(() => (Array.isArray(github.languages) ? github.languages.slice(0, 5) : []));
|
const topLanguages = computed(() =>
|
||||||
|
Array.isArray(github.languages) ? github.languages.slice(0, 5) : []
|
||||||
|
);
|
||||||
|
|
||||||
const colorFor = (name) => {
|
const colorFor = (name) => {
|
||||||
const idx = github.languages.findIndex((l) => l.name === name);
|
const idx = github.languages.findIndex((l) => l.name === name);
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
<template>
|
<template>
|
||||||
<div
|
<div
|
||||||
v-if="music.enable && (music.playlistId || music.songId)" class="netease-mini-player"
|
v-if="music.enable && (music.playlistId || music.songId)"
|
||||||
|
class="netease-mini-player"
|
||||||
:data-playlist-id="music.mode === 'floating' ? music.playlistId : undefined"
|
:data-playlist-id="music.mode === 'floating' ? music.playlistId : undefined"
|
||||||
:data-song-id="music.mode === 'embed' ? music.songId : undefined" :data-embed="music.mode === 'embed'"
|
:data-song-id="music.mode === 'embed' ? music.songId : undefined"
|
||||||
:data-position="music.position" :data-lyric="music.lyric" :data-theme="music.theme"
|
:data-embed="music.mode === 'embed'"
|
||||||
:data-autoplay="music.autoplay" :data-default-minimized="music.defaultMinimized"
|
:data-position="music.position"
|
||||||
:data-auto-pause="music.autoPause" :data-api-urls="JSON.stringify(music.apiUrls)"/>
|
:data-lyric="music.lyric"
|
||||||
|
:data-theme="music.theme"
|
||||||
|
:data-autoplay="music.autoplay"
|
||||||
|
:data-default-minimized="music.defaultMinimized"
|
||||||
|
:data-auto-pause="music.autoPause"
|
||||||
|
:data-api-urls="JSON.stringify(music.apiUrls)"
|
||||||
|
/>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
|||||||
@@ -1,23 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="my-4 mx-auto max-w-3xl w-full px-4 py-3 grid grid-cols-[auto_1fr_auto] gap-3 items-center">
|
<div
|
||||||
|
class="my-4 mx-auto max-w-3xl w-full px-4 py-3 grid grid-cols-[auto_1fr_auto] gap-3 items-center"
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
:disabled="currentIndex <= 0" class="bg-white/10 text-text-primary border border-white/15 rounded-2xl px-3 py-2 cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/40 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
:disabled="currentIndex <= 0"
|
||||||
@click="goPrev">
|
class="bg-white/10 text-text-primary border border-white/15 rounded-2xl px-3 py-2 cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/40 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
@click="goPrev"
|
||||||
|
>
|
||||||
上一页
|
上一页
|
||||||
</button>
|
</button>
|
||||||
<div class="flex gap-2 flex-wrap justify-center">
|
<div class="flex gap-2 flex-wrap justify-center">
|
||||||
<button
|
<button
|
||||||
v-for="item in pages" :key="item.name" :class="{
|
v-for="item in pages"
|
||||||
|
:key="item.name"
|
||||||
|
:class="{
|
||||||
'bg-primary/30 border-primary/60 text-primary shadow-lg shadow-primary/25':
|
'bg-primary/30 border-primary/60 text-primary shadow-lg shadow-primary/25':
|
||||||
item.name === route.name,
|
item.name === route.name,
|
||||||
}" class="px-2.5 py-2 bg-white/10 text-text-primary border border-white/15 rounded-2xl cursor-pointer transition-all duration-200 hover:bg-white/15 hover:border-primary/40"
|
}"
|
||||||
@click="router.push({ name: item.name })">
|
class="px-2.5 py-2 bg-white/10 text-text-primary border border-white/15 rounded-2xl cursor-pointer transition-all duration-200 hover:bg-white/15 hover:border-primary/40"
|
||||||
|
@click="router.push({ name: item.name })"
|
||||||
|
>
|
||||||
{{ item.label }}
|
{{ item.label }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<button
|
<button
|
||||||
:disabled="currentIndex >= pages.length - 1" class="bg-white/10 text-text-primary border border-white/15 rounded-2xl px-3 py-2 cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/40 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
:disabled="currentIndex >= pages.length - 1"
|
||||||
@click="goNext">
|
class="bg-white/10 text-text-primary border border-white/15 rounded-2xl px-3 py-2 cursor-pointer transition-all duration-200 hover:bg-primary/20 hover:border-primary/40 hover:text-primary disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
@click="goNext"
|
||||||
|
>
|
||||||
下一页
|
下一页
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -6,18 +6,26 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
<article
|
<article
|
||||||
v-for="group in skills" :key="group.title"
|
v-for="group in skills"
|
||||||
class="bg-gradient-to-br from-white/5 to-white/2 border border-white/10 rounded-2xl p-3.5 shadow-md-dark transition-all duration-300 hover:-translate-y-1 hover:border-primary/50 hover:shadow-lg-dark hover:bg-gradient-to-br hover:from-primary/8">
|
:key="group.title"
|
||||||
|
class="bg-gradient-to-br from-white/5 to-white/2 border border-white/10 rounded-2xl p-3.5 shadow-md-dark transition-all duration-300 hover:-translate-y-1 hover:border-primary/50 hover:shadow-lg-dark hover:bg-gradient-to-br hover:from-primary/8"
|
||||||
|
>
|
||||||
<header class="mb-3">
|
<header class="mb-3">
|
||||||
<h3 class="text-base font-semibold m-0">{{ group.title }}</h3>
|
<h3 class="text-base font-semibold m-0">{{ group.title }}</h3>
|
||||||
</header>
|
</header>
|
||||||
<div class="flex flex-wrap gap-2">
|
<div class="flex flex-wrap gap-2">
|
||||||
<span
|
<span
|
||||||
v-for="item in group.items" :key="item"
|
v-for="item in group.items"
|
||||||
class="inline-flex items-center p-1.5 rounded-2xl bg-primary/14 border border-primary/18 transition-all duration-200 hover:bg-primary/24 hover:border-primary/40 hover:scale-110">
|
:key="item"
|
||||||
|
class="inline-flex items-center p-1.5 rounded-2xl bg-primary/14 border border-primary/18 transition-all duration-200 hover:bg-primary/24 hover:border-primary/40 hover:scale-110"
|
||||||
|
>
|
||||||
<NuxtImg
|
<NuxtImg
|
||||||
:src="iconSrc(item)" :alt="item" :title="item" loading="lazy"
|
:src="iconSrc(item)"
|
||||||
class="w-7 h-7 rounded-2xl" />
|
:alt="item"
|
||||||
|
:title="item"
|
||||||
|
loading="lazy"
|
||||||
|
class="w-7 h-7 rounded-2xl"
|
||||||
|
/>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|||||||
@@ -69,7 +69,8 @@ onMounted(() => {
|
|||||||
const link = document.createElement("link");
|
const link = document.createElement("link");
|
||||||
link.id = id;
|
link.id = id;
|
||||||
link.rel = "stylesheet";
|
link.rel = "stylesheet";
|
||||||
link.href = "https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css?font-display=swap";
|
link.href =
|
||||||
|
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css?font-display=swap";
|
||||||
link.crossOrigin = "anonymous";
|
link.crossOrigin = "anonymous";
|
||||||
link.referrerPolicy = "no-referrer";
|
link.referrerPolicy = "no-referrer";
|
||||||
document.head.appendChild(link);
|
document.head.appendChild(link);
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h2 class="m-0 mb-1 font-semibold">开发统计</h2>
|
<h2 class="m-0 mb-1 font-semibold">开发统计</h2>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<button class="tab-button" :class="{ active: activeTab === 'github' }" @click="activeTab = 'github'">
|
<button
|
||||||
|
class="tab-button"
|
||||||
|
:class="{ active: activeTab === 'github' }"
|
||||||
|
@click="activeTab = 'github'"
|
||||||
|
>
|
||||||
GitHub
|
GitHub
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -53,24 +57,33 @@
|
|||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{
|
<span class="stat-value">{{
|
||||||
currentWakatimeData?.total_seconds ? formatTime(currentWakatimeData.total_seconds) : "N/A"
|
currentWakatimeData?.total_seconds
|
||||||
|
? formatTime(currentWakatimeData.total_seconds)
|
||||||
|
: "N/A"
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="stat-label">总时间</span>
|
<span class="stat-label">总时间</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{
|
<span class="stat-value">{{
|
||||||
currentWakatimeData?.daily_average ? formatTime(currentWakatimeData.daily_average) : "N/A"
|
currentWakatimeData?.daily_average
|
||||||
|
? formatTime(currentWakatimeData.daily_average)
|
||||||
|
: "N/A"
|
||||||
}}</span>
|
}}</span>
|
||||||
<span class="stat-label">日均</span>
|
<span class="stat-label">日均</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{ currentWakatimeData?.days_including_holidays ?? "N/A" }}</span>
|
<span class="stat-value">{{
|
||||||
|
currentWakatimeData?.days_including_holidays ?? "N/A"
|
||||||
|
}}</span>
|
||||||
<span class="stat-label">活跃天数</span>
|
<span class="stat-label">活跃天数</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="currentWakatimeData?.languages && currentWakatimeData.languages.length" class="lang-wrap">
|
<div
|
||||||
|
v-if="currentWakatimeData?.languages && currentWakatimeData.languages.length"
|
||||||
|
class="lang-wrap"
|
||||||
|
>
|
||||||
<h3>编程语言</h3>
|
<h3>编程语言</h3>
|
||||||
<p class="muted">语言使用统计 · Languages</p>
|
<p class="muted">语言使用统计 · Languages</p>
|
||||||
<div class="lang-chart">
|
<div class="lang-chart">
|
||||||
@@ -146,9 +159,19 @@ const statusData = ref(null);
|
|||||||
const showComponent = ref(true);
|
const showComponent = ref(true);
|
||||||
|
|
||||||
const githubPalette = ["#7cc1ff", "#6bdba6", "#ffd166", "#f497da", "#9b8cfc", "#5ce1e6", "#ffa3a3"];
|
const githubPalette = ["#7cc1ff", "#6bdba6", "#ffd166", "#f497da", "#9b8cfc", "#5ce1e6", "#ffa3a3"];
|
||||||
const wakatimePalette = ["#7cc1ff", "#6bdba6", "#ffd166", "#f497da", "#9b8cfc", "#5ce1e6", "#ffa3a3"];
|
const wakatimePalette = [
|
||||||
|
"#7cc1ff",
|
||||||
|
"#6bdba6",
|
||||||
|
"#ffd166",
|
||||||
|
"#f497da",
|
||||||
|
"#9b8cfc",
|
||||||
|
"#5ce1e6",
|
||||||
|
"#ffa3a3",
|
||||||
|
];
|
||||||
|
|
||||||
const githubLanguages = computed(() => (Array.isArray(github.languages) ? github.languages.slice(0, 5) : []));
|
const githubLanguages = computed(() =>
|
||||||
|
Array.isArray(github.languages) ? github.languages.slice(0, 5) : []
|
||||||
|
);
|
||||||
|
|
||||||
const currentWakatimeData = computed(() => {
|
const currentWakatimeData = computed(() => {
|
||||||
return wakatimeActiveTab.value === "weekly" ? weeklyData.value : allTimeData.value;
|
return wakatimeActiveTab.value === "weekly" ? weeklyData.value : allTimeData.value;
|
||||||
@@ -161,7 +184,8 @@ const wakatimeLanguages = computed(() => {
|
|||||||
|
|
||||||
const colorFor = (name, type) => {
|
const colorFor = (name, type) => {
|
||||||
const palette = type === "github" ? githubPalette : wakatimePalette;
|
const palette = type === "github" ? githubPalette : wakatimePalette;
|
||||||
const languages = type === "github" ? github.languages : currentWakatimeData.value?.languages || [];
|
const languages =
|
||||||
|
type === "github" ? github.languages : currentWakatimeData.value?.languages || [];
|
||||||
const idx = languages.findIndex((l) => l.name === name);
|
const idx = languages.findIndex((l) => l.name === name);
|
||||||
return palette[(idx >= 0 ? idx : 0) % palette.length];
|
return palette[(idx >= 0 ? idx : 0) % palette.length];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,11 @@
|
|||||||
<div class="header">
|
<div class="header">
|
||||||
<h2>Wakatime</h2>
|
<h2>Wakatime</h2>
|
||||||
<div class="tabs">
|
<div class="tabs">
|
||||||
<button class="tab-button" :class="{ active: activeTab === 'weekly' }" @click="activeTab = 'weekly'">
|
<button
|
||||||
|
class="tab-button"
|
||||||
|
:class="{ active: activeTab === 'weekly' }"
|
||||||
|
@click="activeTab = 'weekly'"
|
||||||
|
>
|
||||||
最近7天
|
最近7天
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
@@ -19,7 +23,9 @@
|
|||||||
|
|
||||||
<div class="stats-wrap">
|
<div class="stats-wrap">
|
||||||
<h3>编码统计</h3>
|
<h3>编码统计</h3>
|
||||||
<p class="muted">{{ activeTab === "weekly" ? "最近7天 · Last 7 Days" : "所有时间 · All Time" }}</p>
|
<p class="muted">
|
||||||
|
{{ activeTab === "weekly" ? "最近7天 · Last 7 Days" : "所有时间 · All Time" }}
|
||||||
|
</p>
|
||||||
<div class="stats-grid">
|
<div class="stats-grid">
|
||||||
<div class="stat-item">
|
<div class="stat-item">
|
||||||
<span class="stat-value">{{
|
<span class="stat-value">{{
|
||||||
|
|||||||
@@ -87,7 +87,10 @@ const siteConfig = {
|
|||||||
|
|
||||||
skills: [
|
skills: [
|
||||||
{ title: "前端", items: ["css", "html", "javascript", "typescript", "vue"] },
|
{ title: "前端", items: ["css", "html", "javascript", "typescript", "vue"] },
|
||||||
{ title: "后端 / 云", items: ["cpp", "cloudflare", "docker", "java", "mysql", "nodejs", "python", "vercel"] },
|
{
|
||||||
|
title: "后端 / 云",
|
||||||
|
items: ["cpp", "cloudflare", "docker", "java", "mysql", "nodejs", "python", "vercel"],
|
||||||
|
},
|
||||||
{ title: "工具", items: ["ae", "au", "git", "github", "md", "ps", "pr", "vscode"] },
|
{ title: "工具", items: ["ae", "au", "git", "github", "md", "ps", "pr", "vscode"] },
|
||||||
{ title: "操作系统", items: ["arch", "linux", "windows"] },
|
{ title: "操作系统", items: ["arch", "linux", "windows"] },
|
||||||
],
|
],
|
||||||
@@ -117,8 +120,16 @@ const siteConfig = {
|
|||||||
|
|
||||||
projects: [
|
projects: [
|
||||||
{ name: "Cloud Home", url: "https://github.com/RhenCloud/cloud-home", desc: "个人主页模板" },
|
{ name: "Cloud Home", url: "https://github.com/RhenCloud/cloud-home", desc: "个人主页模板" },
|
||||||
{ name: "ILP", url: "https://github.com/RhenCloud/ILP", desc: "跨平台、多网站、模块化的小说下载器" },
|
{
|
||||||
{ name: "ILP-C++", url: "https://github.com/RhenCloud/ILP-Cpp", desc: "跨平台、多网站、模块化的小说下载器" },
|
name: "ILP",
|
||||||
|
url: "https://github.com/RhenCloud/ILP",
|
||||||
|
desc: "跨平台、多网站、模块化的小说下载器",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ILP-C++",
|
||||||
|
url: "https://github.com/RhenCloud/ILP-Cpp",
|
||||||
|
desc: "跨平台、多网站、模块化的小说下载器",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
|
|
||||||
friends: [
|
friends: [
|
||||||
|
|||||||
@@ -56,9 +56,12 @@ async function fetchGithubMeta() {
|
|||||||
if (githubToken) {
|
if (githubToken) {
|
||||||
headers.Authorization = `Bearer ${githubToken}`;
|
headers.Authorization = `Bearer ${githubToken}`;
|
||||||
}
|
}
|
||||||
const resp = await fetch(`https://api.github.com/users/${github.username}/repos?per_page=100&sort=updated`, {
|
const resp = await fetch(
|
||||||
|
`https://api.github.com/users/${github.username}/repos?per_page=100&sort=updated`,
|
||||||
|
{
|
||||||
headers,
|
headers,
|
||||||
});
|
}
|
||||||
|
);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!Array.isArray(data)) return;
|
if (!Array.isArray(data)) return;
|
||||||
type GithubRepo = { language?: string };
|
type GithubRepo = { language?: string };
|
||||||
|
|||||||
@@ -50,7 +50,9 @@ export default defineNuxtPlugin(() => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const existing = document.querySelector(`script[src="${scriptSrc}"]`) as HTMLScriptElement | null;
|
const existing = document.querySelector(
|
||||||
|
`script[src="${scriptSrc}"]`
|
||||||
|
) as HTMLScriptElement | null;
|
||||||
if (existing) {
|
if (existing) {
|
||||||
// 脚本已存在但未加载,等待它加载
|
// 脚本已存在但未加载,等待它加载
|
||||||
existing.addEventListener("load", () => resolve(), { once: true });
|
existing.addEventListener("load", () => resolve(), { once: true });
|
||||||
|
|||||||
@@ -3,7 +3,8 @@
|
|||||||
/* Font Awesome 字体优化 */
|
/* Font Awesome 字体优化 */
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Font Awesome 6 Solid";
|
font-family: "Font Awesome 6 Solid";
|
||||||
src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-solid-900.woff2") format("woff2");
|
src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-solid-900.woff2")
|
||||||
|
format("woff2");
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -11,7 +12,8 @@
|
|||||||
|
|
||||||
@font-face {
|
@font-face {
|
||||||
font-family: "Font Awesome 6 Brands";
|
font-family: "Font Awesome 6 Brands";
|
||||||
src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-brands-400.woff2") format("woff2");
|
src: url("https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/webfonts/fa-brands-400.woff2")
|
||||||
|
format("woff2");
|
||||||
font-display: swap;
|
font-display: swap;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-style: normal;
|
font-style: normal;
|
||||||
@@ -123,7 +125,11 @@
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.28);
|
||||||
transition: transform 0.18s ease, box-shadow 0.18s ease, background 0.18s ease, border-color 0.18s ease;
|
transition:
|
||||||
|
transform 0.18s ease,
|
||||||
|
box-shadow 0.18s ease,
|
||||||
|
background 0.18s ease,
|
||||||
|
border-color 0.18s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.background-toggle:hover,
|
.background-toggle:hover,
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width,initial-scale=1.0" />
|
||||||
@@ -8,7 +7,12 @@
|
|||||||
<style>
|
<style>
|
||||||
:root {
|
:root {
|
||||||
color-scheme: dark;
|
color-scheme: dark;
|
||||||
font-family: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;
|
font-family:
|
||||||
|
"Inter",
|
||||||
|
"Segoe UI",
|
||||||
|
system-ui,
|
||||||
|
-apple-system,
|
||||||
|
sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@@ -20,9 +24,9 @@
|
|||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
display: grid;
|
display: grid;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
background: radial-gradient(circle at 20% 20%, rgba(244, 151, 218, 0.14), transparent 32%),
|
background:
|
||||||
radial-gradient(circle at 80% 10%, rgba(124, 193, 255, 0.16), transparent 32%),
|
radial-gradient(circle at 20% 20%, rgba(244, 151, 218, 0.14), transparent 32%),
|
||||||
#0f1116;
|
radial-gradient(circle at 80% 10%, rgba(124, 193, 255, 0.16), transparent 32%), #0f1116;
|
||||||
color: #e8eefc;
|
color: #e8eefc;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -58,7 +62,10 @@
|
|||||||
color: #e8eefc;
|
color: #e8eefc;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
transition: transform 0.15s ease, box-shadow 0.15s ease, border-color 0.15s ease;
|
transition:
|
||||||
|
transform 0.15s ease,
|
||||||
|
box-shadow 0.15s ease,
|
||||||
|
border-color 0.15s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
a.btn:hover {
|
a.btn:hover {
|
||||||
@@ -76,5 +83,4 @@
|
|||||||
<a class="btn" href="/">返回首页</a>
|
<a class="btn" href="/">返回首页</a>
|
||||||
</main>
|
</main>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -38,7 +38,8 @@
|
|||||||
--shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);
|
--shadow-outset: 6px 6px 12px var(--shadow-dark), -6px -6px 12px var(--shadow-light);
|
||||||
--border-radius: 12px;
|
--border-radius: 12px;
|
||||||
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
--transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
--theme-transition: background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
|
--theme-transition:
|
||||||
|
background-color 0.3s ease, color 0.3s ease, box-shadow 0.3s ease, border-color 0.3s ease;
|
||||||
|
|
||||||
--flow-color-1: rgba(255, 107, 53, 0.32);
|
--flow-color-1: rgba(255, 107, 53, 0.32);
|
||||||
--flow-color-2: rgba(86, 151, 227, 0.26);
|
--flow-color-2: rgba(86, 151, 227, 0.26);
|
||||||
@@ -158,10 +159,15 @@
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: 8px;
|
gap: 8px;
|
||||||
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1), height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
transition:
|
||||||
border-radius 0.5s cubic-bezier(0.4, 0, 0.2, 1), padding 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
border-radius 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
padding 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
var(--theme-transition);
|
var(--theme-transition);
|
||||||
background: 0.5s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
background:
|
||||||
|
0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player::before {
|
.netease-mini-player::before {
|
||||||
@@ -170,14 +176,25 @@
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: radial-gradient(circle at 15% 20%, var(--flow-color-1) 0%, transparent 60%),
|
background:
|
||||||
|
radial-gradient(circle at 15% 20%, var(--flow-color-1) 0%, transparent 60%),
|
||||||
radial-gradient(circle at 80% 25%, var(--flow-color-2) 0%, transparent 60%),
|
radial-gradient(circle at 80% 25%, var(--flow-color-2) 0%, transparent 60%),
|
||||||
radial-gradient(circle at 30% 85%, var(--flow-color-3) 0%, transparent 60%),
|
radial-gradient(circle at 30% 85%, var(--flow-color-3) 0%, transparent 60%),
|
||||||
radial-gradient(circle at 10% 75%, var(--flow-color-4) 0%, transparent 55%),
|
radial-gradient(circle at 10% 75%, var(--flow-color-4) 0%, transparent 55%),
|
||||||
radial-gradient(circle at 85% 80%, var(--flow-color-5) 0%, transparent 55%);
|
radial-gradient(circle at 85% 80%, var(--flow-color-5) 0%, transparent 55%);
|
||||||
opacity: var(--flow-opacity);
|
opacity: var(--flow-opacity);
|
||||||
background-size: 220% 220%, 220% 220%, 220% 220%, 220% 220%, 220% 220%;
|
background-size:
|
||||||
background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%;
|
||||||
|
background-position:
|
||||||
|
0% 0%,
|
||||||
|
100% 0%,
|
||||||
|
0% 100%,
|
||||||
|
20% 80%,
|
||||||
|
80% 20%;
|
||||||
filter: saturate(1.02) brightness(1.01);
|
filter: saturate(1.02) brightness(1.01);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
animation: flow-breathe var(--flow-speed) ease-in-out infinite;
|
animation: flow-breathe var(--flow-speed) ease-in-out infinite;
|
||||||
@@ -190,17 +207,32 @@
|
|||||||
|
|
||||||
@keyframes flow-breathe {
|
@keyframes flow-breathe {
|
||||||
0% {
|
0% {
|
||||||
background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
|
background-position:
|
||||||
|
0% 0%,
|
||||||
|
100% 0%,
|
||||||
|
0% 100%,
|
||||||
|
20% 80%,
|
||||||
|
80% 20%;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
filter: saturate(1.02) brightness(1.01);
|
filter: saturate(1.02) brightness(1.01);
|
||||||
}
|
}
|
||||||
50% {
|
50% {
|
||||||
background-position: 100% 50%, 0% 100%, 50% 0%, 35% 65%, 65% 35%;
|
background-position:
|
||||||
|
100% 50%,
|
||||||
|
0% 100%,
|
||||||
|
50% 0%,
|
||||||
|
35% 65%,
|
||||||
|
65% 35%;
|
||||||
transform: scale(1.03);
|
transform: scale(1.03);
|
||||||
filter: saturate(1.15) brightness(1.06);
|
filter: saturate(1.15) brightness(1.06);
|
||||||
}
|
}
|
||||||
100% {
|
100% {
|
||||||
background-position: 0% 100%, 100% 50%, 100% 0%, 20% 80%, 80% 20%;
|
background-position:
|
||||||
|
0% 100%,
|
||||||
|
100% 50%,
|
||||||
|
100% 0%,
|
||||||
|
20% 80%,
|
||||||
|
80% 20%;
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
filter: saturate(1.02) brightness(1.01);
|
filter: saturate(1.02) brightness(1.01);
|
||||||
}
|
}
|
||||||
@@ -211,10 +243,13 @@
|
|||||||
height: 80px;
|
height: 80px;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: radial-gradient(120px 120px at 30% 30%, rgba(255, 138, 80, 0.25), transparent 60%),
|
background:
|
||||||
|
radial-gradient(120px 120px at 30% 30%, rgba(255, 138, 80, 0.25), transparent 60%),
|
||||||
radial-gradient(120px 120px at 70% 70%, rgba(0, 210, 255, 0.22), transparent 60%),
|
radial-gradient(120px 120px at 70% 70%, rgba(0, 210, 255, 0.22), transparent 60%),
|
||||||
linear-gradient(135deg, #151515, #1e1e1e);
|
linear-gradient(135deg, #151515, #1e1e1e);
|
||||||
box-shadow: 4px 4px 12px rgba(0, 0, 0, 0.4), -2px -2px 8px rgba(255, 255, 255, 0.1),
|
box-shadow:
|
||||||
|
4px 4px 12px rgba(0, 0, 0, 0.4),
|
||||||
|
-2px -2px 8px rgba(255, 255, 255, 0.1),
|
||||||
inset 0 0 20px rgba(0, 0, 0, 0.3);
|
inset 0 0 20px rgba(0, 0, 0, 0.3);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,13 +258,17 @@
|
|||||||
.netease-mini-player.minimized .playlist-container {
|
.netease-mini-player.minimized .playlist-container {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
transition:
|
||||||
|
opacity 0.3s ease,
|
||||||
|
visibility 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized .player-bottom {
|
.netease-mini-player.minimized .player-bottom {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
transition: opacity 0.3s ease, visibility 0.3s ease;
|
transition:
|
||||||
|
opacity 0.3s ease,
|
||||||
|
visibility 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized .minimize-btn {
|
.netease-mini-player.minimized .minimize-btn {
|
||||||
@@ -261,7 +300,9 @@
|
|||||||
.netease-mini-player .player-bottom {
|
.netease-mini-player .player-bottom {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
transition: opacity 0.3s ease 0.2s, visibility 0.3s ease 0.2s;
|
transition:
|
||||||
|
opacity 0.3s ease 0.2s,
|
||||||
|
visibility 0.3s ease 0.2s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized .album-cover-container {
|
.netease-mini-player.minimized .album-cover-container {
|
||||||
@@ -272,8 +313,11 @@
|
|||||||
left: 0;
|
left: 0;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
box-shadow: none;
|
box-shadow: none;
|
||||||
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1), height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
transition:
|
||||||
top 0.5s cubic-bezier(0.4, 0, 0.2, 1), left 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
top 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
left 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -282,7 +326,9 @@
|
|||||||
height: 100%;
|
height: 100%;
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
filter: brightness(0.8) contrast(1.1);
|
filter: brightness(0.8) contrast(1.1);
|
||||||
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1), height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
transition:
|
||||||
|
width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
filter 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -306,18 +352,27 @@
|
|||||||
rgba(0, 0, 0, 0.15) 46%,
|
rgba(0, 0, 0, 0.15) 46%,
|
||||||
transparent 46%
|
transparent 46%
|
||||||
);
|
);
|
||||||
box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.5), inset 0 0 5px rgba(0, 0, 0, 0.8);
|
box-shadow:
|
||||||
transition: background 0.5s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
inset 0 0 15px rgba(0, 0, 0, 0.5),
|
||||||
|
inset 0 0 5px rgba(0, 0, 0, 0.8);
|
||||||
|
transition:
|
||||||
|
background 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized .vinyl-center {
|
.netease-mini-player.minimized .vinyl-center {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
background: radial-gradient(circle at 30% 30%, #666 0%, #333 50%, #111 100%);
|
background: radial-gradient(circle at 30% 30%, #666 0%, #333 50%, #111 100%);
|
||||||
box-shadow: 0 0 3px rgba(0, 0, 0, 0.8), inset 1px 1px 2px rgba(255, 255, 255, 0.2);
|
box-shadow:
|
||||||
|
0 0 3px rgba(0, 0, 0, 0.8),
|
||||||
|
inset 1px 1px 2px rgba(255, 255, 255, 0.2);
|
||||||
border: 1px solid #000;
|
border: 1px solid #000;
|
||||||
transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1), height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
transition:
|
||||||
background 0.5s cubic-bezier(0.4, 0, 0.2, 1), box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
height 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
background 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
|
box-shadow 0.5s cubic-bezier(0.4, 0, 0.2, 1),
|
||||||
border 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
border 0.5s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -367,8 +422,11 @@
|
|||||||
position: relative;
|
position: relative;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
background: var(--secondary-bg);
|
background: var(--secondary-bg);
|
||||||
box-shadow: inset 2px 2px 4px var(--shadow-dark), inset -2px -2px 4px var(--shadow-light),
|
box-shadow:
|
||||||
2px 2px 6px rgba(0, 0, 0, 0.2), -1px -1px 3px rgba(255, 255, 255, 0.1);
|
inset 2px 2px 4px var(--shadow-dark),
|
||||||
|
inset -2px -2px 4px var(--shadow-light),
|
||||||
|
2px 2px 6px rgba(0, 0, 0, 0.2),
|
||||||
|
-1px -1px 3px rgba(255, 255, 255, 0.1);
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
border: 2px solid rgba(0, 0, 0, 0.1);
|
border: 2px solid rgba(0, 0, 0, 0.1);
|
||||||
@@ -437,7 +495,9 @@
|
|||||||
rgba(0, 0, 0, 0.2) 100%
|
rgba(0, 0, 0, 0.2) 100%
|
||||||
);
|
);
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
box-shadow: inset 0 0 10px rgba(0, 0, 0, 0.3), inset 0 0 20px rgba(0, 0, 0, 0.1);
|
box-shadow:
|
||||||
|
inset 0 0 10px rgba(0, 0, 0, 0.3),
|
||||||
|
inset 0 0 20px rgba(0, 0, 0, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.vinyl-center {
|
.vinyl-center {
|
||||||
@@ -449,7 +509,10 @@
|
|||||||
background: radial-gradient(circle at center, #1a1a1a 0%, #333 50%, #1a1a1a 100%);
|
background: radial-gradient(circle at center, #1a1a1a 0%, #333 50%, #1a1a1a 100%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transform: translate(-50%, -50%);
|
transform: translate(-50%, -50%);
|
||||||
box-shadow: 0 0 4px rgba(0, 0, 0, 0.5), inset 0 0 3px rgba(0, 0, 0, 0.8), inset 0 0 1px rgba(255, 255, 255, 0.1);
|
box-shadow:
|
||||||
|
0 0 4px rgba(0, 0, 0, 0.5),
|
||||||
|
inset 0 0 3px rgba(0, 0, 0, 0.8),
|
||||||
|
inset 0 0 1px rgba(255, 255, 255, 0.1);
|
||||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -536,7 +599,9 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
|
transition:
|
||||||
|
opacity 0.2s ease-in-out,
|
||||||
|
transform 0.2s ease-in-out;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lyric-line.current {
|
.lyric-line.current {
|
||||||
@@ -602,8 +667,18 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.lyric-line.word-lyric-active {
|
.lyric-line.word-lyric-active {
|
||||||
-webkit-mask: linear-gradient(90deg, #000 0%, #000 var(--line-progress, 0%), transparent var(--line-progress, 0%));
|
-webkit-mask: linear-gradient(
|
||||||
mask: linear-gradient(90deg, #000 0%, #000 var(--line-progress, 0%), transparent var(--line-progress, 0%));
|
90deg,
|
||||||
|
#000 0%,
|
||||||
|
#000 var(--line-progress, 0%),
|
||||||
|
transparent var(--line-progress, 0%)
|
||||||
|
);
|
||||||
|
mask: linear-gradient(
|
||||||
|
90deg,
|
||||||
|
#000 0%,
|
||||||
|
#000 var(--line-progress, 0%),
|
||||||
|
transparent var(--line-progress, 0%)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player[data-theme="dark"] .lyric-line.word-lyric-active {
|
.netease-mini-player[data-theme="dark"] .lyric-line.word-lyric-active {
|
||||||
@@ -640,14 +715,18 @@
|
|||||||
justify-content: center;
|
justify-content: center;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
transition: var(--transition);
|
transition: var(--transition);
|
||||||
box-shadow: 2px 2px 4px var(--shadow-dark), -2px -2px 4px var(--shadow-light);
|
box-shadow:
|
||||||
|
2px 2px 4px var(--shadow-dark),
|
||||||
|
-2px -2px 4px var(--shadow-light);
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: var(--text-primary);
|
color: var(--text-primary);
|
||||||
position: relative;
|
position: relative;
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-btn:hover {
|
.control-btn:hover {
|
||||||
box-shadow: inset 1px 1px 2px var(--shadow-dark), inset -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 1px 1px 2px var(--shadow-dark),
|
||||||
|
inset -1px -1px 2px var(--shadow-light);
|
||||||
background: var(--secondary-bg);
|
background: var(--secondary-bg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,7 +735,9 @@
|
|||||||
height: 30px;
|
height: 30px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
background: var(--primary-bg);
|
background: var(--primary-bg);
|
||||||
box-shadow: 2px 2px 4px var(--shadow-dark), -2px -2px 4px var(--shadow-light);
|
box-shadow:
|
||||||
|
2px 2px 4px var(--shadow-dark),
|
||||||
|
-2px -2px 4px var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.play-icon,
|
.play-icon,
|
||||||
@@ -671,11 +752,15 @@
|
|||||||
|
|
||||||
.play-btn:hover {
|
.play-btn:hover {
|
||||||
background: var(--secondary-bg);
|
background: var(--secondary-bg);
|
||||||
box-shadow: inset 1px 1px 2px var(--shadow-dark), inset -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 1px 1px 2px var(--shadow-dark),
|
||||||
|
inset -1px -1px 2px var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.control-btn:active {
|
.control-btn:active {
|
||||||
box-shadow: inset 2px 2px 4px var(--shadow-dark), inset -2px -2px 4px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 2px 2px 4px var(--shadow-dark),
|
||||||
|
inset -2px -2px 4px var(--shadow-light);
|
||||||
background: var(--accent-gradient);
|
background: var(--accent-gradient);
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
@@ -731,7 +816,9 @@
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: inset 1px 1px 2px var(--shadow-dark), inset -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 1px 1px 2px var(--shadow-dark),
|
||||||
|
inset -1px -1px 2px var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar {
|
.progress-bar {
|
||||||
@@ -753,7 +840,10 @@
|
|||||||
background: var(--accent-color-3);
|
background: var(--accent-color-3);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
transform: translateY(-50%);
|
transform: translateY(-50%);
|
||||||
box-shadow: 0 0 8px rgba(0, 210, 255, 0.6), 1px 1px 2px var(--shadow-dark), -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
0 0 8px rgba(0, 210, 255, 0.6),
|
||||||
|
1px 1px 2px var(--shadow-dark),
|
||||||
|
-1px -1px 2px var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.bottom-controls {
|
.bottom-controls {
|
||||||
@@ -785,7 +875,9 @@
|
|||||||
border-radius: 2px;
|
border-radius: 2px;
|
||||||
position: relative;
|
position: relative;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-shadow: inset 1px 1px 2px var(--shadow-dark), inset -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 1px 1px 2px var(--shadow-dark),
|
||||||
|
inset -1px -1px 2px var(--shadow-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
.volume-bar {
|
.volume-bar {
|
||||||
@@ -943,7 +1035,9 @@
|
|||||||
top: 100%;
|
top: 100%;
|
||||||
background: var(--primary-bg);
|
background: var(--primary-bg);
|
||||||
border-radius: var(--border-radius);
|
border-radius: var(--border-radius);
|
||||||
box-shadow: 4px 4px 8px var(--shadow-dark), -4px -4px 8px var(--shadow-light);
|
box-shadow:
|
||||||
|
4px 4px 8px var(--shadow-dark),
|
||||||
|
-4px -4px 8px var(--shadow-light);
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
z-index: 1001;
|
z-index: 1001;
|
||||||
@@ -960,14 +1054,25 @@
|
|||||||
inset: 0;
|
inset: 0;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
background: radial-gradient(circle at 20% 15%, var(--flow-color-1) 0%, transparent 60%),
|
background:
|
||||||
|
radial-gradient(circle at 20% 15%, var(--flow-color-1) 0%, transparent 60%),
|
||||||
radial-gradient(circle at 75% 30%, var(--flow-color-2) 0%, transparent 55%),
|
radial-gradient(circle at 75% 30%, var(--flow-color-2) 0%, transparent 55%),
|
||||||
radial-gradient(circle at 35% 85%, var(--flow-color-3) 0%, transparent 55%),
|
radial-gradient(circle at 35% 85%, var(--flow-color-3) 0%, transparent 55%),
|
||||||
radial-gradient(circle at 25% 70%, var(--flow-color-4) 0%, transparent 50%),
|
radial-gradient(circle at 25% 70%, var(--flow-color-4) 0%, transparent 50%),
|
||||||
radial-gradient(circle at 85% 75%, var(--flow-color-5) 0%, transparent 50%);
|
radial-gradient(circle at 85% 75%, var(--flow-color-5) 0%, transparent 50%);
|
||||||
opacity: var(--playlist-flow-opacity);
|
opacity: var(--playlist-flow-opacity);
|
||||||
background-size: 220% 220%, 220% 220%, 220% 220%, 220% 220%, 220% 220%;
|
background-size:
|
||||||
background-position: 0% 0%, 100% 0%, 0% 100%, 20% 80%, 80% 20%;
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%,
|
||||||
|
220% 220%;
|
||||||
|
background-position:
|
||||||
|
0% 0%,
|
||||||
|
100% 0%,
|
||||||
|
0% 100%,
|
||||||
|
20% 80%,
|
||||||
|
80% 20%;
|
||||||
filter: saturate(1.02) brightness(1.01);
|
filter: saturate(1.02) brightness(1.01);
|
||||||
transform: scale(1);
|
transform: scale(1);
|
||||||
animation: flow-breathe var(--flow-speed) ease-in-out infinite;
|
animation: flow-breathe var(--flow-speed) ease-in-out infinite;
|
||||||
@@ -1021,7 +1126,9 @@
|
|||||||
|
|
||||||
.playlist-item:hover {
|
.playlist-item:hover {
|
||||||
background: rgba(255, 107, 53, 0.08);
|
background: rgba(255, 107, 53, 0.08);
|
||||||
box-shadow: inset 1px 1px 2px var(--shadow-dark), inset -1px -1px 2px var(--shadow-light);
|
box-shadow:
|
||||||
|
inset 1px 1px 2px var(--shadow-dark),
|
||||||
|
inset -1px -1px 2px var(--shadow-light);
|
||||||
transform: translateX(2px);
|
transform: translateX(2px);
|
||||||
transition: all 0.2s ease;
|
transition: all 0.2s ease;
|
||||||
}
|
}
|
||||||
@@ -1193,8 +1300,8 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized.fading-in {
|
.netease-mini-player.minimized.fading-in {
|
||||||
animation: player-fade-in var(--opacity-duration-up, 0.25s) var(--opacity-ease-in, cubic-bezier(0.4, 0, 0.2, 1))
|
animation: player-fade-in var(--opacity-duration-up, 0.25s)
|
||||||
forwards;
|
var(--opacity-ease-in, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes player-fade-out {
|
@keyframes player-fade-out {
|
||||||
@@ -1223,23 +1330,27 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized.fading-out.docked-right {
|
.netease-mini-player.minimized.fading-out.docked-right {
|
||||||
animation: player-fade-out var(--opacity-duration-down, 0.6s)
|
animation:
|
||||||
|
player-fade-out var(--opacity-duration-down, 0.6s)
|
||||||
var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
|
var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
|
||||||
player-dock-right var(--dock-duration, 0.45s) var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1)) forwards;
|
player-dock-right var(--dock-duration, 0.45s)
|
||||||
|
var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1)) forwards;
|
||||||
}
|
}
|
||||||
.netease-mini-player.minimized.fading-out.docked-left {
|
.netease-mini-player.minimized.fading-out.docked-left {
|
||||||
animation: player-fade-out var(--opacity-duration-down, 0.6s)
|
animation:
|
||||||
|
player-fade-out var(--opacity-duration-down, 0.6s)
|
||||||
var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
|
var(--opacity-ease-out, cubic-bezier(0.22, 1, 0.36, 1)) forwards,
|
||||||
player-dock-left var(--dock-duration, 0.45s) var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1)) forwards;
|
player-dock-left var(--dock-duration, 0.45s)
|
||||||
|
var(--dock-ease-out, cubic-bezier(0.18, 0.9, 0.2, 1)) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
.netease-mini-player.minimized.popping-right {
|
.netease-mini-player.minimized.popping-right {
|
||||||
animation: player-popout-right var(--popout-duration, 0.28s) var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1))
|
animation: player-popout-right var(--popout-duration, 0.28s)
|
||||||
forwards;
|
var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
|
||||||
}
|
}
|
||||||
.netease-mini-player.minimized.popping-left {
|
.netease-mini-player.minimized.popping-left {
|
||||||
animation: player-popout-left var(--popout-duration, 0.28s) var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1))
|
animation: player-popout-left var(--popout-duration, 0.28s)
|
||||||
forwards;
|
var(--popout-ease, cubic-bezier(0.4, 0, 0.2, 1)) forwards;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes player-dock-right {
|
@keyframes player-dock-right {
|
||||||
|
|||||||
@@ -318,7 +318,10 @@ class NeteaseMiniPlayer {
|
|||||||
this.elements.minimizeBtn.addEventListener("click", () => this.toggleMinimize());
|
this.elements.minimizeBtn.addEventListener("click", () => this.toggleMinimize());
|
||||||
}
|
}
|
||||||
document.addEventListener("click", (e) => {
|
document.addEventListener("click", (e) => {
|
||||||
if (this.elements.playlistContainer && this.elements.playlistContainer.classList.contains("show")) {
|
if (
|
||||||
|
this.elements.playlistContainer &&
|
||||||
|
this.elements.playlistContainer.classList.contains("show")
|
||||||
|
) {
|
||||||
if (!this.element.contains(e.target)) {
|
if (!this.element.contains(e.target)) {
|
||||||
this.togglePlaylist(false);
|
this.togglePlaylist(false);
|
||||||
}
|
}
|
||||||
@@ -463,14 +466,16 @@ class NeteaseMiniPlayer {
|
|||||||
const isiOS = isiPhone || isiPadUA || isIOSLikePad;
|
const isiOS = isiPhone || isiPadUA || isIOSLikePad;
|
||||||
const isAndroid = /android/.test(ua);
|
const isAndroid = /android/.test(ua);
|
||||||
const isHarmonyOS = /harmonyos/.test(uaRaw) || /huawei|honor/.test(ua);
|
const isHarmonyOS = /harmonyos/.test(uaRaw) || /huawei|honor/.test(ua);
|
||||||
const isMobileToken = /mobile/.test(ua) || /sm-|mi |redmi|huawei|honor|oppo|vivo|oneplus/.test(ua);
|
const isMobileToken =
|
||||||
|
/mobile/.test(ua) || /sm-|mi |redmi|huawei|honor|oppo|vivo|oneplus/.test(ua);
|
||||||
const isHarmonyDesktop = isHarmonyOS && !isMobileToken && !isAndroid && !isiOS;
|
const isHarmonyDesktop = isHarmonyOS && !isMobileToken && !isAndroid && !isiOS;
|
||||||
const isPWA =
|
const isPWA =
|
||||||
(typeof window !== "undefined" &&
|
(typeof window !== "undefined" &&
|
||||||
((window.matchMedia && window.matchMedia("(display-mode: standalone)").matches) ||
|
((window.matchMedia && window.matchMedia("(display-mode: standalone)").matches) ||
|
||||||
nav.standalone === true)) ||
|
nav.standalone === true)) ||
|
||||||
false;
|
false;
|
||||||
const isMobile = isiOS || isAndroid || (isHarmonyOS && !isHarmonyDesktop) || isMobileToken || isInAppWebView;
|
const isMobile =
|
||||||
|
isiOS || isAndroid || (isHarmonyOS && !isHarmonyDesktop) || isMobileToken || isInAppWebView;
|
||||||
const info = {
|
const info = {
|
||||||
isMobile,
|
isMobile,
|
||||||
isiOS,
|
isiOS,
|
||||||
@@ -497,7 +502,10 @@ class NeteaseMiniPlayer {
|
|||||||
if (shouldHideVolume) {
|
if (shouldHideVolume) {
|
||||||
this.elements.volumeContainer.classList.add("sr-visually-hidden");
|
this.elements.volumeContainer.classList.add("sr-visually-hidden");
|
||||||
this.elements.volumeContainer.setAttribute("aria-hidden", "false");
|
this.elements.volumeContainer.setAttribute("aria-hidden", "false");
|
||||||
this.elements.volumeSlider?.setAttribute("aria-label", "音量控制(移动端隐藏,仅无障碍可见)");
|
this.elements.volumeSlider?.setAttribute(
|
||||||
|
"aria-label",
|
||||||
|
"音量控制(移动端隐藏,仅无障碍可见)"
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
this.elements.volumeContainer.classList.remove("sr-visually-hidden");
|
this.elements.volumeContainer.classList.remove("sr-visually-hidden");
|
||||||
this.elements.volumeContainer.removeAttribute("aria-hidden");
|
this.elements.volumeContainer.removeAttribute("aria-hidden");
|
||||||
@@ -765,7 +773,8 @@ class NeteaseMiniPlayer {
|
|||||||
tempElement.style.visibility = "hidden";
|
tempElement.style.visibility = "hidden";
|
||||||
tempElement.style.position = "absolute";
|
tempElement.style.position = "absolute";
|
||||||
tempElement.style.fontSize = "12px";
|
tempElement.style.fontSize = "12px";
|
||||||
tempElement.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
tempElement.style.fontFamily =
|
||||||
|
'-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
|
||||||
tempElement.textContent = artistText;
|
tempElement.textContent = artistText;
|
||||||
document.body.appendChild(tempElement);
|
document.body.appendChild(tempElement);
|
||||||
const fullWidth = tempElement.offsetWidth;
|
const fullWidth = tempElement.offsetWidth;
|
||||||
@@ -830,7 +839,9 @@ class NeteaseMiniPlayer {
|
|||||||
// 如果没有原始 URL,尝试从 API 获取
|
// 如果没有原始 URL,尝试从 API 获取
|
||||||
if (!urlData) {
|
if (!urlData) {
|
||||||
const baseUrls = this.config.apiUrls;
|
const baseUrls = this.config.apiUrls;
|
||||||
const apiUrls = baseUrls.map((baseUrl) => `${baseUrl}?server=netease&type=song&id=${songId}`);
|
const apiUrls = baseUrls.map(
|
||||||
|
(baseUrl) => `${baseUrl}?server=netease&type=song&id=${songId}`
|
||||||
|
);
|
||||||
|
|
||||||
for (const url of apiUrls) {
|
for (const url of apiUrls) {
|
||||||
try {
|
try {
|
||||||
@@ -925,8 +936,10 @@ class NeteaseMiniPlayer {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// 处理 lrc 数据可能是字符串或对象的情况
|
// 处理 lrc 数据可能是字符串或对象的情况
|
||||||
const lrcContent = typeof lyricData.lrc === "string" ? lyricData.lrc : lyricData.lrc?.lyric || "";
|
const lrcContent =
|
||||||
const tlyricContent = typeof lyricData.tlyric === "string" ? lyricData.tlyric : lyricData.tlyric?.lyric || "";
|
typeof lyricData.lrc === "string" ? lyricData.lrc : lyricData.lrc?.lyric || "";
|
||||||
|
const tlyricContent =
|
||||||
|
typeof lyricData.tlyric === "string" ? lyricData.tlyric : lyricData.tlyric?.lyric || "";
|
||||||
const lrcLines = lrcContent.split("\n");
|
const lrcLines = lrcContent.split("\n");
|
||||||
const tlyricLines = tlyricContent ? tlyricContent.split("\n") : [];
|
const tlyricLines = tlyricContent ? tlyricContent.split("\n") : [];
|
||||||
const lrcMap = new Map();
|
const lrcMap = new Map();
|
||||||
@@ -957,7 +970,9 @@ class NeteaseMiniPlayer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const allTimes = Array.from(new Set([...lrcMap.keys(), ...tlyricMap.keys()])).sort((a, b) => a - b);
|
const allTimes = Array.from(new Set([...lrcMap.keys(), ...tlyricMap.keys()])).sort(
|
||||||
|
(a, b) => a - b
|
||||||
|
);
|
||||||
this.lyrics = allTimes.map((time) => ({
|
this.lyrics = allTimes.map((time) => ({
|
||||||
time,
|
time,
|
||||||
text: lrcMap.get(time) || "",
|
text: lrcMap.get(time) || "",
|
||||||
@@ -1018,7 +1033,9 @@ class NeteaseMiniPlayer {
|
|||||||
|
|
||||||
let newIndex;
|
let newIndex;
|
||||||
if (this.playMode === "shuffle") {
|
if (this.playMode === "shuffle") {
|
||||||
const availableIndices = this.playlist.map((_, i) => i).filter((i) => i !== this.currentIndex);
|
const availableIndices = this.playlist
|
||||||
|
.map((_, i) => i)
|
||||||
|
.filter((i) => i !== this.currentIndex);
|
||||||
|
|
||||||
if (availableIndices.length === 0) {
|
if (availableIndices.length === 0) {
|
||||||
newIndex = this.currentIndex;
|
newIndex = this.currentIndex;
|
||||||
@@ -1409,9 +1426,18 @@ class NeteaseMiniPlayer {
|
|||||||
const hex = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);
|
const hex = color.match(/^#([0-9a-f]{3}|[0-9a-f]{6})$/);
|
||||||
if (hex) {
|
if (hex) {
|
||||||
const hexValue = hex[1];
|
const hexValue = hex[1];
|
||||||
const r = parseInt(hexValue.length === 3 ? hexValue[0] + hexValue[0] : hexValue.substr(0, 2), 16);
|
const r = parseInt(
|
||||||
const g = parseInt(hexValue.length === 3 ? hexValue[1] + hexValue[1] : hexValue.substr(2, 2), 16);
|
hexValue.length === 3 ? hexValue[0] + hexValue[0] : hexValue.substr(0, 2),
|
||||||
const b = parseInt(hexValue.length === 3 ? hexValue[2] + hexValue[2] : hexValue.substr(4, 2), 16);
|
16
|
||||||
|
);
|
||||||
|
const g = parseInt(
|
||||||
|
hexValue.length === 3 ? hexValue[1] + hexValue[1] : hexValue.substr(2, 2),
|
||||||
|
16
|
||||||
|
);
|
||||||
|
const b = parseInt(
|
||||||
|
hexValue.length === 3 ? hexValue[2] + hexValue[2] : hexValue.substr(4, 2),
|
||||||
|
16
|
||||||
|
);
|
||||||
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
|
||||||
return brightness < 128;
|
return brightness < 128;
|
||||||
}
|
}
|
||||||
@@ -1564,13 +1590,17 @@ class NMPv2ShortcodeParser {
|
|||||||
}
|
}
|
||||||
|
|
||||||
processExistingElements(container) {
|
processExistingElements(container) {
|
||||||
container.querySelectorAll(".netease-mini-player:not([data-shortcode-processed])").forEach((element) => {
|
container
|
||||||
|
.querySelectorAll(".netease-mini-player:not([data-shortcode-processed])")
|
||||||
|
.forEach((element) => {
|
||||||
element.setAttribute("data-shortcode-processed", "true");
|
element.setAttribute("data-shortcode-processed", "true");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
initializePlayers(container) {
|
initializePlayers(container) {
|
||||||
container.querySelectorAll(".netease-mini-player:not([data-initialized])").forEach((element) => {
|
container
|
||||||
|
.querySelectorAll(".netease-mini-player:not([data-initialized])")
|
||||||
|
.forEach((element) => {
|
||||||
element.setAttribute("data-initialized", "true");
|
element.setAttribute("data-initialized", "true");
|
||||||
NeteaseMiniPlayer.initPlayer(element);
|
NeteaseMiniPlayer.initPlayer(element);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -22,7 +22,8 @@ type SendMailPayload = {
|
|||||||
message?: string;
|
message?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
const ensureValue = (value?: string, fallback = "未填写") => (value?.trim() ? value.trim() : fallback);
|
const ensureValue = (value?: string, fallback = "未填写") =>
|
||||||
|
value?.trim() ? value.trim() : fallback;
|
||||||
|
|
||||||
export default defineEventHandler(async (event) => {
|
export default defineEventHandler(async (event) => {
|
||||||
const method = event.node.req.method;
|
const method = event.node.req.method;
|
||||||
@@ -46,7 +47,15 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const config = useRuntimeConfig() as MailConfig;
|
const config = useRuntimeConfig() as MailConfig;
|
||||||
const { smtpHost, smtpPort: configSmtpPort, smtpUser, smtpPass, senderEmail, adminEmail, smtpSecure } = config;
|
const {
|
||||||
|
smtpHost,
|
||||||
|
smtpPort: configSmtpPort,
|
||||||
|
smtpUser,
|
||||||
|
smtpPass,
|
||||||
|
senderEmail,
|
||||||
|
adminEmail,
|
||||||
|
smtpSecure,
|
||||||
|
} = config;
|
||||||
|
|
||||||
const smtpPort = Number(configSmtpPort ?? 465);
|
const smtpPort = Number(configSmtpPort ?? 465);
|
||||||
if (!smtpHost || !smtpUser || !smtpPass || !senderEmail || !adminEmail) {
|
if (!smtpHost || !smtpUser || !smtpPass || !senderEmail || !adminEmail) {
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
import type { Config } from "tailwindcss";
|
import type { Config } from "tailwindcss";
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: ["./app/**/*.{vue,js,ts}", "./app/components/**/*.vue", "./app/pages/**/*.vue", "./app/layouts/**/*.vue"],
|
content: [
|
||||||
|
"./app/**/*.{vue,js,ts}",
|
||||||
|
"./app/components/**/*.vue",
|
||||||
|
"./app/pages/**/*.vue",
|
||||||
|
"./app/layouts/**/*.vue",
|
||||||
|
],
|
||||||
theme: {
|
theme: {
|
||||||
extend: {
|
extend: {
|
||||||
colors: {
|
colors: {
|
||||||
@@ -15,7 +20,14 @@ export default {
|
|||||||
"text-muted": "rgb(104, 120, 152)",
|
"text-muted": "rgb(104, 120, 152)",
|
||||||
},
|
},
|
||||||
fontFamily: {
|
fontFamily: {
|
||||||
sans: ['"Inter"', "system-ui", "-apple-system", "BlinkMacSystemFont", '"Segoe UI"', "sans-serif"],
|
sans: [
|
||||||
|
'"Inter"',
|
||||||
|
"system-ui",
|
||||||
|
"-apple-system",
|
||||||
|
"BlinkMacSystemFont",
|
||||||
|
'"Segoe UI"',
|
||||||
|
"sans-serif",
|
||||||
|
],
|
||||||
},
|
},
|
||||||
spacing: {
|
spacing: {
|
||||||
"safe-x": "max(1rem, env(safe-area-inset-left))",
|
"safe-x": "max(1rem, env(safe-area-inset-left))",
|
||||||
|
|||||||
Reference in New Issue
Block a user