Files
sunct 3c3bf53ae4 docs(website): 添加简记memo产品原型和UI设计规范文档
- 新增「简记memo」一体化小程序产品原型设计文档
- 新增简记memo完整版UI视觉设计规范和界面细节
- 添加IDEA项目配置文件.gitignore
- 创建404页面HTML文件,包含响应式布局和错误提示
- 添加关于页面HTML文件,展示品牌介绍和团队信息
- 实现AES加解密工具函数,支持请求体加密
- 添加用户协议页面基础框架
2026-07-31 14:12:32 +08:00

429 lines
28 KiB
HTML

<!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>