
<template><div class="notes-app"><nav class="app-nav" :class="{ 'nav-with-back': currentView !== 'list' }"><buttonclass="back-btn"@click="handleBack"v-if="currentView !== 'list'"><i class="icon-arrow-left"></i></button><h1 class="app-title">{{ currentView === 'list' ? '记事本' :currentView === 'detail' ? '笔记详情' :currentView === 'edit' ? '编辑笔记' : '新建笔记' }}</h1><buttonclass="add-btn"@click="goToCreateNote"v-if="currentView === 'list'"><i class="icon-plus"></i></button></nav><div class="view list-view" v-if="currentView === 'list'"><div class="empty-state" v-if="notes.length === 0"><div class="empty-icon">📝</div><p class="empty-text">还没有笔记</p><p class="empty-subtext">点击右上角 + 创建你的第一条笔记</p></div><ul class="notes-list" v-else><liclass="note-item"v-for="note in notes":key="note.id"@click="goToNoteDetail(note.id)"><div class="note-info"><h3 class="note-title">{{ note.title || '无标题' }}</h3><p class="note-content">{{ note.content.substring(0, 60) }}{{ note.content.length > 60 ? '...' : '' }}</p><time class="note-time">{{ formatDate(note.updatedAt) }}</time></div><div class="note-actions"><buttonclass="action-btn edit-btn"@click.stop="goToEditNote(note.id)"aria-label="编辑笔记"><i class="icon-edit"></i></button><buttonclass="action-btn delete-btn"@click.stop="showDeleteDialog(note.id)"aria-label="删除笔记"><i class="icon-delete"></i></button></div></li></ul></div><div class="view detail-view" v-if="currentView === 'detail'"><div class="detail-content" v-if="currentNote"><h2 class="detail-title">{{ currentNote.title || '无标题' }}</h2><time class="detail-time">{{ formatDate(currentNote.updatedAt) }}</time><div class="detail-text">{{ currentNote.content }}</div></div><div class="detail-actions"><buttonclass="detail-btn edit-detail-btn"@click="goToEditNote(currentNote?.id)"><i class="icon-edit"></i> 编辑</button><buttonclass="detail-btn delete-detail-btn"@click="showDeleteDialog(currentNote?.id)"><i class="icon-delete"></i> 删除</button></div></div><div class="view editor-view" v-if="currentView === 'create' || currentView === 'edit'"><div class="editor-form"><inputtype="text"v-model="noteForm.title"class="title-input"placeholder="请输入标题..."maxlength="50"><textareav-model="noteForm.content"class="content-input"placeholder="请输入笔记内容..."rows="15"></textarea></div><buttonclass="save-btn"@click="saveNote":disabled="!noteForm.title && !noteForm.content">保存</button></div><div class="modal-backdrop" v-if="showDeleteModal"><div class="delete-modal"><h3 class="modal-title">确认删除</h3><p class="modal-message">你确定要删除这条笔记吗?此操作不可撤销。</p><div class="modal-buttons"><buttonclass="modal-btn cancel-btn"@click="showDeleteModal = false">取消</button><buttonclass="modal-btn confirm-btn"@click="confirmDelete">删除</button></div></div></div></div>
</template><script setup>
import { ref, computed, onMounted, watch } from 'vue';
const currentView = ref('list');
const currentNoteId = ref(null);
const showDeleteModal = ref(false);
const noteToDelete = ref(null);
const notes = ref([]);
const noteForm = ref({title: '',content: ''
});
const currentNote = computed(() => {return notes.value.find(note => note.id === currentNoteId.value) || null;
});
const loadNotes = () => {const savedNotes = localStorage.getItem('mobileNotes');if (savedNotes) {notes.value = JSON.parse(savedNotes);}
};
const saveNotesToStorage = () => {localStorage.setItem('mobileNotes', JSON.stringify(notes.value));
};
onMounted(() => {loadNotes();
});
watch(notes, () => {saveNotesToStorage();
}, { deep: true });
const handleBack = () => {switch(currentView.value) {case 'detail':case 'create':currentView.value = 'list';break;case 'edit':currentView.value = 'detail';break;}
};
const goToNoteDetail = (id) => {currentNoteId.value = id;currentView.value = 'detail';
};
const goToCreateNote = () => {noteForm.value = { title: '', content: '' };currentView.value = 'create';
};
const goToEditNote = (id) => {const note = notes.value.find(n => n.id === id);if (note) {noteForm.value = {title: note.title,content: note.content};currentNoteId.value = id;currentView.value = 'edit';}
};
const saveNote = () => {const now = new Date().toISOString();if (currentView.value === 'create') {const newNote = {id: Date.now().toString(),title: noteForm.value.title,content: noteForm.value.content,createdAt: now,updatedAt: now};notes.value.unshift(newNote); } else {const index = notes.value.findIndex(n => n.id === currentNoteId.value);if (index !== -1) {notes.value[index].title = noteForm.value.title;notes.value[index].content = noteForm.value.content;notes.value[index].updatedAt = now;}}currentView.value = 'list';
};
const showDeleteDialog = (id) => {noteToDelete.value = id;showDeleteModal.value = true;
};
const confirmDelete = () => {if (noteToDelete.value) {notes.value = notes.value.filter(note => note.id !== noteToDelete.value);showDeleteModal.value = false;currentView.value = 'list';noteToDelete.value = null;}
};
const formatDate = (isoString) => {const date = new Date(isoString);return date.toLocaleString('zh-CN', {month: 'short',day: 'numeric',hour: '2-digit',minute: '2-digit'});
};
</script><style scoped>
.notes-app {max-width: 500px;margin: 0 auto;min-height: 100vh;background-color: #f9f9f9;color: #333;font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;position: relative;
}
.app-nav {display: flex;align-items: center;justify-content: center;height: 56px;background-color: #2196f3;color: white;padding: 0 16px;box-shadow: 0 2px 4px rgba(0,0,0,0.1);position: sticky;top: 0;z-index: 10;
}.nav-with-back {justify-content: flex-start;
}.app-title {font-size: 18px;font-weight: 500;margin: 0;
}.back-btn, .add-btn {width: 40px;height: 40px;border-radius: 50%;background: transparent;border: none;color: white;display: flex;align-items: center;justify-content: center;cursor: pointer;font-size: 20px;
}.back-btn {margin-right: 16px;
}.add-btn {position: absolute;right: 16px;
}
.icon-plus::before {content: "+";font-weight: bold;
}.icon-arrow-left::before {content: "←";font-weight: bold;
}.icon-edit::before {content: "✏️";
}.icon-delete::before {content: "🗑️";
}
.view {padding: 16px;min-height: calc(100vh - 56px);box-sizing: border-box;
}
.notes-list {list-style: none;margin: 0;padding: 0;
}.note-item {display: flex;justify-content: space-between;align-items: center;background-color: white;border-radius: 12px;padding: 16px;margin-bottom: 12px;box-shadow: 0 1px 3px rgba(0,0,0,0.05);cursor: pointer;transition: transform 0.1s ease, box-shadow 0.1s ease;
}.note-item:active {transform: translateY(1px);box-shadow: 0 1px 2px rgba(0,0,0,0.1);
}.note-info {flex: 1;overflow: hidden;padding-right: 12px;
}.note-title {margin: 0 0 6px 0;font-size: 16px;font-weight: 500;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;
}.note-content {margin: 0 0 8px 0;font-size: 14px;color: #666;display: -webkit-box;-webkit-line-clamp: 2;-webkit-box-orient: vertical;overflow: hidden;line-height: 1.4;
}.note-time {font-size: 12px;color: #999;
}.note-actions {display: flex;gap: 8px;
}.action-btn {width: 36px;height: 36px;border-radius: 50%;border: none;background-color: #f0f0f0;display: flex;align-items: center;justify-content: center;cursor: pointer;transition: background-color 0.2s ease;
}.action-btn:active {transform: scale(0.95);
}.edit-btn {color: #2196f3;
}.delete-btn {color: #f44336;
}
.empty-state {text-align: center;padding: 60px 20px;color: #999;
}.empty-icon {font-size: 60px;margin-bottom: 20px;
}.empty-text {font-size: 18px;margin: 0 0 8px 0;
}.empty-subtext {font-size: 14px;margin: 0;color: #bbb;
}
.detail-view {background-color: white;padding-bottom: 80px;
}.detail-content {margin-bottom: 24px;
}.detail-title {margin: 0 0 16px 0;font-size: 22px;font-weight: 500;line-height: 1.3;
}.detail-time {display: block;font-size: 14px;color: #999;margin-bottom: 24px;padding-bottom: 16px;border-bottom: 1px solid #eee;
}.detail-text {font-size: 16px;line-height: 1.6;color: #444;white-space: pre-line;
}.detail-actions {display: flex;gap: 16px;position: fixed;bottom: 20px;left: 50%;transform: translateX(-50%);max-width: calc(100% - 32px);width: 100%;
}.detail-btn {flex: 1;padding: 14px;border-radius: 8px;border: none;font-size: 16px;font-weight: 500;display: flex;align-items: center;justify-content: center;gap: 8px;cursor: pointer;transition: opacity 0.2s ease;
}.detail-btn:active {opacity: 0.8;
}.edit-detail-btn {background-color: #2196f3;color: white;
}.delete-detail-btn {background-color: #f44336;color: white;
}
.editor-view {background-color: white;padding-bottom: 80px;
}.editor-form {margin-bottom: 24px;
}.title-input {width: 100%;font-size: 22px;font-weight: 500;padding: 12px 0;margin-bottom: 16px;border: none;border-bottom: 1px solid #eee;outline: none;background: transparent;color: #333;
}.title-input::placeholder {color: #ccc;
}.content-input {width: 100%;font-size: 16px;line-height: 1.6;border: none;outline: none;resize: none;background: transparent;color: #444;min-height: 300px;
}.content-input::placeholder {color: #ccc;
}.save-btn {position: fixed;bottom: 20px;left: 50%;transform: translateX(-50%);width: calc(100% - 32px);max-width: 468px;padding: 14px;background-color: #2196f3;color: white;border: none;border-radius: 8px;font-size: 16px;font-weight: 500;cursor: pointer;transition: background-color 0.2s ease, opacity 0.2s ease;
}.save-btn:disabled {background-color: #ccc;cursor: not-allowed;
}.save-btn:not(:disabled):active {opacity: 0.8;
}
.modal-backdrop {position: fixed;top: 0;left: 0;right: 0;bottom: 0;background-color: rgba(0,0,0,0.5);display: flex;align-items: center;justify-content: center;z-index: 100;padding: 0 16px;
}.delete-modal {background-color: white;border-radius: 12px;width: 100%;max-width: 300px;padding: 24px;box-shadow: 0 4px 20px rgba(0,0,0,0.15);
}.modal-title {margin: 0 0 12px 0;font-size: 18px;font-weight: 500;text-align: center;
}.modal-message {margin: 0 0 24px 0;font-size: 14px;color: #666;text-align: center;line-height: 1.5;
}.modal-buttons {display: flex;gap: 12px;
}.modal-btn {flex: 1;padding: 10px;border-radius: 6px;font-size: 16px;font-weight: 500;cursor: pointer;transition: opacity 0.2s ease;border: none;
}.modal-btn:active {opacity: 0.8;
}.cancel-btn {background-color: #f0f0f0;color: #333;
}.confirm-btn {background-color: #f44336;color: white;
}
@media (max-width: 320px) {.app-title {font-size: 16px;}.note-title {font-size: 15px;}.note-content {font-size: 13px;}.detail-title {font-size: 20px;}
}
</style>