455 lines
9.6 KiB
Vue
455 lines
9.6 KiB
Vue
<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>
|