style: 更新主题配色方案为蓝紫调
将全局渐变色从暖红/粉橙主题调整为蓝紫/粉色调,统一卡片、导航栏、页面背景及装饰元素的视觉风格,并重构音乐播放器组件的初始化逻辑
This commit is contained in:
@@ -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
@@ -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
@@ -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),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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%)]">
|
||||
|
||||
@@ -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%)]">
|
||||
|
||||
@@ -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
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user