用trae自动生成的网页日记本小项目

先看效果图

使用 localStorage 保存,关闭浏览器后数据不丢失;

存储键: my_diary_data

直接po代码

文件名是diary.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>我的日记</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            padding: 30px 20px;
        }

        .container {
            max-width: 1100px;
            margin: 0 auto;
        }

        header {
            text-align: center;
            color: #fff;
            margin-bottom: 30px;
        }

        header h1 {
            font-size: 36px;
            margin-bottom: 8px;
            text-shadow: 0 2px 10px rgba(0,0,0,0.2);
        }

        header p {
            font-size: 16px;
            opacity: 0.9;
        }

        .stats {
            display: flex;
            justify-content: center;
            gap: 30px;
            margin-bottom: 30px;
        }

        .stat-card {
            background: rgba(255,255,255,0.2);
            backdrop-filter: blur(10px);
            border: 1px solid rgba(255,255,255,0.3);
            border-radius: 16px;
            padding: 16px 28px;
            color: #fff;
            text-align: center;
        }

        .stat-card .number {
            font-size: 32px;
            font-weight: bold;
        }

        .stat-card .label {
            font-size: 14px;
            opacity: 0.9;
        }

        .calendar {
            background: #fff;
            border-radius: 20px;
            box-shadow: 0 20px 60px rgba(0,0,0,0.2);
            padding: 30px;
        }

        .calendar-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 25px;
            padding-bottom: 15px;
            border-bottom: 2px solid #f0f0f5;
        }

        .calendar-header h2 {
            color: #333;
            font-size: 24px;
        }

        .calendar-header .today {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: #fff;
            padding: 8px 20px;
            border-radius: 20px;
            font-size: 14px;
            cursor: pointer;
            border: none;
            transition: transform 0.2s;
        }

        .calendar-header .today:hover {
            transform: scale(1.05);
        }

        .weekdays {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 10px;
            margin-bottom: 10px;
        }

        .weekdays div {
            text-align: center;
            font-weight: bold;
            color: #888;
            font-size: 14px;
            padding: 10px 0;
        }

        .weekdays div.weekend {
            color: #e74c3c;
        }

        .days {
            display: grid;
            grid-template-columns: repeat(7, 1fr);
            gap: 10px;
        }

        .day {
            aspect-ratio: 1;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            background: #f8f9fc;
            border-radius: 12px;
            cursor: pointer;
            transition: all 0.25s ease;
            position: relative;
            border: 2px solid transparent;
            min-height: 70px;
        }

        .day:hover {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: #fff;
            transform: translateY(-3px);
            box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);
        }

        .day .date {
            font-size: 20px;
            font-weight: 600;
        }

        .day .month {
            font-size: 11px;
            opacity: 0.7;
            margin-top: 2px;
        }

        .day.today {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: #fff;
            border-color: #fff;
            box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4);
        }

        .day.has-content::after {
            content: '';
            position: absolute;
            top: 8px;
            right: 8px;
            width: 8px;
            height: 8px;
            background: #ff6b6b;
            border-radius: 50%;
        }

        .day.other-month {
            opacity: 0.4;
        }

        .day.weekend .date {
            color: #e74c3c;
        }

        .day:hover.weekend .date {
            color: #fff;
        }

        .day.today.weekend .date {
            color: #fff;
        }

        .modal {
            display: none;
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            backdrop-filter: blur(5px);
            z-index: 1000;
            justify-content: center;
            align-items: center;
            padding: 20px;
        }

        .modal.show {
            display: flex;
            animation: fadeIn 0.3s ease;
        }

        @keyframes fadeIn {
            from { opacity: 0; }
            to { opacity: 1; }
        }

        .modal-content {
            background: #fff;
            border-radius: 20px;
            width: 100%;
            max-width: 500px;
            box-shadow: 0 30px 80px rgba(0,0,0,0.3);
            overflow: hidden;
            animation: slideUp 0.3s ease;
        }

        @keyframes slideUp {
            from { transform: translateY(30px); opacity: 0; }
            to { transform: translateY(0); opacity: 1; }
        }

        .modal-header {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: #fff;
            padding: 20px 25px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }

        .modal-header h3 {
            font-size: 18px;
        }

        .modal-close {
            background: none;
            border: none;
            color: #fff;
            font-size: 24px;
            cursor: pointer;
            opacity: 0.8;
            transition: opacity 0.2s;
            line-height: 1;
        }

        .modal-close:hover {
            opacity: 1;
        }

        .modal-body {
            padding: 25px;
        }

        .modal-body textarea {
            width: 100%;
            height: 200px;
            border: 2px solid #e0e0e8;
            border-radius: 12px;
            padding: 15px;
            font-size: 15px;
            font-family: inherit;
            resize: vertical;
            transition: border-color 0.2s;
            outline: none;
        }

        .modal-body textarea:focus {
            border-color: #667eea;
        }

        .modal-body textarea::placeholder {
            color: #bbb;
        }

        .modal-footer {
            padding: 0 25px 25px;
            display: flex;
            gap: 12px;
            justify-content: flex-end;
        }

        .btn {
            padding: 10px 24px;
            border: none;
            border-radius: 10px;
            font-size: 15px;
            cursor: pointer;
            transition: all 0.2s;
            font-weight: 600;
        }

        .btn-save {
            background: linear-gradient(135deg, #667eea, #764ba2);
            color: #fff;
        }

        .btn-save:hover {
            transform: translateY(-2px);
            box-shadow: 0 6px 18px rgba(102, 126, 234, 0.4);
        }

        .btn-delete {
            background: #ffecec;
            color: #e74c3c;
        }

        .btn-delete:hover {
            background: #ffd9d9;
        }

        .empty-hint {
            text-align: center;
            color: #bbb;
            font-size: 14px;
            margin-top: 15px;
        }

        @media (max-width: 600px) {
            header h1 {
                font-size: 26px;
            }

            .calendar {
                padding: 20px 15px;
            }

            .day {
                min-height: 50px;
                padding: 5px;
            }

            .day .date {
                font-size: 16px;
            }

            .day .month {
                font-size: 10px;
            }

            .stats {
                flex-wrap: wrap;
                gap: 15px;
            }

            .stat-card {
                padding: 12px 20px;
            }

            .stat-card .number {
                font-size: 24px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>📖 我的日记</h1>
            <p>记录每一天的美好与成长</p>
        </header>

        <div class="stats">
            <div class="stat-card">
                <div class="number" id="totalDays">0</div>
                <div class="label">记录天数</div>
            </div>
            <div class="stat-card">
                <div class="number" id="totalWords">0</div>
                <div class="label">累计字数</div>
            </div>
            <div class="stat-card">
                <div class="number" id="streakDays">0</div>
                <div class="label">连续记录</div>
            </div>
        </div>

        <div class="calendar">
            <div class="calendar-header">
                <h2 id="monthTitle"></h2>
                <button class="today" onclick="goToday()">回到今天</button>
            </div>
            <div class="weekdays">
                <div class="weekend">日</div>
                <div>一</div>
                <div>二</div>
                <div>三</div>
                <div>四</div>
                <div>五</div>
                <div class="weekend">六</div>
            </div>
            <div class="days" id="daysContainer"></div>
        </div>
    </div>

    <div class="modal" id="modal">
        <div class="modal-content">
            <div class="modal-header">
                <h3 id="modalTitle">编辑日记</h3>
                <button class="modal-close" onclick="closeModal()">&times;</button>
            </div>
            <div class="modal-body">
                <textarea id="diaryContent" placeholder="写下这一天的工作内容、心情、感悟..."></textarea>
                <div class="empty-hint" id="emptyHint">还没有记录,开始写下你的故事吧 ✨</div>
            </div>
            <div class="modal-footer">
                <button class="btn btn-delete" id="deleteBtn" onclick="deleteDiary()" style="display:none;">删除</button>
                <button class="btn btn-save" onclick="saveDiary()">保存</button>
            </div>
        </div>
    </div>

    <script>
        const STORAGE_KEY = 'my_diary_data';
        let diaryData = JSON.parse(localStorage.getItem(STORAGE_KEY) || '{}');
        let currentDate = new Date();
        let editingDateKey = null;

        function formatDateKey(date) {
            const y = date.getFullYear();
            const m = String(date.getMonth() + 1).padStart(2, '0');
            const d = String(date.getDate()).padStart(2, '0');
            return `${y}-${m}-${d}`;
        }

        function isSameDay(d1, d2) {
            return d1.getFullYear() === d2.getFullYear() &&
                   d1.getMonth() === d2.getMonth() &&
                   d1.getDate() === d2.getDate();
        }

        function renderCalendar() {
            const year = currentDate.getFullYear();
            const month = currentDate.getMonth();
            const monthNames = ['一月', '二月', '三月', '四月', '五月', '六月',
                               '七月', '八月', '九月', '十月', '十一月', '十二月'];
            document.getElementById('monthTitle').textContent = `${year}年 ${monthNames[month]}`;

            const firstDay = new Date(year, month, 1);
            const lastDay = new Date(year, month + 1, 0);
            const startWeekday = firstDay.getDay();
            const daysInMonth = lastDay.getDate();
            const prevMonthDays = new Date(year, month, 0).getDate();

            const container = document.getElementById('daysContainer');
            container.innerHTML = '';

            const today = new Date();

            for (let i = 0; i < startWeekday; i++) {
                const dayEl = document.createElement('div');
                dayEl.className = 'day other-month';
                const prevDay = prevMonthDays - startWeekday + i + 1;
                const prevDate = new Date(year, month - 1, prevDay);
                dayEl.innerHTML = `
                    <span class="date">${prevDay}</span>
                    <span class="month">${prevDate.getMonth() + 1}月</span>
                `;
                if (prevDate.getDay() === 0 || prevDate.getDay() === 6) {
                    dayEl.classList.add('weekend');
                }
                dayEl.onclick = () => openModal(prevDate);
                container.appendChild(dayEl);
            }

            for (let d = 1; d <= daysInMonth; d++) {
                const dayEl = document.createElement('div');
                const dateObj = new Date(year, month, d);
                const dateKey = formatDateKey(dateObj);

                dayEl.className = 'day';
                if (isSameDay(dateObj, today)) dayEl.classList.add('today');
                if (dateObj.getDay() === 0 || dateObj.getDay() === 6) dayEl.classList.add('weekend');
                if (diaryData[dateKey]) dayEl.classList.add('has-content');

                dayEl.innerHTML = `<span class="date">${d}</span>`;
                dayEl.onclick = () => openModal(dateObj);
                container.appendChild(dayEl);
            }

            const totalCells = startWeekday + daysInMonth;
            const remaining = (7 - (totalCells % 7)) % 7;
            for (let i = 1; i <= remaining; i++) {
                const dayEl = document.createElement('div');
                dayEl.className = 'day other-month';
                const nextDate = new Date(year, month + 1, i);
                dayEl.innerHTML = `
                    <span class="date">${i}</span>
                    <span class="month">${nextDate.getMonth() + 1}月</span>
                `;
                if (nextDate.getDay() === 0 || nextDate.getDay() === 6) {
                    dayEl.classList.add('weekend');
                }
                dayEl.onclick = () => openModal(nextDate);
                container.appendChild(dayEl);
            }

            updateStats();
        }

        function openModal(date) {
            editingDateKey = formatDateKey(date);
            const weekdayNames = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
            const title = `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${weekdayNames[date.getDay()]}`;
            document.getElementById('modalTitle').textContent = title;

            const content = diaryData[editingDateKey] || '';
            document.getElementById('diaryContent').value = content;

            const deleteBtn = document.getElementById('deleteBtn');
            const emptyHint = document.getElementById('emptyHint');
            if (content) {
                deleteBtn.style.display = 'block';
                emptyHint.style.display = 'none';
            } else {
                deleteBtn.style.display = 'none';
                emptyHint.style.display = 'block';
            }

            document.getElementById('modal').classList.add('show');
            setTimeout(() => document.getElementById('diaryContent').focus(), 100);
        }

        function closeModal() {
            document.getElementById('modal').classList.remove('show');
            editingDateKey = null;
        }

        function saveDiary() {
            const content = document.getElementById('diaryContent').value.trim();
            if (!editingDateKey) return;

            if (content) {
                diaryData[editingDateKey] = content;
            } else {
                delete diaryData[editingDateKey];
            }

            localStorage.setItem(STORAGE_KEY, JSON.stringify(diaryData));
            renderCalendar();
            closeModal();
        }

        function deleteDiary() {
            if (!editingDateKey) return;
            if (confirm('确定要删除这一天的日记吗?')) {
                delete diaryData[editingDateKey];
                localStorage.setItem(STORAGE_KEY, JSON.stringify(diaryData));
                renderCalendar();
                closeModal();
            }
        }

        function updateStats() {
            const entries = Object.entries(diaryData).filter(([_, v]) => v.trim());
            document.getElementById('totalDays').textContent = entries.length;

            const totalWords = entries.reduce((sum, [_, v]) => sum + v.length, 0);
            document.getElementById('totalWords').textContent = totalWords;

            let streak = 0;
            const today = new Date();
            for (let i = 0; i < 365; i++) {
                const d = new Date(today);
                d.setDate(d.getDate() - i);
                if (diaryData[formatDateKey(d)] && diaryData[formatDateKey(d)].trim()) {
                    streak++;
                } else {
                    break;
                }
            }
            document.getElementById('streakDays').textContent = streak;
        }

        function goToday() {
            currentDate = new Date();
            renderCalendar();
        }

        document.getElementById('modal').addEventListener('click', function(e) {
            if (e.target === this) closeModal();
        });

        document.addEventListener('keydown', function(e) {
            if (e.key === 'Escape') closeModal();
            if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) saveDiary();
        });

        renderCalendar();
    </script>
</body>
</html>

Logo

AtomGit AI 社区提供模型库、数据集、Agent、Token等资源

更多推荐