Zkit工具站
This commit is contained in:
168
Zkit-C/src/App.vue
Normal file
168
Zkit-C/src/App.vue
Normal file
@@ -0,0 +1,168 @@
|
||||
<script setup>
|
||||
import { ref, onMounted, onUnmounted } from 'vue'
|
||||
import { RouterView } from 'vue-router'
|
||||
import AppHeader from './components/AppHeader.vue'
|
||||
import AppFooter from './components/AppFooter.vue'
|
||||
import LoginModal from './components/LoginModal.vue'
|
||||
import AnnouncementModal from './components/AnnouncementModal.vue'
|
||||
|
||||
// --- 主题切换逻辑 ---
|
||||
const isDark = ref(false)
|
||||
|
||||
// 执行切换操作
|
||||
const applyTheme = (dark) => {
|
||||
isDark.value = dark
|
||||
if (dark) {
|
||||
document.documentElement.classList.add('dark')
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark')
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮点击事件
|
||||
const toggleTheme = () => {
|
||||
applyTheme(!isDark.value)
|
||||
}
|
||||
|
||||
// --- 回到顶部逻辑 ---
|
||||
const showBackTop = ref(false)
|
||||
|
||||
const handleScroll = () => {
|
||||
showBackTop.value = window.scrollY > 200
|
||||
}
|
||||
|
||||
const scrollToTop = () => {
|
||||
window.scrollTo({
|
||||
top: 0,
|
||||
behavior: 'smooth'
|
||||
})
|
||||
}
|
||||
|
||||
// 初始化:根据时间判断 (19:00 - 06:00为暗色)
|
||||
onMounted(() => {
|
||||
const hour = new Date().getHours()
|
||||
const isNight = hour >= 19 || hour < 6
|
||||
applyTheme(isNight)
|
||||
|
||||
// 添加滚动监听
|
||||
window.addEventListener('scroll', handleScroll)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// 移除滚动监听
|
||||
window.removeEventListener('scroll', handleScroll)
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AppHeader />
|
||||
<!-- 路由出口 -->
|
||||
<RouterView />
|
||||
<AppFooter />
|
||||
<LoginModal />
|
||||
<AnnouncementModal />
|
||||
|
||||
<!-- 全局主题切换按钮 -->
|
||||
<div class="theme-switch-btn" @click="toggleTheme" :title="isDark ? '切换到亮色' : '切换到暗色'">
|
||||
<i v-if="isDark" class="fa-solid fa-moon"></i>
|
||||
<i v-else class="fa-solid fa-sun"></i>
|
||||
</div>
|
||||
|
||||
<!-- 全局回到顶部按钮 -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-show="showBackTop"
|
||||
class="back-to-top"
|
||||
@click="scrollToTop"
|
||||
title="回到顶部"
|
||||
>
|
||||
<i class="fa-solid fa-arrow-up"></i>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<style>
|
||||
/* 引入修改后的main.css */
|
||||
@import './assets/main.css';
|
||||
|
||||
/* 切换按钮样式 */
|
||||
.theme-switch-btn {
|
||||
position: fixed;
|
||||
bottom: 100px; /* 位于返回顶部按钮上方 */
|
||||
right: 40px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-main);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-switch-btn:hover {
|
||||
transform: rotate(15deg) scale(1.1);
|
||||
border-color: var(--primary);
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* 回到顶部按钮样式 */
|
||||
.back-to-top {
|
||||
position: fixed;
|
||||
bottom: 40px;
|
||||
right: 40px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background-color: var(--primary);
|
||||
color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.2rem;
|
||||
box-shadow: 0 10px 15px -3px rgba(37, 99, 235, 0.3);
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.back-to-top:hover {
|
||||
transform: translateY(-5px);
|
||||
background-color: var(--primary-dark);
|
||||
box-shadow: 0 15px 20px -3px rgba(37, 99, 235, 0.4);
|
||||
}
|
||||
|
||||
/* Vue 过渡动画 */
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.theme-switch-btn {
|
||||
right: 20px;
|
||||
bottom: 80px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.back-to-top {
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
width: 45px;
|
||||
height: 45px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
306
Zkit-C/src/assets/main.css
Normal file
306
Zkit-C/src/assets/main.css
Normal file
@@ -0,0 +1,306 @@
|
||||
/* src/assets/main.css */
|
||||
|
||||
/* 1. 定义变量 (Light 默认) */
|
||||
:root {
|
||||
/* 核心色调 */
|
||||
--primary: #2563eb;
|
||||
--primary-dark: #1d4ed8;
|
||||
--accent: #f59e0b;
|
||||
|
||||
/* 背景色 */
|
||||
--bg-body: #f8fafc; /* 页面大背景 */
|
||||
--bg-card: #ffffff; /* 卡片/弹窗背景 */
|
||||
--bg-header: rgba(255, 255, 255, 0.9); /* 导航栏毛玻璃 */
|
||||
--bg-input: #ffffff; /* 输入框背景 */
|
||||
--bg-ad: #f1f5f9; /* 广告位背景 */
|
||||
|
||||
/* 文本色 */
|
||||
--text-main: #0f172a;
|
||||
--text-sub: #64748b;
|
||||
--text-placeholder: #94a3b8;
|
||||
--text-sub-dim:#94a3b8;
|
||||
|
||||
/* 边框与装饰 */
|
||||
--border-color: #e2e8f0;
|
||||
--border-hover: #bfdbfe;
|
||||
--dot-color: #cbd5e1; /* Hero区域的点阵颜色 */
|
||||
--shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.02);
|
||||
--shadow-md: 0 10px 25px -5px rgba(0, 0, 0, 0.1);
|
||||
|
||||
--radius: 12px;
|
||||
}
|
||||
|
||||
/* 2. 定义暗色模式变量 (Dark) - 挂载在 html.dark 上 */
|
||||
html.dark {
|
||||
--primary: #3b82f6; /* 暗色下蓝色稍微亮一点 */
|
||||
--primary-dark: #2563eb;
|
||||
|
||||
--bg-body: #0f172a; /* 深蓝背景 */
|
||||
--bg-card: #1e293b; /* 卡片深色 */
|
||||
--bg-header: rgba(30, 41, 59, 0.9);
|
||||
--bg-input: #1e293b;
|
||||
--bg-ad: #1e293b;
|
||||
|
||||
--text-main: #f1f5f9; /* 亮白字 */
|
||||
--text-sub: #94a3b8; /* 灰字 */
|
||||
--text-placeholder: #475569;
|
||||
--text-sub-dim: #e2e8f0;
|
||||
|
||||
--border-color: #334155;
|
||||
--border-hover: #3b82f6;
|
||||
--dot-color: #334155; /* 点阵变暗 */
|
||||
--shadow-sm: 0 4px 6px rgba(0, 0, 0, 0.3);
|
||||
--shadow-md: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
/* 3. 全局基础样式 */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
|
||||
/* 增加颜色过渡,让切换平滑 */
|
||||
transition: background-color 0.3s ease, border-color 0.3s ease, color 0.3s ease;
|
||||
}
|
||||
|
||||
body {
|
||||
background: var(--bg-body);
|
||||
color: var(--text-main);
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
a { text-decoration: none; color: inherit; transition: 0.2s; cursor: pointer; }
|
||||
ul { list-style: none; }
|
||||
|
||||
/* 通用组件 */
|
||||
.container { max-width: 1200px; margin: 0 auto; padding: 0 1.5rem; }
|
||||
|
||||
.btn-login {
|
||||
padding: 8px 20px;
|
||||
background: var(--bg-card); /* 修改:使用变量 */
|
||||
color: var(--primary);
|
||||
border: 1px solid var(--border-hover);
|
||||
border-radius: 50px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn-login:hover { background: var(--bg-body); }
|
||||
|
||||
.btn-cta {
|
||||
background: var(--primary);
|
||||
color: white; /* 按钮文字保持白色 */
|
||||
padding: 12px 30px;
|
||||
border-radius: 50px;
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
box-shadow: 0 10px 20px rgba(37, 99, 235, 0.2);
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
}
|
||||
.btn-cta:hover { background: var(--primary-dark); transform: translateY(-2px); }
|
||||
|
||||
/* 导航栏 */
|
||||
header {
|
||||
background: var(--bg-header); /* 修改:使用变量 */
|
||||
backdrop-filter: blur(10px);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
border-bottom: 1px solid var(--border-color); /* 修改 */
|
||||
}
|
||||
|
||||
.nav-inner {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
height: 70px;
|
||||
padding: 0 1.5rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brand { font-size: 1.4rem; font-weight: 800; color: var(--primary); display: flex; align-items: center; gap: 8px; }
|
||||
.nav-menu a { margin-left: 24px; color: var(--text-sub); font-weight: 500; font-size: 0.95rem; }
|
||||
.nav-menu a:hover { color: var(--primary); }
|
||||
|
||||
/* Hero 区域 */
|
||||
.hero-wrapper {
|
||||
position: relative;
|
||||
padding: 6rem 1rem 5rem;
|
||||
text-align: center;
|
||||
/* 修改:使用变量处理点阵图 */
|
||||
background-image: radial-gradient(var(--dot-color) 1.5px, transparent 1.5px);
|
||||
background-size: 24px 24px;
|
||||
background-color: var(--bg-body); /* 修改 */
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3rem;
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
margin-bottom: 1.5rem;
|
||||
color: var(--text-main); /* 修改 */
|
||||
}
|
||||
.hero-title span { color: var(--primary); }
|
||||
.hero-sub { font-size: 1.1rem; color: var(--text-sub); max-width: 600px; margin: 0 auto 2.5rem; }
|
||||
|
||||
/* 搜索框 */
|
||||
.search-container { max-width: 600px; margin: 0 auto; position: relative; z-index: 10; }
|
||||
.search-input {
|
||||
width: 100%;
|
||||
padding: 1.2rem 1.5rem 1.2rem 3.5rem;
|
||||
border-radius: 50px;
|
||||
border: 1px solid var(--border-color); /* 修改 */
|
||||
font-size: 1rem;
|
||||
background: var(--bg-input); /* 修改 */
|
||||
color: var(--text-main); /* 修改 */
|
||||
box-shadow: var(--shadow-md); /* 修改 */
|
||||
outline: none;
|
||||
transition: 0.3s;
|
||||
}
|
||||
.search-input:focus { border-color: var(--primary); }
|
||||
.search-icon { position: absolute; left: 1.5rem; top: 50%; transform: translateY(-50%); color: var(--text-sub); }
|
||||
|
||||
/* 统计栏 */
|
||||
.stats-bar {
|
||||
max-width: 1000px;
|
||||
margin: -40px auto 4rem;
|
||||
position: relative;
|
||||
z-index: 20;
|
||||
background: var(--bg-card); /* 修改 */
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
.stat-item { text-align: center; }
|
||||
.stat-item:last-child { border-right: none; }
|
||||
.stat-num { font-size: 1.8rem; font-weight: 800; margin-bottom: 5px; color: var(--text-main); }
|
||||
.stat-desc { font-size: 0.9rem; color: var(--text-sub); }
|
||||
|
||||
/* 卡片 */
|
||||
.section-header { display: flex; justify-content: space-between; align-items: center; margin: 3rem 0 1.5rem; }
|
||||
.section-header h2 { color: var(--text-main); }
|
||||
.tools-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 1.5rem; }
|
||||
|
||||
.card {
|
||||
background: var(--bg-card); /* 修改 */
|
||||
padding: 1.5rem;
|
||||
border-radius: var(--radius);
|
||||
border: 1px solid var(--border-color);
|
||||
box-shadow: var(--shadow-sm);
|
||||
transition: 0.3s;
|
||||
position: relative;
|
||||
display: block;
|
||||
cursor: pointer;
|
||||
}
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
.card-icon {
|
||||
width: 48px; height: 48px;
|
||||
background: var(--bg-body); /* 修改 */
|
||||
border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 1.25rem;
|
||||
margin-bottom: 1rem;
|
||||
color: var(--text-sub);
|
||||
transition: 0.3s;
|
||||
}
|
||||
.card:hover .card-icon { background: var(--primary); color: white; }
|
||||
.card h3 { color: var(--text-main); margin-bottom: 0.5rem; } /* 确保标题颜色 */
|
||||
.card p { color: var(--text-sub); font-size: 0.9rem; } /* 确保描述颜色 */
|
||||
|
||||
/* 广告位 */
|
||||
.ad-banner {
|
||||
width: 100%; height: 100px;
|
||||
background: var(--bg-ad); /* 修改 */
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
color: var(--text-sub);
|
||||
margin: 2rem 0;
|
||||
}
|
||||
|
||||
/* ---------- 全局暗黑主题样式优化 ---------- */
|
||||
|
||||
/* 图标背景色 - Light Mode */
|
||||
.icon-bg-blue { background: #eff6ff; color: #2563eb; }
|
||||
.icon-bg-indigo { background: #eef2ff; color: #4f46e5; }
|
||||
.icon-bg-rose { background: #fff1f2; color: #e11d48; }
|
||||
.icon-bg-cyan { background: #ecfeff; color: #0891b2; }
|
||||
.icon-bg-emerald { background: #ecfdf5; color: #059669; }
|
||||
.icon-bg-amber { background: #fffbeb; color: #d97706; }
|
||||
.icon-bg-slate { background: #f1f5f9; color: #475569; }
|
||||
.icon-bg-violet { background: #f5f3ff; color: #7c3aed; }
|
||||
.icon-bg-fuchsia { background: #fdf4ff; color: #c026d3; }
|
||||
.icon-bg-orange { background: #fff7ed; color: #ea580c; }
|
||||
.icon-bg-teal { background: #f0fdfa; color: #0d9488; }
|
||||
.icon-bg-gray { background: #f3f4f6; color: #4b5563; }
|
||||
.icon-bg-red { background: #fef2f2; color: #ef4444; }
|
||||
|
||||
/* 图标背景色 - Dark Mode */
|
||||
html.dark .icon-bg-blue { background: rgba(37, 99, 235, 0.15); color: #60a5fa; }
|
||||
html.dark .icon-bg-indigo { background: rgba(79, 70, 229, 0.15); color: #818cf8; }
|
||||
html.dark .icon-bg-rose { background: rgba(225, 29, 72, 0.15); color: #f43f5e; }
|
||||
html.dark .icon-bg-cyan { background: rgba(8, 145, 178, 0.15); color: #22d3ee; }
|
||||
html.dark .icon-bg-emerald { background: rgba(5, 150, 105, 0.15); color: #34d399; }
|
||||
html.dark .icon-bg-amber { background: rgba(217, 119, 6, 0.15); color: #fbbf24; }
|
||||
html.dark .icon-bg-slate { background: rgba(51, 65, 85, 0.5); color: #94a3b8; }
|
||||
html.dark .icon-bg-violet { background: rgba(124, 58, 237, 0.15); color: #a78bfa; }
|
||||
html.dark .icon-bg-fuchsia { background: rgba(192, 38, 211, 0.15); color: #e879f9; }
|
||||
html.dark .icon-bg-orange { background: rgba(234, 88, 12, 0.15); color: #fb923c; }
|
||||
html.dark .icon-bg-teal { background: rgba(13, 148, 136, 0.15); color: #2dd4bf; }
|
||||
html.dark .icon-bg-gray { background: rgba(51, 65, 85, 0.5); color: #cbd5e1; }
|
||||
html.dark .icon-bg-red { background: rgba(239, 68, 68, 0.15); color: #f87171; }
|
||||
|
||||
/* VIP 徽章 - Light Mode */
|
||||
.vip-badge {
|
||||
background: #fff7ed;
|
||||
color: #d97706;
|
||||
transition: background-color 0.3s, color 0.3s;
|
||||
}
|
||||
|
||||
/* VIP 徽章 - Dark Mode */
|
||||
html.dark .vip-badge {
|
||||
background: rgba(217, 119, 6, 0.2);
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
/* 工具标题暗色优化 */
|
||||
html.dark .tool-title {
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
/* 图标容器暗色优化 */
|
||||
html.dark .icon-wrapper {
|
||||
filter: brightness(0.92) saturate(0.92);
|
||||
}
|
||||
|
||||
/* 联系客服卡片 - Dark Mode */
|
||||
html.dark .contact-card {
|
||||
background: rgba(37, 99, 235, 0.08);
|
||||
border-color: rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
html.dark .contact-icon {
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
html.dark .ad-banner {
|
||||
background: var(--bg-card);
|
||||
border-color: var(--text-placeholder);
|
||||
}
|
||||
|
||||
/* 响应式 */
|
||||
@media (max-width: 768px) {
|
||||
.hero-title { font-size: 2rem; }
|
||||
.nav-menu { display: none; }
|
||||
.stats-bar { grid-template-columns: 1fr; gap: 2rem; margin-top: 2rem; }
|
||||
}
|
||||
517
Zkit-C/src/components/AnnouncementModal.vue
Normal file
517
Zkit-C/src/components/AnnouncementModal.vue
Normal file
@@ -0,0 +1,517 @@
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue'
|
||||
|
||||
const visible = ref(false)
|
||||
const STORAGE_KEY = 'has_seen_announcement_v5' // 唯一标识,如果要发新公告,改一下这个名字(比如v2)即可再次弹出
|
||||
|
||||
onMounted(() => {
|
||||
// 核心逻辑:检查 localStorage 中是否有记录
|
||||
const hasSeen = localStorage.getItem(STORAGE_KEY)
|
||||
|
||||
// 如果没有记录(null),说明是首次访问,设置为显示
|
||||
if (!hasSeen) {
|
||||
// 延迟 0.5 秒弹出,体验更好
|
||||
setTimeout(() => {
|
||||
visible.value = true
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
// 核心逻辑:点击关闭后,在本地存储写入记录
|
||||
localStorage.setItem(STORAGE_KEY, 'true')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Transition name="modal-fade">
|
||||
<div v-if="visible" class="modal-overlay" @click.self="handleClose">
|
||||
<div class="modal-content">
|
||||
|
||||
<!-- 1. 顶部装饰区 (增加SVG背景纹理) -->
|
||||
<div class="modal-header-bg">
|
||||
<svg class="bg-pattern" width="100%" height="100%">
|
||||
<defs>
|
||||
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
|
||||
<circle cx="2" cy="2" r="1" fill="rgba(255,255,255,0.3)" />
|
||||
</pattern>
|
||||
</defs>
|
||||
<rect width="100%" height="100%" fill="url(#pattern-circles)" />
|
||||
</svg>
|
||||
|
||||
<!-- 浮动的装饰圆圈 -->
|
||||
<div class="circle-decor c1"></div>
|
||||
<div class="circle-decor c2"></div>
|
||||
</div>
|
||||
|
||||
<!-- 悬浮图标容器 -->
|
||||
<div class="icon-wrapper-outer">
|
||||
<div class="icon-wrapper">
|
||||
<!-- 使用 SVG 替代 Emoji -->
|
||||
<svg class="main-icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"></path>
|
||||
<path d="M13.73 21a2 2 0 0 1-3.46 0"></path>
|
||||
</svg>
|
||||
</div>
|
||||
<!-- 呼吸光环 -->
|
||||
<div class="icon-glow"></div>
|
||||
</div>
|
||||
|
||||
<!-- 关闭按钮 (移入卡片内部右上角) -->
|
||||
<button class="close-btn" @click="handleClose" title="关闭">
|
||||
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>
|
||||
</button>
|
||||
|
||||
<!-- 文本内容区域 -->
|
||||
<div class="modal-body">
|
||||
<h3 class="modal-title">全新版本 <span class="gradient-text">V2.0</span> 已发布</h3>
|
||||
<p class="intro-text">为了提升您的工作效率,我们带来了一些令人兴奋的改进:</p>
|
||||
|
||||
<ul class="feature-list">
|
||||
<li style="--delay: 0.1s">
|
||||
<div class="icon-box green">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><polyline points="20 6 9 17 4 12"></polyline></svg>
|
||||
</div>
|
||||
<div class="text-box">
|
||||
<span class="title">PDF 工具箱</span>
|
||||
<span class="desc">支持一键合并、拆分与压缩</span>
|
||||
</div>
|
||||
</li>
|
||||
<li style="--delay: 0.2s">
|
||||
<div class="icon-box blue">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"></path></svg>
|
||||
</div>
|
||||
<div class="text-box">
|
||||
<span class="title">安全升级</span>
|
||||
<span class="desc">全新的 MD5 加密算法,速度提升 50%</span>
|
||||
</div>
|
||||
</li>
|
||||
<li style="--delay: 0.3s">
|
||||
<div class="icon-box purple">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2.69l5.74 5.88-5.74 5.88-5.74-5.88z"></path><path d="M2 12l10 10 10-10"></path></svg>
|
||||
</div>
|
||||
<div class="text-box">
|
||||
<span class="title">体验优化</span>
|
||||
<span class="desc">修复移动端显示 bug,更加丝滑</span>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<!-- 底部 -->
|
||||
<div class="modal-footer">
|
||||
<p class="footer-note">💖 祝您使用愉快!</p>
|
||||
<button class="confirm-btn" @click="handleClose">
|
||||
<span>立即体验</span>
|
||||
<svg class="arrow-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="5" y1="12" x2="19" y2="12"></line><polyline points="12 5 19 12 12 19"></polyline></svg>
|
||||
<!-- 按钮光效 -->
|
||||
<div class="btn-shine"></div>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
|
||||
|
||||
<style scoped>
|
||||
/* --- 遮罩层 (磨砂质感) --- */
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
background: rgba(30, 30, 35, 0.6);
|
||||
backdrop-filter: blur(12px); /* 强力毛玻璃 */
|
||||
-webkit-backdrop-filter: blur(12px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 9999;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* --- 模态框主体 --- */
|
||||
.modal-content {
|
||||
background: var(--bg-card);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
border-radius: 28px;
|
||||
box-shadow:
|
||||
0 25px 50px -12px rgba(0, 0, 0, 0.25),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.1); /* 极细内描边 */
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
animation: jelly-pop 0.6s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
/* --- 顶部装饰背景 --- */
|
||||
.modal-header-bg {
|
||||
height: 120px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, #2c3e50 100%);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bg-pattern {
|
||||
opacity: 0.3;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
/* 装饰性浮动圆圈 */
|
||||
.circle-decor {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background: rgba(255,255,255,0.1);
|
||||
}
|
||||
.c1 { width: 100px; height: 100px; top: -20px; right: -20px; }
|
||||
.c2 { width: 60px; height: 60px; bottom: 20px; left: 10%; }
|
||||
|
||||
/* --- 悬浮图标 --- */
|
||||
.icon-wrapper-outer {
|
||||
position: absolute;
|
||||
top: 80px; /* 120px header - 40px (half icon height) */
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
background: var(--bg-card);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.main-icon {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
animation: swing 2s ease-in-out infinite; /* 铃铛摇摆动画 */
|
||||
}
|
||||
|
||||
.icon-glow {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: var(--primary);
|
||||
border-radius: 50%;
|
||||
opacity: 0.3;
|
||||
z-index: 1;
|
||||
animation: pulse-glow 2s infinite;
|
||||
}
|
||||
|
||||
/* --- 关闭按钮 --- */
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 15px;
|
||||
right: 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
border: none;
|
||||
color: rgba(255,255,255,0.8);
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
z-index: 20;
|
||||
}
|
||||
.close-btn:hover {
|
||||
background: rgba(255,255,255,0.25);
|
||||
transform: rotate(90deg);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* --- 内容区域 --- */
|
||||
.modal-body {
|
||||
padding: 50px 28px 10px; /* Top padding for icon space */
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
margin: 0 0 10px;
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-main);
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.5px;
|
||||
}
|
||||
|
||||
.gradient-text {
|
||||
background: linear-gradient(120deg, var(--primary), #3b82f6);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
}
|
||||
|
||||
.intro-text {
|
||||
color: var(--text-sub);
|
||||
font-size: 0.95rem;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
/* --- 特性列表 (卡片式) --- */
|
||||
.feature-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.feature-list li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 12px;
|
||||
margin-bottom: 12px;
|
||||
border-radius: 16px;
|
||||
background: var(--bg-body);
|
||||
border: 1px solid transparent;
|
||||
transition: all 0.2s ease;
|
||||
|
||||
/* 动画初始状态 */
|
||||
opacity: 0;
|
||||
animation: slide-in-up 0.5s cubic-bezier(0.2, 0.8, 0.2, 1) forwards;
|
||||
animation-delay: var(--delay);
|
||||
}
|
||||
|
||||
.feature-list li:hover {
|
||||
background: var(--bg-card);
|
||||
border-color: var(--border-hover);
|
||||
transform: translateY(-2px);
|
||||
box-shadow: var(--shadow-sm);
|
||||
}
|
||||
|
||||
/* 图标盒子 */
|
||||
.icon-box {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 14px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.icon-box svg { width: 20px; height: 20px; }
|
||||
|
||||
.icon-box.green {
|
||||
background: rgba(66, 184, 131, 0.15);
|
||||
color: var(--primary);
|
||||
}
|
||||
.icon-box.blue {
|
||||
background: rgba(59, 130, 246, 0.15);
|
||||
color: #3b82f6;
|
||||
}
|
||||
.icon-box.purple {
|
||||
background: rgba(139, 92, 246, 0.15);
|
||||
color: #8b5cf6;
|
||||
}
|
||||
|
||||
.text-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.text-box .title {
|
||||
font-weight: 700;
|
||||
font-size: 0.95rem;
|
||||
color: var(--text-main);
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.text-box .desc {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-sub);
|
||||
}
|
||||
|
||||
/* --- 底部 --- */
|
||||
.modal-footer {
|
||||
padding: 10px 28px 30px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-note {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-sub);
|
||||
margin-bottom: 15px;
|
||||
font-weight: 500;
|
||||
opacity: 0;
|
||||
animation: fade-in 0.5s ease forwards 0.5s;
|
||||
}
|
||||
|
||||
/* 按钮美化 */
|
||||
.confirm-btn {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
background: var(--primary);
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 16px;
|
||||
border-radius: 14px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
overflow: hidden;
|
||||
transition: all 0.3s ease;
|
||||
box-shadow: 0 4px 15px rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
.confirm-btn:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 25px rgba(37, 99, 235, 0.4);
|
||||
background: var(--primary-dark);
|
||||
}
|
||||
|
||||
.arrow-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
.confirm-btn:hover .arrow-icon {
|
||||
transform: translateX(4px);
|
||||
}
|
||||
|
||||
/* 按钮光泽扫光动画 */
|
||||
.btn-shine {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -100%;
|
||||
width: 50%;
|
||||
height: 100%;
|
||||
background: linear-gradient(
|
||||
to right,
|
||||
rgba(255,255,255,0) 0%,
|
||||
rgba(255,255,255,0.2) 50%,
|
||||
rgba(255,255,255,0) 100%
|
||||
);
|
||||
transform: skewX(-25deg);
|
||||
animation: shine 3s infinite;
|
||||
}
|
||||
|
||||
/* --- 动画关键帧 --- */
|
||||
|
||||
@keyframes jelly-pop {
|
||||
0% { transform: scale(0.9); opacity: 0; }
|
||||
100% { transform: scale(1); opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes slide-in-up {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes fade-in {
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
@keyframes pulse-glow {
|
||||
0% { transform: scale(1); opacity: 0.3; }
|
||||
50% { transform: scale(1.4); opacity: 0; }
|
||||
100% { transform: scale(1); opacity: 0; }
|
||||
}
|
||||
|
||||
@keyframes swing {
|
||||
0%, 100% { transform: rotate(0deg); }
|
||||
20% { transform: rotate(15deg); }
|
||||
40% { transform: rotate(-10deg); }
|
||||
60% { transform: rotate(5deg); }
|
||||
80% { transform: rotate(-5deg); }
|
||||
}
|
||||
|
||||
@keyframes shine {
|
||||
0% { left: -100%; }
|
||||
20% { left: 200%; }
|
||||
100% { left: 200%; }
|
||||
}
|
||||
|
||||
/* Vue Transition: Fade */
|
||||
.modal-fade-enter-active,
|
||||
.modal-fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
.modal-fade-enter-from,
|
||||
.modal-fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* --- 暗黑模式适配 --- */
|
||||
:global(.dark) .modal-content {
|
||||
box-shadow:
|
||||
0 25px 50px -12px rgba(0, 0, 0, 0.5),
|
||||
0 0 0 1px rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
:global(.dark) .feature-list li {
|
||||
background: var(--bg-body);
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
:global(.dark) .feature-list li:hover {
|
||||
background: var(--bg-card);
|
||||
border-color: var(--border-hover);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
:global(.dark) .icon-box.green {
|
||||
background: rgba(66, 184, 131, 0.2);
|
||||
}
|
||||
|
||||
:global(.dark) .icon-box.blue {
|
||||
background: rgba(59, 130, 246, 0.2);
|
||||
}
|
||||
|
||||
:global(.dark) .icon-box.purple {
|
||||
background: rgba(139, 92, 246, 0.2);
|
||||
}
|
||||
|
||||
:global(.dark) .close-btn {
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
:global(.dark) .close-btn:hover {
|
||||
background: rgba(255, 255, 255, 0.2);
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* --- 响应式设计 --- */
|
||||
@media (max-width: 480px) {
|
||||
.modal-content {
|
||||
max-width: 350px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
padding: 50px 20px 10px;
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
padding: 10px 20px 30px;
|
||||
}
|
||||
|
||||
.feature-list li {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.modal-title {
|
||||
font-size: 1.3rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
19
Zkit-C/src/components/AppFooter.vue
Normal file
19
Zkit-C/src/components/AppFooter.vue
Normal file
@@ -0,0 +1,19 @@
|
||||
<template>
|
||||
<footer class="site-footer">
|
||||
<div class="container">
|
||||
<p>© 2023 OmniKit Tool Station. All rights reserved.</p>
|
||||
</div>
|
||||
</footer>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.site-footer {
|
||||
background: var(--bg-card);
|
||||
border-top: 1px solid var(--border-color);
|
||||
color: var(--text-sub);
|
||||
padding: 3rem 0;
|
||||
margin-top: 4rem;
|
||||
text-align: center;
|
||||
transition: background 0.3s, border-color 0.3s, color 0.3s;
|
||||
}
|
||||
</style>
|
||||
228
Zkit-C/src/components/AppHeader.vue
Normal file
228
Zkit-C/src/components/AppHeader.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<script setup>
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
const route = useRoute()
|
||||
|
||||
// --- 移动端菜单状态 ---
|
||||
const isMenuOpen = ref(false)
|
||||
const toggleMenu = () => {
|
||||
isMenuOpen.value = !isMenuOpen.value
|
||||
document.body.style.overflow = isMenuOpen.value ? 'hidden' : ''
|
||||
}
|
||||
const closeMenu = () => {
|
||||
isMenuOpen.value = false
|
||||
document.body.style.overflow = ''
|
||||
}
|
||||
watch(() => route.fullPath, closeMenu)
|
||||
|
||||
// --- 返回按钮逻辑 ---
|
||||
const backConfig = computed(() => {
|
||||
if (route.path === '/') return null
|
||||
const previousPath = router.options.history.state.back
|
||||
if (typeof previousPath === 'string' && previousPath.includes('/all-tools')) {
|
||||
return { text: '返回工具库', path: '/all-tools' }
|
||||
}
|
||||
return { text: '返回首页', path: '/' }
|
||||
})
|
||||
|
||||
const handleAuth = () => {
|
||||
closeMenu()
|
||||
if (userStore.isLoggedIn) {
|
||||
if(confirm('退出登录?')) userStore.logout()
|
||||
} else {
|
||||
userStore.openModal()
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="site-header">
|
||||
<div class="container nav-inner">
|
||||
|
||||
<div class="header-left">
|
||||
<button class="hamburger-btn" @click="toggleMenu" :class="{ 'active': isMenuOpen }">
|
||||
<span class="bar"></span>
|
||||
<span class="bar"></span>
|
||||
<span class="bar"></span>
|
||||
</button>
|
||||
|
||||
<router-link to="/" class="brand desktop-brand">
|
||||
<div class="logo-icon">
|
||||
<i class="fa-solid fa-cube" style="color: var(--primary);"></i>
|
||||
</div>
|
||||
<span class="brand-text">ZKit</span>
|
||||
</router-link>
|
||||
|
||||
<transition name="fade">
|
||||
<router-link
|
||||
v-if="backConfig"
|
||||
:to="backConfig.path"
|
||||
class="btn-capsule btn-back"
|
||||
>
|
||||
<i class="fa-solid fa-angle-left"></i>
|
||||
<span class="back-text">{{ backConfig.text }}</span>
|
||||
</router-link>
|
||||
</transition>
|
||||
</div>
|
||||
|
||||
<div class="header-center-mobile">
|
||||
<router-link to="/" class="brand mobile-brand">
|
||||
<div class="logo-icon">
|
||||
<i class="fa-solid fa-cube" style="color: var(--primary);"></i>
|
||||
</div>
|
||||
<span class="brand-text">ZKit</span>
|
||||
</router-link>
|
||||
</div>
|
||||
|
||||
<nav class="header-right">
|
||||
<div class="desktop-links">
|
||||
<router-link to="/all-tools" active-class="active-link" class="nav-link">全部工具</router-link>
|
||||
<router-link to="/faq" active-class="active-link" class="nav-link">常见问题</router-link>
|
||||
</div>
|
||||
|
||||
<button class="btn-capsule btn-login" @click="handleAuth" :class="{ 'is-vip': userStore.isVip }">
|
||||
<i v-if="userStore.isVip" class="fa-solid fa-crown vip-icon"></i>
|
||||
<span class="login-text">
|
||||
{{ userStore.isLoggedIn ? (userStore.isVip ? 'VIP' : '我的') : '登录' }}
|
||||
</span>
|
||||
</button>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
<transition name="slide-down">
|
||||
<div v-if="isMenuOpen" class="mobile-menu">
|
||||
<div class="mobile-menu-inner">
|
||||
<router-link to="/" class="mobile-link" active-class="active-mobile">首页</router-link>
|
||||
<router-link to="/all-tools" class="mobile-link" active-class="active-mobile">全部工具</router-link>
|
||||
<router-link to="/faq" class="mobile-link" active-class="active-mobile">常见问题</router-link>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* --- 基础布局 --- */
|
||||
.site-header {
|
||||
background: var(--bg-header);
|
||||
backdrop-filter: blur(12px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 100;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.nav-inner {
|
||||
height: 72px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative; /* 用于绝对定位中间的Logo */
|
||||
}
|
||||
|
||||
.header-left, .header-right {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
z-index: 102; /* 保证按钮在Logo图层之上 */
|
||||
}
|
||||
|
||||
/* --- Logo 样式 --- */
|
||||
.brand { display: flex; gap: 8px; align-items: center; text-decoration: none; }
|
||||
.brand-text { font-size: 1.5rem; font-weight: 900; color: var(--text-main); letter-spacing: -0.5px; }
|
||||
|
||||
/* 默认显示桌面Logo,隐藏移动端Logo */
|
||||
.desktop-brand { display: flex; }
|
||||
.header-center-mobile { display: none; }
|
||||
|
||||
/* --- 按钮通用 --- */
|
||||
.btn-capsule {
|
||||
display: inline-flex; align-items: center; gap: 6px; font-size: 0.9rem; font-weight: 600; padding: 8px 18px; border-radius: 99px; text-decoration: none; transition: all 0.2s ease; border: 1px solid transparent; cursor: pointer; line-height: 1; white-space: nowrap;
|
||||
}
|
||||
.btn-back { color: var(--text-sub); border-color: var(--border-color); background: transparent; }
|
||||
.btn-back:hover { color: var(--text-main); border-color: var(--border-hover); background: var(--bg-body); }
|
||||
|
||||
.btn-login { color: var(--primary); border-color: var(--primary); background: transparent; }
|
||||
.btn-login:hover { background: var(--bg-body); transform: translateY(-1px); }
|
||||
.btn-login.is-vip { color: #d97706; border-color: #fbbf24; }
|
||||
|
||||
/* --- 导航链接 --- */
|
||||
.desktop-links { display: flex; gap: 24px; margin-right: 8px; }
|
||||
.nav-link { color: var(--text-sub); font-size: 0.95rem; font-weight: 500; text-decoration: none; transition: color 0.2s; }
|
||||
.nav-link:hover { color: var(--primary); }
|
||||
|
||||
/* --- 汉堡包按钮 --- */
|
||||
.hamburger-btn {
|
||||
display: none; /* 默认隐藏 */
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
gap: 5px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
z-index: 103;
|
||||
}
|
||||
.hamburger-btn .bar {
|
||||
width: 20px; height: 2px; background-color: var(--text-main); border-radius: 2px; transition: all 0.3s;
|
||||
}
|
||||
/* 汉堡包动画 */
|
||||
.hamburger-btn.active .bar:nth-child(1) { transform: translateY(7px) rotate(45deg); }
|
||||
.hamburger-btn.active .bar:nth-child(2) { opacity: 0; }
|
||||
.hamburger-btn.active .bar:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
|
||||
|
||||
/* --- 移动端菜单 --- */
|
||||
.mobile-menu {
|
||||
position: fixed; top: 72px; left: 0; width: 100%; height: calc(100vh - 72px);
|
||||
background: var(--bg-header); backdrop-filter: blur(15px); z-index: 101;
|
||||
border-top: 1px solid var(--border-color); overflow-y: auto;
|
||||
}
|
||||
.mobile-menu-inner { padding: 24px; display: flex; flex-direction: column; gap: 12px; }
|
||||
.mobile-link { font-size: 1.1rem; font-weight: 600; color: var(--text-main); padding: 16px; border-radius: 8px; text-decoration: none; background: var(--bg-body); }
|
||||
.mobile-link.active-mobile { color: var(--primary); border: 1px solid var(--primary); }
|
||||
|
||||
/* 动画 */
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.3s ease; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
.slide-down-enter-active, .slide-down-leave-active { transition: all 0.3s cubic-bezier(0.16, 1, 0.3, 1); }
|
||||
.slide-down-enter-from, .slide-down-leave-to { opacity: 0; transform: translateY(-10px); }
|
||||
|
||||
/* --- 响应式适配 (Max Width 640px) --- */
|
||||
@media (max-width: 640px) {
|
||||
/* 1. 布局调整 */
|
||||
.nav-inner { padding: 0 15px; }
|
||||
|
||||
/* 左侧:显示汉堡包,隐藏桌面Logo */
|
||||
.hamburger-btn { display: flex; }
|
||||
.desktop-brand { display: none; }
|
||||
|
||||
/* 中间:绝对定位显示 Logo */
|
||||
.header-center-mobile {
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 右侧:隐藏链接,调整登录按钮 */
|
||||
.desktop-links { display: none; }
|
||||
.btn-capsule { padding: 6px 12px; font-size: 0.85rem; }
|
||||
|
||||
/* 返回按钮:隐藏文字,只留箭头,防止挤压Logo */
|
||||
.back-text { display: none; }
|
||||
.btn-back { padding: 6px 10px; border: none; }
|
||||
.btn-back i { font-size: 1.1rem; }
|
||||
|
||||
/* 调整间距 */
|
||||
.header-left { gap: 8px; }
|
||||
}
|
||||
</style>
|
||||
282
Zkit-C/src/components/LoginModal.vue
Normal file
282
Zkit-C/src/components/LoginModal.vue
Normal file
@@ -0,0 +1,282 @@
|
||||
<script setup>
|
||||
import { ref, computed } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 表单数据
|
||||
const phone = ref('')
|
||||
const password = ref('')
|
||||
const confirmPassword = ref('')
|
||||
|
||||
// 当前模式: 'login' | 'register' | 'forgot'
|
||||
const authType = ref('login')
|
||||
|
||||
// 标题和按钮文字的计算属性
|
||||
const config = computed(() => {
|
||||
switch (authType.value) {
|
||||
case 'register':
|
||||
return { title: '创建账号', subtitle: '注册以享受更多会员权益', btn: '立即注册' }
|
||||
case 'forgot':
|
||||
return { title: '重置密码', subtitle: '请输入手机号以接收验证码', btn: '发送重置链接' }
|
||||
default:
|
||||
return { title: '欢迎回来', subtitle: '登录以保存您的使用记录', btn: '立即登录' }
|
||||
}
|
||||
})
|
||||
|
||||
// 处理提交
|
||||
const handleSubmit = () => {
|
||||
if (!phone.value) return alert('请输入手机号')
|
||||
|
||||
if (authType.value === 'login') {
|
||||
userStore.login(phone.value)
|
||||
} else if (authType.value === 'register') {
|
||||
if (password.value !== confirmPassword.value) return alert('两次输入的密码不一致')
|
||||
alert('注册逻辑待接入 API')
|
||||
authType.value = 'login'
|
||||
} else {
|
||||
alert('重置密码逻辑待接入 API')
|
||||
authType.value = 'login'
|
||||
}
|
||||
}
|
||||
|
||||
// 关闭弹窗时重置状态
|
||||
const handleClose = () => {
|
||||
userStore.closeModal()
|
||||
setTimeout(() => {
|
||||
authType.value = 'login'
|
||||
phone.value = ''
|
||||
password.value = ''
|
||||
confirmPassword.value = ''
|
||||
}, 300)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div v-if="userStore.showLoginModal" class="modal-overlay" @click.self="handleClose">
|
||||
<div class="modal-box">
|
||||
<button class="close-btn" @click="handleClose">
|
||||
<i class="fa-solid fa-xmark"></i>
|
||||
</button>
|
||||
|
||||
<div class="modal-header">
|
||||
<h2>{{ config.title }}</h2>
|
||||
<p>{{ config.subtitle }}</p>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-wrapper">
|
||||
<i class="fa-solid fa-mobile-screen input-icon"></i>
|
||||
<input v-model="phone" type="text" class="modal-input" placeholder="手机号">
|
||||
</div>
|
||||
|
||||
<div class="input-wrapper" v-if="authType !== 'forgot'">
|
||||
<i class="fa-solid fa-lock input-icon"></i>
|
||||
<input v-model="password" type="password" class="modal-input" placeholder="密码">
|
||||
</div>
|
||||
|
||||
<div class="input-wrapper" v-if="authType === 'register'">
|
||||
<i class="fa-solid fa-shield-halved input-icon"></i>
|
||||
<input v-model="confirmPassword" type="password" class="modal-input" placeholder="确认密码">
|
||||
</div>
|
||||
|
||||
<div class="form-options" v-if="authType === 'login'">
|
||||
<a @click="authType = 'forgot'" class="link-text">忘记密码?</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn-submit" @click="handleSubmit">
|
||||
{{ config.btn }} <i class="fa-solid fa-arrow-right"></i>
|
||||
</button>
|
||||
|
||||
<div class="modal-footer">
|
||||
<span v-if="authType === 'login'">
|
||||
还没有账号? <a @click="authType = 'register'">去注册</a>
|
||||
</span>
|
||||
<span v-else>
|
||||
已有账号? <a @click="authType = 'login'">去登录</a>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
backdrop-filter: blur(4px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
z-index: 1000;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
}
|
||||
|
||||
.modal-box {
|
||||
background: var(--bg-card);
|
||||
width: 380px;
|
||||
max-width: 90%;
|
||||
padding: 40px 30px 30px;
|
||||
border-radius: var(--radius);
|
||||
box-shadow: var(--shadow-md);
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
animation: slideUp 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
||||
}
|
||||
|
||||
.close-btn {
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
font-size: 1.2rem;
|
||||
color: var(--text-sub);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.close-btn:hover {
|
||||
background: var(--bg-body);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.modal-header h2 {
|
||||
margin: 0;
|
||||
font-size: 1.75rem;
|
||||
color: var(--text-main);
|
||||
font-weight: 700;
|
||||
}
|
||||
.modal-header p {
|
||||
margin: 8px 0 0;
|
||||
color: var(--text-sub);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.input-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 52px;
|
||||
background: var(--bg-input);
|
||||
border: 2px solid var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
padding: 0 16px;
|
||||
transition: all 0.2s;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.input-icon {
|
||||
font-size: 1rem;
|
||||
color: var(--text-placeholder);
|
||||
margin-right: 12px;
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.modal-input {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none !important;
|
||||
background: transparent !important;
|
||||
outline: none !important;
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
font-size: 1rem;
|
||||
color: var(--text-main);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.input-wrapper:focus-within {
|
||||
background: var(--bg-card);
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
|
||||
}
|
||||
.input-wrapper:focus-within .input-icon {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.form-options {
|
||||
text-align: right;
|
||||
}
|
||||
.link-text {
|
||||
font-size: 0.85rem;
|
||||
color: var(--text-sub);
|
||||
cursor: pointer;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.link-text:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
.btn-submit {
|
||||
width: 100%;
|
||||
padding: 14px;
|
||||
border: none;
|
||||
border-radius: var(--radius);
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark) 100%);
|
||||
color: white;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: transform 0.1s, box-shadow 0.2s;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-submit:hover {
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||
background: linear-gradient(135deg, var(--primary-dark) 0%, var(--primary) 100%);
|
||||
}
|
||||
.btn-submit:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.modal-footer {
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-sub);
|
||||
margin-top: 10px;
|
||||
}
|
||||
.modal-footer a {
|
||||
color: var(--primary);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
margin-left: 4px;
|
||||
}
|
||||
.modal-footer a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
@keyframes slideUp {
|
||||
from { opacity: 0; transform: translateY(20px) scale(0.95); }
|
||||
to { opacity: 1; transform: translateY(0) scale(1); }
|
||||
}
|
||||
</style>
|
||||
163
Zkit-C/src/components/ToolCard.vue
Normal file
163
Zkit-C/src/components/ToolCard.vue
Normal file
@@ -0,0 +1,163 @@
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
title: String,
|
||||
desc: String,
|
||||
icon: String,
|
||||
isVip: Boolean,
|
||||
// 传入 main.css 中定义的颜色名,例如 'blue', 'purple', 'orange'
|
||||
colorTheme: {
|
||||
type: String,
|
||||
default: 'blue'
|
||||
}
|
||||
})
|
||||
|
||||
// 动态生成符合 main.css 规范的类名,例如 "icon-bg-blue"
|
||||
// 这样就能利用全局 CSS 中定义的 Dark Mode 适配逻辑
|
||||
const iconThemeClass = computed(() => `icon-bg-${props.colorTheme}`)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="tool-card">
|
||||
<!-- VIP 标签 (使用 main.css 的基础样式并增强定位) -->
|
||||
<div v-if="isVip" class="vip-badge card-badge">
|
||||
<i class="fa-solid fa-crown"></i> VIP
|
||||
</div>
|
||||
|
||||
<div class="card-content">
|
||||
<!-- 图标容器: 结合全局颜色类和局部布局类 -->
|
||||
<div class="icon-box" :class="iconThemeClass">
|
||||
<i :class="icon"></i>
|
||||
</div>
|
||||
|
||||
<!-- 文本区域 -->
|
||||
<div class="text-area">
|
||||
<h3 class="card-title">
|
||||
{{ title }}
|
||||
<!-- 悬停出现的箭头 -->
|
||||
<span class="arrow-icon">→</span>
|
||||
</h3>
|
||||
<p class="card-desc">{{ desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/*
|
||||
注意:这里不再定义颜色变量,而是直接使用 main.css 中的变量
|
||||
这样可以确保切换 html.dark 时组件自动变色
|
||||
*/
|
||||
|
||||
.tool-card {
|
||||
/* 使用全局变量 */
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: var(--radius); /* 使用全局圆角 */
|
||||
box-shadow: var(--shadow-sm);
|
||||
|
||||
padding: 1.5rem;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
/* 动效配置 */
|
||||
transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
|
||||
box-shadow 0.3s ease,
|
||||
border-color 0.3s ease;
|
||||
}
|
||||
|
||||
/* 悬停状态 */
|
||||
.tool-card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: var(--shadow-md);
|
||||
border-color: var(--border-hover);
|
||||
}
|
||||
|
||||
/* 图标容器样式 */
|
||||
.icon-box {
|
||||
width: 52px;
|
||||
height: 52px;
|
||||
border-radius: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
margin-bottom: 1.2rem;
|
||||
|
||||
/* 颜色由 main.css 的 .icon-bg-xxx 类控制,这里只控制动效 */
|
||||
transition: transform 0.4s ease;
|
||||
}
|
||||
|
||||
/* 悬停时图标微动 */
|
||||
.tool-card:hover .icon-box {
|
||||
transform: scale(1.1) rotate(3deg);
|
||||
}
|
||||
|
||||
/* 标题样式 */
|
||||
.card-title {
|
||||
font-size: 1.15rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-main); /* 适配暗黑模式 */
|
||||
margin-bottom: 0.6rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
transition: color 0.3s;
|
||||
}
|
||||
|
||||
/* 悬停时标题高亮 - 使用全局主色 */
|
||||
.tool-card:hover .card-title {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* 描述文字 */
|
||||
.card-desc {
|
||||
font-size: 0.9rem;
|
||||
color: var(--text-sub); /* 适配暗黑模式 */
|
||||
line-height: 1.6;
|
||||
|
||||
/* 多行文本截断 */
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/*
|
||||
VIP 徽章定位调整
|
||||
颜色样式已在 main.css 的 .vip-badge 中定义
|
||||
这里只处理它在卡片中的位置
|
||||
*/
|
||||
.card-badge {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 4px 10px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 800;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
/* 箭头图标 */
|
||||
.arrow-icon {
|
||||
font-size: 1rem;
|
||||
opacity: 0;
|
||||
transform: translateX(-10px);
|
||||
transition: all 0.3s ease;
|
||||
color: var(--primary); /* 使用全局主色 */
|
||||
}
|
||||
|
||||
.tool-card:hover .arrow-icon {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
</style>
|
||||
14
Zkit-C/src/main.js
Normal file
14
Zkit-C/src/main.js
Normal file
@@ -0,0 +1,14 @@
|
||||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
|
||||
import App from './App.vue'
|
||||
import router from './router'
|
||||
|
||||
import './assets/main.css' // 引入全局样式
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
app.use(createPinia())
|
||||
app.use(router)
|
||||
|
||||
app.mount('#app')
|
||||
26
Zkit-C/src/router/index.js
Normal file
26
Zkit-C/src/router/index.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { createRouter, createWebHistory } from 'vue-router'
|
||||
import HomeView from '../views/HomeView.vue'
|
||||
|
||||
// 路由懒加载
|
||||
const Md5View = () => import('../views/Md5View.vue')
|
||||
const PdfView = () => import('../views/PdfView.vue')
|
||||
const AllToolsView = () => import('../views/AllToolsView.vue') // 新增
|
||||
const FaqView = () => import('../views/FaqView.vue') // 新增
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(import.meta.env.BASE_URL),
|
||||
routes: [
|
||||
{ path: '/', name: 'home', component: HomeView },
|
||||
{ path: '/tool/md5', name: 'md5', component: Md5View },
|
||||
{ path: '/tool/pdf', name: 'pdf', component: PdfView },
|
||||
|
||||
// 新增路由
|
||||
{ path: '/all-tools', name: 'all-tools', component: AllToolsView },
|
||||
{ path: '/faq', name: 'faq', component: FaqView }
|
||||
],
|
||||
scrollBehavior() {
|
||||
return { top: 0 }
|
||||
}
|
||||
})
|
||||
|
||||
export default router
|
||||
43
Zkit-C/src/stores/user.js
Normal file
43
Zkit-C/src/stores/user.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// src/stores/user.js
|
||||
import { ref, computed } from 'vue'
|
||||
import { defineStore } from 'pinia'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
// 状态 State
|
||||
const user = ref(localStorage.getItem('omni_user') || null)
|
||||
const isVip = ref(localStorage.getItem('omni_vip') === 'true')
|
||||
const showLoginModal = ref(false) // 控制全局登录弹窗显示
|
||||
|
||||
// 计算属性 Getters
|
||||
const isLoggedIn = computed(() => !!user.value)
|
||||
|
||||
// 动作 Actions
|
||||
function login(username) {
|
||||
user.value = username
|
||||
localStorage.setItem('omni_user', username)
|
||||
localStorage.setItem('omni_vip', 'false')
|
||||
showLoginModal.value = false
|
||||
}
|
||||
|
||||
function logout() {
|
||||
user.value = null
|
||||
isVip.value = false
|
||||
localStorage.removeItem('omni_user')
|
||||
localStorage.removeItem('omni_vip')
|
||||
}
|
||||
|
||||
function upgradeVip() {
|
||||
isVip.value = true
|
||||
localStorage.setItem('omni_vip', 'true')
|
||||
}
|
||||
|
||||
function openModal() {
|
||||
showLoginModal.value = true
|
||||
}
|
||||
|
||||
function closeModal() {
|
||||
showLoginModal.value = false
|
||||
}
|
||||
|
||||
return { user, isVip, isLoggedIn, showLoginModal, login, logout, upgradeVip, openModal, closeModal }
|
||||
})
|
||||
552
Zkit-C/src/views/AllToolsView.vue
Normal file
552
Zkit-C/src/views/AllToolsView.vue
Normal file
@@ -0,0 +1,552 @@
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import { ref, computed } from 'vue'
|
||||
|
||||
const router = useRouter()
|
||||
|
||||
// 1. 定义分类配置
|
||||
const categories = [
|
||||
{ id: 'all', name: '全部', icon: 'fa-solid fa-layer-group' },
|
||||
{ id: 'dev', name: '开发', icon: 'fa-solid fa-code' },
|
||||
{ id: 'media', name: '媒体', icon: 'fa-solid fa-photo-film' },
|
||||
{ id: 'crypto', name: '加密', icon: 'fa-solid fa-shield-halved' },
|
||||
{ id: 'life', name: '生活', icon: 'fa-solid fa-mug-hot' },
|
||||
]
|
||||
|
||||
// 当前选中的分类 ID
|
||||
const activeCategory = ref('all')
|
||||
|
||||
// 2. 工具数据
|
||||
const rawTools = [
|
||||
{ title: 'PDF 转 Word', desc: '精准保持排版,一键转换', icon: 'fa-solid fa-file-word', color: 'blue', vip: true, path: '/tool/pdf', category: 'media' },
|
||||
{ title: '图片压缩', desc: '智能压缩,保持清晰度', icon: 'fa-solid fa-image', color: 'rose', vip: false, category: 'media' },
|
||||
{ title: '图片去底', desc: 'AI 自动识别主体抠图', icon: 'fa-solid fa-wand-magic-sparkles', color: 'fuchsia', vip: false, category: 'media' },
|
||||
{ title: '视频转 GIF', desc: '截取片段生成表情包', icon: 'fa-solid fa-film', color: 'orange', vip: true, category: 'media' },
|
||||
{ title: '二维码生成', desc: '自定义颜色、Logo嵌入', icon: 'fa-solid fa-qrcode', color: 'gray', vip: false, category: 'media' },
|
||||
{ title: 'SQL 转 ER', desc: '一键生成数据库关系图', icon: 'fa-solid fa-database', color: 'cyan', vip: true, category: 'dev' },
|
||||
{ title: 'JSON 格式化', desc: '校验、高亮、折叠代码', icon: 'fa-solid fa-code', color: 'emerald', vip: false, category: 'dev' },
|
||||
{ title: '正则测试', desc: '实时匹配,包含常用公式', icon: 'fa-solid fa-check-double', color: 'violet', vip: false, category: 'dev' },
|
||||
{ title: 'Unix 时间戳', desc: '各时区时间快速转换', icon: 'fa-solid fa-clock', color: 'amber', vip: false, category: 'dev' },
|
||||
{ title: 'MD5 加密', desc: '不可逆加密,支持多种盐值', icon: 'fa-solid fa-lock', color: 'indigo', vip: false, path: '/tool/md5', category: 'crypto' },
|
||||
{ title: 'Base64 转码', desc: '文件与字符串互转', icon: 'fa-solid fa-font', color: 'slate', vip: false, category: 'crypto' },
|
||||
{ title: '随机密码', desc: '自定义长度与字符集', icon: 'fa-solid fa-key', color: 'teal', vip: false, category: 'crypto' },
|
||||
]
|
||||
|
||||
// 广告数据池
|
||||
const adPool = [
|
||||
{ title: '云服务器', desc: '新用户首年99元', tag: '广告', icon: 'fa-solid fa-server', btnText: '抢购' },
|
||||
{ title: 'AI 写作', desc: '自动生成文案', tag: '广告', icon: 'fa-solid fa-robot', btnText: '试用' },
|
||||
]
|
||||
|
||||
// 3. 计算逻辑
|
||||
const displayList = computed(() => {
|
||||
let tools = activeCategory.value === 'all' ? rawTools : rawTools.filter(t => t.category === activeCategory.value)
|
||||
const list = []
|
||||
let adIndex = 0
|
||||
tools.forEach((tool, index) => {
|
||||
list.push({ type: 'tool', ...tool })
|
||||
// 每 6 个工具插一个广告
|
||||
if (index > 0 && (index + 1) % 6 === 0) {
|
||||
const ad = adPool[adIndex % adPool.length]
|
||||
list.push({ type: 'ad', ...ad })
|
||||
adIndex++
|
||||
}
|
||||
})
|
||||
return list
|
||||
})
|
||||
|
||||
const getColorClass = (color) => `icon-bg-${color}`
|
||||
const handleToolClick = (item) => { if (item.path) router.push(item.path) }
|
||||
|
||||
const setCategory = (id) => {
|
||||
activeCategory.value = id
|
||||
if(window.innerWidth < 768) {
|
||||
const offset = document.querySelector('.main-layout').offsetTop - 140
|
||||
window.scrollTo({ top: Math.max(0, offset), behavior: 'smooth' })
|
||||
} else {
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="content-wrapper">
|
||||
|
||||
<div class="header-area">
|
||||
<span class="header-subtitle">ZKit Tools</span>
|
||||
<h1>全能在线工具库</h1>
|
||||
<p>收录 <strong>{{ rawTools.length }}+</strong> 款开发者与设计师必备工具</p>
|
||||
</div>
|
||||
|
||||
<div class="main-layout">
|
||||
|
||||
<aside class="category-sidebar">
|
||||
<div class="sidebar-track">
|
||||
<div
|
||||
v-for="cat in categories"
|
||||
:key="cat.id"
|
||||
class="nav-item"
|
||||
:class="{ active: activeCategory === cat.id }"
|
||||
@click="setCategory(cat.id)"
|
||||
>
|
||||
<i :class="cat.icon"></i>
|
||||
<span>{{ cat.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
<main class="tools-section">
|
||||
<transition name="fade" mode="out-in">
|
||||
<div v-if="displayList.length === 0" class="empty-state">
|
||||
<i class="fa-solid fa-box-open"></i>
|
||||
<p>该分类下暂无工具</p>
|
||||
</div>
|
||||
|
||||
<div v-else class="tools-grid" :key="activeCategory">
|
||||
<div
|
||||
v-for="(item, index) in displayList"
|
||||
:key="index"
|
||||
class="card-wrapper"
|
||||
:style="{ animationDelay: `${index * 0.03}s` }"
|
||||
>
|
||||
<!-- 普通工具卡片 -->
|
||||
<div
|
||||
v-if="item.type === 'tool'"
|
||||
class="card tool-card"
|
||||
@click="handleToolClick(item)"
|
||||
>
|
||||
<div v-if="item.vip" class="vip-badge">VIP</div>
|
||||
<div class="card-content">
|
||||
<div class="icon-wrapper" :class="getColorClass(item.color)">
|
||||
<i :class="item.icon"></i>
|
||||
</div>
|
||||
<div class="text-content">
|
||||
<h3 class="tool-title">{{ item.title }}</h3>
|
||||
<p class="tool-desc">{{ item.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 动态箭头 -->
|
||||
<div class="arrow-indicator">
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</div>
|
||||
<div class="hover-overlay"></div>
|
||||
</div>
|
||||
|
||||
<!-- 广告卡片:已统一为箭头样式 -->
|
||||
<div v-else class="card ad-card">
|
||||
<!-- 广告标签 (类似VIP标签) -->
|
||||
<div class="ad-badge">{{ item.tag }}</div>
|
||||
|
||||
<div class="card-content">
|
||||
<!-- 广告图标背景使用与工具卡片相同的样式 -->
|
||||
<div class="icon-wrapper" :class="getColorClass('blue')">
|
||||
<i :class="item.icon"></i>
|
||||
</div>
|
||||
<div class="text-content">
|
||||
<h3 class="tool-title">{{ item.title }}</h3>
|
||||
<p class="tool-desc">{{ item.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 统一的动态箭头 (代替原按钮) -->
|
||||
<div class="arrow-indicator">
|
||||
<i class="fa-solid fa-arrow-right"></i>
|
||||
</div>
|
||||
<div class="hover-overlay"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ---------- 基础变量适配 ---------- */
|
||||
:root {
|
||||
--sidebar-width: 200px;
|
||||
}
|
||||
|
||||
.page-container {
|
||||
background: var(--bg-body);
|
||||
min-height: 100vh;
|
||||
padding: 2rem 20px;
|
||||
color: var(--text-sub);
|
||||
}
|
||||
|
||||
.content-wrapper {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* ---------- 头部区域 ---------- */
|
||||
.header-area {
|
||||
text-align: center;
|
||||
margin-bottom: 3rem;
|
||||
animation: fadeInDown 0.6s ease-out;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
display: inline-block;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
color: var(--primary);
|
||||
background: rgba(37, 99, 235, 0.1);
|
||||
padding: 4px 10px;
|
||||
border-radius: 99px;
|
||||
margin-bottom: 0.8rem;
|
||||
letter-spacing: 0.5px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.header-area h1 {
|
||||
font-size: 2.2rem;
|
||||
color: var(--text-main);
|
||||
font-weight: 800;
|
||||
margin: 0 0 0.5rem 0;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
.header-area p {
|
||||
color: var(--text-sub);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* ---------- 布局结构 ---------- */
|
||||
.main-layout {
|
||||
display: flex;
|
||||
gap: 32px;
|
||||
align-items: flex-start;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* ---------- 侧边栏 / 导航 ---------- */
|
||||
.category-sidebar {
|
||||
width: var(--sidebar-width);
|
||||
flex-shrink: 0;
|
||||
position: sticky;
|
||||
top: 90px;
|
||||
}
|
||||
|
||||
.sidebar-track {
|
||||
background: var(--bg-card);
|
||||
border-radius: 12px;
|
||||
padding: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.nav-item {
|
||||
display: flex; align-items: center; gap: 10px;
|
||||
padding: 10px 14px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
color: var(--text-sub);
|
||||
font-weight: 500;
|
||||
font-size: 0.95rem;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.nav-item:hover {
|
||||
background: var(--bg-body);
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.3);
|
||||
}
|
||||
|
||||
.nav-item i { width: 20px; text-align: center; }
|
||||
|
||||
/* ---------- 右侧网格 ---------- */
|
||||
.tools-section { flex: 1; min-width: 0; }
|
||||
|
||||
.tools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.card-wrapper {
|
||||
animation: fadeInUp 0.5s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ---------- 卡片通用 ---------- */
|
||||
.card {
|
||||
background: var(--bg-card);
|
||||
border-radius: 16px;
|
||||
padding: 20px;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
border: 1px solid var(--border-color);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-4px);
|
||||
border-color: var(--border-hover);
|
||||
box-shadow: var(--shadow-md);
|
||||
}
|
||||
|
||||
/* Tool & Ad Card 内容通用布局 */
|
||||
.card-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 图标容器 */
|
||||
.icon-wrapper {
|
||||
width: 48px; height: 48px; border-radius: 12px;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
font-size: 1.3rem; flex-shrink: 0;
|
||||
transition: transform 0.3s;
|
||||
}
|
||||
|
||||
.card:hover .icon-wrapper { transform: scale(1.1); }
|
||||
|
||||
/* 文字 */
|
||||
.tool-title {
|
||||
font-size: 1.05rem; font-weight: 700; color: var(--text-main);
|
||||
margin: 0 0 6px 0;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.tool-desc {
|
||||
font-size: 0.85rem; color: var(--text-sub); margin: 0;
|
||||
line-height: 1.5;
|
||||
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
|
||||
}
|
||||
|
||||
/* 统一箭头动画样式 */
|
||||
.arrow-indicator {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
color: var(--primary);
|
||||
opacity: 0; /* 默认隐藏 */
|
||||
transform: translateX(-10px);
|
||||
transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
/* 悬停显现 */
|
||||
.card:hover .arrow-indicator {
|
||||
opacity: 1;
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
/* 标签样式 (VIP & 广告) */
|
||||
.vip-badge, .ad-badge {
|
||||
position: absolute; top: 12px; right: 12px;
|
||||
font-size: 0.6rem; font-weight: 800; padding: 2px 6px; border-radius: 4px;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.vip-badge {
|
||||
background: #fffbeb; color: #b45309; border: 1px solid #fde68a;
|
||||
}
|
||||
|
||||
.ad-badge {
|
||||
background: var(--primary); color: #fff; opacity: 0.8;
|
||||
}
|
||||
|
||||
/* Ad Card 样式 - 完全匹配工具卡片,只保留虚线边框作为区分 */
|
||||
.ad-card {
|
||||
/* 使用与普通卡片相同的背景和边框变量 */
|
||||
background: var(--bg-card);
|
||||
border: 1px dashed var(--border-color);
|
||||
}
|
||||
|
||||
/* 广告图标使用与工具卡片相同的样式,无需特殊处理 */
|
||||
/* 移除原有的 .ad-icon-bg 样式 */
|
||||
|
||||
/* 动画 */
|
||||
@keyframes fadeInDown { from { opacity: 0; transform: translateY(-20px); } to { opacity: 1; transform: translateY(0); } }
|
||||
@keyframes fadeInUp { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: translateY(0); } }
|
||||
.fade-enter-active, .fade-leave-active { transition: opacity 0.2s ease; }
|
||||
.fade-enter-from, .fade-leave-to { opacity: 0; }
|
||||
|
||||
/* =========================================
|
||||
📱 移动端布局终极修复 - 适配夜间模式
|
||||
========================================= */
|
||||
@media (max-width: 768px) {
|
||||
.page-container {
|
||||
padding: 1rem 12px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.content-wrapper { width: 100%; margin: 0; }
|
||||
|
||||
/* 头部区域 */
|
||||
.header-area {
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin-bottom: 1.5rem;
|
||||
padding: 0 4px;
|
||||
}
|
||||
|
||||
.header-subtitle {
|
||||
display: inline-block;
|
||||
font-size: 0.65rem;
|
||||
padding: 3px 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.header-area h1 {
|
||||
font-size: 1.8rem;
|
||||
margin-bottom: 0.5rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.header-area p {
|
||||
font-size: 0.9rem;
|
||||
opacity: 0.9;
|
||||
max-width: 90%;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
/* 布局重置 */
|
||||
.main-layout {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
/* 侧边栏 - 适配夜间模式 */
|
||||
.category-sidebar {
|
||||
width: 100% !important;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
margin: 0 0 12px 0;
|
||||
padding: 8px 4px;
|
||||
/* 使用CSS变量适配夜间模式 */
|
||||
background: var(--bg-body);
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.sidebar-track {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
overflow-x: auto;
|
||||
gap: 8px;
|
||||
padding: 0 4px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
scrollbar-width: none;
|
||||
}
|
||||
.sidebar-track::-webkit-scrollbar { display: none; }
|
||||
|
||||
.nav-item {
|
||||
flex-shrink: 0;
|
||||
padding: 6px 14px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 99px;
|
||||
font-size: 0.85rem;
|
||||
margin: 0;
|
||||
color: var(--text-sub);
|
||||
}
|
||||
|
||||
.nav-item.active {
|
||||
background: var(--primary);
|
||||
color: #fff;
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
.tools-section { width: 100% !important; display: block; }
|
||||
|
||||
/* 网格布局 */
|
||||
.tools-grid {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 10px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 卡片样式统一 (工具 & 广告现在完全一样) */
|
||||
.card {
|
||||
padding: 14px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
/* 因为没有了大按钮,高度统一为 155px 即可 */
|
||||
height: 155px;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
text-align: left;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.icon-wrapper {
|
||||
width: 40px; height: 40px;
|
||||
font-size: 1rem;
|
||||
border-radius: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.text-content { width: 100%; }
|
||||
|
||||
.tool-title {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 4px;
|
||||
white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
|
||||
width: 100%;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.tool-desc {
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.4;
|
||||
color: var(--text-sub);
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
overflow: hidden;
|
||||
height: 2.8em;
|
||||
padding-right: 14px;
|
||||
}
|
||||
|
||||
/* 箭头适配移动端:始终显示,但颜色淡 */
|
||||
.arrow-indicator {
|
||||
opacity: 1;
|
||||
transform: none;
|
||||
bottom: 12px;
|
||||
right: 12px;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text-sub); /* 使用主题变量 */
|
||||
}
|
||||
|
||||
.vip-badge, .ad-badge { top: 10px; right: 10px; font-size: 0.6rem; padding: 1px 5px; }
|
||||
}
|
||||
</style>
|
||||
215
Zkit-C/src/views/FaqView.vue
Normal file
215
Zkit-C/src/views/FaqView.vue
Normal file
@@ -0,0 +1,215 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
|
||||
const faqs = ref([
|
||||
{ q: 'OmniKit 需要下载安装吗?', a: '不需要。OmniKit 是完全基于浏览器的云端工具箱,您只需打开网页即可在 Windows、Mac 或手机上使用所有工具。', open: true },
|
||||
{ q: '我的文件安全吗?会泄露吗?', a: '非常安全。对于图片压缩、MD5 计算等工具,我们采用 WebAssembly 技术在您本地浏览器处理,文件从未上传至服务器。对于 PDF 转换等必须上传的工具,文件会在处理完成 1 小时后从服务器物理删除。', open: false },
|
||||
{ q: '如何开通 VIP 会员?', a: '点击右上角的“登录”按钮,注册或登录账号后,在个人中心选择“升级 VIP”。支持微信和支付宝支付,开通后即时生效。', open: false },
|
||||
{ q: 'VIP 有什么特权?', a: '1. 解锁 PDF 转 Word、视频处理等高算力工具;2. 移除全站所有广告;3. 优先获得极速服务器资源;4. 专属客服支持。', open: false },
|
||||
{ q: '如果转换失败怎么办?', a: '如果遇到文件转换失败,通常是因为文件加密或损坏。您可以尝试重新上传,或者通过底部的“联系我们”发送邮件反馈,我们的人工客服会帮您处理。', open: false },
|
||||
])
|
||||
|
||||
const toggle = (index) => {
|
||||
faqs.value[index].open = !faqs.value[index].open
|
||||
}
|
||||
|
||||
// 客服点击事件
|
||||
const handleContact = () => {
|
||||
// 这里可以换成跳转到工单系统,或者弹出邮箱
|
||||
alert('客服邮箱:support@omnikit.com\n工作时间:周一至周五 9:00 - 18:00')
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="page-container">
|
||||
<div class="container faq-wrapper">
|
||||
<h1 class="page-title">常见问题解答</h1>
|
||||
<p class="page-sub">这里列出了用户最关心的问题,希望能帮到您。</p>
|
||||
|
||||
<!-- 问题列表 -->
|
||||
<div class="faq-list">
|
||||
<div
|
||||
v-for="(item, index) in faqs"
|
||||
:key="index"
|
||||
class="faq-item"
|
||||
:class="{ active: item.open }"
|
||||
@click="toggle(index)"
|
||||
>
|
||||
<div class="faq-question">
|
||||
<span>{{ item.q }}</span>
|
||||
<i class="fa-solid" :class="item.open ? 'fa-chevron-up' : 'fa-chevron-down'"></i>
|
||||
</div>
|
||||
<div class="faq-answer" v-show="item.open">
|
||||
{{ item.a }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 新增:联系客服模块 -->
|
||||
<div class="contact-card">
|
||||
<div class="contact-icon">
|
||||
<i class="fa-solid fa-headset"></i>
|
||||
</div>
|
||||
<div class="contact-content">
|
||||
<h3>没有找到想要的答案?</h3>
|
||||
<p>如果您遇到技术问题或有商务合作意向,欢迎随时联系我们。</p>
|
||||
</div>
|
||||
<button class="btn-contact" @click="handleContact">联系客服</button>
|
||||
</div>
|
||||
|
||||
<!-- 页面底部广告 -->
|
||||
<div class="ad-banner">
|
||||
<i class="fa-brands fa-google"></i> 广告位:相关服务推荐
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* ---------- 基础 ---------- */
|
||||
.page-container {
|
||||
padding: 4rem 0;
|
||||
min-height: 80vh;
|
||||
background: var(--bg-body); /* 变量 */
|
||||
}
|
||||
.faq-wrapper {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
text-align: center;
|
||||
font-size: 2rem;
|
||||
color: var(--text-main); /* 变量 */
|
||||
margin-bottom: 0.5rem;
|
||||
font-weight: 800;
|
||||
}
|
||||
.page-sub {
|
||||
text-align: center;
|
||||
color: var(--text-sub); /* 变量 */
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
/* ---------- FAQ 列表 ---------- */
|
||||
.faq-item {
|
||||
border-bottom: 1px solid var(--border-color); /* 变量 */
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.faq-question {
|
||||
padding: 1.2rem 0;
|
||||
font-weight: 600;
|
||||
font-size: 1.1rem;
|
||||
color: var(--text-main); /* 变量 */
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
transition: color 0.2s;
|
||||
}
|
||||
.faq-question:hover {
|
||||
color: var(--primary); /* 变量 */
|
||||
}
|
||||
.faq-answer {
|
||||
padding-bottom: 1.5rem;
|
||||
color: var(--text-sub); /* 变量 */
|
||||
line-height: 1.7;
|
||||
font-size: 0.95rem;
|
||||
animation: slideDown 0.2s ease-out;
|
||||
}
|
||||
.faq-item.active .faq-question {
|
||||
color: var(--primary); /* 变量 */
|
||||
}
|
||||
|
||||
/* ---------- 联系客服卡片 ---------- */
|
||||
.contact-card {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-hover);
|
||||
border-radius: var(--radius);
|
||||
padding: 2rem;
|
||||
margin-top: 3rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
transition: background 0.3s, border 0.3s;
|
||||
}
|
||||
|
||||
.contact-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
background: var(--bg-body);
|
||||
color: var(--primary);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 1.5rem;
|
||||
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.05);
|
||||
flex-shrink: 0;
|
||||
transition: background 0.3s;
|
||||
}
|
||||
|
||||
.contact-content {
|
||||
flex: 1;
|
||||
}
|
||||
.contact-content h3 {
|
||||
margin: 0 0 5px 0;
|
||||
color: var(--text-main); /* 变量 */
|
||||
font-size: 1.1rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
.contact-content p {
|
||||
margin: 0;
|
||||
color: var(--text-sub); /* 变量 */
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
|
||||
.btn-contact {
|
||||
background: var(--primary); /* 变量 */
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 8px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.btn-contact:hover {
|
||||
background: var(--primary-dark); /* 变量 */
|
||||
}
|
||||
|
||||
/* ---------- 底部广告位 ---------- */
|
||||
.ad-banner {
|
||||
margin-top: 2rem;
|
||||
height: 100px;
|
||||
background: var(--bg-ad);
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: var(--radius);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-placeholder);
|
||||
transition: background 0.3s, border 0.3s;
|
||||
}
|
||||
|
||||
/* ---------- 动画 ---------- */
|
||||
@keyframes slideDown {
|
||||
from { opacity: 0; transform: translateY(-10px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
/* ---------- 移动端 ---------- */
|
||||
@media (max-width: 640px) {
|
||||
.contact-card {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
padding: 1.5rem;
|
||||
}
|
||||
.contact-content p {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.btn-contact {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
384
Zkit-C/src/views/HomeView.vue
Normal file
384
Zkit-C/src/views/HomeView.vue
Normal file
@@ -0,0 +1,384 @@
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
import ToolCard from '@/components/ToolCard.vue'
|
||||
|
||||
const router = useRouter()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="home-container">
|
||||
<!-- 1. Hero 区域 -->
|
||||
<div class="hero-wrapper">
|
||||
<h1 class="hero-title">
|
||||
瞬间解决繁琐工作<br>
|
||||
<span>让效率触手可及</span>
|
||||
</h1>
|
||||
|
||||
<p class="hero-desc">集成 50+ 款开发者、设计师及办公必备的在线工具。无需下载,安全隐私。</p>
|
||||
|
||||
<div class="search-box">
|
||||
<i class="fa-solid fa-magnifying-glass"></i>
|
||||
<input placeholder="搜索工具,例如: PDF 转 Word, MD5, 正则..." />
|
||||
</div>
|
||||
|
||||
<div class="hot-tags">
|
||||
<span class="label">热门搜索:</span>
|
||||
<div class="tag-list">
|
||||
<span class="tag">PDF转换</span>
|
||||
<span class="tag">SQL格式化</span>
|
||||
<span class="tag">颜色提取</span>
|
||||
<span class="tag">Base64</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 2. 统计栏 (悬浮) -->
|
||||
<div class="stats-container container">
|
||||
<div class="stats-bar">
|
||||
<div class="stat-item">
|
||||
<span>50+</span> <small>免费在线工具</small>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span>0s</span> <small>无需安装等待</small>
|
||||
</div>
|
||||
<div class="stat-item">
|
||||
<span>100%</span> <small>本地处理更安全</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 3. 主内容区 -->
|
||||
<div class="container content-area">
|
||||
|
||||
<div class="ad-banner-top">
|
||||
<i class="fa-brands fa-google"></i> Google AdSense 顶部横幅 (Leaderboard)
|
||||
</div>
|
||||
|
||||
<!-- 第一部分:热门工具 -->
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">
|
||||
<i class="fa-solid fa-fire"></i> 热门工具
|
||||
</h2>
|
||||
<router-link to="/all-tools" active-class="active-link" class="view-all">查看全部 <i class="fa-solid fa-arrow-right"></i></router-link>
|
||||
</div>
|
||||
|
||||
<div class="tools-grid">
|
||||
<ToolCard
|
||||
title="PDF 转 Word"
|
||||
desc="精准保留原文档格式,一键转换为可编辑 Word。"
|
||||
icon="fa-solid fa-file-word"
|
||||
bgClass="bg-blue"
|
||||
:isVip="true"
|
||||
@click="router.push('/tool/pdf')"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="MD5 加密"
|
||||
desc="不可逆加密算法,支持批量生成与校验。"
|
||||
icon="fa-solid fa-lock"
|
||||
bgClass="bg-gray"
|
||||
@click="router.push('/tool/md5')"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="图片压缩"
|
||||
desc="智能压缩 PNG/JPG 图片体积,画质几乎无损。"
|
||||
icon="fa-solid fa-image"
|
||||
bgClass="bg-gray"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="图片去底"
|
||||
desc="AI 自动识别主体,3秒钟完成一键抠图。"
|
||||
icon="fa-solid fa-wand-magic-sparkles"
|
||||
bgClass="bg-gray"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="ad-banner-middle">
|
||||
<i class="fa-solid fa-ad"></i> 列表中间广告位 (Banner)
|
||||
</div>
|
||||
|
||||
<!-- 第二部分:开发人员 -->
|
||||
<div class="section-header">
|
||||
<h2 class="section-title">
|
||||
<i class="fa-solid fa-code" style="color: var(--primary);"></i> 开发人员
|
||||
</h2>
|
||||
</div>
|
||||
|
||||
<div class="tools-grid">
|
||||
<ToolCard
|
||||
title="SQL 转 ER 图"
|
||||
desc="粘贴建表语句,自动生成数据库关系图表。"
|
||||
icon="fa-solid fa-database"
|
||||
bgClass="bg-gray"
|
||||
:isVip="true"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="JSON 格式化"
|
||||
desc="校验、美化、压缩 JSON 数据,开发必备。"
|
||||
icon="fa-solid fa-brackets-curly"
|
||||
bgClass="bg-gray"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="Unix 时间戳"
|
||||
desc="时间戳与北京时间互转,支持毫秒级。"
|
||||
icon="fa-solid fa-clock"
|
||||
bgClass="bg-gray"
|
||||
/>
|
||||
|
||||
<ToolCard
|
||||
title="Crontab 表达式"
|
||||
desc="Linux 定时任务可视化生成器,无需记忆语法。"
|
||||
icon="fa-solid fa-terminal"
|
||||
bgClass="bg-gray"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
/* 动画定义 */
|
||||
@keyframes fadeInDown {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(-20px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
/* --- 英雄区域 --- */
|
||||
.hero-wrapper {
|
||||
text-align: center;
|
||||
padding: 5rem 1rem 7rem;
|
||||
background-image: radial-gradient(var(--dot-color) 1px, transparent 1px);
|
||||
background-size: 20px 20px;
|
||||
background-color: var(--bg-body);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 3.2rem;
|
||||
font-weight: 900;
|
||||
color: var(--text-main);
|
||||
margin-bottom: 1.5rem;
|
||||
line-height: 1.3;
|
||||
letter-spacing: -1px;
|
||||
animation: fadeInDown 0.8s ease-out;
|
||||
}
|
||||
|
||||
.hero-title span {
|
||||
color: var(--primary);
|
||||
display: block;
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.hero-desc {
|
||||
color: var(--text-sub);
|
||||
margin-bottom: 2.5rem;
|
||||
font-size: 1.15rem;
|
||||
animation: fadeInDown 0.8s ease-out 0.1s backwards;
|
||||
}
|
||||
|
||||
/* --- 搜索框 --- */
|
||||
.search-box {
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
animation: fadeInDown 0.8s ease-out 0.2s backwards;
|
||||
}
|
||||
|
||||
.search-box input {
|
||||
width: 100%;
|
||||
padding: 18px 25px 18px 55px;
|
||||
border-radius: 50px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-main);
|
||||
border: 1px solid var(--border-color);
|
||||
outline: none;
|
||||
font-size: 1.05rem;
|
||||
box-shadow: 0 10px 25px -5px rgba(0,0,0,0.05);
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.search-box input:focus {
|
||||
border-color: var(--primary);
|
||||
box-shadow: 0 0 0 4px rgba(37,99,235,0.15);
|
||||
}
|
||||
|
||||
.search-box input::placeholder {
|
||||
color: var(--text-sub);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.search-box i {
|
||||
position: absolute;
|
||||
left: 25px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--text-sub);
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
|
||||
/* --- 热门标签 --- */
|
||||
.hot-tags {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
margin-top: 25px;
|
||||
color: var(--text-sub);
|
||||
font-size: 0.95rem;
|
||||
animation: fadeInDown 0.8s ease-out 0.3s backwards;
|
||||
}
|
||||
|
||||
.tag-list {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.tag {
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border-color);
|
||||
padding: 6px 16px;
|
||||
border-radius: 8px;
|
||||
color: var(--text-sub);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.tag:hover {
|
||||
color: var(--primary);
|
||||
border-color: var(--border-hover);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
/* --- 统计栏(悬浮) --- */
|
||||
.stats-container {
|
||||
position: relative;
|
||||
margin-top: -40px;
|
||||
z-index: 10;
|
||||
animation: fadeInDown 0.8s ease-out 0.4s backwards;
|
||||
}
|
||||
|
||||
.stats-bar {
|
||||
background: var(--bg-card);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
box-shadow: var(--shadow-md);
|
||||
border: 1px solid var(--border-color);
|
||||
display: flex;
|
||||
justify-content: space-around;
|
||||
transition: background 0.3s ease;
|
||||
}
|
||||
|
||||
.stat-item {
|
||||
text-align: center;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.stat-item span {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 800;
|
||||
color: var(--primary);
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.stat-item:nth-child(2) span {
|
||||
color: #10b981;
|
||||
}
|
||||
|
||||
.stat-item:nth-child(3) span {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.stat-item small {
|
||||
color: var(--text-sub);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
/* --- 主内容区 --- */
|
||||
.content-area {
|
||||
padding-bottom: 4rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.ad-banner-top {
|
||||
margin-top: 2rem;
|
||||
margin-bottom: 1rem;
|
||||
height: 100px;
|
||||
background: var(--bg-ad);
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--text-sub);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.ad-banner-middle {
|
||||
margin: 4rem 0;
|
||||
padding: 2rem;
|
||||
background: var(--bg-ad);
|
||||
border: 2px dashed var(--border-color);
|
||||
border-radius: 12px;
|
||||
color: var(--text-sub);
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 1.5rem;
|
||||
margin-top: 2rem;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 1.5rem;
|
||||
color: var(--text-main);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.section-title i {
|
||||
color: #ef4444;
|
||||
}
|
||||
|
||||
.view-all {
|
||||
color: var(--primary);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.tools-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(270px, 1fr));
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.stats-bar {
|
||||
flex-direction: column;
|
||||
gap: 2rem;
|
||||
}
|
||||
|
||||
.hero-title {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.hot-tags {
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
76
Zkit-C/src/views/Md5View.vue
Normal file
76
Zkit-C/src/views/Md5View.vue
Normal file
@@ -0,0 +1,76 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import md5 from 'blueimp-md5'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const input = ref('')
|
||||
const result = ref('')
|
||||
|
||||
const doEncrypt = () => {
|
||||
// 权限控制:未登录则弹窗
|
||||
if (!userStore.isLoggedIn) {
|
||||
userStore.openModal()
|
||||
return
|
||||
}
|
||||
if (!input.value) return alert('请输入内容')
|
||||
|
||||
result.value = md5(input.value).toUpperCase()
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container layout-grid">
|
||||
<main>
|
||||
<div class="ad-banner">内容页顶部广告</div>
|
||||
|
||||
<div class="tool-content">
|
||||
<h1>MD5 在线加密</h1>
|
||||
<p class="tool-desc">输入文本,生成 32 位哈希值。</p>
|
||||
|
||||
<textarea v-model="input" class="tool-textarea"></textarea>
|
||||
<button class="btn-full" @click="doEncrypt">生成 MD5</button>
|
||||
|
||||
<div v-if="result" class="result-box">
|
||||
<strong>结果:</strong> {{ result }}
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<aside>
|
||||
<div class="ad-sidebar">侧边栏广告</div>
|
||||
<div class="ad-sidebar ad-sidebar-tall">长条广告</div>
|
||||
</aside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tool-desc {
|
||||
color: var(--text-sub);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.tool-textarea {
|
||||
width: 100%;
|
||||
height: 120px;
|
||||
padding: 15px;
|
||||
border: 1px solid var(--border-color);
|
||||
border-radius: 8px;
|
||||
background: var(--bg-input);
|
||||
color: var(--text-main);
|
||||
font-family: inherit;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
.result-box {
|
||||
margin-top: 20px;
|
||||
background: var(--bg-body);
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.ad-sidebar-tall {
|
||||
height: 400px;
|
||||
}
|
||||
</style>
|
||||
131
Zkit-C/src/views/PdfView.vue
Normal file
131
Zkit-C/src/views/PdfView.vue
Normal file
@@ -0,0 +1,131 @@
|
||||
<script setup>
|
||||
import { useUserStore } from '@/stores/user'
|
||||
|
||||
const userStore = useUserStore()
|
||||
|
||||
const handleUpgrade = () => {
|
||||
if(confirm('确认支付 9.9元 开通 VIP?')) {
|
||||
userStore.upgradeVip()
|
||||
alert('支付成功!')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="container layout-grid">
|
||||
<main>
|
||||
<div class="ad-banner">VIP 页顶部广告</div>
|
||||
|
||||
<div class="tool-content tool-locked">
|
||||
<!-- 真实内容 (根据权限决定是否模糊) -->
|
||||
<div :class="{ 'content-blur': !userStore.isLoggedIn || !userStore.isVip }">
|
||||
<h1>PDF 转 Word</h1>
|
||||
<div class="upload-area">
|
||||
<i class="fa-solid fa-cloud-upload-alt upload-icon"></i>
|
||||
<p>点击上传 PDF 文件</p>
|
||||
</div>
|
||||
<button class="btn-full">开始转换</button>
|
||||
</div>
|
||||
|
||||
<!-- 遮罩层 -->
|
||||
<div v-if="!userStore.isLoggedIn" class="modal-overlay lock-overlay">
|
||||
<div class="lock-content">
|
||||
<i class="fa-solid fa-lock lock-icon"></i>
|
||||
<h3>请登录后使用</h3>
|
||||
<button class="btn-login" @click="userStore.openModal()">立即登录</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-else-if="!userStore.isVip" class="modal-overlay lock-overlay">
|
||||
<div class="lock-content">
|
||||
<i class="fa-solid fa-crown vip-icon"></i>
|
||||
<h3>会员专享功能</h3>
|
||||
<button class="btn-full btn-upgrade" @click="handleUpgrade">升级 VIP (¥9.9)</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<aside>
|
||||
<div class="vip-promo">
|
||||
<h3>升级 VIP</h3>
|
||||
<p>解锁所有高级功能,去除广告。</p>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.tool-locked {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.content-blur {
|
||||
filter: blur(5px);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.upload-area {
|
||||
border: 2px dashed var(--border-color);
|
||||
padding: 50px;
|
||||
text-align: center;
|
||||
margin-top: 30px;
|
||||
border-radius: 12px;
|
||||
background: var(--bg-body);
|
||||
}
|
||||
|
||||
.upload-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--text-placeholder);
|
||||
}
|
||||
|
||||
.lock-overlay {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.lock-content {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.lock-icon {
|
||||
font-size: 3rem;
|
||||
color: var(--text-sub);
|
||||
}
|
||||
|
||||
.vip-icon {
|
||||
font-size: 3rem;
|
||||
color: #f59e0b;
|
||||
}
|
||||
|
||||
.btn-upgrade {
|
||||
background: #f59e0b;
|
||||
}
|
||||
|
||||
.vip-promo {
|
||||
background: #fff7ed;
|
||||
padding: 20px;
|
||||
border-radius: 12px;
|
||||
border: 1px solid #fed7aa;
|
||||
}
|
||||
|
||||
.vip-promo h3 {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.vip-promo p {
|
||||
font-size: 0.9rem;
|
||||
color: #d97706;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
html.dark .vip-promo {
|
||||
background: rgba(217, 119, 6, 0.1);
|
||||
border-color: rgba(217, 119, 6, 0.3);
|
||||
}
|
||||
|
||||
html.dark .vip-promo h3,
|
||||
html.dark .vip-promo p {
|
||||
color: #fbbf24;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user