<script>
new Vue({
el: '#app',
data: {
userName: '月亮',
months: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
days: ['周一', '周二', '周三', '周四', '周五', '周六', '周日'],
books: [
{
title: '财富方程式',
content: '不要做工作狂,很多人认为工作狂是品质,特别是在国内内卷的常态下,不愿意重读工作以外的自己,不想面对责任。而且在互联网的很多公司中特别希望员工去加班,哪怕是花钱、给加班费等等。但是这其实对于个人来说是有代价的,无论是健康、生活、时间等等。',
formula: '专注+(品格×时间×投资)',
date: '2025-12-22'
},
{
title: '财富方程式',
content: '不要做工作狂,很多人认为工作狂是品质,特别是在国内内卷的常态下,不愿意重读工作以外的自己,不想面对责任。而且在互联网的很多公司中特别希望员工去加班,哪怕是花钱、给加班费等等。但是这其实对于个人来说是有代价的,无论是健康、生活、时间等等。',
formula: '专注+(品格×时间×投资)',
date: '2025-11-17'
},
{
title: '财富方程式',
content: '书中提到了投资,主动投资和被动投资,其实大多数人在多数情况下都是被动投资,但是要不要接触主动投资呢?我的回答也是要的。就像繁华里面一样,投资就是要先学会亏,知道怎么亏的才能知道怎么赚的,这部分学费是一定要交的。我在没有投资以前我是完全不信这个的,但是实际进去发现才知道,人性的弱点是那么可怕。然后还是就是实业投资的风险,当我们投资实业的时候,我们能想到的往往都是成本和回报,但是往往一般人都会看的东西往往越看不准。因为所有的投资并不是在看能赚多少钱,而是最多会亏多少,什么时候止损。这个损失如果可以接受那么你才有可能去做。',
formula: '',
date: '2025-11-16'
},
{
title: '财富方程式',
content: '“时间才是真正的货币”。把时间真正当作财富的一种,并不是先积累金钱,而获得自由。对于年轻来说有时间的财富。<br>1. 把自己扔进世界,感受世界',
formula: '',
date: '2025-11-15'
}
],
weeksData: []
},
computed: {
totalReadingDays() {
return this.books.filter(book => book.date).length;
}
},
mounted() {
this.generateHeatmapData();
},
methods: {
generateHeatmapData() {
const weeks = [];
const currentYear = 2025;
const readingDates = this.books
.filter(book => book.date)
.map(book => book.date);
for (let week = 0; week < 52; week++) {
const weekDays = [];
for (let dayOfWeek = 0; dayOfWeek < 7; dayOfWeek++) {
const date = this.calculateDate(currentYear, week, dayOfWeek);
let level = 0;
if (readingDates.includes(date)) {
level = Math.floor(Math.random() * 4) + 1;
}
weekDays.push({
date: date,
level: level
});
}
weeks.push(weekDays);
}
this.weeksData = weeks;
},
calculateDate(year, week, dayOfWeek) {
const days = week * 7 + dayOfWeek;
const date = new Date(year, 0, 1 + days);
const month = (date.getMonth() + 1).toString().padStart(2, '0');
const day = date.getDate().toString().padStart(2, '0');
return `${year}-${month}-${day}`;
}
}
});
</script>
<!-- run -->
<style>
.container {
max-width: 1150px;
margin: 0 auto;
}
#mainContent {
background: #23d5ac57;
}
.heatmap-card {
background-color: #fff;
border-radius: 10px;
padding: 18px;
margin-bottom: 20px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #eee;
transition: all 0.3s ease;
position: relative; /* 确保子元素绝对定位基于此容器 */
}
.heatmap-card:hover {
border-color: #1e88e5;
box-shadow: 0 6px 16px rgba(30, 136, 229, 0.15);
transform: translateY(1px);
}
.heatmap-number {
position: absolute;
top: 12px;
right: 12px;
background: linear-gradient(135deg, #1e88e5, #1976d2);
color: #fff;
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 14px;
font-weight: bold;
box-shadow: 0 2px 8px rgba(30, 136, 229, 0.3);
z-index: 10; /* 确保在最上层 */
}
.contributions-graph {
display: flex;
flex-direction: column;
width: 100%;
}
.months-row {
display: flex;
margin-left: 35px;
margin-bottom: 10px;
flex-wrap: wrap;
}
.month-label {
font-size: 12px;
color: #57606a;
width: 65px;
font-weight: 500;
text-align: center;
margin-left:17px;
}
.graph-body {
display: flex;
flex-wrap: wrap;
}
.days-column {
display: flex;
flex-direction: column;
margin-right: 8px;
width: 35px;
}
.day-label {
font-size: 12px;
color: #57606a;
height: 15px;
margin-bottom: 4px;
text-align: right;
padding-right: 8px;
font-weight: 500;
}
.weeks-container {
display: flex;
flex-wrap: wrap;
flex: 1;
}
.week-column {
display: flex;
flex-direction: column;
margin-right: 4px;
}
.contribution-cell {
width: 15px;
height: 15px;
border-radius: 3px;
margin-bottom: 4px;
background-color: #ebedf0;
transition: all 0.15s ease;
position: relative;
cursor: default;
}
.level-0 { background-color: #ebedf0; }
.level-1 { background-color: #9be9a8; }
.level-2 { background-color: #40c463; }
.level-3 { background-color: #30a14e; }
.level-4 { background-color: #216e39; }
.contribution-cell:hover {
border: 1.5px solid #0969da;
transform: scale(1.15);
z-index: 1;
}
.contribution-cell:hover::after,
.contribution-cell:hover::before {
position: absolute;
left: 50%;
transform: translateX(-50%) translateY(-100%);
z-index: 9999;
pointer-events: none;
}
.contribution-cell:hover::after {
content: attr(data-date) " 未打卡";
background-color: #24292f;
color: #ffffff;
padding: 6px 10px;
border-radius: 4px;
font-size: 12px;
white-space: nowrap;
font-weight: 500;
box-shadow: 0 2px 8px rgba(0,0,0,0.2);
}
.contribution-cell:hover::before {
content: '';
top: 4px;
border-width: 4px;
border-style: solid;
border-color: #24292f transparent transparent transparent;
}
.level-1:hover::after,
.level-2:hover::after,
.level-3:hover::after,
.level-4:hover::after {
content: attr(data-date) " 已打卡";
}
.heatmap-footer {
display: flex;
justify-content: flex-end;
align-items: center;
margin-top: 20px;
font-size: 14px;
color: #666;
}
.heatmap-footer img {
width: 28px;
height: 28px;
border-radius: 50%;
margin-right: 10px;
border: 2px solid #f0f8f7;
transition: transform 0.2s ease;
}
.heatmap-footer img:hover {
transform: rotate(5deg);
}
.book-card {
background-color: #fff;
border-radius: 10px;
padding: 18px;
margin-bottom: 20px;
box-shadow: 0 3px 8px rgba(0, 0, 0, 0.06);
border: 1px solid #eee;
transition: all 0.3s ease;
}
.book-card:hover {
border-color: #1e88e5;
box-shadow: 0 6px 16px rgba(30, 136, 229, 0.15);
transform: translateY(1px);
}
.book-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 12px;
color: #222;
display: flex;
align-items: center;
}
.book-title::before {
content: "📖";
margin-right: 8px;
font-size: 16px;
}
.book-content {
margin-bottom: 12px;
line-height: 1.5;
font-size: 15px;
font-weight: 500;
color: #434343;
}
.book-formula {
font-size: 14px;
color: #1e88e5;
font-weight: 500;
margin-bottom: 12px;
padding: 8px 12px;
background-color: #f5f9ff;
border-radius: 4px;
display: inline-block;
}
.book-footer {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 12px;
color: #999;
padding-top: 12px;
border-top: 1px dashed #eee;
}
.footer-date {
display: flex;
align-items: center;
}
.footer-date img {
width: 16px;
height: 16px;
margin-right: 6px;
opacity: 0.8;
}
.footer-detail {
cursor: pointer;
color: #1e88e5;
transition: all 0.2s ease;
padding: 4px 8px;
border-radius: 4px;
}
.footer-detail:hover {
background-color: #f5f9ff;
color: #1976d2;
}
</style>
<div id="app">
<div class="container">
<div class="heatmap-card">
<div class="heatmap-number">{{ totalReadingDays }}</div>
<div class="contributions-graph">
<div class="months-row">
<div class="month-label" v-for="month in months" :key="month">{{ month }}</div>
</div>
<div class="graph-body">
<div class="days-column">
<div class="day-label" v-for="day in days" :key="day">{{ day }}</div>
</div>
<div class="weeks-container">
<div class="week-column" v-for="(week, weekIndex) in weeksData" :key="weekIndex">
<div
class="contribution-cell"
v-for="(day, dayIndex) in week"
:key="dayIndex"
:class="'level-' + day.level"
:data-date="day.date"
></div>
</div>
</div>
</div>
</div>
<div class="heatmap-footer">
<img src="https://pic.cnblogs.com/avatar/3509019/20251027094540.png" alt="头像">
<span>{{ userName }}</span>
</div>
</div>
<div class="book-card" v-for="(book, index) in books" :key="index">
<div class="book-title">每日读书《{{ book.title }}》</div>
<div class="book-content" v-html="book.content"></div>
<div class="book-formula" v-if="book.formula">{{ book.formula }}</div>
<div class="book-footer">
<div class="footer-date">
<img src="https://images.cnblogs.com/cnblogs_com/blogs/829502/galleries/2418081/o_240826010705_moon.png" alt="日历图标">
{{ book.date }}
</div>
<div class="footer-detail">详情</div>
</div>
</div>
</div>
</div>