mirror of
https://github.com/StepanovPlaton/AboutMe.git
synced 2026-04-04 12:50:49 +04:00
Initial commit
This commit is contained in:
260
src/pages/diary.astro
Normal file
260
src/pages/diary.astro
Normal file
@@ -0,0 +1,260 @@
|
||||
---
|
||||
import { LinkPresets } from "@constants/link-presets";
|
||||
import { LinkPreset } from "@/types/config";
|
||||
import { siteConfig } from "@/config";
|
||||
import { sortedMoments } from "@/utils/diary";
|
||||
import { i18n } from "@i18n/translation";
|
||||
import I18nKey from "@i18n/i18nKey";
|
||||
import GridLayout from "@layouts/grid.astro";
|
||||
import BackwardButton from "@components/backwardButton.astro";
|
||||
|
||||
|
||||
const pageTitle = LinkPresets[LinkPreset.Diary].name;
|
||||
const pageDescription = LinkPresets[LinkPreset.Diary].description;
|
||||
|
||||
// 时间格式化函数
|
||||
function formatTime(dateString: string): string {
|
||||
var TG = 8;
|
||||
if (siteConfig.timeZone >= -12 && siteConfig.timeZone <= 12) TG = siteConfig.timeZone;
|
||||
const timeGap = TG;
|
||||
const now = new Date();
|
||||
const date = new Date(dateString);
|
||||
const diffInMinutes = Math.floor(
|
||||
(now.getTime() + timeGap*60*60*1000 - date.getTime()) / (1000 * 60),
|
||||
);
|
||||
if (diffInMinutes < 60) {
|
||||
return `${diffInMinutes}${i18n(I18nKey.diaryMinutesAgo)}`;
|
||||
}
|
||||
if (diffInMinutes < 1440) {
|
||||
// 24小时
|
||||
const hours = Math.floor(diffInMinutes / 60);
|
||||
return `${hours}${i18n(I18nKey.diaryHoursAgo)}`;
|
||||
}
|
||||
const days = Math.floor(diffInMinutes / 1440);
|
||||
return `${days}${i18n(I18nKey.diaryDaysAgo)}`;
|
||||
}
|
||||
---
|
||||
|
||||
<GridLayout title={pageTitle} description={pageDescription}>
|
||||
<div class="flex w-full rounded-(--radius-large) overflow-hidden relative min-h-32">
|
||||
<div class="card-base z-10 px-4 py-4 md:px-6 md:py-5 relative w-full">
|
||||
<BackwardButton currentPath={Astro.url.pathname} />
|
||||
<div class="relative max-w-4xl">
|
||||
<!-- 页面头部 -->
|
||||
<div class="moments-header mb-6">
|
||||
<div class="header-content">
|
||||
<div class="header-info">
|
||||
<h1 class="moments-title text-xl md:text-2xl lg:text-3xl font-bold text-90 mb-1">{pageTitle}</h1>
|
||||
<p class="moments-subtitle text-sm md:text-base lg:text-lg text-75">{pageDescription}</p>
|
||||
</div>
|
||||
<div class="header-stats">
|
||||
<div class="stat-item text-center">
|
||||
<span class="stat-number text-lg md:text-xl lg:text-2xl font-bold text-(--primary)">{sortedMoments.length}</span>
|
||||
<span class="stat-label text-xs md:text-sm lg:text-base text-75">{i18n(I18nKey.diaryCount)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 短文列表 -->
|
||||
<div class="moments-timeline">
|
||||
<div class="timeline-list space-y-4">
|
||||
{sortedMoments.map(moment => (
|
||||
<div class="moment-item card-base p-4 md:p-6 lg:p-8 hover:shadow-lg transition-all">
|
||||
<div class="moment-content">
|
||||
<p class="moment-text text-sm md:text-base lg:text-lg text-90 leading-relaxed mb-3 md:mb-4">{moment.content}</p>
|
||||
{moment.images && moment.images.length > 0 && (
|
||||
<div class="moment-images grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-2 md:gap-3 lg:gap-4 mb-3 md:mb-4">
|
||||
{moment.images.map((image, index) => (
|
||||
<div class="image-item relative rounded-md overflow-hidden aspect-square cursor-pointer hover:scale-105 transition">
|
||||
<img
|
||||
src={image}
|
||||
alt={i18n(I18nKey.diaryImage)}
|
||||
class="w-full h-full object-cover"
|
||||
loading="lazy"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<hr class="moment-divider border-t border-(--line-divider) my-3 md:my-4" />
|
||||
<div class="moment-footer flex justify-between items-center">
|
||||
<div class="moment-time flex items-center gap-1.5 text-75 text-xs md:text-sm lg:text-base">
|
||||
<i class="time-icon text-xs md:text-sm">🕐</i>
|
||||
<time datetime={moment.date}>
|
||||
{formatTime(moment.date)}
|
||||
</time>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
<!-- 底部提示 -->
|
||||
<div class="moments-tips text-center mt-6 md:mt-8 lg:mt-10 text-75 text-xs md:text-sm lg:text-base italic">
|
||||
{i18n(I18nKey.diaryTips)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</GridLayout>
|
||||
|
||||
<style>
|
||||
.card-base {
|
||||
background: var(--card-bg);
|
||||
border: 1px solid var(--line-divider);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.moments-header {
|
||||
padding: 1rem;
|
||||
border-radius: 8px;
|
||||
background: linear-gradient(135deg, var(--primary) 0%, var(--primary-dark, var(--primary)) 100%);
|
||||
color: white;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.moments-title {
|
||||
color: white;
|
||||
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.moments-subtitle {
|
||||
color: rgba(255, 255, 255, 0.9);
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
color: white;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
|
||||
.image-item img {
|
||||
transition: scale 0.3s ease;
|
||||
}
|
||||
|
||||
.image-item:hover img {
|
||||
scale: 1.05;
|
||||
}
|
||||
|
||||
.action-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
cursor: pointer;
|
||||
color: var(--text-muted);
|
||||
}
|
||||
|
||||
.action-btn:hover {
|
||||
color: var(--primary);
|
||||
}
|
||||
|
||||
/* 让内容中的换行符生效(\n -> 换行) */
|
||||
.moment-text {
|
||||
white-space: pre-line;
|
||||
}
|
||||
|
||||
/* 响应式设计 */
|
||||
|
||||
/* 桌面端 (大于1280px) */
|
||||
@media (min-width: 1280px) {
|
||||
.moments-header {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.moment-item {
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.moment-images {
|
||||
grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
|
||||
max-width: 600px;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.moment-text {
|
||||
font-size: 1.1rem;
|
||||
line-height: 1.8;
|
||||
}
|
||||
}
|
||||
|
||||
/* 平板竖屏 (768px - 1279px) - 优化显示 */
|
||||
@media (min-width: 768px) and (max-width: 1279px) {
|
||||
.moments-header {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.moment-item {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.moment-images {
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.75rem;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.moment-text {
|
||||
font-size: 1rem;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.moment-footer {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 手机端 (小于768px) */
|
||||
@media (max-width: 768px) {
|
||||
.moments-header {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.header-content {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.moment-images {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
|
||||
.moment-footer {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
/* 优化小屏幕显示 */
|
||||
@media (max-width: 512px) {
|
||||
.card-base {
|
||||
margin: 0 -0.5rem;
|
||||
}
|
||||
|
||||
.moment-item {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.moments-header {
|
||||
border-radius: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user