2025-08-04 02:13:12 +08:00

455 lines
9.6 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div v-if="visible" class="notes-modal-overlay" @click="handleOverlayClick">
<div
class="notes-modal"
@click.stop
:style="{
transform: `translate(${modalPosition.x}px, ${modalPosition.y}px)`,
cursor: isDragging ? 'grabbing' : 'default'
}"
>
<!-- 顶部状态栏 -->
<div
class="modal-header"
@mousedown="handleMouseDown"
:style="{ cursor: isDragging ? 'grabbing' : 'grab' }"
>
<span class="save-status">保存成功 2015.7.23 14:23:52</span>
<button class="close-btn" @click="closeModal">×</button>
</div>
<!-- 主要内容区域 -->
<div class="modal-content">
<!-- 左侧笔记列表 -->
<div class="notes-sidebar">
<div class="notes-list">
<div
v-for="note in notesList"
:key="note.id"
class="note-item"
:class="{ active: note.id === currentNoteId }"
@click="selectNote(note.id)"
>
<div class="note-title">{{ note.title }}</div>
<div class="note-date">{{ note.date }}</div>
</div>
</div>
</div>
<!-- 右侧编辑区域 -->
<div class="editor-area">
<!-- 工具栏 -->
<div class="editor-toolbar">
<div class="toolbar-left">
<select class="font-size-select" v-model="fontSize">
<option value="12">12</option>
<option value="14">14</option>
<option value="16">16</option>
<option value="18">18</option>
</select>
<button class="toolbar-btn bold" @click="toggleBold">B</button>
<button class="toolbar-btn underline" @click="toggleUnderline">U</button>
<button class="toolbar-btn font-size" @click="increaseFontSize">Aa</button>
<button class="toolbar-btn font-size small" @click="decreaseFontSize">A</button>
<button class="toolbar-btn color" @click="toggleColor">🎨</button>
<button class="toolbar-btn list" @click="toggleList"></button>
<button class="toolbar-btn image" @click="insertImage">🖼</button>
</div>
<div class="toolbar-right">
<button class="save-btn" @click="saveNote">
<i class="save-icon">💾</i>
保存
</button>
</div>
</div>
<!-- 编辑器 -->
<div class="editor-content">
<textarea
v-model="currentNoteContent"
class="note-editor"
placeholder="开始记录您的笔记..."
:style="{ fontSize: fontSize + 'px' }"
></textarea>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
// computed, onMounted 暂时注释,后续需要时再启用
interface Note {
id: number
title: string
date: string
content: string
}
defineProps<{
visible: boolean
}>()
const emit = defineEmits<{
close: []
save: [content: string]
}>()
// 响应式数据
const fontSize = ref(14)
const currentNoteId = ref(1)
const currentNoteContent = ref('为了让科学教育有效,它必须广泛包容,而且应该认识到科学教师、科学家、家庭和社区如何合作实现学习和教学的目标。')
// 笔记列表数据
const notesList = ref<Note[]>([
{ id: 1, title: '第七课笔记', date: '2015.7.23', content: '为了让科学教育有效,它必须广泛包容,而且应该认识到科学教师、科学家、家庭和社区如何合作实现学习和教学的目标。' },
{ id: 2, title: '第六课笔记 DeepSeek的...', date: '2015.7.21', content: '' },
{ id: 3, title: '第五课笔记', date: '2015.7.23', content: '' },
{ id: 4, title: '第四课笔记', date: '2015.7.21', content: '' },
{ id: 5, title: '第三课笔记', date: '2015.7.23', content: '' },
{ id: 6, title: '第二课笔记', date: '2015.7.21', content: '' },
{ id: 7, title: '第一课笔记', date: '2015.7.23', content: '' },
{ id: 8, title: '第六课笔记 DeepSeek的...', date: '2015.7.21', content: '' },
{ id: 9, title: '第七课笔记', date: '2015.7.23', content: '' }
])
// 方法
const closeModal = () => {
emit('close')
}
const handleOverlayClick = () => {
closeModal()
}
const selectNote = (noteId: number) => {
const note = notesList.value.find(n => n.id === noteId)
if (note) {
currentNoteId.value = noteId
currentNoteContent.value = note.content
}
}
const saveNote = () => {
const note = notesList.value.find(n => n.id === currentNoteId.value)
if (note) {
note.content = currentNoteContent.value
}
emit('save', currentNoteContent.value)
}
const toggleBold = () => {
// 粗体功能
}
const toggleUnderline = () => {
// 下划线功能
}
const increaseFontSize = () => {
if (fontSize.value < 24) {
fontSize.value += 2
}
}
const decreaseFontSize = () => {
if (fontSize.value > 10) {
fontSize.value -= 2
}
}
const toggleColor = () => {
// 颜色功能
}
const toggleList = () => {
// 列表功能
}
const insertImage = () => {
// 插入图片功能
}
// 拖拽功能
const isDragging = ref(false)
const dragOffset = ref({ x: 0, y: 0 })
const modalPosition = ref({ x: 0, y: 0 })
const handleMouseDown = (event: MouseEvent) => {
isDragging.value = true
dragOffset.value = {
x: event.clientX - modalPosition.value.x,
y: event.clientY - modalPosition.value.y
}
document.addEventListener('mousemove', handleMouseMove)
document.addEventListener('mouseup', handleMouseUp)
}
const handleMouseMove = (event: MouseEvent) => {
if (!isDragging.value) return
modalPosition.value = {
x: event.clientX - dragOffset.value.x,
y: event.clientY - dragOffset.value.y
}
}
const handleMouseUp = () => {
isDragging.value = false
document.removeEventListener('mousemove', handleMouseMove)
document.removeEventListener('mouseup', handleMouseUp)
}
</script>
<style scoped>
.notes-modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
}
.notes-modal {
width: 75vw;
max-width: 800px;
height: 65vh;
min-height: 500px;
background: white;
border-radius: 8px;
overflow: hidden;
display: flex;
flex-direction: column;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
position: relative;
user-select: none;
}
/* 顶部状态栏 */
.modal-header {
background: #f5f5f5;
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid #e0e0e0;
font-size: 12px;
color: #666;
}
.save-status {
font-size: 12px;
color: #666;
}
.close-btn {
background: none;
border: none;
font-size: 18px;
color: #999;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
.close-btn:hover {
color: #333;
}
/* 主要内容区域 */
.modal-content {
flex: 1;
display: flex;
overflow: hidden;
}
/* 左侧笔记列表 */
.notes-sidebar {
width: 240px;
background: #f8f9fa;
border-right: 1px solid #e0e0e0;
overflow-y: auto;
}
.notes-list {
padding: 0;
}
.note-item {
padding: 12px 16px;
border-bottom: 1px solid #e8e9ea;
cursor: pointer;
transition: background-color 0.2s;
}
.note-item:hover {
background: #e9ecef;
}
.note-item.active {
background: #e3f2fd;
border-left: 3px solid #2196f3;
}
.note-title {
font-size: 14px;
color: #333;
margin-bottom: 4px;
font-weight: 500;
}
.note-date {
font-size: 12px;
color: #999;
}
/* 右侧编辑区域 */
.editor-area {
flex: 1;
display: flex;
flex-direction: column;
}
/* 工具栏 */
.editor-toolbar {
background: #fff;
border-bottom: 1px solid #e0e0e0;
padding: 8px 16px;
display: flex;
justify-content: space-between;
align-items: center;
}
.toolbar-left {
display: flex;
align-items: center;
gap: 8px;
}
.font-size-select {
border: 1px solid #ddd;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
background: white;
}
.toolbar-btn {
background: #f8f9fa;
border: 1px solid #ddd;
border-radius: 4px;
padding: 4px 8px;
font-size: 12px;
cursor: pointer;
transition: background-color 0.2s;
}
.toolbar-btn:hover {
background: #e9ecef;
}
.toolbar-btn.bold {
font-weight: bold;
}
.toolbar-btn.underline {
text-decoration: underline;
}
.toolbar-btn.font-size {
font-size: 14px;
}
.toolbar-btn.font-size.small {
font-size: 10px;
}
.save-btn {
background: #2196f3;
color: white;
border: none;
border-radius: 4px;
padding: 6px 12px;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
gap: 4px;
}
.save-btn:hover {
background: #1976d2;
}
/* 编辑器内容 */
.editor-content {
flex: 1;
padding: 16px;
overflow: hidden;
}
.note-editor {
width: 100%;
height: 100%;
border: none;
outline: none;
resize: none;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: #333;
}
/* 响应式设计 */
@media (max-width: 768px) {
.notes-modal {
width: 90vw;
height: 80vh;
min-height: 400px;
}
.notes-sidebar {
width: 180px;
}
.toolbar-left {
gap: 4px;
}
.toolbar-btn {
padding: 3px 6px;
font-size: 11px;
}
}
@media (max-width: 480px) {
.notes-modal {
width: 95vw;
height: 85vh;
}
.notes-sidebar {
width: 150px;
}
.modal-header {
padding: 6px 12px;
}
.save-status {
font-size: 10px;
}
}
</style>