docs(website): 添加简记memo产品原型和UI设计规范文档
- 新增「简记memo」一体化小程序产品原型设计文档 - 新增简记memo完整版UI视觉设计规范和界面细节 - 添加IDEA项目配置文件.gitignore - 创建404页面HTML文件,包含响应式布局和错误提示 - 添加关于页面HTML文件,展示品牌介绍和团队信息 - 实现AES加解密工具函数,支持请求体加密 - 添加用户协议页面基础框架
This commit is contained in:
@@ -0,0 +1,238 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>404 - 页面走丢了</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
|
||||
min-height: 100vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
|
||||
overflow: hidden;
|
||||
}
|
||||
.container {
|
||||
text-align: center;
|
||||
padding: 40px 20px;
|
||||
max-width: 600px;
|
||||
width: 100%;
|
||||
}
|
||||
.error-code {
|
||||
font-size: 120px;
|
||||
font-weight: 800;
|
||||
background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 50%, #8b5cf6 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
line-height: 1;
|
||||
margin-bottom: 20px;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
.error-code::after {
|
||||
content: '404';
|
||||
position: absolute;
|
||||
left: 4px;
|
||||
top: 4px;
|
||||
background: linear-gradient(135deg, rgba(79, 158, 222, 0.2) 0%, rgba(139, 92, 246, 0.2) 100%);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
z-index: -1;
|
||||
}
|
||||
.memo-icon {
|
||||
font-size: 80px;
|
||||
margin-bottom: 24px;
|
||||
animation: float 3s ease-in-out infinite;
|
||||
}
|
||||
@keyframes float {
|
||||
0%, 100% { transform: translateY(0px); }
|
||||
50% { transform: translateY(-10px); }
|
||||
}
|
||||
.title {
|
||||
font-size: 28px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 16px;
|
||||
color: #64748b;
|
||||
margin-bottom: 32px;
|
||||
line-height: 1.6;
|
||||
}
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.btn {
|
||||
padding: 12px 28px;
|
||||
border-radius: 8px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
text-decoration: none;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.btn-primary {
|
||||
background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%);
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
.btn-primary:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 20px rgba(79, 158, 222, 0.3);
|
||||
}
|
||||
.btn-secondary {
|
||||
background: #fff;
|
||||
color: #475569;
|
||||
border: 1px solid #e2e8f0;
|
||||
}
|
||||
.btn-secondary:hover {
|
||||
background: #f8fafc;
|
||||
border-color: #cbd5e1;
|
||||
}
|
||||
.fun-fact {
|
||||
margin-top: 40px;
|
||||
padding: 16px 24px;
|
||||
background: rgba(255, 255, 255, 0.7);
|
||||
border-radius: 12px;
|
||||
font-size: 14px;
|
||||
color: #64748b;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.fun-fact strong {
|
||||
color: #4F9EDE;
|
||||
}
|
||||
/* 装饰元素 */
|
||||
.decoration {
|
||||
position: fixed;
|
||||
pointer-events: none;
|
||||
opacity: 0.1;
|
||||
}
|
||||
.decoration-1 {
|
||||
top: 10%;
|
||||
left: 10%;
|
||||
font-size: 60px;
|
||||
animation: spin 20s linear infinite;
|
||||
}
|
||||
.decoration-2 {
|
||||
top: 20%;
|
||||
right: 15%;
|
||||
font-size: 40px;
|
||||
animation: spin 15s linear infinite reverse;
|
||||
}
|
||||
.decoration-3 {
|
||||
bottom: 20%;
|
||||
left: 15%;
|
||||
font-size: 50px;
|
||||
animation: spin 25s linear infinite;
|
||||
}
|
||||
.decoration-4 {
|
||||
bottom: 15%;
|
||||
right: 10%;
|
||||
font-size: 35px;
|
||||
animation: spin 18s linear infinite reverse;
|
||||
}
|
||||
@keyframes spin {
|
||||
from { transform: rotate(0deg); }
|
||||
to { transform: rotate(360deg); }
|
||||
}
|
||||
/* 移动端适配 */
|
||||
@media (max-width: 768px) {
|
||||
.error-code {
|
||||
font-size: 80px;
|
||||
}
|
||||
.memo-icon {
|
||||
font-size: 60px;
|
||||
}
|
||||
.title {
|
||||
font-size: 22px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 14px;
|
||||
}
|
||||
.actions {
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
.btn {
|
||||
width: 100%;
|
||||
max-width: 280px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- 装饰元素 -->
|
||||
<div class="decoration decoration-1">📝</div>
|
||||
<div class="decoration decoration-2">✨</div>
|
||||
<div class="decoration decoration-3">🎯</div>
|
||||
<div class="decoration decoration-4">💡</div>
|
||||
|
||||
<div class="container">
|
||||
<div class="memo-icon">📝</div>
|
||||
<div class="error-code">404</div>
|
||||
<h1 class="title">哎呀,页面走丢了!</h1>
|
||||
<p class="subtitle">
|
||||
这个页面可能被风吹走了,或者被外星人带走了...<br>
|
||||
别担心,我们可以带你回到安全的地方!
|
||||
</p>
|
||||
<div class="actions">
|
||||
<a href="/" class="btn btn-primary">
|
||||
<svg style="width: 18px; height: 18px; fill: none; stroke: currentColor;" viewBox="0 0 24 24">
|
||||
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/>
|
||||
<polyline points="9 22 9 12 15 12 15 22"/>
|
||||
</svg>
|
||||
返回首页
|
||||
</a>
|
||||
<a href="/web/calendar" class="btn btn-secondary">
|
||||
<svg style="width: 18px; height: 18px; fill: none; stroke: currentColor;" viewBox="0 0 24 24">
|
||||
<rect x="3" y="4" width="18" height="18" rx="2" ry="2"/>
|
||||
<line x1="16" y1="2" x2="16" y2="6"/>
|
||||
<line x1="8" y1="2" x2="8" y2="6"/>
|
||||
<line x1="3" y1="10" x2="21" y2="10"/>
|
||||
</svg>
|
||||
去日程
|
||||
</a>
|
||||
</div>
|
||||
<div class="fun-fact">
|
||||
💡 <strong>冷知识:</strong>404 错误代码源于 1990 年的 CERN,当时办公室在 4 楼 404 房间!
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// 简单的鼠标跟随效果
|
||||
document.addEventListener('mousemove', function(e) {
|
||||
const decorations = document.querySelectorAll('.decoration');
|
||||
const x = e.clientX / window.innerWidth;
|
||||
const y = e.clientY / window.innerHeight;
|
||||
|
||||
decorations.forEach((dec, index) => {
|
||||
const speed = (index + 1) * 10;
|
||||
const xOffset = (x - 0.5) * speed;
|
||||
const yOffset = (y - 0.5) * speed;
|
||||
dec.style.transform = `translate(${xOffset}px, ${yOffset}px)`;
|
||||
});
|
||||
});
|
||||
|
||||
// 键盘快捷键
|
||||
document.addEventListener('keydown', function(e) {
|
||||
if (e.key === 'h' || e.key === 'H') {
|
||||
window.location.href = '/';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,148 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>关于我们 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<main style="padding: 84px 24px 40px; min-height: calc(100vh - 140px);">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<!-- 品牌介绍 -->
|
||||
<section style="text-align: center; margin-bottom: 48px;">
|
||||
<img id="brandLogo" src="/static/images/logo.png" alt="logo" style="width: 80px; height: 80px; border-radius: 20px; margin: 0 auto 20px; display: block;" onerror="this.style.display='none'; document.getElementById('fallbackLogo').style.display='flex';">
|
||||
<div id="fallbackLogo" style="width: 80px; height: 80px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 20px; display: none; align-items: center; justify-content: center; margin: 0 auto 20px;">
|
||||
<span style="font-size: 32px;">📝</span>
|
||||
</div>
|
||||
<h1 style="font-size: 28px; font-weight: 700; color: #1e293b; margin-bottom: 12px;" id="brandName">简记memo</h1>
|
||||
<p style="font-size: 15px; color: #64748b; max-width: 500px; margin: 0 auto;" id="brandDesc">集待办任务、账单记录、心情日记于一体的个人事务管理工具</p>
|
||||
</section>
|
||||
|
||||
<!-- 关于我们 -->
|
||||
<section style="background: #fff; border-radius: 16px; padding: 24px; margin-bottom: 20px;">
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b; margin-bottom: 20px; display: flex; align-items: center;">
|
||||
<span style="width: 4px; height: 18px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 2px; margin-right: 10px;"></span>
|
||||
关于我们
|
||||
</h2>
|
||||
<div style="line-height: 1.8;">
|
||||
<p style="font-size: 14px; color: #475569; margin-bottom: 16px;">
|
||||
简记memo是一款致力于帮助用户更好地管理个人事务的工具。我们相信,良好的记录习惯能够让生活更加有序,让每一天都变得更有意义。
|
||||
</p>
|
||||
<p style="font-size: 14px; color: #475569; margin-bottom: 16px;">
|
||||
我们的愿景是打造一个简洁、优雅、实用的记录平台,让用户能够轻松记录待办任务、心情变化和收支情况,同时通过成长体系激励用户养成良好的习惯。
|
||||
</p>
|
||||
<p style="font-size: 14px; color: #475569;">
|
||||
团队成员来自不同领域,我们怀着对产品的热爱和对用户体验的追求,不断优化和完善产品,希望能够为用户带来更好的使用体验。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 联系我们 -->
|
||||
<section style="background: #fff; border-radius: 16px; padding: 24px; margin-bottom: 20px;">
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b; margin-bottom: 20px; display: flex; align-items: center;">
|
||||
<span style="width: 4px; height: 18px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 2px; margin-right: 10px;"></span>
|
||||
联系我们
|
||||
</h2>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 20px;">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(79, 158, 222, 0.1); border-radius: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<svg style="width: 20px; height: 20px; fill: none; stroke: #4F9EDE;" viewBox="0 0 24 24">
|
||||
<path d="M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 14px; color: #64748b;">邮箱</div>
|
||||
<div style="font-size: 15px; color: #1e293b;" id="contactEmail">support@jianji-memo.com</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(34, 197, 94, 0.1); border-radius: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<svg style="width: 20px; height: 20px; fill: none; stroke: #22c55e;" viewBox="0 0 24 24">
|
||||
<path d="M8.691 2.188C3.891 2.188 0 5.476 0 9.53c0 2.212 1.17 4.203 3.002 5.55a.59.59 0 01.213.665l-.39 1.48c-.019.07-.048.141-.048.213 0 .163.13.295.29.295a.326.326 0 00.167-.054l1.903-1.114a.864.864 0 01.717-.098 10.16 10.16 0 002.837.403c4.801 0 8.692-3.287 8.692-7.342 0-4.054-3.891-7.34-8.692-7.34z"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 14px; color: #64748b;">微信公众号</div>
|
||||
<div style="font-size: 15px; color: #1e293b;" id="contactWechat">简记memo</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 团队介绍 -->
|
||||
<section style="background: #fff; border-radius: 16px; padding: 24px;">
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b; margin-bottom: 20px; display: flex; align-items: center;">
|
||||
<span style="width: 4px; height: 18px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 2px; margin-right: 10px;"></span>
|
||||
团队成员
|
||||
</h2>
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 20px;">
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 64px; height: 64px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px;">
|
||||
<span style="font-size: 24px;">👨💻</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">产品经理</div>
|
||||
<div style="font-size: 12px; color: #64748b;">负责产品规划和设计</div>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 64px; height: 64px; background: linear-gradient(135deg, #22c55e 0%, #16a34a 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px;">
|
||||
<span style="font-size: 24px;">👩💻</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">前端开发</div>
|
||||
<div style="font-size: 12px; color: #64748b;">负责界面开发</div>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 64px; height: 64px; background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px;">
|
||||
<span style="font-size: 24px;">👨🔧</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">后端开发</div>
|
||||
<div style="font-size: 12px; color: #64748b;">负责服务端开发</div>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 64px; height: 64px; background: linear-gradient(135deg, #ec4899 0%, #db2777 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px;">
|
||||
<span style="font-size: 24px;">🎨</span>
|
||||
</div>
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">UI设计师</div>
|
||||
<div style="font-size: 12px; color: #64748b;">负责界面设计</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="commonFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/common-components.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('commonHeader').innerHTML = renderCommonHeader('/about');
|
||||
document.getElementById('commonFooter').innerHTML = renderCommonFooter();
|
||||
initCommonComponents();
|
||||
|
||||
if (typeof SITE_CONFIG !== 'undefined') {
|
||||
document.getElementById('brandName').textContent = SITE_CONFIG.name || '简记memo';
|
||||
document.getElementById('brandDesc').textContent = SITE_CONFIG.description || '集待办任务、账单记录、心情日记于一体的个人事务管理工具';
|
||||
document.getElementById('contactEmail').textContent = SITE_CONFIG.email || 'support@jianji-memo.com';
|
||||
if (SITE_CONFIG.wechat) {
|
||||
document.getElementById('contactWechat').textContent = SITE_CONFIG.wechat.name || '简记memo';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,106 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>用户协议 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f1f5f9; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<main style="padding: 84px 24px 40px; min-height: calc(100vh - 140px);">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div style="background: #fff; border-radius: 16px; padding: 40px;">
|
||||
<h1 style="font-size: 28px; font-weight: 700; color: #1e293b; text-align: center; margin-bottom: 12px;">简记memo用户协议</h1>
|
||||
<p style="font-size: 14px; color: #64748b; text-align: center; margin-bottom: 32px; padding-bottom: 20px; border-bottom: 1px dashed rgba(79, 158, 222, 0.2);">生效日期:2025年04月24日</p>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">1. 协议主体与范围</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">1.1 本协议是您(以下简称"用户")与简记memo服务(以下简称"本服务")个人运营者(以下简称"运营者")之间关于使用本服务所订立的契约,运营者为个人独立开发者,无所属企业/公司主体。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">1.2 您通过网站访问、使用本服务的行为,即视为您已阅读、理解并同意本协议全部条款及《隐私政策》;若您不同意本协议或《隐私政策》,应立即停止使用本服务。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">1.3 本协议适用于您使用本服务的所有行为,包括但不限于账号注册登录、数据录入、存储、编辑等核心功能。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">2. 服务内容与范围</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">2.1 本服务为用户提供免费的备忘录、待办事项、账单记录服务,核心功能包括文字内容录入、服务器存储、编辑、删除、数据同步等,所有功能仅基于用户身份识别提供。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">2.2 运营者有权根据技术发展、用户需求、法律法规调整等因素,不定期优化、调整或暂停部分/全部服务,重大调整将通过服务内公告提前7日告知用户。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">2.3 本服务为免费服务,运营者不承诺提供商业级别的服务稳定性,仅保障基础功能的正常可用。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">3. 用户权利与义务</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">3.1 权利:您有权按照本协议约定使用本服务核心功能;有权查看、编辑、删除自身存储在服务器的所有数据;有权向运营者反馈功能问题或优化建议;在遵守本协议的前提下,运营者保障您正常使用本服务的基础权益。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">3.2 义务:您保证使用本服务的行为符合《中华人民共和国网络安全法》《互联网信息服务管理办法》等法律法规及公序良俗;不得利用本服务存储、传播违法、违规、侵权、色情、暴力等内容;应对自身录入的内容及使用行为承担全部法律责任;不得对本服务进行反向工程、篡改、破解、爬虫抓取等损害服务正常运行的操作。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">3.3 您理解并同意,用户身份信息仅用于身份识别,运营者不会通过该信息获取您的其他账号信息。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">4. 服务的暂停与终止</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">4.1 运营者有权在以下情形暂停/终止服务:(1)您违反本协议约定且未在通知期限内纠正;(2)法律法规、相关平台规则要求暂停/终止;(3)不可抗力(如服务器故障、网络中断);(4)运营者自主停止运营(提前30日在服务内公告)。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">4.2 服务暂停期间,运营者将尽力恢复服务,但不承诺恢复时限;服务终止后,您存储在服务器的所有数据将保留7日,逾期将永久删除,运营者不承担数据找回、备份或赔偿责任。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">4.3 您可主动申请终止服务(联系运营者删除账号及所有数据),申请生效后,运营者将在15个工作日内完成数据清理。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">5. 免责声明</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">5.1 本服务仅提供基础数据存储与管理功能,运营者不对您存储内容的完整性、准确性、安全性做绝对担保;因网络故障、服务器维护、平台规则调整、您的设备问题等非运营方可控因素导致的数据丢失、功能异常,运营者不承担赔偿责任。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">5.2 本服务为个人开发的免费服务,运营者无义务提供7×24小时技术支持,仅根据实际情况在合理期限内解答用户咨询(通过预留邮箱/公众号)。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">5.3 因您违反法律法规或本协议约定,导致第三方索赔、行政机关处罚的,由您自行承担全部责任,运营者不承担连带责任。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">6. 协议的变更</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">6.1 运营者可根据法律法规调整、业务优化需要修改本协议,修改后的协议将在服务内公示,公示满7日后自动生效。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">6.2 若您不同意修改后的协议,应立即停止使用本服务并可申请删除账号数据;继续使用本服务的,视为您同意修改后的协议。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">7. 法律适用与争议解决</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">7.1 本协议适用中华人民共和国法律(不含港澳台地区法律)。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">7.2 因本协议产生的争议,双方应友好协商解决;协商不成的,任何一方可向运营者所在地有管辖权的人民法院提起诉讼。</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">8. 联系与其他</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;" id="agreementContact">8.1 若您对本协议有任何疑问,可通过以下方式联系运营者:联系邮箱:memo@miaoall.cn;微信公众号:孙三苗(sunsanmiao)。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">8.2 本协议的条款如被有权机关认定为无效或不可执行,不影响其余条款的效力。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">8.3 本协议与《隐私政策》共同构成您与运营者之间的完整约定,两者冲突时,以《隐私政策》中关于个人信息保护的内容为准。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="commonFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/common-components.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('commonHeader').innerHTML = renderCommonHeader('/agreement');
|
||||
document.getElementById('commonFooter').innerHTML = renderCommonFooter();
|
||||
initCommonComponents();
|
||||
|
||||
if (typeof SITE_CONFIG !== 'undefined') {
|
||||
const email = SITE_CONFIG.email || 'memo@miaoall.cn';
|
||||
const wechatName = SITE_CONFIG.wechat?.name || '孙三苗';
|
||||
const wechatAccount = SITE_CONFIG.wechat?.account || 'sunsanmiao';
|
||||
document.getElementById('agreementContact').textContent = `8.1 若您对本协议有任何疑问,可通过以下方式联系运营者:联系邮箱:${email};微信公众号:${wechatName}(${wechatAccount})。`;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,429 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>成长规则 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; }
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { transform: scale(1); }
|
||||
50% { transform: scale(1.05); }
|
||||
}
|
||||
|
||||
@keyframes glow {
|
||||
0%, 100% { box-shadow: 0 0 5px currentColor; }
|
||||
50% { box-shadow: 0 0 20px currentColor; }
|
||||
}
|
||||
|
||||
@keyframes slideIn {
|
||||
from { opacity: 0; transform: translateX(-20px); }
|
||||
to { opacity: 1; transform: translateX(0); }
|
||||
}
|
||||
|
||||
.animate-fadeInUp { animation: fadeInUp 0.6s ease forwards; }
|
||||
.animate-pulse { animation: pulse 2s ease-in-out infinite; }
|
||||
.animate-glow { animation: glow 2s ease-in-out infinite; }
|
||||
.animate-slideIn { animation: slideIn 0.4s ease forwards; }
|
||||
|
||||
.delay-1 { animation-delay: 0.1s; }
|
||||
.delay-2 { animation-delay: 0.2s; }
|
||||
.delay-3 { animation-delay: 0.3s; }
|
||||
.delay-4 { animation-delay: 0.4s; }
|
||||
.delay-5 { animation-delay: 0.5s; }
|
||||
|
||||
.card-hover:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 32px rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.rarity-primary { color: #9CA3AF; }
|
||||
.rarity-secondary { color: #10B981; }
|
||||
.rarity-advanced { color: #3B82F6; }
|
||||
.rarity-rare { color: #8B5CF6; }
|
||||
.rarity-epic { color: #F59E0B; }
|
||||
.rarity-legendary { color: #EF4444; }
|
||||
|
||||
.bg-rarity-primary { background: rgba(156, 163, 175, 0.1); }
|
||||
.bg-rarity-secondary { background: rgba(16, 185, 129, 0.1); }
|
||||
.bg-rarity-advanced { background: rgba(59, 130, 246, 0.1); }
|
||||
.bg-rarity-rare { background: rgba(139, 92, 246, 0.1); }
|
||||
.bg-rarity-epic { background: rgba(245, 158, 11, 0.1); }
|
||||
.bg-rarity-legendary { background: rgba(239, 68, 68, 0.1); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<main style="padding: 84px 24px 40px; min-height: calc(100vh - 140px);">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div style="text-align: center; margin-bottom: 48px;" class="animate-fadeInUp">
|
||||
<div style="width: 80px; height: 80px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 20px; display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; box-shadow: 0 8px 24px rgba(79, 158, 222, 0.3);">
|
||||
<span style="font-size: 40px;">📊</span>
|
||||
</div>
|
||||
<h1 style="font-size: 32px; font-weight: 700; color: #1e293b; margin-bottom: 12px;">成长规则</h1>
|
||||
<p style="font-size: 16px; color: #64748b; max-width: 500px; margin: 0 auto;">了解如何获取经验,提升等级,解锁成就与特权</p>
|
||||
</div>
|
||||
|
||||
<section id="levelSystem" class="animate-fadeInUp delay-1" style="background: #fff; border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(79, 158, 222, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">📈</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">等级体系</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">从Lv.1到Lv.10,等级越高,称号越拉风</p>
|
||||
</div>
|
||||
</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.6; margin-bottom: 24px;">
|
||||
成长等级从Lv.1到Lv.10,通过累计经验值提升等级。<strong style="color: #4F9EDE;">所有核心功能完全免费开放</strong>,不受等级限制!等级特权只是额外的荣誉展示和增值服务。
|
||||
</p>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px;">
|
||||
<div v-for="(level, index) in levels" :key="level.level"
|
||||
class="card-hover"
|
||||
style="padding: 16px; background: #f8fafc; border-radius: 8px; transition: all 0.3s; cursor: pointer; position: relative; overflow: hidden;"
|
||||
:style="{ animationDelay: `${0.1 * index}s` }">
|
||||
<div style="position: absolute; top: 0; right: 0; width: 60px; height: 60px; opacity: 0.1;">{{ level.icon }}</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; margin-bottom: 8px;">
|
||||
<div style="width: 32px; height: 32px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; color: #fff;"
|
||||
:style="{ background: `linear-gradient(135deg, ${level.color} 0%, ${adjustColor(level.color, -20)} 100%)` }">
|
||||
Lv.{{ level.level }}
|
||||
</div>
|
||||
<span style="font-size: 14px; font-weight: 600; color: #1e293b;">{{ level.title }}</span>
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin-bottom: 8px;">需要 {{ level.exp.toLocaleString() }} EXP</div>
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 4px;">
|
||||
<span v-for="privilege in level.privileges" :key="privilege"
|
||||
style="padding: 3px 8px; background: rgba(79, 158, 222, 0.08); border-radius: 4px; font-size: 11px; color: #4F9EDE;">
|
||||
{{ privilege }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="expRules" class="animate-fadeInUp delay-2" style="background: #fff; border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(245, 158, 11, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">⭐</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">经验获取</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">完成任务、记账、记录心情都能获得经验</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 16px; margin-bottom: 20px;">
|
||||
<div v-for="(rule, index) in expRules" :key="rule.name"
|
||||
class="card-hover"
|
||||
style="padding: 20px; background: #f8fafc; border-radius: 8px; transition: all 0.3s; animation: fadeInUp 0.5s ease forwards;"
|
||||
:style="{ animationDelay: `${0.15 * index}s` }">
|
||||
<div style="display: flex; align-items: center; gap: 12px; margin-bottom: 12px;">
|
||||
<div style="width: 44px; height: 44px; background: rgba(79, 158, 222, 0.1); border-radius: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<span style="font-size: 22px;">{{ rule.icon }}</span>
|
||||
</div>
|
||||
<div>
|
||||
<div style="font-size: 15px; font-weight: 600; color: #1e293b;">{{ rule.name }}</div>
|
||||
<div style="font-size: 12px; color: #64748b;">{{ rule.desc }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div style="padding: 10px 12px; background: rgba(79, 158, 222, 0.05); border-radius: 6px; margin-bottom: 8px;">
|
||||
<div style="font-size: 14px; font-weight: 600; color: #4F9EDE;">{{ rule.exp }}</div>
|
||||
</div>
|
||||
<p style="font-size: 12px; color: #64748b; line-height: 1.5;">{{ rule.detail }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 16px; background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.05) 100%); border-radius: 8px;">
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<svg style="width: 20px; height: 20px; fill: #f59e0b;" viewBox="0 0 24 24">
|
||||
<path d="M12 2l3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2z"/>
|
||||
</svg>
|
||||
<span style="font-size: 14px; font-weight: 500; color: #d97706;">每日经验上限:120 EXP</span>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="streakBonus" class="animate-fadeInUp delay-3" style="background: #fff; border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(239, 68, 68, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">🔥</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">连续打卡加成</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">连续记录天数越多,加成越高</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px;">
|
||||
<div v-for="(rule, index) in streakBonusRules" :key="rule.range"
|
||||
style="padding: 16px; background: #f8fafc; border-radius: 8px; text-align: center; animation: fadeInUp 0.5s ease forwards;"
|
||||
:style="{ animationDelay: `${0.1 * index}s` }">
|
||||
<div style="font-size: 16px; font-weight: 700; margin-bottom: 4px;" :style="{ color: rule.color }">
|
||||
{{ rule.bonus }} EXP/天
|
||||
</div>
|
||||
<div style="font-size: 12px; color: #64748b;">{{ rule.range }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="padding: 16px; background: #f0fdf4; border-radius: 8px; border-left: 4px solid #22c55e;">
|
||||
<p style="font-size: 13px; color: #166534; line-height: 1.5;">
|
||||
<strong>💡 温馨提示:</strong>断签后连续天数不会完全清零,而是根据断签前的连续天数保留一部分:1-7天清零,8-30天保留3天,31-90天保留7天,91天以上保留15天。
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="protection" class="animate-fadeInUp delay-4" style="background: #fff; border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(59, 130, 246, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">🛡️</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">新用户保护期</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">帮助新用户快速成长</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; flex-wrap: wrap; gap: 16px; align-items: center;">
|
||||
<div style="flex: 1; min-width: 200px; padding: 20px; background: linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(59, 130, 246, 0.05) 100%); border-radius: 8px;">
|
||||
<div style="font-size: 32px; font-weight: 700; color: #3B82F6; margin-bottom: 4px;">50%</div>
|
||||
<div style="font-size: 13px; color: #475569;">保护期内额外经验加成</div>
|
||||
</div>
|
||||
<div style="flex: 2; min-width: 280px;">
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.6;">
|
||||
新用户前7天享受保护期加成!保护期内每次获取经验都会额外获得50%的经验加成,帮助你快速成长。
|
||||
</p>
|
||||
<p style="font-size: 13px; color: #64748b; margin-top: 8px;">
|
||||
保护期结束后,连续天数中断时,只会减少部分天数,不会完全重置。
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="achievements" class="animate-fadeInUp delay-5" style="background: #fff; border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(139, 92, 246, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">🎖️</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">成就系统</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">完成特定目标可解锁成就,获得额外经验奖励</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap;">
|
||||
<button v-for="cat in achievementCategories" :key="cat.key"
|
||||
@click="activeCategory = cat.key"
|
||||
style="padding: 8px 16px; border-radius: 6px; font-size: 13px; font-weight: 500; cursor: pointer; transition: all 0.2s; border: none;"
|
||||
:style="activeCategory === cat.key
|
||||
? 'background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); color: #fff;'
|
||||
: 'background: #f1f5f9; color: #64748b;'">
|
||||
<span>{{ cat.icon }}</span> {{ cat.name }}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 20px; flex-wrap: wrap;">
|
||||
<div v-for="(rarity, key) in rarityConfigs" :key="key"
|
||||
style="display: flex; align-items: center; gap: 6px;">
|
||||
<div style="width: 12px; height: 12px; border-radius: 4px;" :style="{ background: rarity.color }"></div>
|
||||
<span style="font-size: 12px; color: #64748b;">{{ rarity.name }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px;">
|
||||
<div v-for="(achievement, index) in filteredAchievements" :key="achievement.id"
|
||||
class="card-hover"
|
||||
style="padding: 16px; border-radius: 8px; transition: all 0.3s; animation: fadeInUp 0.5s ease forwards;"
|
||||
:class="`bg-rarity-${achievement.rarity}`"
|
||||
:style="{ animationDelay: `${0.05 * index}s` }">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
||||
<div style="width: 40px; height: 40px; border-radius: 8px; display: flex; align-items: center; justify-content: center; font-size: 18px; background: rgba(255,255,255,0.8);">
|
||||
{{ achievement.icon || '🏆' }}
|
||||
</div>
|
||||
<div :class="`rarity-${achievement.rarity}`" style="font-size: 11px; font-weight: 500;">
|
||||
{{ achievement.rarityName || achievement.rarity }}
|
||||
</div>
|
||||
</div>
|
||||
<h4 style="font-size: 14px; font-weight: 600; color: #1e293b; margin-bottom: 4px;">{{ achievement.name }}</h4>
|
||||
<p style="font-size: 12px; color: #64748b; margin-bottom: 8px; line-height: 1.4;">{{ achievement.description || achievement.desc || '' }}</p>
|
||||
<div style="display: flex; align-items: center; justify-content: space-between;">
|
||||
<span style="font-size: 13px; font-weight: 600; color: #4F9EDE;">+{{ achievement.expReward || achievement.exp || 0 }} EXP</span>
|
||||
<span v-if="achievement.target" style="font-size: 11px; color: #94a3b8;">目标: {{ achievement.target }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="tips" class="animate-fadeInUp delay-1" style="background: linear-gradient(135deg, rgba(79, 158, 222, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%); border-radius: 8px; padding: 28px; margin-bottom: 20px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(255,255,255,0.8); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">💡</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">快速升级秘籍</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">掌握这些技巧,升级更快</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px;">
|
||||
<div v-for="(tip, index) in growthTips" :key="index"
|
||||
style="display: flex; align-items: flex-start; gap: 10px; padding: 14px; background: rgba(255,255,255,0.6); border-radius: 8px; animation: fadeInUp 0.5s ease forwards;"
|
||||
:style="{ animationDelay: `${0.1 * index}s` }">
|
||||
<div style="width: 24px; height: 24px; background: rgba(79, 158, 222, 0.1); border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; color: #4F9EDE; flex-shrink: 0;">
|
||||
{{ index + 1 }}
|
||||
</div>
|
||||
<p style="font-size: 13px; color: #475569; line-height: 1.5;">{{ tip }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="faq" class="animate-fadeInUp delay-2" style="background: #fff; border-radius: 8px; padding: 28px;">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px;">
|
||||
<div style="width: 40px; height: 40px; background: rgba(100, 116, 139, 0.1); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-right: 12px;">
|
||||
<span style="font-size: 20px;">❓</span>
|
||||
</div>
|
||||
<div>
|
||||
<h2 style="font-size: 18px; font-weight: 600; color: #1e293b;">常见问题</h2>
|
||||
<p style="font-size: 13px; color: #64748b;">解答你的疑惑</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="faq-list">
|
||||
<div v-for="(item, index) in faqList" :key="index"
|
||||
class="faq-item"
|
||||
style="border-bottom: 1px solid #f1f5f9; padding: 16px 0; animation: fadeInUp 0.5s ease forwards;"
|
||||
:style="{ animationDelay: `${0.1 * index}s` }">
|
||||
<div style="display: flex; align-items: center; gap: 10px; cursor: pointer;" @click="toggleFaq(index)">
|
||||
<div style="width: 24px; height: 24px; background: rgba(79, 158, 222, 0.1); border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; color: #4F9EDE; flex-shrink: 0;">
|
||||
Q
|
||||
</div>
|
||||
<span style="font-size: 14px; font-weight: 500; color: #1e293b;">{{ item.Q }}</span>
|
||||
<svg style="width: 16px; height: 16px; fill: none; stroke: #94a3b8; margin-left: auto; transition: transform 0.2s;"
|
||||
:style="{ transform: faqOpen === index ? 'rotate(180deg)' : 'rotate(0)' }" viewBox="0 0 24 24">
|
||||
<path d="M6 9l6 6 6-6"/>
|
||||
</svg>
|
||||
</div>
|
||||
<div v-show="faqOpen === index"
|
||||
style="margin-top: 12px; padding-left: 34px; animation: slideIn 0.3s ease;">
|
||||
<p style="font-size: 14px; color: #64748b; line-height: 1.6;">{{ item.A }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<div id="commonFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/toast.js"></script>
|
||||
<script src="/static/js/auth.js"></script>
|
||||
<script src="/static/js/common-components.js"></script>
|
||||
<script>
|
||||
var app = new Vue({
|
||||
el: '#app',
|
||||
data: {
|
||||
levels: [],
|
||||
expRules: [],
|
||||
streakBonusRules: [],
|
||||
achievementCategories: [],
|
||||
achievements: [],
|
||||
rarityConfigs: {},
|
||||
growthTips: [],
|
||||
faqList: [],
|
||||
activeCategory: "all",
|
||||
faqOpen: -1,
|
||||
loading: true
|
||||
},
|
||||
computed: {
|
||||
filteredAchievements: function() {
|
||||
if (this.activeCategory === "all") {
|
||||
return this.achievements;
|
||||
}
|
||||
return this.achievements.filter(item => item.category === this.activeCategory);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
adjustColor: function(hex, amount) {
|
||||
const num = parseInt(hex.replace('#', ''), 16);
|
||||
const r = Math.min(255, Math.max(0, (num >> 16) + amount));
|
||||
const g = Math.min(255, Math.max(0, ((num >> 8) & 0x00FF) + amount));
|
||||
const b = Math.min(255, Math.max(0, (num & 0x0000FF) + amount));
|
||||
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
||||
},
|
||||
toggleFaq: function(index) {
|
||||
this.faqOpen = this.faqOpen === index ? -1 : index;
|
||||
},
|
||||
loadRulesFromAPI: function() {
|
||||
fetch('/api/web/growth/rules')
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const sections = data.data.sections;
|
||||
sections.forEach(section => {
|
||||
switch (section.type) {
|
||||
case 'level_system':
|
||||
this.levels = section.levels || [];
|
||||
break;
|
||||
case 'exp_rules':
|
||||
this.expRules = section.rules || [];
|
||||
break;
|
||||
case 'streak_bonus':
|
||||
this.streakBonusRules = section.rules || [];
|
||||
break;
|
||||
case 'achievement_system':
|
||||
this.achievementCategories = [{ key: "all", name: "全部", icon: "📦" }].concat(section.categories || []);
|
||||
this.achievements = section.achievements || [];
|
||||
// 构建 rarityConfigs - 使用固定的稀有度配置
|
||||
this.rarityConfigs = {
|
||||
"初级": { name: "初级", color: "#9CA3AF" },
|
||||
"中级": { name: "中级", color: "#22C55E" },
|
||||
"高级": { name: "高级", color: "#3B82F6" },
|
||||
"稀有": { name: "稀有", color: "#8B5CF6" },
|
||||
"史诗": { name: "史诗", color: "#F59E0B" },
|
||||
"传说": { name: "传说", color: "#EF4444" }
|
||||
};
|
||||
break;
|
||||
case 'tips':
|
||||
this.growthTips = section.tips || [];
|
||||
break;
|
||||
case 'faq':
|
||||
this.faqList = section.questions || [];
|
||||
break;
|
||||
}
|
||||
});
|
||||
this.loading = false;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载规则失败:', err);
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
},
|
||||
mounted: function() {
|
||||
// 使用公共头部底部
|
||||
document.getElementById('commonHeader').innerHTML = renderCommonHeader('/growth-rules');
|
||||
document.getElementById('commonFooter').innerHTML = renderCommonFooter();
|
||||
initCommonComponents();
|
||||
// 从接口加载数据
|
||||
this.loadRulesFromAPI();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,257 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>成长中心 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; }
|
||||
.nav-link.active { background: rgba(79, 158, 222, 0.1); color: #4F9EDE; }
|
||||
.achievement-card { transition: all 0.2s; }
|
||||
.achievement-card:hover { transform: translateY(-2px); box-shadow: 0 4px 12px rgba(0,0,0,0.08); }
|
||||
.level-item { transition: all 0.15s; }
|
||||
.level-item:hover { background: #f1f5f9; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="webHeader"></div>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<div style="margin-top: 56px; min-height: calc(100vh - 56px - 40px); padding: 24px;">
|
||||
<div style="max-width: 1200px; margin: 0 auto;">
|
||||
<!-- 左右布局 -->
|
||||
<div style="display: grid; grid-template-columns: 360px 1fr; gap: 24px;">
|
||||
<!-- 左侧:等级信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<!-- 等级卡片 -->
|
||||
<div style="background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 12px; padding: 32px 24px; color: #fff; text-align: center;">
|
||||
<div style="width: 100px; height: 100px; background: rgba(255,255,255,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px; position: relative;">
|
||||
<div style="font-size: 36px; font-weight: 700;" id="userLevel">Lv.1</div>
|
||||
<div style="position: absolute; bottom: -6px; right: -6px; width: 32px; height: 32px; background: #fbbf24; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 14px;">⭐</div>
|
||||
</div>
|
||||
<h2 style="font-size: 20px; font-weight: 600; margin-bottom: 4px;">成长等级</h2>
|
||||
<p style="font-size: 13px; opacity: 0.9; margin-bottom: 20px;" id="expInfo">0 / 100 经验</p>
|
||||
|
||||
<div style="background: rgba(255,255,255,0.2); border-radius: 4px; height: 8px; overflow: hidden;">
|
||||
<div id="expBar" style="height: 100%; background: #fff; border-radius: 4px; transition: width 0.5s; width: 0%;"></div>
|
||||
</div>
|
||||
<p style="font-size: 12px; opacity: 0.85; margin-top: 8px;" id="expRemain">还需 100 经验升级</p>
|
||||
</div>
|
||||
|
||||
<!-- 统计数据 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 20px;">
|
||||
<h3 style="font-size: 14px; font-weight: 600; color: #1e293b; margin-bottom: 16px;">数据统计</h3>
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px;">
|
||||
<div style="text-align: center; padding: 16px; background: #f8fafc; border-radius: 8px;">
|
||||
<div style="font-size: 24px; font-weight: 700; color: #4F9EDE;" id="statTotalExp">0</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">累计经验</div>
|
||||
</div>
|
||||
<div style="text-align: center; padding: 16px; background: #f8fafc; border-radius: 8px;">
|
||||
<div style="font-size: 24px; font-weight: 700; color: #f59e0b;" id="statAchievements">0</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">获得成就</div>
|
||||
</div>
|
||||
<div style="text-align: center; padding: 16px; background: #f8fafc; border-radius: 8px;">
|
||||
<div style="font-size: 24px; font-weight: 700; color: #22c55e;" id="statDays">0</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">连续记录</div>
|
||||
</div>
|
||||
<div style="text-align: center; padding: 16px; background: #f8fafc; border-radius: 8px;">
|
||||
<div style="font-size: 24px; font-weight: 700; color: #8b5cf6;" id="statTasks">0</div>
|
||||
<div style="font-size: 12px; color: #64748b; margin-top: 4px;">完成任务</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 等级列表 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 20px; flex: 1;">
|
||||
<h3 style="font-size: 14px; font-weight: 600; color: #1e293b; margin-bottom: 16px;">等级列表</h3>
|
||||
<div id="levelList" style="display: flex; flex-direction: column; gap: 8px; max-height: 400px; overflow-y: auto;">
|
||||
<!-- 动态生成 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:成就展示 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 24px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 24px;">
|
||||
<h3 style="font-size: 18px; font-weight: 600; color: #1e293b;">我的成就</h3>
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<select id="achievementFilter" onchange="filterAchievements()" style="padding: 6px 12px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 13px; color: #475569; background: #fff; cursor: pointer;">
|
||||
<option value="all">全部</option>
|
||||
<option value="unlocked">已获取</option>
|
||||
<option value="locked">未获取</option>
|
||||
</select>
|
||||
<span style="font-size: 13px; color: #4F9EDE;" id="achievementProgress">0 / 8</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="achievementsGrid" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 16px;">
|
||||
<!-- 动态渲染 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="webFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/toast.js"></script>
|
||||
<script src="/static/js/auth.js"></script>
|
||||
<script src="/static/js/web-components.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 初始化公共头部底部
|
||||
initWebPage('growth');
|
||||
// 加载成长信息
|
||||
loadGrowthInfo();
|
||||
});
|
||||
|
||||
function loadGrowthInfo() {
|
||||
const token = localStorage.getItem('user_token');
|
||||
if (!token) return;
|
||||
|
||||
fetch('/api/web/growth/info', {
|
||||
headers: { 'token': token }
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const g = data.data;
|
||||
|
||||
// 等级信息(使用后端返回数据)
|
||||
document.getElementById('userLevel').textContent = 'Lv.' + g.level;
|
||||
document.getElementById('expInfo').textContent = g.levelProgress + '%';
|
||||
document.getElementById('expRemain').textContent = '还需 ' + g.nextLevelExp + ' 经验升级';
|
||||
document.getElementById('expBar').style.width = Math.min(g.levelProgress, 100) + '%';
|
||||
|
||||
// 统计数据
|
||||
document.getElementById('statTotalExp').textContent = g.experience || 0;
|
||||
document.getElementById('statAchievements').textContent = g.achievements?.unlocked || 0;
|
||||
document.getElementById('statDays').textContent = g.streakDays || 0;
|
||||
document.getElementById('statTasks').textContent = g.todayProgress?.tasksCompleted || 0;
|
||||
|
||||
// 成就进度
|
||||
const totalAch = g.achievements?.total || 0;
|
||||
const unlockedAch = g.achievements?.unlocked || 0;
|
||||
document.getElementById('achievementProgress').textContent = unlockedAch + ' / ' + totalAch;
|
||||
|
||||
// 渲染成就列表
|
||||
renderAchievements(g.achievements?.list || [], totalAch);
|
||||
|
||||
// 渲染等级列表(使用后端数据)
|
||||
renderLevelListFromAPI(g.level);
|
||||
}
|
||||
})
|
||||
.catch(err => console.error('加载成长信息失败:', err));
|
||||
}
|
||||
|
||||
// 渲染成就列表
|
||||
// 存储成就数据用于过滤
|
||||
let allAchievementsData = [];
|
||||
let unlockedAchievementIds = [];
|
||||
|
||||
function renderAchievements(unlockedList, total) {
|
||||
const container = document.getElementById('achievementsGrid');
|
||||
const token = localStorage.getItem('user_token');
|
||||
|
||||
// 先获取所有成就定义
|
||||
fetch('/api/web/growth/rules', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const sections = data.data.sections;
|
||||
const achSection = sections.find(s => s.type === 'achievement_system');
|
||||
if (achSection && achSection.achievements) {
|
||||
allAchievementsData = achSection.achievements;
|
||||
unlockedAchievementIds = unlockedList.map(a => a.id || a.achievement_id);
|
||||
|
||||
renderAchievementList('all');
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function filterAchievements() {
|
||||
const filter = document.getElementById('achievementFilter').value;
|
||||
renderAchievementList(filter);
|
||||
}
|
||||
|
||||
function renderAchievementList(filter) {
|
||||
const container = document.getElementById('achievementsGrid');
|
||||
let html = '';
|
||||
|
||||
let filteredAchievements = allAchievementsData;
|
||||
if (filter === 'unlocked') {
|
||||
filteredAchievements = allAchievementsData.filter(ach => unlockedAchievementIds.includes(ach.id));
|
||||
} else if (filter === 'locked') {
|
||||
filteredAchievements = allAchievementsData.filter(ach => !unlockedAchievementIds.includes(ach.id));
|
||||
}
|
||||
|
||||
filteredAchievements.forEach(ach => {
|
||||
const isUnlocked = unlockedAchievementIds.includes(ach.id);
|
||||
const color = ach.rarityColor || '#9CA3AF';
|
||||
const icon = ach.icon || '🏆';
|
||||
const name = ach.name || '未知成就';
|
||||
const desc = ach.description || ach.desc || '';
|
||||
const rarityName = ach.rarityName || '';
|
||||
|
||||
html += `
|
||||
<div class="achievement-card" style="text-align: center; padding: 20px; border-radius: 12px; background: ${isUnlocked ? 'linear-gradient(135deg, rgba(79,158,222,0.08) 0%, rgba(79,158,222,0.02) 100%)' : '#f8fafc'}; ${isUnlocked ? '' : 'opacity: 0.6;'}">
|
||||
<div style="width: 48px; height: 48px; border-radius: 12px; display: flex; align-items: center; justify-content: center; margin: 0 auto 12px; background: ${isUnlocked ? color : '#e2e8f0'}20;">
|
||||
<span style="font-size: 24px;">${isUnlocked ? icon : '🔒'}</span>
|
||||
</div>
|
||||
<h4 style="font-size: 14px; font-weight: 600; color: ${isUnlocked ? color : '#1e293b'}; margin-bottom: 4px;">${name}</h4>
|
||||
${desc ? '<p style="font-size: 12px; color: #64748b; margin-bottom: 4px;">' + desc + '</p>' : ''}
|
||||
${rarityName ? '<span style="display: inline-block; padding: 2px 8px; background: ' + color + '15; color: ' + color + '; font-size: 11px; border-radius: 4px;">' + rarityName + '</span>' : ''}
|
||||
${isUnlocked ? '<span style="display: inline-block; margin-top: 6px; padding: 2px 8px; background: #22c55e15; color: #22c55e; font-size: 11px; border-radius: 4px;">✓ 已解锁</span>' : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
if (filteredAchievements.length === 0) {
|
||||
html = '<div style="text-align: center; padding: 40px; color: #94a3b8; grid-column: 1/-1;"><span style="font-size: 48px; display: block; margin-bottom: 12px;">🏆</span><p>' + (filter === 'unlocked' ? '暂无已解锁成就' : filter === 'locked' ? '所有成就已解锁!' : '暂无成就数据') + '</p></div>';
|
||||
}
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderLevelListFromAPI(currentLevel) {
|
||||
const token = localStorage.getItem('user_token');
|
||||
fetch('/api/web/growth/rules', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const sections = data.data.sections;
|
||||
const levelSection = sections.find(s => s.type === 'level_system');
|
||||
if (levelSection && levelSection.levels) {
|
||||
const container = document.getElementById('levelList');
|
||||
let html = '';
|
||||
levelSection.levels.forEach(item => {
|
||||
const isCurrent = item.level === currentLevel;
|
||||
html += `
|
||||
<div style="display: flex; align-items: center; gap: 12px; padding: 12px; border-radius: 8px; ${isCurrent ? 'background: linear-gradient(135deg, rgba(79,158,222,0.1) 0%, rgba(79,158,222,0.05) 100%); border: 1px solid rgba(79,158,222,0.2);' : 'background: #f8fafc;'}">
|
||||
<div style="width: 36px; height: 36px; border-radius: 6px; display: flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; background: ${isCurrent ? 'linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%)' : '#e2e8f0'}; color: ${isCurrent ? '#fff' : '#64748b'};">Lv.${item.level}</div>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 13px; font-weight: 600; color: ${isCurrent ? '#4F9EDE' : '#1e293b'};">${item.icon} ${item.title}</div>
|
||||
<div style="font-size: 12px; color: #64748b;">${item.exp} 经验</div>
|
||||
</div>
|
||||
${isCurrent ? '<span style="font-size: 11px; color: #4F9EDE; background: rgba(79,158,222,0.1); padding: 2px 8px; border-radius: 4px;">当前</span>' : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
container.innerHTML = html;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,437 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>简记memo - 让记录更简单</title>
|
||||
<meta name="description" content="集待办任务、账单记录、心情日记于一体的个人事务管理工具">
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; }
|
||||
.nav-link.active { background: rgba(79, 158, 222, 0.1); color: #4F9EDE; }
|
||||
|
||||
/* 头部滚动效果增强 */
|
||||
.header-scrolled {
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.08) !important;
|
||||
}
|
||||
|
||||
/* Hero Section 增强 */
|
||||
.hero-section {
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-section::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: -50%;
|
||||
right: -10%;
|
||||
width: 600px;
|
||||
height: 600px;
|
||||
background: radial-gradient(circle, rgba(79, 158, 222, 0.08) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-section::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -30%;
|
||||
left: -5%;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: radial-gradient(circle, rgba(139, 92, 246, 0.06) 0%, transparent 70%);
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.hero-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
background: rgba(79, 158, 222, 0.1);
|
||||
border-radius: 20px;
|
||||
margin-bottom: 20px;
|
||||
border: 1px solid rgba(79, 158, 222, 0.15);
|
||||
animation: fadeInUp 0.6s ease forwards;
|
||||
}
|
||||
|
||||
@keyframes fadeInUp {
|
||||
from { opacity: 0; transform: translateY(20px); }
|
||||
to { opacity: 1; transform: translateY(0); }
|
||||
}
|
||||
|
||||
.hero-title-anim {
|
||||
animation: fadeInUp 0.6s ease 0.1s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hero-desc-anim {
|
||||
animation: fadeInUp 0.6s ease 0.2s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hero-btn-anim {
|
||||
animation: fadeInUp 0.6s ease 0.3s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.hero-card-anim {
|
||||
animation: fadeInUp 0.6s ease 0.4s forwards;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
transition: transform 0.25s ease, box-shadow 0.25s ease;
|
||||
}
|
||||
|
||||
.feature-card:hover {
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 24px rgba(0,0,0,0.06);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.hero-section {
|
||||
padding: 32px 16px 20px !important;
|
||||
min-height: calc(100vh - 56px) !important;
|
||||
padding-top: calc(56px + 32px) !important;
|
||||
}
|
||||
|
||||
.hero-section > div {
|
||||
max-width: 100% !important;
|
||||
}
|
||||
|
||||
.hero-section > div > div {
|
||||
grid-template-columns: 1fr !important;
|
||||
gap: 24px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.hero-section h1 {
|
||||
font-size: 32px !important;
|
||||
line-height: 1.3 !important;
|
||||
margin-bottom: 12px !important;
|
||||
}
|
||||
|
||||
.hero-section p {
|
||||
font-size: 15px !important;
|
||||
line-height: 1.6 !important;
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
.hero-section .feature-card {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.hero-section > div > div > div:last-child {
|
||||
order: -1;
|
||||
}
|
||||
|
||||
.hero-section > div > div > div:last-child > div:first-child {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.hero-section > div > div > div:last-child > div:first-child > div {
|
||||
width: 100% !important;
|
||||
height: auto !important;
|
||||
min-height: 180px;
|
||||
}
|
||||
|
||||
.hero-section .btn-group {
|
||||
justify-content: center !important;
|
||||
}
|
||||
|
||||
.hero-section .btn-group a {
|
||||
padding: 14px 24px !important;
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
#features {
|
||||
padding: 40px 16px !important;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: repeat(2, 1fr) !important;
|
||||
gap: 16px !important;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 20px !important;
|
||||
}
|
||||
|
||||
.feature-card h3 {
|
||||
font-size: 15px !important;
|
||||
}
|
||||
|
||||
.feature-card p {
|
||||
font-size: 13px !important;
|
||||
}
|
||||
|
||||
#about {
|
||||
padding: 40px 16px !important;
|
||||
}
|
||||
|
||||
.about-content {
|
||||
flex-direction: column !important;
|
||||
gap: 24px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.about-content > div {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
#footer {
|
||||
padding: 30px 16px !important;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.footer-links {
|
||||
flex-direction: column !important;
|
||||
gap: 12px !important;
|
||||
margin-bottom: 16px !important;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 480px) {
|
||||
.hero-section h1 {
|
||||
font-size: 28px !important;
|
||||
}
|
||||
|
||||
.hero-section p {
|
||||
font-size: 14px !important;
|
||||
}
|
||||
|
||||
.features-grid {
|
||||
grid-template-columns: 1fr !important;
|
||||
}
|
||||
|
||||
.feature-card {
|
||||
padding: 16px !important;
|
||||
}
|
||||
|
||||
.hero-section .btn-group {
|
||||
flex-direction: column !important;
|
||||
}
|
||||
|
||||
.hero-section .btn-group a {
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<!-- Hero Section -->
|
||||
<section class="hero-section" style="min-height: calc(100vh - 64px); display: flex; align-items: center; padding: 80px 24px 40px; background: linear-gradient(135deg, #e0f2fe 0%, #f0f9ff 50%, #faf5ff 100%);">
|
||||
<div style="max-width: 1000px; margin: 0 auto; width: 100%;">
|
||||
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 40px; align-items: center; position: relative; z-index: 1;">
|
||||
<div>
|
||||
<div class="hero-badge">
|
||||
<span style="width: 8px; height: 8px; background: #4F9EDE; border-radius: 50%; display: inline-block;"></span>
|
||||
<span style="font-size: 13px; color: #4F9EDE; font-weight: 500;">全新版本上线</span>
|
||||
</div>
|
||||
<h1 class="hero-title-anim" style="font-size: 42px; font-weight: 700; color: #1e293b; line-height: 1.2; margin-bottom: 16px;" id="siteName">简记memo</h1>
|
||||
<p class="hero-desc-anim" style="font-size: 16px; color: #64748b; line-height: 1.7; margin-bottom: 28px;" id="siteDesc">集待办任务、账单记录、心情日记于一体的个人事务管理工具。简洁优雅的界面设计,让记录成为一种享受。</p>
|
||||
<div class="btn-group hero-btn-anim" style="display: flex; gap: 12px; flex-wrap: wrap;">
|
||||
<a href="/login" style="display: inline-flex; align-items: center; justify-content: center; padding: 12px 28px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 8px; color: #fff; font-size: 14px; font-weight: 600; text-decoration: none; box-shadow: 0 4px 12px rgba(79, 158, 222, 0.3); transition: all 0.25s ease;">
|
||||
立即体验
|
||||
</a>
|
||||
<a href="#features" style="display: inline-flex; align-items: center; justify-content: center; padding: 12px 28px; background: #fff; border: 1px solid #e2e8f0; border-radius: 8px; color: #475569; font-size: 14px; font-weight: 500; text-decoration: none; transition: all 0.25s ease;">
|
||||
了解更多
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="hero-card-anim" style="position: relative;">
|
||||
<div style="width: 380px; height: 380px; background: linear-gradient(135deg, rgba(79, 158, 222, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%); border-radius: 16px; display: flex; align-items: center; justify-content: center;">
|
||||
<div style="width: 320px; height: 320px; background: #fff; border-radius: 12px; box-shadow: 0 8px 24px rgba(0,0,0,0.06); padding: 20px;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 16px;">
|
||||
<span style="font-size: 15px; font-weight: 600; color: #1e293b;">今日待办</span>
|
||||
<span style="font-size: 11px; color: #94a3b8;" id="currentDate">5月13日</span>
|
||||
</div>
|
||||
<div style="space-y: 10px;">
|
||||
<div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: #f8fafc; border-radius: 6px;">
|
||||
<div style="width: 20px; height: 20px; border-radius: 50%; border: 2px solid #4F9EDE; display: flex; align-items: center; justify-content: center;">
|
||||
<span style="color: #4F9EDE; font-size: 10px;">✓</span>
|
||||
</div>
|
||||
<span style="font-size: 13px; color: #1e293b; text-decoration: line-through; opacity: 0.6;">完成项目文档</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: #fef3c7; border-radius: 6px;">
|
||||
<div style="width: 20px; height: 20px; border-radius: 50%; border: 2px solid #f59e0b; display: flex; align-items: center; justify-content: center;">
|
||||
</div>
|
||||
<span style="font-size: 13px; color: #1e293b;">参加会议</span>
|
||||
<span style="margin-left: auto; padding: 3px 6px; background: rgba(245, 158, 11, 0.2); border-radius: 4px; font-size: 10px; color: #d97706;">重要</span>
|
||||
</div>
|
||||
<div style="display: flex; align-items: center; gap: 10px; padding: 10px; background: #f8fafc; border-radius: 6px;">
|
||||
<div style="width: 20px; height: 20px; border-radius: 50%; border: 2px solid #cbd5e1; display: flex; align-items: center; justify-content: center;">
|
||||
</div>
|
||||
<span style="font-size: 13px; color: #64748b;">整理文件</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Features Section -->
|
||||
<section id="features" style="padding: 60px 24px; background: #fff;">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div style="text-align: center; margin-bottom: 48px;">
|
||||
<span style="font-size: 13px; color: #4F9EDE; font-weight: 500;">功能特性</span>
|
||||
<h2 style="font-size: 28px; font-weight: 700; color: #1e293b; margin-top: 8px; margin-bottom: 12px;" id="featureTitle">为什么选择简记memo</h2>
|
||||
<p style="font-size: 15px; color: #64748b; max-width: 500px; margin: 0 auto;">简洁优雅的界面设计,强大实用的功能,帮助您更好地管理生活</p>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px;">
|
||||
<div class="feature-card" style="background: #f8fafc; border-radius: 8px; padding: 24px; transition: all 0.2s;">
|
||||
<div style="width: 48px; height: 48px; background: linear-gradient(135deg, rgba(79, 158, 222, 0.1) 0%, rgba(79, 158, 222, 0.05) 100%); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px;">📝</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">待办管理</h3>
|
||||
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">轻松创建和管理待办任务,设置优先级和截止日期,让您的日程井井有条。</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card" style="background: #fef3c7; border-radius: 8px; padding: 24px; transition: all 0.2s;">
|
||||
<div style="width: 48px; height: 48px; background: rgba(245, 158, 11, 0.15); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px;">😊</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">心情日记</h3>
|
||||
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">记录每一天的心情变化,用文字和表情记录生活的点点滴滴。</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card" style="background: #dcfce7; border-radius: 8px; padding: 24px; transition: all 0.2s;">
|
||||
<div style="width: 48px; height: 48px; background: rgba(34, 197, 94, 0.15); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px;">💰</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">账单记录</h3>
|
||||
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">便捷记录收支情况,分类统计一目了然,让理财变得简单。</p>
|
||||
</div>
|
||||
|
||||
<div class="feature-card" style="background: #e0e7ff; border-radius: 8px; padding: 24px; transition: all 0.2s;">
|
||||
<div style="width: 48px; height: 48px; background: rgba(99, 102, 241, 0.15); border-radius: 8px; display: flex; align-items: center; justify-content: center; margin-bottom: 16px;">
|
||||
<span style="font-size: 24px;">🏆</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">成长体系</h3>
|
||||
<p style="font-size: 13px; color: #64748b; line-height: 1.5;">完成任务获得经验值,提升等级解锁成就,让记录更有成就感。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- How It Works -->
|
||||
<section style="padding: 60px 24px; background: #f8fafc;">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div style="text-align: center; margin-bottom: 48px;">
|
||||
<span style="font-size: 13px; color: #4F9EDE; font-weight: 500;">使用流程</span>
|
||||
<h2 style="font-size: 28px; font-weight: 700; color: #1e293b; margin-top: 8px;">简单三步开始记录</h2>
|
||||
</div>
|
||||
|
||||
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 32px;">
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 72px; height: 72px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px;">
|
||||
<span style="font-size: 32px;">1️⃣</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">注册账号</h3>
|
||||
<p style="font-size: 13px; color: #64748b;">使用邮箱快速注册,开启您的记录之旅</p>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 72px; height: 72px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px;">
|
||||
<span style="font-size: 32px;">2️⃣</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">创建任务</h3>
|
||||
<p style="font-size: 13px; color: #64748b;">添加待办事项、记录心情或账单</p>
|
||||
</div>
|
||||
<div style="text-align: center;">
|
||||
<div style="width: 72px; height: 72px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; margin: 0 auto 16px;">
|
||||
<span style="font-size: 32px;">3️⃣</span>
|
||||
</div>
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 8px;">查看统计</h3>
|
||||
<p style="font-size: 13px; color: #64748b;">查看完成进度、等级成长和成就</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- CTA Section -->
|
||||
<section style="padding: 60px 24px; background: #fff;">
|
||||
<div style="max-width: 600px; margin: 0 auto; text-align: center;">
|
||||
<h2 style="font-size: 26px; font-weight: 700; color: #1e293b; margin-bottom: 12px;">开始您的记录之旅</h2>
|
||||
<p style="font-size: 15px; color: #64748b; margin-bottom: 28px;" id="ctaDesc">加入简记memo,让每一天都值得记录</p>
|
||||
<a href="/login" style="display: inline-flex; align-items: center; justify-content: center; padding: 14px 40px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 8px; color: #fff; font-size: 15px; font-weight: 600; text-decoration: none; box-shadow: 0 4px 12px rgba(79, 158, 222, 0.3);">
|
||||
立即体验
|
||||
</a>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="commonFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/common-components.js"></script>
|
||||
<script src="/static/js/toast.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('commonHeader').innerHTML = renderCommonHeader('/');
|
||||
document.getElementById('commonFooter').innerHTML = renderCommonFooter();
|
||||
initCommonComponents();
|
||||
|
||||
// 设置动态内容
|
||||
if (typeof SITE_CONFIG !== 'undefined') {
|
||||
document.getElementById('siteName').textContent = SITE_CONFIG.name || '简记memo';
|
||||
document.getElementById('featureTitle').textContent = '为什么选择' + (SITE_CONFIG.name || '简记memo');
|
||||
document.getElementById('ctaDesc').textContent = '加入' + (SITE_CONFIG.name || '简记memo') + ',让每一天都值得记录';
|
||||
}
|
||||
if (typeof APP_INFO !== 'undefined') {
|
||||
document.getElementById('siteDesc').textContent = APP_INFO.description + '。简洁优雅的界面设计,让记录成为一种享受。';
|
||||
}
|
||||
|
||||
// 设置当前日期
|
||||
const today = new Date();
|
||||
document.getElementById('currentDate').textContent = today.getMonth() + 1 + '月' + today.getDate() + '日';
|
||||
|
||||
const navLinks = document.querySelectorAll('a.nav-link');
|
||||
navLinks.forEach(link => {
|
||||
link.addEventListener('click', function(e) {
|
||||
if (this.getAttribute('href') === '#features') {
|
||||
e.preventDefault();
|
||||
document.querySelector('#features').scrollIntoView({ behavior: 'smooth' });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 头部滚动效果
|
||||
const header = document.querySelector('#commonHeader header');
|
||||
if (header) {
|
||||
let ticking = false;
|
||||
window.addEventListener('scroll', function() {
|
||||
if (!ticking) {
|
||||
window.requestAnimationFrame(function() {
|
||||
if (window.scrollY > 10) {
|
||||
header.classList.add('header-scrolled');
|
||||
} else {
|
||||
header.classList.remove('header-scrolled');
|
||||
}
|
||||
ticking = false;
|
||||
});
|
||||
ticking = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<script src="/static/js/auth.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,123 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>隐私政策 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
html { scroll-behavior: smooth; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f1f5f9; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="commonHeader"></div>
|
||||
|
||||
<!-- 页面内容 -->
|
||||
<main style="padding: 84px 24px 40px; min-height: calc(100vh - 140px);">
|
||||
<div style="max-width: 1000px; margin: 0 auto;">
|
||||
<div style="background: #fff; border-radius: 16px; padding: 40px;">
|
||||
<h1 style="font-size: 28px; font-weight: 700; color: #1e293b; text-align: center; margin-bottom: 12px;">简记memo隐私政策</h1>
|
||||
<p style="font-size: 14px; color: #64748b; text-align: center; margin-bottom: 32px; padding-bottom: 20px; border-bottom: 1px dashed rgba(79, 158, 222, 0.2);">生效日期:2025年04月24日</p>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">1. 总则</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">1.1 为保护用户隐私,规范本服务的个人信息处理行为,本服务运营者(个人开发者,以下简称"运营者")依据《中华人民共和国个人信息保护法》《中华人民共和国网络安全法》等法律法规,制定本隐私政策。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">1.2 本隐私政策适用于您使用本服务过程中涉及的个人信息处理活动,您使用本服务即视为同意本政策;若您不同意本政策,请勿使用本服务。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">1.3 本服务由个人独立开发及运营,无所属企业/公司主体,所有信息处理行为均由运营者个人负责。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">2. 个人信息的收集</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">2.1 登录相关信息:您注册账号时提供的邮箱地址、昵称等信息,用于识别您的用户身份。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">2.2 业务数据:您在本服务内录入的备忘录、待办事项、账单内容、编辑记录、时间戳等数据,将存储于运营者的服务器中,仅用于为您提供数据同步、查看、编辑等核心功能。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">2.3 设备与日志信息:为保障服务正常运行,可能被动获取基础设备信息(如设备型号、系统版本)、操作日志(如页面访问记录、功能使用记录),该类信息仅用于故障排查、功能优化。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">2.4 运营者不会主动收集您的姓名、身份证号、手机号、地理位置等其他个人敏感信息。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">3. 个人信息的使用</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">3.1 用户身份信息仅用于识别您的用户身份,保障您能正常访问、管理自己存储在服务器的业务数据。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">3.2 您的业务数据仅用于为您本人提供查看、编辑、删除、同步等核心功能,运营者不会利用该数据进行商业推广、数据分析、第三方共享等其他用途。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">3.3 设备与日志信息仅用于定位和解决服务运行故障、优化产品体验,不会用于识别个人身份或向第三方披露。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">4. 个人信息的存储与保护</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">4.1 存储方式:用户身份信息及您的业务数据存储于运营者的服务器中;设备与日志信息仅在故障排查期间临时存储,排查完成后即时删除。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">4.2 存储期限:用户身份信息与业务数据将持续存储,直至您主动申请删除账号;设备与日志信息存储期限不超过7日。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">4.3 安全措施:运营者通过服务器访问权限控制、数据加密传输等技术手段保障数据安全,但因网络攻击、服务器故障等非运营方可控因素导致的数据泄露,运营者不承担责任。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">4.4 您可通过服务内的删除功能清理自身业务数据,如需彻底删除账号及所有数据,可联系运营者协助处理。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">5. 个人信息的共享、转让与公开披露</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">5.1 运营者不会将您的身份信息、业务数据、设备信息等任何信息共享、转让给第三方,除非获得您的明确授权,或法律法规、司法机关/行政机关的强制性要求。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">5.2 未经您同意,运营者不会公开披露您的任何信息;因法律法规要求披露时,将在允许范围内尽可能提前通知您。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">6. 您的权利</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">6.1 您有权随时查看、编辑、删除自己存储在本服务内的业务数据;</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">6.2 您有权联系运营者申请删除账号及所有关联数据;</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">6.3 您有权向运营者查询自身个人信息的处理情况,运营者将在合理期限内予以答复;</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">6.4 若您发现本服务存在违规处理您信息的行为,有权向运营者反馈或向相关平台投诉。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">7. 未成年人保护</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">7.1 本服务面向所有年龄段用户,若未成年人使用本服务,应在监护人指导下进行,监护人应承担相应监护责任。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">7.2 运营者不会主动收集未成年人的任何额外个人信息,若监护人发现未成年人的信息被不当处理,可联系运营者协助处理。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">8. 隐私政策的变更</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">8.1 运营者可根据法律法规调整或业务优化,修改本隐私政策,修改后的政策将在服务内公示,公示满7日后生效。</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">8.2 若您不同意修改后的政策,应停止使用本服务并可申请删除账号数据;继续使用的,视为同意修改后的政策。</p>
|
||||
</div>
|
||||
|
||||
<div style="margin-bottom: 28px;">
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">9. 联系我们</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">9.1 若您对本隐私政策有任何疑问、意见或投诉,可通过以下方式联系运营者:</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; padding-left: 2em; margin-bottom: 6px;" id="privacyEmail">联系邮箱:memo@miaoall.cn</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; padding-left: 2em;" id="privacyWechat">微信公众号:孙三苗(sunsanmiao)</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">9.2 运营者将在15个工作日内回复您的咨询或处理请求。</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div style="font-size: 16px; font-weight: 600; color: #4F9EDE; background: rgba(79, 158, 222, 0.1); border-left: 4px solid #4F9EDE; padding: 10px 14px; border-radius: 0 8px 8px 0; margin-bottom: 14px;">10. 其他</div>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">10.1 本隐私政策的解释权归运营者个人所有;</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em; margin-bottom: 10px;">10.2 若本政策与法律法规冲突,以法律法规为准;</p>
|
||||
<p style="font-size: 14px; color: #475569; line-height: 1.8; text-indent: 2em;">10.3 您使用本服务的行为,同时受相关平台的隐私政策及运营规范约束。</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="commonFooter"></div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/common-components.js"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
document.getElementById('commonHeader').innerHTML = renderCommonHeader('/privacy');
|
||||
document.getElementById('commonFooter').innerHTML = renderCommonFooter();
|
||||
initCommonComponents();
|
||||
|
||||
if (typeof SITE_CONFIG !== 'undefined') {
|
||||
document.getElementById('privacyEmail').textContent = '联系邮箱:' + (SITE_CONFIG.email || 'memo@miaoall.cn');
|
||||
if (SITE_CONFIG.wechat) {
|
||||
document.getElementById('privacyWechat').textContent = '微信公众号:' + (SITE_CONFIG.wechat.name || '孙三苗') + '(' + (SITE_CONFIG.wechat.account || 'sunsanmiao') + ')';
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,509 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>个人中心 - 简记memo</title>
|
||||
<link rel="stylesheet" href="https://unpkg.com/element-ui@2.15.14/lib/theme-chalk/index.css">
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; background: #f8fafc; }
|
||||
.nav-link.active { background: rgba(79, 158, 222, 0.1); color: #4F9EDE; }
|
||||
.menu-card { transition: all 0.2s; cursor: pointer; }
|
||||
.menu-card:hover { transform: translateY(-2px); box-shadow: 0 8px 24px rgba(0,0,0,0.08); }
|
||||
.stat-card { transition: all 0.2s; }
|
||||
.stat-card:hover { transform: translateY(-2px); }
|
||||
.avatar-option { cursor: pointer; transition: all 0.2s; border: 2px solid transparent; }
|
||||
.avatar-option:hover { transform: scale(1.05); }
|
||||
.avatar-option.selected { border-color: #4F9EDE; box-shadow: 0 0 0 3px rgba(79, 158, 222, 0.2); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app">
|
||||
<!-- 公共头部 -->
|
||||
<div id="webHeader"></div>
|
||||
|
||||
<div style="margin-top: 56px; min-height: calc(100vh - 56px - 40px); padding: 24px;">
|
||||
<div style="max-width: 1200px; margin: 0 auto;">
|
||||
<!-- 左右布局 -->
|
||||
<div style="display: grid; grid-template-columns: 380px 1fr; gap: 24px;">
|
||||
<!-- 左侧:用户信息 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<!-- 用户信息卡片 -->
|
||||
<div style="background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 12px; padding: 32px 24px; color: #fff; text-align: center;">
|
||||
<div id="userAvatarContainer" style="width: 100px; height: 100px; border-radius: 50%; background: rgba(255,255,255,0.2); display: flex; align-items: center; justify-content: center; margin: 0 auto 20px; font-size: 40px; font-weight: 600; cursor: pointer; position: relative; overflow: hidden;" onclick="showAvatarModal()">
|
||||
U
|
||||
<div style="position: absolute; bottom: 0; left: 0; right: 0; height: 28px; background: rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 12px;">更换</div>
|
||||
</div>
|
||||
<h2 style="font-size: 22px; font-weight: 700; margin-bottom: 6px;" id="userNickname">用户</h2>
|
||||
<p style="font-size: 14px; opacity: 0.9; margin-bottom: 8px;" id="userEmail">未绑定邮箱</p>
|
||||
<p style="font-size: 13px; opacity: 0.8;" id="userSignature">还没有个性签名</p>
|
||||
<button onclick="showEditModal()" style="margin-top: 20px; padding: 10px 24px; background: rgba(255,255,255,0.2); border: 1px solid rgba(255,255,255,0.3); border-radius: 8px; font-size: 13px; color: #fff; cursor: pointer;">
|
||||
编辑资料
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<!-- 快捷入口 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 20px;">
|
||||
<h3 style="font-size: 14px; font-weight: 600; color: #1e293b; margin-bottom: 16px;">快捷入口</h3>
|
||||
<div style="display: flex; flex-direction: column; gap: 8px;">
|
||||
<div onclick="navigateTo('/web/calendar')" class="menu-card" style="display: flex; align-items: center; gap: 12px; padding: 14px; border-radius: 10px; background: #f8fafc;">
|
||||
<div style="width: 40px; height: 40px; background: linear-gradient(135deg, rgba(79, 158, 222, 0.1) 0%, rgba(79, 158, 222, 0.05) 100%); border-radius: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<svg style="width: 20px; height: 20px; fill: none; stroke: #4F9EDE;" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">日程管理</div>
|
||||
<div style="font-size: 12px; color: #64748b;">管理每日待办任务</div>
|
||||
</div>
|
||||
<svg style="width: 16px; height: 16px; fill: none; stroke: #94a3b8;" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg>
|
||||
</div>
|
||||
<div onclick="navigateTo('/web/growth')" class="menu-card" style="display: flex; align-items: center; gap: 12px; padding: 14px; border-radius: 10px; background: #f8fafc;">
|
||||
<div style="width: 40px; height: 40px; background: linear-gradient(135deg, rgba(245, 158, 11, 0.1) 0%, rgba(245, 158, 11, 0.05) 100%); border-radius: 10px; display: flex; align-items: center; justify-content: center;">
|
||||
<svg style="width: 20px; height: 20px; fill: none; stroke: #f59e0b;" viewBox="0 0 24 24"><path d="M6 9H4.5a2.5 2.5 0 0 1 0-5H6"/><path d="M18 9h1.5a2.5 2.5 0 0 0 0-5H18"/><path d="M4 22h16"/><path d="M10 14.66V17c0 .55-.47.98-.97 1.21C7.85 18.75 7 20.24 7 22"/><path d="M14 14.66V17c0 .55.47.98.97 1.21C16.15 18.75 17 20.24 17 22"/><path d="M18 2H6v7a6 6 0 0 0 12 0V2Z"/></svg>
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<div style="font-size: 14px; font-weight: 600; color: #1e293b;">成长中心</div>
|
||||
<div style="font-size: 12px; color: #64748b;">查看等级与成就</div>
|
||||
</div>
|
||||
<svg style="width: 16px; height: 16px; fill: none; stroke: #94a3b8;" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 设置 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 8px;">
|
||||
<div onclick="navigateTo('/about')" style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; cursor: pointer; border-radius: 8px; transition: all 0.15s;" onmouseover="this.style.background='#f8fafc'" onmouseout="this.style.background='transparent'">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<svg style="width: 18px; height: 18px; fill: none; stroke: #64748b;" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg>
|
||||
<span style="font-size: 14px; color: #1e293b;">关于我们</span>
|
||||
</div>
|
||||
<svg style="width: 16px; height: 16px; fill: none; stroke: #94a3b8;" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg>
|
||||
</div>
|
||||
<div onclick="navigateTo('/privacy')" style="display: flex; align-items: center; justify-content: space-between; padding: 14px 16px; cursor: pointer; border-radius: 8px; transition: all 0.15s;" onmouseover="this.style.background='#f8fafc'" onmouseout="this.style.background='transparent'">
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<svg style="width: 18px; height: 18px; fill: none; stroke: #64748b;" viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
|
||||
<span style="font-size: 14px; color: #1e293b;">隐私政策</span>
|
||||
</div>
|
||||
<svg style="width: 16px; height: 16px; fill: none; stroke: #94a3b8;" viewBox="0 0 24 24"><path d="M9 5l7 7-7 7"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 右侧:统计数据 -->
|
||||
<div style="display: flex; flex-direction: column; gap: 16px;">
|
||||
<!-- 统计卡片 -->
|
||||
<div style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px;">
|
||||
<div class="stat-card" style="background: #fff; border-radius: 12px; padding: 24px; text-align: center;">
|
||||
<div style="font-size: 32px; font-weight: 700; color: #4F9EDE;" id="statTodoTotal">0</div>
|
||||
<div style="font-size: 13px; color: #64748b; margin-top: 6px;">总待办</div>
|
||||
</div>
|
||||
<div class="stat-card" style="background: #fff; border-radius: 12px; padding: 24px; text-align: center;">
|
||||
<div style="font-size: 32px; font-weight: 700; color: #22c55e;" id="statTodoDone">0</div>
|
||||
<div style="font-size: 13px; color: #64748b; margin-top: 6px;">已完成</div>
|
||||
</div>
|
||||
<div class="stat-card" style="background: #fff; border-radius: 12px; padding: 24px; text-align: center;">
|
||||
<div style="font-size: 32px; font-weight: 700; color: #f59e0b;" id="statLevel">1</div>
|
||||
<div style="font-size: 13px; color: #64748b; margin-top: 6px;">当前等级</div>
|
||||
</div>
|
||||
<div class="stat-card" style="background: #fff; border-radius: 12px; padding: 24px; text-align: center;">
|
||||
<div style="font-size: 32px; font-weight: 700; color: #8b5cf6;" id="statExp">0</div>
|
||||
<div style="font-size: 13px; color: #64748b; margin-top: 6px;">经验值</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 成长进度 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 24px;">
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b; margin-bottom: 20px;">成长进度</h3>
|
||||
<div style="display: flex; align-items: center; gap: 20px;">
|
||||
<div style="width: 80px; height: 80px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 50%; display: flex; align-items: center; justify-content: center; color: #fff; font-size: 24px; font-weight: 700;">
|
||||
Lv.<span id="growthLevel">1</span>
|
||||
</div>
|
||||
<div style="flex: 1;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px;">
|
||||
<span style="font-size: 15px; font-weight: 600; color: #1e293b;">当前等级</span>
|
||||
<span style="font-size: 13px; color: #64748b;" id="growthExpText">0 / 100 经验</span>
|
||||
</div>
|
||||
<div style="background: #e2e8f0; border-radius: 4px; height: 10px; overflow: hidden;">
|
||||
<div id="growthExpBar" style="height: 100%; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border-radius: 4px; transition: width 0.5s; width: 0%;"></div>
|
||||
</div>
|
||||
<p style="font-size: 12px; color: #94a3b8; margin-top: 8px;" id="growthRemain">还需 100 经验升级</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 最近成就 -->
|
||||
<div style="background: #fff; border-radius: 12px; padding: 24px; flex: 1;">
|
||||
<div style="display: flex; align-items: center; justify-content: space-between; margin-bottom: 20px;">
|
||||
<h3 style="font-size: 16px; font-weight: 600; color: #1e293b;">我的成就</h3>
|
||||
<div style="display: flex; align-items: center; gap: 12px;">
|
||||
<select id="profileAchFilter" onchange="filterProfileAchievements()" style="padding: 6px 12px; border: 1px solid #e2e8f0; border-radius: 6px; font-size: 13px; color: #475569; background: #fff; cursor: pointer;">
|
||||
<option value="all">全部分类</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div id="recentAchievements" style="display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px;">
|
||||
<!-- 动态渲染 -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 公共底部 -->
|
||||
<div id="webFooter"></div>
|
||||
</div>
|
||||
|
||||
<!-- 编辑资料弹窗 -->
|
||||
<div id="editModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; padding: 24px;">
|
||||
<div style="background: #fff; border-radius: 16px; padding: 28px; width: 100%; max-width: 400px;">
|
||||
<h3 style="font-size: 18px; font-weight: 600; color: #1e293b; margin-bottom: 24px;">编辑资料</h3>
|
||||
<div style="margin-bottom: 20px;">
|
||||
<label style="display: block; font-size: 13px; font-weight: 500; color: #475569; margin-bottom: 8px;">昵称</label>
|
||||
<input type="text" id="editNickname" placeholder="请输入昵称" style="width: 100%; padding: 12px 16px; border: 1px solid #e2e8f0; border-radius: 10px; font-size: 14px; outline: none;" onfocus="this.style.borderColor='#4F9EDE'" onblur="this.style.borderColor='#e2e8f0'">
|
||||
</div>
|
||||
<div style="margin-bottom: 28px;">
|
||||
<label style="display: block; font-size: 13px; font-weight: 500; color: #475569; margin-bottom: 8px;">签名</label>
|
||||
<textarea id="editSignature" placeholder="请输入个性签名" rows="3" style="width: 100%; padding: 12px 16px; border: 1px solid #e2e8f0; border-radius: 10px; font-size: 14px; resize: none; outline: none;" onfocus="this.style.borderColor='#4F9EDE'" onblur="this.style.borderColor='#e2e8f0'"></textarea>
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
||||
<button onclick="closeEditModal()" style="padding: 10px 20px; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 14px; color: #475569; cursor: pointer; background: #fff;">取消</button>
|
||||
<button onclick="saveProfile()" style="padding: 10px 20px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border: none; border-radius: 8px; font-size: 14px; color: #fff; cursor: pointer;">保存</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 头像选择弹窗 -->
|
||||
<div id="avatarModal" style="display: none; position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.5); z-index: 1000; align-items: center; justify-content: center; padding: 24px;">
|
||||
<div style="background: #fff; border-radius: 16px; padding: 28px; width: 100%; max-width: 480px;">
|
||||
<h3 style="font-size: 18px; font-weight: 600; color: #1e293b; margin-bottom: 24px;">选择头像</h3>
|
||||
<div id="avatarGrid" style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; margin-bottom: 24px;">
|
||||
<!-- 动态生成头像选项 -->
|
||||
</div>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 12px;">
|
||||
<button onclick="closeAvatarModal()" style="padding: 10px 20px; border: 1px solid #e2e8f0; border-radius: 8px; font-size: 14px; color: #475569; cursor: pointer; background: #fff;">取消</button>
|
||||
<button onclick="saveAvatar()" style="padding: 10px 20px; background: linear-gradient(135deg, #4F9EDE 0%, #3A8BC9 100%); border: none; border-radius: 8px; font-size: 14px; color: #fff; cursor: pointer;">确认</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="https://unpkg.com/vue@2.6.14/dist/vue.min.js"></script>
|
||||
<script src="https://unpkg.com/element-ui@2.15.14/lib/index.js"></script>
|
||||
<script src="/static/js/config.js"></script>
|
||||
<script src="/static/js/toast.js"></script>
|
||||
<script src="/static/js/auth.js"></script>
|
||||
<script src="/static/js/web-components.js"></script>
|
||||
<script>
|
||||
// 默认头像列表
|
||||
const DEFAULT_AVATARS = [
|
||||
{ url: '/static/avatar/ginger_cat.jpg', name: 'ginger_cat', label: '橘猫' },
|
||||
{ url: '/static/avatar/ragdoll.jpg', name: 'ragdoll', label: '布偶猫' },
|
||||
{ url: '/static/avatar/hamster.jpg', name: 'hamster', label: '小仓鼠' },
|
||||
{ url: '/static/avatar/dragon.jpg', name: 'dragon', label: '龙' },
|
||||
{ url: '/static/avatar/pony.jpg', name: 'pony', label: '小马' },
|
||||
{ url: '/static/avatar/lamb.jpg', name: 'lamb', label: '小羊' },
|
||||
{ url: '/static/avatar/corgi.jpg', name: 'corgi', label: '柯基' },
|
||||
{ url: '/static/avatar/piglet.jpg', name: 'piglet', label: '小猪' },
|
||||
];
|
||||
|
||||
let selectedAvatarUrl = '';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
// 初始化公共头部底部
|
||||
initWebPage('profile');
|
||||
// 加载页面数据
|
||||
loadUserInfo();
|
||||
loadStats();
|
||||
loadGrowthInfo();
|
||||
renderAvatarGrid();
|
||||
});
|
||||
|
||||
function navigateTo(url) {
|
||||
window.location.href = url;
|
||||
}
|
||||
|
||||
function loadUserInfo() {
|
||||
const token = localStorage.getItem('user_token');
|
||||
const nickname = localStorage.getItem('user_nickname') || '用户';
|
||||
const avatar = localStorage.getItem('user_avatar');
|
||||
const email = localStorage.getItem('user_email');
|
||||
|
||||
document.getElementById('userNickname').textContent = nickname;
|
||||
// 优先显示localStorage中的邮箱
|
||||
if (email) {
|
||||
document.getElementById('userEmail').textContent = email;
|
||||
}
|
||||
|
||||
const avatarContainer = document.getElementById('userAvatarContainer');
|
||||
if (avatar) {
|
||||
avatarContainer.innerHTML = `<img src="${avatar}" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;"><div style="position: absolute; bottom: 0; left: 0; right: 0; height: 28px; background: rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 12px;">更换</div>`;
|
||||
selectedAvatarUrl = avatar;
|
||||
} else {
|
||||
avatarContainer.textContent = nickname.charAt(0).toUpperCase();
|
||||
}
|
||||
|
||||
fetch('/api/web/user/info', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const user = data.data;
|
||||
document.getElementById('userNickname').textContent = user.nickname || '用户';
|
||||
// 接口返回的邮箱优先
|
||||
if (user.email) {
|
||||
document.getElementById('userEmail').textContent = user.email;
|
||||
localStorage.setItem('user_email', user.email);
|
||||
}
|
||||
if (user.signature) {
|
||||
document.getElementById('userSignature').textContent = user.signature;
|
||||
}
|
||||
if (user.avatar) {
|
||||
const container = document.getElementById('userAvatarContainer');
|
||||
container.innerHTML = `<img src="${user.avatar}" style="width: 100%; height: 100%; border-radius: 50%; object-fit: cover;"><div style="position: absolute; bottom: 0; left: 0; right: 0; height: 28px; background: rgba(0,0,0,0.3); display: flex; align-items: center; justify-content: center; font-size: 12px;">更换</div>`;
|
||||
localStorage.setItem('user_avatar', user.avatar);
|
||||
selectedAvatarUrl = user.avatar;
|
||||
}
|
||||
localStorage.setItem('user_nickname', user.nickname || '用户');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadStats() {
|
||||
const token = localStorage.getItem('user_token');
|
||||
|
||||
// 加载成长信息
|
||||
fetch('/api/web/growth/info', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
document.getElementById('statLevel').textContent = data.data.level || 1;
|
||||
document.getElementById('statExp').textContent = data.data.total_exp || 0;
|
||||
}
|
||||
});
|
||||
|
||||
// 加载待办统计
|
||||
fetch('/api/web/todo/list', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'token': token },
|
||||
body: JSON.stringify({})
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const todos = data.data || [];
|
||||
const total = todos.length;
|
||||
const done = todos.filter(t => t.done === 1 || t.status === 1).length;
|
||||
document.getElementById('statTodoTotal').textContent = total;
|
||||
document.getElementById('statTodoDone').textContent = done;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadGrowthInfo() {
|
||||
const token = localStorage.getItem('user_token');
|
||||
|
||||
fetch('/api/web/growth/info', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const g = data.data;
|
||||
document.getElementById('growthLevel').textContent = g.level || 1;
|
||||
|
||||
// 直接使用后端返回的字段
|
||||
const currentExp = g.currentExp || 0;
|
||||
const expToNext = g.nextLevelExp || 100;
|
||||
const progress = g.levelProgress || 0;
|
||||
|
||||
document.getElementById('growthExpText').textContent = currentExp + ' / ' + expToNext + ' 经验';
|
||||
document.getElementById('growthRemain').textContent = '还需 ' + expToNext + ' 经验升级';
|
||||
document.getElementById('growthExpBar').style.width = Math.min(progress, 100) + '%';
|
||||
|
||||
// 更新统计数据
|
||||
document.getElementById('statLevel').textContent = g.level || 1;
|
||||
document.getElementById('statExp').textContent = g.experience || 0;
|
||||
|
||||
// 渲染成就
|
||||
renderProfileAchievements(g.achievements?.list || []);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 存储成就数据用于筛选
|
||||
let profileAchievementsData = [];
|
||||
let profileAchCategories = [];
|
||||
|
||||
// 渲染个人中心成就
|
||||
function renderProfileAchievements(unlockedList) {
|
||||
const token = localStorage.getItem('user_token');
|
||||
|
||||
// 获取成就分类
|
||||
fetch('/api/web/growth/rules', { headers: { 'token': token } })
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
const sections = data.data.sections;
|
||||
const achSection = sections.find(s => s.type === 'achievement_system');
|
||||
if (achSection && achSection.categories) {
|
||||
profileAchCategories = achSection.categories;
|
||||
// 更新分类下拉框 - 使用小写字段名
|
||||
const select = document.getElementById('profileAchFilter');
|
||||
if (select) {
|
||||
select.innerHTML = '<option value="all">全部分类</option>' +
|
||||
profileAchCategories.map(c => `<option value="${c.key || c.Key}">${c.name || c.Name}</option>`).join('');
|
||||
}
|
||||
}
|
||||
|
||||
// 按稀有度排序(传说 > 史诗 > 稀有 > 高级 > 中级 > 初级)
|
||||
const rarityOrder = { '传说': 6, '史诗': 5, '稀有': 4, '高级': 3, '中级': 2, '初级': 1 };
|
||||
profileAchievementsData = (unlockedList || []).sort((a, b) => {
|
||||
return (rarityOrder[b.rarityName || b.rarity] || 0) - (rarityOrder[a.rarityName || a.rarity] || 0);
|
||||
});
|
||||
|
||||
renderProfileAchList('all');
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
console.error('加载成就分类失败:', err);
|
||||
// 即使没有分类,也尝试渲染成就
|
||||
profileAchievementsData = unlockedList || [];
|
||||
renderProfileAchList('all');
|
||||
});
|
||||
}
|
||||
|
||||
function filterProfileAchievements() {
|
||||
const filter = document.getElementById('profileAchFilter').value;
|
||||
renderProfileAchList(filter);
|
||||
}
|
||||
|
||||
function renderProfileAchList(filter) {
|
||||
const container = document.getElementById('recentAchievements');
|
||||
|
||||
let filtered = profileAchievementsData;
|
||||
if (filter !== 'all') {
|
||||
filtered = profileAchievementsData.filter(ach => ach.category === filter);
|
||||
}
|
||||
|
||||
if (filtered.length === 0) {
|
||||
container.innerHTML = '<div style="text-align: center; padding: 40px; color: #94a3b8; grid-column: 1/-1;"><span style="font-size: 48px; display: block; margin-bottom: 12px;">🏆</span><p>' + (filter === 'all' ? '暂无成就,继续努力吧!' : '该分类暂无成就') + '</p></div>';
|
||||
return;
|
||||
}
|
||||
|
||||
let html = '';
|
||||
filtered.forEach(ach => {
|
||||
const color = ach.rarityColor || '#4F9EDE';
|
||||
const icon = ach.icon || '🏆';
|
||||
const name = ach.name || '未知成就';
|
||||
const desc = ach.description || ach.desc || '';
|
||||
const rarityName = ach.rarityName || ach.rarity || '';
|
||||
|
||||
html += `
|
||||
<div style="text-align: center; padding: 16px; border-radius: 12px; background: linear-gradient(135deg, rgba(79,158,222,0.08) 0%, rgba(79,158,222,0.02) 100%);">
|
||||
<div style="width: 40px; height: 40px; border-radius: 10px; display: flex; align-items: center; justify-content: center; margin: 0 auto 10px; background: ${color}20;">
|
||||
<span style="font-size: 20px;">${icon}</span>
|
||||
</div>
|
||||
<h4 style="font-size: 13px; font-weight: 600; color: ${color}; margin-bottom: 2px;">${name}</h4>
|
||||
${desc ? '<p style="font-size: 11px; color: #64748b; margin-bottom: 4px;">' + desc + '</p>' : ''}
|
||||
${rarityName ? '<span style="display: inline-block; padding: 2px 8px; background: ' + color + '15; color: ' + color + '; font-size: 10px; border-radius: 4px;">' + rarityName + '</span>' : ''}
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function renderAvatarGrid() {
|
||||
const container = document.getElementById('avatarGrid');
|
||||
let html = '';
|
||||
DEFAULT_AVATARS.forEach(avatar => {
|
||||
const isSelected = selectedAvatarUrl === avatar.url;
|
||||
html += `
|
||||
<div class="avatar-option ${isSelected ? 'selected' : ''}" onclick="selectAvatar('${avatar.url}', this)" style="border-radius: 50%; overflow: hidden; width: 80px; height: 80px; margin: 0 auto;">
|
||||
<img src="${avatar.url}" style="width: 100%; height: 100%; object-fit: cover;" alt="${avatar.label}">
|
||||
</div>
|
||||
`;
|
||||
});
|
||||
container.innerHTML = html;
|
||||
}
|
||||
|
||||
function selectAvatar(url, element) {
|
||||
selectedAvatarUrl = url;
|
||||
document.querySelectorAll('.avatar-option').forEach(el => el.classList.remove('selected'));
|
||||
element.classList.add('selected');
|
||||
}
|
||||
|
||||
function showAvatarModal() {
|
||||
renderAvatarGrid();
|
||||
document.getElementById('avatarModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeAvatarModal() {
|
||||
document.getElementById('avatarModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function saveAvatar() {
|
||||
if (!selectedAvatarUrl) {
|
||||
$toast.error('请选择头像');
|
||||
return;
|
||||
}
|
||||
|
||||
const token = localStorage.getItem('user_token');
|
||||
fetch('/api/web/user/edit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'token': token },
|
||||
body: JSON.stringify({ avatar: selectedAvatarUrl })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
$toast.success('头像更换成功');
|
||||
localStorage.setItem('user_avatar', selectedAvatarUrl);
|
||||
loadUserInfo();
|
||||
closeAvatarModal();
|
||||
} else {
|
||||
$toast.error(data.msg || '更换失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function showEditModal() {
|
||||
const nickname = document.getElementById('userNickname').textContent;
|
||||
const signature = document.getElementById('userSignature').textContent;
|
||||
document.getElementById('editNickname').value = nickname;
|
||||
document.getElementById('editSignature').value = signature === '还没有个性签名' ? '' : signature;
|
||||
document.getElementById('editModal').style.display = 'flex';
|
||||
}
|
||||
|
||||
function closeEditModal() {
|
||||
document.getElementById('editModal').style.display = 'none';
|
||||
}
|
||||
|
||||
function saveProfile() {
|
||||
const token = localStorage.getItem('user_token');
|
||||
const nickname = document.getElementById('editNickname').value;
|
||||
const signature = document.getElementById('editSignature').value;
|
||||
|
||||
if (!nickname) {
|
||||
$toast.error('请输入昵称');
|
||||
return;
|
||||
}
|
||||
|
||||
fetch('/api/web/user/edit', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json', 'token': token },
|
||||
body: JSON.stringify({ nickname, signature })
|
||||
})
|
||||
.then(res => res.json())
|
||||
.then(data => {
|
||||
if (data.code === 200) {
|
||||
$toast.success('保存成功');
|
||||
closeEditModal();
|
||||
document.getElementById('userNickname').textContent = nickname;
|
||||
document.getElementById('userSignature').textContent = signature || '还没有个性签名';
|
||||
localStorage.setItem('user_nickname', nickname);
|
||||
} else {
|
||||
$toast.error(data.msg || '保存失败');
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user