style: 更新主题配色方案为蓝紫调

将全局渐变色从暖红/粉橙主题调整为蓝紫/粉色调,统一卡片、导航栏、页面背景及装饰元素的视觉风格,并重构音乐播放器组件的初始化逻辑
This commit is contained in:
2026-08-07 17:43:34 +08:00
parent 88999c4961
commit 28612006a6
9 changed files with 223 additions and 136 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
<template>
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(219,95,81,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(249,154,159,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(255,239,234,0.78))] p-8 shadow-[0_28px_60px_rgba(128,118,126,0.18)] backdrop-blur-[20px]">
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(135,196,235,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(200,182,224,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(200,182,224,0.10))] p-8 shadow-[0_28px_60px_rgba(128,118,126,0.18)] backdrop-blur-[20px]">
<n-tabs class="bg-transparent" v-model:value="tid" justify-content="space-evenly" animated>
<n-tab-pane name="tab1" tab="登录">
<n-form ref="formLogin" layout="vertical" :model="loginData" :rules="rules">
+105 -67
View File
@@ -1,6 +1,6 @@
<template>
<!-- 准备一个容器用来存放音乐播放器 -->
<div id="aplayer"></div>
<div ref="containerRef" id="aplayer"></div>
</template>
<script setup lang="ts">
@@ -8,81 +8,119 @@
import APlayer from "aplayer"; // 引入音乐插件
import "aplayer/dist/APlayer.min.css"; // 引入音乐插件的样式
// window.Hls = Hls;
const data: any = reactive({
audio: [],
info: {
fixed: true, // 开启吸底模式
// mini: false, // 开启迷你模式
// autoplay: true, // 自动播放(已经没用了)
// theme: '#000', // 主题色
loop: "all", // 音频循环播放, 可选值: 'all', 'one', 'none'
order: "list", // 音频循环顺序, 可选值: 'list', 'random'
preload: "metadata", //预加载,可选值: 'none', 'metadata', 'auto'
volume: 0.5, // 默认音量,可选值: 0-1
mutex: true, // 互斥,默认为true,即不允许多个音频同时播放,当前播放的音频会暂停其他音频
lrcType: 3, // 歌词类型,可选值: 1, 2, 3
},
});
/** 歌单接口 */
interface MusicItem {
title: string;
author: string;
url: string;
pic: string;
lrc: string;
}
/** 歌单来源配置 */
const PLAYLIST_SERVER = "tencent";
const PLAYLIST_ID = "7681108669";
/** 歌单拉取接口(公共 meting API */
const METING_API = `https://meting.mikus.ink/api?server=${PLAYLIST_SERVER}&type=playlist&id=${PLAYLIST_ID}`;
/** APlayer 基础配置 */
const APLAYER_INFO = {
fixed: true, // 开启吸底模式
loop: "all", // 音频循环播放, 可选值: 'all', 'one', 'none'
order: "list", // 音频循环顺序, 可选值: 'list', 'random'
preload: "metadata", // 预加载,可选值: 'none', 'metadata', 'auto'
volume: 0.5, // 默认音量,可选值: 0-1
mutex: true, // 互斥,默认为true,即不允许多个音频同时播放,当前播放的音频会暂停其他音频
lrcType: 3, // 歌词类型,可选值: 1, 2, 3
};
const containerRef = ref<HTMLDivElement | null>(null);
let ap: InstanceType<typeof APlayer> | null = null;
let miniswitcherEl: HTMLElement | null = null;
let listScrollEl: HTMLElement | null = null;
let onClickMiniswitcher: (() => void) | null = null;
let onListWheel: ((e: Event) => void) | null = null;
/** 拉取歌单并解析为 MusicItem[] */
async function fetchPlaylist(): Promise<MusicItem[]> {
const res = await fetch(METING_API);
if (!res.ok) throw new Error(`歌单请求失败: ${res.status}`);
const mlist: MusicItem[] = await res.json();
return mlist;
}
/** 初始化迷你切换按钮的显隐动画(自定义展开/收起效果) */
function setupMiniswitcher() {
miniswitcherEl = document.querySelector("#aplayer > div.aplayer-body > div.aplayer-miniswitcher > button");
const app = document.querySelector<HTMLElement>("#aplayer > div.aplayer-lrc");
const apic = document.querySelector<HTMLElement>("#aplayer div.aplayer-body");
if (!miniswitcherEl || !app || !apic) return;
let expanded = false;
app.style.display = "block";
app.style.transform = "translateY(50px)";
apic.style.transform = "translateX(-66px)";
onClickMiniswitcher = () => {
if (expanded) {
app.style.transform = "translateY(50px)";
ap.lrc?.hide();
ap.list.hide();
apic.style.transform = "translateX(-66px)";
expanded = false;
} else {
app.style.transform = "translateY(0)";
ap.lrc?.show();
apic.style.transform = "translateX(0)";
expanded = true;
}
};
miniswitcherEl.addEventListener("click", onClickMiniswitcher);
}
/** 阻止歌单列表滚轮事件冒泡,避免联动外层滚动 */
function setupListWheelGuard() {
listScrollEl = document.querySelector(".aplayer-list > ol");
if (!listScrollEl) return;
onListWheel = (e: Event) => e.stopPropagation();
listScrollEl.addEventListener("mousewheel", onListWheel);
}
/** 移动端(宽度 < 767)自动收起播放器为迷你模式 */
function applyMobileCollapse() {
const width = document.documentElement.clientWidth;
if (width < 767) miniswitcherEl?.click();
}
onMounted(async () => {
try {
const server = 'tencent'
const gdid = '7681108669'
const res = await fetch(`https://meting.mikus.ink/api?server=${server}&type=playlist&id=${gdid}`)
const mlist: any = await res.json()
data.audio = []
mlist.forEach((i: any) => {
data.audio.push({
title: i.title,
author: i.author,
url: i.url,
pic: i.pic,
lrc: i.lrc,
});
const audio = await fetchPlaylist();
ap = new APlayer({
container: containerRef.value ?? undefined,
audio, // 音乐信息
...APLAYER_INFO, // 其他配置信息
});
// 创建一个音乐播放器实例,并挂载到DOM上,同时进行相关配置
const ap = new APlayer({
container: document.getElementById("aplayer"),
audio: data.audio, // 音乐信息
...data.info, // 其他配置信息
});
const d: HTMLElement | null = document.querySelector("#aplayer > div.aplayer-body > div.aplayer-miniswitcher > button");
let flag = true;
const app: HTMLElement | null = document.querySelector("#aplayer > div.aplayer-lrc");
const apic: HTMLElement | null = document.querySelector("#aplayer div.aplayer-body");
if (!d || !app || !apic) return;
app.style.display = "block";
app.style.transform = "translateY(50px)";
apic.style.transform = "translateX(-66px)";
d.addEventListener("click", () => {
if (flag) {
app.style.transform = "translateY(0)";
ap.lrc.show();
apic.style.transform = "translateX(0)";
flag = false;
} else {
app.style.transform = "translateY(50px)";
ap.lrc.hide();
ap.list.hide();
apic.style.transform = "translateX(-66px)";
flag = true;
}
});
document.querySelector(".aplayer-list > ol")?.addEventListener("mousewheel", (e) => {
e.stopPropagation();
})
// 获取当前屏幕宽度
const width = document.documentElement.clientWidth; // 获取屏幕宽度
if (width < 767) d.click();
setupMiniswitcher();
setupListWheelGuard();
applyMobileCollapse();
} catch (error) {
console.error('音乐列表加载失败:', error)
console.error("音乐列表加载失败:", error);
}
});
onUnmounted(() => {
// 清理事件监听,避免内存泄漏
if (miniswitcherEl && onClickMiniswitcher) {
miniswitcherEl.removeEventListener("click", onClickMiniswitcher);
}
if (listScrollEl && onListWheel) {
listScrollEl.removeEventListener("mousewheel", onListWheel);
}
// 销毁 APlayer 实例
ap?.destroy();
ap = null;
});
</script>
<style lang="less" scoped>
#aplayer {
+54 -25
View File
@@ -22,7 +22,7 @@
<div class="mt-1 text-[14px] text-slate-500">{{ d }}</div>
</div>
<img
v-if="jq.type != 0"
v-if="jq?.type != 0"
class="pointer-events-none w-[88px] shrink-0 drop-shadow-[0_10px_22px_rgba(236,160,102,0.18)]"
src="@/assets/images/offwork.png"
alt="offwork"
@@ -36,7 +36,7 @@
</div>
<div class="mt-2">
<div class="flex items-center justify-between text-[13px] text-slate-500">
<span>今年已过 {{ jq.dayOfYear }} </span>
<span>今年已过 {{ jq?.dayOfYear ?? 0 }} </span>
<span>{{ yearProgress }}%</span>
</div>
<div
@@ -106,17 +106,17 @@
<div class="flex items-center font-semibold text-slate-700">
<icon-date class="mr-2 h-[20px] w-5"></icon-date>
农历节气
<div class="ml-8 font-semibold text-[#D71A44]">{{ jq.yearTips }} {{ jq.lunarCalendar }}</div>
<div class="ml-8 font-semibold text-[#D71A44]">{{ jq?.yearTips }} {{ jq?.lunarCalendar }}</div>
</div>
</template>
<template #default>
<div class="mx-1 flex items-center gap-1.5 text-sm text-slate-600">
<icon-star class="w-4 shrink-0 text-[#DB5F51]"></icon-star>
<span class="truncate">{{ jq.suit }}</span>
<span class="truncate">{{ jq?.suit }}</span>
</div>
<div class="mx-1 mt-1.5 flex items-center gap-1.5 text-sm text-slate-600">
<icon-lock class="w-4 shrink-0 text-[#d99a9a]"></icon-lock>
<span class="truncate">{{ jq.avoid }}</span>
<span class="truncate">{{ jq?.avoid }}</span>
</div>
<div class="flex w-full justify-center">
<img
@@ -125,10 +125,10 @@
alt="节气图"
>
</div>
<div class="mt-1.5 text-center font-medium text-slate-600">{{ jq.solarTerms }}</div>
<div class="mt-1.5 text-center font-medium text-slate-600">{{ jq?.solarTerms }}</div>
<div
v-if="holiName && holiDays !== null"
class="mx-auto mt-2 flex w-fit items-center gap-1.5 rounded-full border border-white/70 bg-[linear-gradient(135deg,rgba(255,240,246,0.85),rgba(255,233,223,0.8))] px-3.5 py-0.5 text-[13px] font-medium text-[#D71A44] shadow-[0_2px_8px_rgba(215,26,68,0.12),inset_0_1px_0_rgba(255,255,255,0.7)]"
class="mx-auto mt-2 flex w-fit items-center gap-1.5 rounded-full border border-white/70 bg-[linear-gradient(135deg,rgba(220,240,252,0.85),rgba(200,182,224,0.6))] px-3.5 py-0.5 text-[13px] font-medium text-[#D71A44] shadow-[0_2px_8px_rgba(215,26,68,0.12),inset_0_1px_0_rgba(255,255,255,0.7)]"
>
<icon-date class="w-3.5"></icon-date>
<b class="font-bold">{{ holiName }}</b> 还有
@@ -140,15 +140,44 @@
</template>
<script setup lang="ts">
import { formatTime } from '@/util/index'
import { formatTime } from '@/util/index';
const t = ref<any>('')
const d = ref<any>('')
let timer: any = null
const jq = ref<any>('')
const jqImg = ref<any>('')
const bdNews = ref<any>([])
const holi = ref<any>(null)
/** 节气/黄历接口 */
interface JqData {
type: number;
dayOfYear: number;
yearTips: string;
lunarCalendar: string;
suit: string;
avoid: string;
solarTerms: string;
}
/** 百度热搜条目 */
interface BdNewsItem {
id: string | number;
url: string;
index: number;
title: string;
hot: string | number;
}
/** 节假日数据(后端字段名不固定,全部可选,防御式取值) */
interface HoliData {
name?: string;
holiday?: string;
days?: number;
countdown?: number;
day?: number;
}
const t = ref('')
const d = ref('')
let timer: ReturnType<typeof setInterval> | null = null
const jq = ref<JqData | null>(null)
const jqImg = ref('')
const bdNews = ref<BdNewsItem[]>([])
const holi = ref<HoliData | null>(null)
// 今年进度百分比
const yearProgress = computed(() => {
@@ -170,8 +199,8 @@ const holiDays = computed(() => {
function getTime() {
const date = new Date()
t.value = formatTime(date, 'hh:mm:ss')
d.value = formatTime(date, 'YYYY 年 MM 月 DD 日')
t.value = formatTime(date, 'hh:mm:ss') || ''
d.value = formatTime(date, 'YYYY 年 MM 月 DD 日') || ''
}
async function getJq() {
@@ -180,9 +209,9 @@ async function getJq() {
date: formatTime(new Date(), 'YYYYMMDD') || ''
})
jq.value = res.data
const j = res.data.solarTerms.slice(0, 2)
const j = String(res.data.solarTerms ?? '').slice(0, 2)
const timestamp = new Date().getTime()
jqImg.value = `https://www.hxyouzi.com/img/jq/${j}.png?${timestamp}`
jqImg.value = `https://www.hxyouzi.com/fs/jieqi/${j}.png?${timestamp}`
} catch {
// 接口失败时静默处理,保持空状态
}
@@ -191,7 +220,7 @@ async function getJq() {
async function getBdhot() {
try {
const res = await $http.mix.getBdhot()
bdNews.value = res.data.slice(0, 5)
bdNews.value = (res.data ?? []).slice(0, 5)
} catch {
bdNews.value = []
}
@@ -219,7 +248,7 @@ onMounted(() => {
})
onUnmounted(() => {
clearInterval(timer)
if (timer) clearInterval(timer)
})
</script>
@@ -283,7 +312,7 @@ onUnmounted(() => {
.side-glass-card::before {
background:
linear-gradient(135deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.08) 48%, rgba(151, 197, 224, 0.08)),
linear-gradient(135deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.08) 48%, rgba(200, 182, 224, 0.08)),
radial-gradient(circle at top left, rgba(255, 255, 255, 0.45), transparent 42%);
opacity: 0.95;
}
@@ -297,10 +326,10 @@ onUnmounted(() => {
.side-glass-card--tint {
background:
radial-gradient(circle at top left, rgba(255, 199, 132, 0.12), transparent 34%),
radial-gradient(circle at top right, rgba(136, 203, 255, 0.1), transparent 28%),
radial-gradient(circle at top left, rgba(135, 196, 235, 0.10), transparent 34%),
radial-gradient(circle at top right, rgba(249, 199, 216, 0.08), transparent 28%),
linear-gradient(135deg, rgba(255, 255, 255, 0.34), rgba(255, 255, 255, 0.16)),
linear-gradient(180deg, rgba(241, 248, 252, 0.2), rgba(224, 236, 245, 0.08)) !important;
linear-gradient(180deg, rgba(216, 238, 252, 0.2), rgba(200, 182, 224, 0.06)) !important;
}
:deep(.n-card > .n-card__content),
+3 -3
View File
@@ -1,7 +1,7 @@
<template>
<div
ref="nav"
class="relative hidden items-center justify-between bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.04),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.03),transparent_24%),linear-gradient(180deg,rgba(242,232,214,0.82)_0%,rgba(244,222,218,0.68)_46%,rgba(251,231,232,0.28)_100%)] px-4 py-1.5 shadow-[0_6px_16px_rgba(128,118,126,0.05)] backdrop-blur-[14px] after:pointer-events-none after:absolute after:inset-x-0 after:bottom-[-28px] after:h-[34px] after:bg-[linear-gradient(180deg,rgba(240,236,226,0.58),rgba(244,222,218,0.22),transparent)] after:content-[''] lg:flex"
class="relative hidden items-center justify-between bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.04),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.03),transparent_24%),linear-gradient(180deg,rgba(190,220,245,0.30)_0%,rgba(200,182,224,0.18)_46%,rgba(249,199,216,0.10)_100%)] px-4 py-1.5 shadow-[0_6px_16px_rgba(128,118,126,0.05)] backdrop-blur-[14px] after:pointer-events-none after:absolute after:inset-x-0 after:bottom-[-28px] after:h-[34px] after:bg-[linear-gradient(180deg,rgba(220,240,252,0.40),rgba(200,182,224,0.14),transparent)] after:content-[''] lg:flex"
>
<div
class="menu-pill-glass flex ml-4 cursor-pointer items-center rounded-full px-4 py-1 transition-colors duration-200"
@@ -66,7 +66,7 @@
</masked>
</div>
<div class="relative flex items-center justify-between bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.04),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.03),transparent_24%),linear-gradient(180deg,rgba(242,232,214,0.84)_0%,rgba(244,222,218,0.68)_46%,rgba(251,231,232,0.32)_100%)] px-3 py-2 shadow-[0_6px_16px_rgba(128,118,126,0.05)] backdrop-blur-[14px] after:pointer-events-none after:absolute after:inset-x-0 after:bottom-[-24px] after:h-[30px] after:bg-[linear-gradient(180deg,rgba(240,236,226,0.58),rgba(244,222,218,0.22),transparent)] after:content-[''] lg:hidden">
<div class="relative flex items-center justify-between bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.04),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.03),transparent_24%),linear-gradient(180deg,rgba(190,220,245,0.30)_0%,rgba(200,182,224,0.18)_46%,rgba(249,199,216,0.12)_100%)] px-3 py-2 shadow-[0_6px_16px_rgba(128,118,126,0.05)] backdrop-blur-[14px] after:pointer-events-none after:absolute after:inset-x-0 after:bottom-[-24px] after:h-[30px] after:bg-[linear-gradient(180deg,rgba(220,240,252,0.40),rgba(200,182,224,0.14),transparent)] after:content-[''] lg:hidden">
<div class="menu-pill-glass flex items-center rounded-full px-3 py-1">
<img :src="logo" alt="柚子的网站" class="h-9 align-middle drop-shadow-[0_6px_14px_rgba(215,26,68,0.15)]" />
</div>
@@ -91,7 +91,7 @@
</div>
<masked v-if="usrLog.isLogin" :visible="editModal" :setVisible="handdleItemCancel">
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(219,95,81,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(249,154,159,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(255,239,234,0.78))] p-8 shadow-[0_28px_60px_rgba(128,118,126,0.18)] backdrop-blur-[20px]">
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(135,196,235,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(200,182,224,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(200,182,224,0.10))] p-8 shadow-[0_28px_60px_rgba(128,118,126,0.18)] backdrop-blur-[20px]">
<div class="mb-8 text-center text-xl font-bold tracking-[0.02em] text-slate-800">修改头像</div>
<n-upload
+3 -3
View File
@@ -1,11 +1,11 @@
<template>
<div
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(222,217,178,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]">
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(249,199,216,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]">
<div
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(219,95,81,0.10)] opacity-48 blur-[88px]">
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(135,196,235,0.10)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(222,217,178,0.08)] opacity-48 blur-[88px]">
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(249,199,216,0.08)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute inset-0 opacity-14 [background-image:linear-gradient(rgba(255,255,255,0.18)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.18)_1px,transparent_1px)] [background-size:36px_36px] [mask-image:linear-gradient(180deg,rgba(0,0,0,0.8),transparent_85%)]">
+3 -3
View File
@@ -1,12 +1,12 @@
<template>
<div
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(222,217,178,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(249,199,216,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
:style="boxStyle">
<div
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(219,95,81,0.10)] opacity-48 blur-[88px]">
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(135,196,235,0.10)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(222,217,178,0.08)] opacity-48 blur-[88px]">
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(249,199,216,0.08)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute inset-0 opacity-14 [background-image:linear-gradient(rgba(255,255,255,0.18)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.18)_1px,transparent_1px)] [background-size:36px_36px] [mask-image:linear-gradient(180deg,rgba(0,0,0,0.8),transparent_85%)]">
+3 -3
View File
@@ -1,12 +1,12 @@
<template>
<div
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(222,217,178,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(249,199,216,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
:style="boxStyle">
<div
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(219,95,81,0.10)] opacity-48 blur-[88px]">
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(135,196,235,0.10)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(222,217,178,0.08)] opacity-48 blur-[88px]">
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(249,199,216,0.08)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute inset-0 opacity-14 [background-image:linear-gradient(rgba(255,255,255,0.18)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.18)_1px,transparent_1px)] [background-size:36px_36px] [mask-image:linear-gradient(180deg,rgba(0,0,0,0.8),transparent_85%)]">
+7 -7
View File
@@ -1,12 +1,12 @@
<template>
<div
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(222,217,178,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(249,199,216,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
:style="boxStyle">
<div
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(219,95,81,0.10)] opacity-48 blur-[88px]">
class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(135,196,235,0.10)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(222,217,178,0.08)] opacity-48 blur-[88px]">
class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(249,199,216,0.08)] opacity-48 blur-[88px]">
</div>
<div
class="pointer-events-none absolute inset-0 opacity-14 [background-image:linear-gradient(rgba(255,255,255,0.18)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.18)_1px,transparent_1px)] [background-size:36px_36px] [mask-image:linear-gradient(180deg,rgba(0,0,0,0.8),transparent_85%)]">
@@ -88,8 +88,8 @@
</aside>
<section class="blog-list-panel min-w-0 flex h-full flex-1 flex-col p-4 sm:p-6">
<div class="pointer-events-none absolute right-[-4rem] top-12 h-40 w-40 rounded-full bg-[rgba(219,95,81,0.04)] blur-[88px]"></div>
<div class="pointer-events-none absolute left-[-3rem] bottom-10 h-36 w-36 rounded-full bg-[rgba(249,154,159,0.03)] blur-[82px]"></div>
<div class="pointer-events-none absolute right-[-4rem] top-12 h-40 w-40 rounded-full bg-[rgba(135,196,235,0.04)] blur-[88px]"></div>
<div class="pointer-events-none absolute left-[-3rem] bottom-10 h-36 w-36 rounded-full bg-[rgba(200,182,224,0.03)] blur-[82px]"></div>
<div class="mb-5 flex flex-col gap-4 md:flex-row md:items-start md:justify-between">
<div class="relative z-[1]">
<div class="text-[0.82rem] font-semibold tracking-[0.16em] text-[#718195]">文章列表</div>
@@ -128,10 +128,10 @@
class="blog-list-card blog-card-animate group cursor-pointer"
:style="{ animationDelay: `${index * 90}ms` }" @click="$router.push(`/blog/${item.aid}`)">
<div
class="pointer-events-none absolute -right-10 top-[-2rem] h-28 w-28 rounded-full bg-[rgba(219,95,81,0.04)] transition duration-500 group-hover:scale-125 group-hover:opacity-100">
class="pointer-events-none absolute -right-10 top-[-2rem] h-28 w-28 rounded-full bg-[rgba(135,196,235,0.04)] transition duration-500 group-hover:scale-125 group-hover:opacity-100">
</div>
<div
class="pointer-events-none absolute -left-8 bottom-[-2rem] h-24 w-24 rounded-full bg-[rgba(219,95,81,0.04)] transition duration-500 group-hover:translate-x-2">
class="pointer-events-none absolute -left-8 bottom-[-2rem] h-24 w-24 rounded-full bg-[rgba(135,196,235,0.04)] transition duration-500 group-hover:translate-x-2">
</div>
<div class="pointer-events-none absolute inset-x-6 top-0 h-18 bg-[linear-gradient(180deg,rgba(255,255,255,0.24),transparent)] opacity-80 "></div>
+44 -24
View File
@@ -1,10 +1,10 @@
<template>
<div
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(219,95,81,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(249,154,159,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(222,217,178,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
class="relative overflow-hidden bg-[radial-gradient(circle_at_12%_18%,rgba(135,196,235,0.05),transparent_24%),radial-gradient(circle_at_86%_12%,rgba(200,182,224,0.04),transparent_24%),radial-gradient(circle_at_50%_100%,rgba(249,199,216,0.03),transparent_28%),linear-gradient(180deg,#fcfaf6_0%,#fbf8f7_44%,#fefcfb_100%)]"
:style="contentStyle"
>
<div class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(219,95,81,0.10)] opacity-48 blur-[88px]"></div>
<div class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(222,217,178,0.08)] opacity-48 blur-[88px]"></div>
<div class="pointer-events-none absolute left-[-5rem] top-6 h-80 w-80 rounded-full bg-[rgba(135,196,235,0.10)] opacity-48 blur-[88px]"></div>
<div class="pointer-events-none absolute right-[-5rem] top-32 h-[22rem] w-[22rem] rounded-full bg-[rgba(249,199,216,0.08)] opacity-48 blur-[88px]"></div>
<div class="pointer-events-none absolute inset-y-0 left-5 right-5 opacity-14 [background-image:linear-gradient(rgba(255,255,255,0.18)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.18)_1px,transparent_1px)] [background-position:18px_0,18px_0] [background-size:36px_36px] [mask-image:linear-gradient(180deg,rgba(0,0,0,0.8),transparent_85%)]"></div>
<n-layout has-sider>
<n-layout-content class="relative !bg-transparent">
@@ -184,7 +184,7 @@
<maskX :visible="visible" :setVisible="navCancel">
<n-form
class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(219,95,81,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(249,154,159,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(255,239,234,0.78))] p-8 shadow-[0_4px_12px_rgba(128,118,126,0.08),0_20px_44px_rgba(128,118,126,0.12),0_64px_120px_rgba(215,26,68,0.08)] backdrop-blur-[20px]"
class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(135,196,235,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(200,182,224,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(200,182,224,0.10))] p-8 shadow-[0_4px_12px_rgba(128,118,126,0.08),0_20px_44px_rgba(128,118,126,0.12),0_64px_120px_rgba(215,26,68,0.08)] backdrop-blur-[20px]"
ref="formNav"
layout="vertical"
:data="navData"
@@ -237,43 +237,43 @@
<contextMenu :show="menuShow" :options="MenuOptions">
<div class="menu-pop glass-floating-surface !m-0 cursor-pointer !rounded-2xl !p-0" @mouseenter="removeTimer" @mouseleave="hideMenu">
<div class="menu-head truncate">{{ currentItem?.menu_name || '导航操作' }}</div>
<div class="menu-item" @click="handdleMenuItem(1)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.RENAME)">
<icon-pen class="menu-icon" />修改名称
</div>
<div class="menu-item" @click="handdleMenuItem(2)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.EDIT_LINK)">
<icon-loc class="menu-icon" />修改链接
</div>
<div class="menu-item" @click="handdleMenuItem(3)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.EDIT_TAG)">
<icon-tag class="menu-icon" />修改标签
</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="handdleMenuItem(5)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.COPY_LINK)">
<icon-download class="menu-icon" />复制链接
</div>
<div class="menu-item" @click="handdleMenuItem(6)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.TOGGLE_PIN)">
<icon-star class="menu-icon" :class="isPinned(currentItem) ? '!text-[#f0a13c]' : ''" />{{ isPinned(currentItem) ? '取消置顶' : '置顶导航' }}
</div>
<div class="menu-item" @click="handdleMenuItem(7)">
<div class="menu-item" @click="handdleMenuItem(MenuAction.RETRY_ICON)">
<icon-loading class="menu-icon" />重新加载图标
</div>
<div class="menu-divider"></div>
<div class="menu-item menu-item--danger" @click="handdleMenuItem(4)">
<div class="menu-item menu-item--danger" @click="handdleMenuItem(MenuAction.DELETE)">
删除导航
</div>
</div>
</contextMenu>
<maskX :visible="editModal" :setVisible="handleItemCancel">
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(219,95,81,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(249,154,159,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(255,239,234,0.78))] p-8 shadow-[0_4px_12px_rgba(128,118,126,0.08),0_20px_44px_rgba(128,118,126,0.12),0_64px_120px_rgba(215,26,68,0.08)] backdrop-blur-[20px]">
<div class="w-[500px] rounded-[28px] border border-white/80 bg-[radial-gradient(circle_at_top_left,rgba(135,196,235,0.04),transparent_34%),radial-gradient(circle_at_top_right,rgba(200,182,224,0.03),transparent_28%),linear-gradient(180deg,rgba(255,255,255,0.84),rgba(200,182,224,0.10))] p-8 shadow-[0_4px_12px_rgba(128,118,126,0.08),0_20px_44px_rgba(128,118,126,0.12),0_64px_120px_rgba(215,26,68,0.08)] backdrop-blur-[20px]">
<div class="mb-8 flex items-center justify-center gap-2 text-center text-xl font-bold tracking-[0.02em] text-slate-800">
<icon-pen class="w-5 text-primary" />
修改导航内容
</div>
<div class="mb-4 text-slate-500">
原内容
<span class="text-primary" v-if="currentClickedItem === 1">导航名称{{ currentItem?.menu_name }}</span>
<span class="text-primary" v-else-if="currentClickedItem === 2">导航链接{{ currentItem?.menu_link }}</span>
<span class="text-primary" v-else-if="currentClickedItem === 3">导航标签{{ currentItem?.tag }}</span>
<span class="text-primary" v-if="currentClickedItem === MenuAction.RENAME">导航名称{{ currentItem?.menu_name }}</span>
<span class="text-primary" v-else-if="currentClickedItem === MenuAction.EDIT_LINK">导航链接{{ currentItem?.menu_link }}</span>
<span class="text-primary" v-else-if="currentClickedItem === MenuAction.EDIT_TAG">导航标签{{ currentItem?.tag }}</span>
</div>
<n-input class="modal-input" v-model:value="editInput" placeholder="请输入修改内容" @keyup.enter="handleItemSubmit" />
<div class="mt-8 flex justify-between">
@@ -328,6 +328,26 @@ interface SearchEngineOption {
url: string
}
/** 右键菜单动作(避免魔法数字) */
const MenuAction = {
/** 修改名称 */
RENAME: 1,
/** 修改链接 */
EDIT_LINK: 2,
/** 修改标签 */
EDIT_TAG: 3,
/** 删除导航 */
DELETE: 4,
/** 复制链接 */
COPY_LINK: 5,
/** 置顶 / 取消置顶 */
TOGGLE_PIN: 6,
/** 重新加载图标 */
RETRY_ICON: 7,
} as const
type MenuActionType = (typeof MenuAction)[keyof typeof MenuAction]
/** localStorage 键名 */
const HITS_KEY = 'nav_hits'
const PINNED_KEY = 'nav_pinned'
@@ -336,7 +356,7 @@ const ENGINE_KEY = 'nav_search_engine'
const menuShow = ref(false)
const MenuOptions = reactive({ x: 0, y: 0 })
const currentItem = ref<NavItem | null>(null)
const currentClickedItem = ref(0)
const currentClickedItem = ref<MenuActionType | 0>(0)
const editModal = ref(false)
const editInput = ref('')
const searchInputRef = ref<HTMLInputElement | null>(null)
@@ -701,11 +721,11 @@ const removeTimer = () => {
}
}
function handdleMenuItem(type: number) {
function handdleMenuItem(type: MenuActionType) {
menuShow.value = false
currentClickedItem.value = type
if (type === 4) {
if (type === MenuAction.DELETE) {
if (!currentItem.value) return
$modal({
@@ -730,19 +750,19 @@ function handdleMenuItem(type: number) {
}
// 复制链接
if (type === 5) {
if (type === MenuAction.COPY_LINK) {
copyLink()
return
}
// 置顶 / 取消置顶
if (type === 6) {
if (type === MenuAction.TOGGLE_PIN) {
togglePin()
return
}
// 重新加载图标
if (type === 7) {
if (type === MenuAction.RETRY_ICON) {
retryIcon()
return
}
@@ -809,11 +829,11 @@ async function handleItemSubmit() {
if (!currentItem.value?.nid) return
const updateData: Partial<NavItem> = {}
if (currentClickedItem.value === 1) {
if (currentClickedItem.value === MenuAction.RENAME) {
updateData.menu_name = value
} else if (currentClickedItem.value === 2) {
} else if (currentClickedItem.value === MenuAction.EDIT_LINK) {
updateData.menu_link = value
} else if (currentClickedItem.value === 3) {
} else if (currentClickedItem.value === MenuAction.EDIT_TAG) {
updateData.tag = formatTag(value)
}