You've already forked SmartisanNote.Remake
Merge pull request 'future' (#14) from future into main
Reviewed-on: yuantao/SmartisanNote.Remake#14
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -1,440 +1,518 @@
|
|||||||
<template>
|
<template>
|
||||||
<ion-page>
|
<ion-page>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
<!-- 头部:编辑模式 -->
|
<!-- 头部:编辑模式 -->
|
||||||
<Header v-if="isEditorFocus" :onBack="handleCancel" :onAction="handleAction" actionIcon="edit" />
|
<Header v-if="isEditorFocus" :onBack="handleCancel" :onAction="handleAction" actionIcon="edit" />
|
||||||
<!-- 头部:预览模式 -->
|
<!-- 头部:预览模式 -->
|
||||||
<Header v-else ref="headerRef" :onBack="handleCancel" :onAction="handleAction" actionIcon="preview" />
|
<Header v-else ref="headerRef" :onBack="handleCancel" :onAction="handleAction" actionIcon="preview" />
|
||||||
<section>
|
<section>
|
||||||
<!-- 顶部信息栏 -->
|
<!-- 顶部信息栏 -->
|
||||||
<div class="header-info">
|
<div ref="headerInfoRef" class="header-info">
|
||||||
<span class="edit-time">{{ formattedTime }}</span>
|
<span class="edit-time">{{ formattedTime }}</span>
|
||||||
<span>|</span>
|
<span>|</span>
|
||||||
<span class="word-count">{{ wordCount }}</span>
|
<span class="word-count">{{ wordCount }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 富文本编辑器 -->
|
<!-- 富文本编辑器 -->
|
||||||
<div class="editor-container">
|
<div class="note-container" :style="{ height: noteContainerHeight }">
|
||||||
<RichTextEditor ref="editorRef" :modelValue="content" @update:modelValue="handleContentChange" @focus="handleEditorFocus" @blur="handleEditorBlur" class="rich-text-editor" />
|
<RichTextEditor ref="editorRef" :modelValue="content" @update:modelValue="handleContentChange" @focus="handleEditorFocus" @blur="handleEditorBlur" class="rich-text-editor" />
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</ion-page>
|
</ion-page>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
|
import { ref, computed, onMounted, nextTick, watch, onBeforeUnmount } from 'vue'
|
||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { useAppStore } from '../stores/useAppStore'
|
import { useAppStore } from '../stores/useAppStore'
|
||||||
import Header from '../components/Header.vue'
|
import Header from '../components/Header.vue'
|
||||||
import RichTextEditor from '../components/RichTextEditor.vue'
|
import RichTextEditor from '../components/RichTextEditor.vue'
|
||||||
import { formatNoteEditorDate } from '../utils/dateUtils'
|
import { formatNoteEditorDate } from '../utils/dateUtils'
|
||||||
import { IonPage } from '@ionic/vue'
|
import { IonPage } from '@ionic/vue'
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
id: {
|
id: {
|
||||||
type: String,
|
type: String,
|
||||||
default: null,
|
default: null,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
// 为了保持向后兼容性,我们也支持noteId属性
|
// 为了保持向后兼容性,我们也支持noteId属性
|
||||||
// 通过计算属性确保无论使用id还是noteId都能正确获取便签ID
|
// 通过计算属性确保无论使用id还是noteId都能正确获取便签ID
|
||||||
const noteId = computed(() => props.id || props.noteId)
|
const noteId = computed(() => props.id || props.noteId)
|
||||||
|
|
||||||
const store = useAppStore()
|
const store = useAppStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const editorRef = ref(null)
|
const editorRef = ref(null)
|
||||||
const headerRef = ref(null)
|
const headerRef = ref(null)
|
||||||
// 是否聚焦编辑器
|
const headerInfoRef = ref(null)
|
||||||
const isEditorFocus = ref(false)
|
// 是否聚焦编辑器
|
||||||
|
const isEditorFocus = ref(false)
|
||||||
// 设置便签内容的函数
|
|
||||||
// 用于在编辑器中加载指定便签的内容
|
// 计算.note-container的高度
|
||||||
const setNoteContent = async noteId => {
|
const noteContainerHeight = ref('100vh')
|
||||||
// 确保store数据已加载,如果便签列表为空则先加载数据
|
|
||||||
if (store.notes.length === 0) {
|
// 设置便签内容的函数
|
||||||
await store.loadData()
|
// 用于在编辑器中加载指定便签的内容
|
||||||
}
|
const setNoteContent = async noteId => {
|
||||||
|
// 确保store数据已加载,如果便签列表为空则先加载数据
|
||||||
// 从store中查找指定ID的便签
|
if (store.notes.length === 0) {
|
||||||
const note = store.notes.find(n => n.id === noteId)
|
await store.loadData()
|
||||||
|
}
|
||||||
// 确保编辑器已经初始化完成
|
|
||||||
await nextTick()
|
// 从store中查找指定ID的便签
|
||||||
|
const note = store.notes.find(n => n.id === noteId)
|
||||||
if (note) {
|
|
||||||
// 无论editorRef是否可用,都先设置content的值作为备份
|
// 确保编辑器已经初始化完成
|
||||||
content.value = note.content || ''
|
await nextTick()
|
||||||
// 如果editorRef可用,直接设置编辑器内容
|
|
||||||
if (editorRef.value) {
|
if (note) {
|
||||||
editorRef.value.setContent(note.content || '')
|
// 无论editorRef是否可用,都先设置content的值作为备份
|
||||||
}
|
content.value = note.content || ''
|
||||||
}
|
// 如果editorRef可用,直接设置编辑器内容
|
||||||
}
|
if (editorRef.value) {
|
||||||
|
editorRef.value.setContent(note.content || '')
|
||||||
// 加载初始数据
|
}
|
||||||
onMounted(async () => {
|
}
|
||||||
await store.loadData()
|
}
|
||||||
|
|
||||||
// 如果是编辑现有便签,在组件挂载后设置内容
|
// 加载初始数据
|
||||||
if (noteId.value) {
|
onMounted(async () => {
|
||||||
await setNoteContent(noteId.value)
|
await store.loadData()
|
||||||
}
|
|
||||||
})
|
// 如果是编辑现有便签,在组件挂载后设置内容
|
||||||
|
if (noteId.value) {
|
||||||
// 监听noteId变化,确保在编辑器准备好后设置内容
|
await setNoteContent(noteId.value)
|
||||||
watch(
|
}
|
||||||
noteId,
|
|
||||||
async newNoteId => {
|
// 等待DOM更新后计算.note-container的高度
|
||||||
if (newNoteId) {
|
calculateNoteContainerHeight()
|
||||||
await setNoteContent(newNoteId)
|
})
|
||||||
}
|
|
||||||
},
|
// 监听noteId变化,确保在编辑器准备好后设置内容
|
||||||
{ immediate: true }
|
watch(
|
||||||
)
|
noteId,
|
||||||
|
async newNoteId => {
|
||||||
// 监听store变化,确保在store加载后设置内容
|
if (newNoteId) {
|
||||||
watch(
|
await setNoteContent(newNoteId)
|
||||||
() => store.notes,
|
// 重新计算.note-container的高度
|
||||||
async newNotes => {
|
calculateNoteContainerHeight()
|
||||||
if (noteId.value && newNotes.length > 0) {
|
}
|
||||||
await setNoteContent(noteId.value)
|
},
|
||||||
}
|
{ immediate: true }
|
||||||
},
|
)
|
||||||
{ immediate: true }
|
|
||||||
)
|
// 监听store变化,确保在store加载后设置内容
|
||||||
|
watch(
|
||||||
// 检查是否正在编辑现有便签
|
() => store.notes,
|
||||||
// 如果noteId存在则表示是编辑模式,否则是新建模式
|
async newNotes => {
|
||||||
const isEditing = !!noteId.value
|
if (noteId.value && newNotes.length > 0) {
|
||||||
const existingNote = isEditing ? store.notes.find(n => n.id === noteId.value) : null
|
await setNoteContent(noteId.value)
|
||||||
|
// 重新计算.note-container的高度
|
||||||
// 初始化内容状态,如果是编辑现有便签则使用便签内容,否则为空字符串
|
calculateNoteContainerHeight()
|
||||||
const content = ref(existingNote?.content || '')
|
}
|
||||||
|
},
|
||||||
// 当组件挂载时,确保编辑器初始化为空内容(针对新建便签)
|
{ immediate: true }
|
||||||
onMounted(() => {
|
)
|
||||||
if (!isEditing && editorRef.value) {
|
|
||||||
console.log('Initializing editor for new note')
|
// 检查是否正在编辑现有便签
|
||||||
editorRef.value.setContent('')
|
// 如果noteId存在则表示是编辑模式,否则是新建模式
|
||||||
}
|
const isEditing = !!noteId.value
|
||||||
})
|
const existingNote = isEditing ? store.notes.find(n => n.id === noteId.value) : null
|
||||||
|
|
||||||
// 监听store变化,确保在store加载后设置内容
|
// 初始化内容状态,如果是编辑现有便签则使用便签内容,否则为空字符串
|
||||||
watch(
|
const content = ref(existingNote?.content || '')
|
||||||
() => store.notes,
|
|
||||||
async newNotes => {
|
// 当组件挂载时,确保编辑器初始化为空内容(针对新建便签)
|
||||||
if (noteId.value && newNotes.length > 0) {
|
onMounted(() => {
|
||||||
await setNoteContent(noteId.value)
|
if (!isEditing && editorRef.value) {
|
||||||
}
|
console.log('Initializing editor for new note')
|
||||||
},
|
editorRef.value.setContent('')
|
||||||
{ immediate: true }
|
}
|
||||||
)
|
})
|
||||||
const showAlert = ref(false)
|
|
||||||
|
// 监听store变化,确保在store加载后设置内容
|
||||||
// 防抖函数
|
watch(
|
||||||
// 用于避免函数在短时间内被频繁调用,提高性能
|
() => store.notes,
|
||||||
const debounce = (func, delay) => {
|
async newNotes => {
|
||||||
let timeoutId
|
if (noteId.value && newNotes.length > 0) {
|
||||||
return function (...args) {
|
await setNoteContent(noteId.value)
|
||||||
clearTimeout(timeoutId)
|
// 重新计算.note-container的高度
|
||||||
timeoutId = setTimeout(() => func.apply(this, args), delay)
|
calculateNoteContainerHeight()
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
{ immediate: true }
|
||||||
// 防抖处理内容变化
|
)
|
||||||
// 延迟300ms更新内容,避免用户输入时频繁触发更新
|
const showAlert = ref(false)
|
||||||
const debouncedHandleContentChange = debounce(newContent => {
|
|
||||||
content.value = newContent
|
// 防抖函数
|
||||||
}, 300)
|
// 用于避免函数在短时间内被频繁调用,提高性能
|
||||||
|
const debounce = (func, delay) => {
|
||||||
// 监听编辑器内容变化
|
let timeoutId
|
||||||
// 当编辑器内容发生变化时调用此函数
|
return function (...args) {
|
||||||
const handleContentChange = newContent => {
|
clearTimeout(timeoutId)
|
||||||
debouncedHandleContentChange(newContent)
|
timeoutId = setTimeout(() => func.apply(this, args), delay)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// 计算属性 - 格式化时间显示
|
|
||||||
// 如果是编辑现有便签则显示便签的更新时间,否则显示当前时间
|
// 防抖处理内容变化
|
||||||
const formattedTime = computed(() => {
|
// 延迟300ms更新内容,避免用户输入时频繁触发更新
|
||||||
if (existingNote?.updatedAt) {
|
const debouncedHandleContentChange = debounce(newContent => {
|
||||||
return formatNoteEditorDate(existingNote.updatedAt)
|
content.value = newContent
|
||||||
}
|
}, 300)
|
||||||
return formatNoteEditorDate(new Date())
|
|
||||||
})
|
// 监听编辑器内容变化
|
||||||
|
// 当编辑器内容发生变化时调用此函数
|
||||||
// 计算属性 - 计算字数
|
const handleContentChange = newContent => {
|
||||||
// 移除HTML标签后计算纯文本字数
|
debouncedHandleContentChange(newContent)
|
||||||
const wordCount = computed(() => {
|
}
|
||||||
// 使用正则表达式移除HTML标签,只保留纯文本内容
|
|
||||||
const textContent = content.value.replace(/<[^>]*>/g, '')
|
// 计算属性 - 格式化时间显示
|
||||||
return textContent.length || 0
|
// 如果是编辑现有便签则显示便签的更新时间,否则显示当前时间
|
||||||
})
|
const formattedTime = computed(() => {
|
||||||
|
if (existingNote?.updatedAt) {
|
||||||
// 处理保存
|
return formatNoteEditorDate(existingNote.updatedAt)
|
||||||
const handleSave = async () => {
|
}
|
||||||
try {
|
return formatNoteEditorDate(new Date())
|
||||||
// 获取编辑器中的实际内容
|
})
|
||||||
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
|
||||||
|
// 计算属性 - 计算字数
|
||||||
// 检查内容是否为空
|
// 移除HTML标签后计算纯文本字数
|
||||||
if (isContentEmpty(editorContent)) {
|
const wordCount = computed(() => {
|
||||||
// 如果是编辑模式且内容为空,则删除便签
|
// 使用正则表达式移除HTML标签,只保留纯文本内容
|
||||||
if (isEditing && existingNote) {
|
const textContent = content.value.replace(/<[^>]*>/g, '')
|
||||||
await store.deleteNote(noteId.value)
|
return textContent.length || 0
|
||||||
console.log('空便签已删除')
|
})
|
||||||
// 删除后返回便签列表页面
|
|
||||||
router.push('/notes')
|
// 处理保存
|
||||||
return
|
const handleSave = async () => {
|
||||||
}
|
try {
|
||||||
} else if (isEditing && existingNote) {
|
// 获取编辑器中的实际内容
|
||||||
// 更新现有便签
|
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
||||||
await store.updateNote(noteId.value, {
|
|
||||||
content: editorContent,
|
// 检查内容是否为空
|
||||||
})
|
if (isContentEmpty(editorContent)) {
|
||||||
console.log('便签已保存')
|
// 如果是编辑模式且内容为空,则删除便签
|
||||||
} else {
|
if (isEditing && existingNote) {
|
||||||
// 创建新便签
|
await store.deleteNote(noteId.value)
|
||||||
await store.addNote({
|
console.log('空便签已删除')
|
||||||
content: editorContent,
|
// 删除后返回便签列表页面
|
||||||
isStarred: false,
|
router.push('/notes')
|
||||||
})
|
return
|
||||||
console.log('新便签已创建')
|
}
|
||||||
}
|
} else if (isEditing && existingNote) {
|
||||||
|
// 更新现有便签
|
||||||
// 保存后切换到预览模式(失去编辑器焦点)
|
await store.updateNote(noteId.value, {
|
||||||
isEditorFocus.value = false
|
content: editorContent,
|
||||||
} catch (error) {
|
})
|
||||||
// In a full implementation, show an alert or toast
|
console.log('便签已保存')
|
||||||
console.log('Save error: Failed to save note. Please try again.')
|
} else {
|
||||||
}
|
// 创建新便签
|
||||||
}
|
await store.addNote({
|
||||||
|
content: editorContent,
|
||||||
// 检查内容是否为空(无实质性内容)
|
isStarred: false,
|
||||||
const isContentEmpty = content => {
|
})
|
||||||
if (!content) return true
|
console.log('新便签已创建')
|
||||||
|
}
|
||||||
// 检查是否包含图片元素
|
|
||||||
const hasImages = /<img[^>]*>|<div[^>]*class="[^"]*editor-image[^"]*"[^>]*>/.test(content)
|
// 保存后切换到预览模式(失去编辑器焦点)
|
||||||
if (hasImages) return false
|
isEditorFocus.value = false
|
||||||
|
} catch (error) {
|
||||||
// 移除HTML标签和空白字符后检查是否为空
|
// In a full implementation, show an alert or toast
|
||||||
const plainText = content.replace(/<[^>]*>/g, '').trim()
|
console.log('Save error: Failed to save note. Please try again.')
|
||||||
if (plainText === '') return true
|
}
|
||||||
|
}
|
||||||
// 检查是否只有空的HTML元素
|
|
||||||
const strippedContent = content
|
// 检查内容是否为空(无实质性内容)
|
||||||
.replace(/\s+/g, '')
|
const isContentEmpty = content => {
|
||||||
.replace(/<br>/g, '')
|
if (!content) return true
|
||||||
.replace(/<br\/>/g, '')
|
|
||||||
if (strippedContent === '<p></p>' || strippedContent === '<div></div>' || strippedContent === '') return true
|
// 检查是否包含图片元素
|
||||||
|
const hasImages = /<img[^>]*>|<div[^>]*class="[^"]*editor-image[^"]*"[^>]*>/.test(content)
|
||||||
return false
|
if (hasImages) return false
|
||||||
}
|
|
||||||
|
// 移除HTML标签和空白字符后检查是否为空
|
||||||
// 自动保存便签(仅在非主动保存操作时调用)
|
const plainText = content.replace(/<[^>]*>/g, '').trim()
|
||||||
const autoSaveNote = async () => {
|
if (plainText === '') return true
|
||||||
try {
|
|
||||||
// 获取编辑器中的实际内容
|
// 检查是否只有空的HTML元素
|
||||||
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
const strippedContent = content
|
||||||
|
.replace(/\s+/g, '')
|
||||||
if (isEditing && existingNote) {
|
.replace(/<br>/g, '')
|
||||||
// 检查内容是否为空,如果为空则删除便签
|
.replace(/<br\/>/g, '')
|
||||||
if (isContentEmpty(editorContent)) {
|
if (strippedContent === '<p></p>' || strippedContent === '<div></div>' || strippedContent === '') return true
|
||||||
// 删除便签
|
|
||||||
await store.deleteNote(noteId.value)
|
return false
|
||||||
console.log('空便签已自动删除')
|
}
|
||||||
} else if (editorContent !== (existingNote?.content || '')) {
|
|
||||||
// 更新现有便签(仅当内容有变化时)
|
// 自动保存便签(仅在非主动保存操作时调用)
|
||||||
await store.updateNote(noteId.value, {
|
const autoSaveNote = async () => {
|
||||||
content: editorContent,
|
try {
|
||||||
})
|
// 获取编辑器中的实际内容
|
||||||
console.log('便签已自动保存')
|
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
||||||
}
|
|
||||||
} else if (!isContentEmpty(editorContent)) {
|
if (isEditing && existingNote) {
|
||||||
// 创建新便签(仅当有内容时)
|
// 检查内容是否为空,如果为空则删除便签
|
||||||
// 检查是否已经存在相同内容的便签以避免重复创建
|
if (isContentEmpty(editorContent)) {
|
||||||
const existingNotes = store.notes.filter(n => n.content === editorContent && !n.isDeleted)
|
// 删除便签
|
||||||
if (existingNotes.length === 0) {
|
await store.deleteNote(noteId.value)
|
||||||
await store.addNote({
|
console.log('空便签已自动删除')
|
||||||
content: editorContent,
|
} else if (editorContent !== (existingNote?.content || '')) {
|
||||||
isStarred: false,
|
// 更新现有便签(仅当内容有变化时)
|
||||||
})
|
await store.updateNote(noteId.value, {
|
||||||
console.log('新便签已自动保存')
|
content: editorContent,
|
||||||
}
|
})
|
||||||
}
|
console.log('便签已自动保存')
|
||||||
} catch (error) {
|
}
|
||||||
console.error('自动保存失败:', error)
|
} else if (!isContentEmpty(editorContent)) {
|
||||||
}
|
// 创建新便签(仅当有内容时)
|
||||||
}
|
// 检查是否已经存在相同内容的便签以避免重复创建
|
||||||
|
const existingNotes = store.notes.filter(n => n.content === editorContent && !n.isDeleted)
|
||||||
// 处理取消
|
if (existingNotes.length === 0) {
|
||||||
const handleCancel = async () => {
|
await store.addNote({
|
||||||
// 自动保存便签
|
content: editorContent,
|
||||||
await autoSaveNote()
|
isStarred: false,
|
||||||
|
})
|
||||||
// 直接导航回便签列表页面,因为已经处理了保存或删除逻辑
|
console.log('新便签已自动保存')
|
||||||
router.push('/notes')
|
}
|
||||||
}
|
}
|
||||||
|
} catch (error) {
|
||||||
// 处理创建(用于新建便签)
|
console.error('自动保存失败:', error)
|
||||||
const handleCreate = async () => {
|
}
|
||||||
try {
|
}
|
||||||
// 获取编辑器中的实际内容
|
|
||||||
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
// 处理取消
|
||||||
|
const handleCancel = async () => {
|
||||||
// 只有当有内容时才创建新便签
|
// 自动保存便签
|
||||||
if (!isContentEmpty(editorContent)) {
|
await autoSaveNote()
|
||||||
await store.addNote({
|
|
||||||
content: editorContent,
|
// 直接导航回便签列表页面,因为已经处理了保存或删除逻辑
|
||||||
isStarred: false,
|
router.push('/notes')
|
||||||
})
|
}
|
||||||
console.log('新便签已创建')
|
|
||||||
}
|
// 处理创建(用于新建便签)
|
||||||
|
const handleCreate = async () => {
|
||||||
// 创建后切换到预览模式(失去编辑器焦点)
|
try {
|
||||||
isEditorFocus.value = false
|
// 获取编辑器中的实际内容
|
||||||
} catch (error) {
|
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
||||||
// In a full implementation, show an alert or toast
|
|
||||||
console.log('Create error: Failed to create note. Please try again.')
|
// 只有当有内容时才创建新便签
|
||||||
}
|
if (!isContentEmpty(editorContent)) {
|
||||||
}
|
await store.addNote({
|
||||||
|
content: editorContent,
|
||||||
// 处理Header组件的操作按钮点击事件
|
isStarred: false,
|
||||||
const handleAction = actionType => {
|
})
|
||||||
if (actionType === 'save') {
|
console.log('新便签已创建')
|
||||||
handleSave()
|
}
|
||||||
} else if (actionType === 'create') {
|
|
||||||
handleCreate()
|
// 创建后切换到预览模式(失去编辑器焦点)
|
||||||
} else if (actionType === 'insertImage') {
|
isEditorFocus.value = false
|
||||||
// 插入图片功能
|
} catch (error) {
|
||||||
if (editorRef.value) {
|
// In a full implementation, show an alert or toast
|
||||||
// 通过editorRef调用RichTextEditor组件的方法来插入图片
|
console.log('Create error: Failed to create note. Please try again.')
|
||||||
editorRef.value.insertImage()
|
}
|
||||||
}
|
}
|
||||||
} else if (actionType === 'delete') {
|
|
||||||
// 删除便签
|
// 处理Header组件的操作按钮点击事件
|
||||||
handleDelete()
|
const handleAction = actionType => {
|
||||||
} else if (actionType === 'share') {
|
if (actionType === 'save') {
|
||||||
// 分享便签
|
handleSave()
|
||||||
handleShare()
|
} else if (actionType === 'create') {
|
||||||
}
|
handleCreate()
|
||||||
}
|
} else if (actionType === 'insertImage') {
|
||||||
|
// 插入图片功能
|
||||||
const setShowAlert = value => {
|
if (editorRef.value) {
|
||||||
showAlert.value = value
|
// 通过editorRef调用RichTextEditor组件的方法来插入图片
|
||||||
}
|
editorRef.value.insertImage()
|
||||||
|
}
|
||||||
// 处理编辑器获得焦点
|
} else if (actionType === 'delete') {
|
||||||
const handleEditorFocus = () => {
|
// 删除便签
|
||||||
isEditorFocus.value = true
|
handleDelete()
|
||||||
}
|
} else if (actionType === 'share') {
|
||||||
|
// 分享便签
|
||||||
// 处理编辑器失去焦点
|
handleShare()
|
||||||
const handleEditorBlur = () => {
|
}
|
||||||
isEditorFocus.value = false
|
}
|
||||||
}
|
|
||||||
|
const setShowAlert = value => {
|
||||||
// 处理删除便签
|
showAlert.value = value
|
||||||
const handleDelete = async () => {
|
}
|
||||||
if (isEditing && existingNote) {
|
|
||||||
// 播放删除按钮动画
|
// 处理编辑器获得焦点
|
||||||
if (headerRef.value && headerRef.value.playDeleteAnimation) {
|
const handleEditorFocus = () => {
|
||||||
headerRef.value.playDeleteAnimation()
|
isEditorFocus.value = true
|
||||||
}
|
// 重新计算.note-container的高度
|
||||||
|
nextTick(() => {
|
||||||
// 等待动画播放完成后再执行删除操作
|
calculateNoteContainerHeight()
|
||||||
// 15帧 * 50ms = 750ms,再加上一些缓冲时间
|
})
|
||||||
setTimeout(async () => {
|
}
|
||||||
try {
|
|
||||||
// 删除便签
|
// 处理编辑器失去焦点
|
||||||
await store.deleteNote(noteId.value)
|
const handleEditorBlur = () => {
|
||||||
console.log('便签已删除')
|
isEditorFocus.value = false
|
||||||
// 返回便签列表页面
|
// 重新计算.note-container的高度
|
||||||
router.push('/notes')
|
nextTick(() => {
|
||||||
} catch (error) {
|
calculateNoteContainerHeight()
|
||||||
console.error('删除便签失败:', error)
|
})
|
||||||
}
|
}
|
||||||
}, 800) // 等待约800ms让动画播放完成
|
|
||||||
}
|
// 处理删除便签
|
||||||
}
|
const handleDelete = async () => {
|
||||||
|
if (isEditing && existingNote) {
|
||||||
// 处理分享便签
|
// 播放删除按钮动画
|
||||||
const handleShare = () => {
|
if (headerRef.value && headerRef.value.playDeleteAnimation) {
|
||||||
// 获取编辑器中的实际内容
|
headerRef.value.playDeleteAnimation()
|
||||||
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
}
|
||||||
|
|
||||||
// 移除HTML标签,获取纯文本内容用于分享
|
// 等待动画播放完成后再执行删除操作
|
||||||
const plainText = editorContent.replace(/<[^>]*>/g, '').trim()
|
// 15帧 * 50ms = 750ms,再加上一些缓冲时间
|
||||||
|
setTimeout(async () => {
|
||||||
if (plainText) {
|
try {
|
||||||
// 在实际应用中,这里会调用设备的分享功能
|
// 删除便签
|
||||||
// 为了演示,我们使用Web Share API(如果支持)
|
await store.deleteNote(noteId.value)
|
||||||
if (navigator.share) {
|
console.log('便签已删除')
|
||||||
navigator
|
// 返回便签列表页面
|
||||||
.share({
|
router.push('/notes')
|
||||||
title: '分享便签',
|
} catch (error) {
|
||||||
text: plainText,
|
console.error('删除便签失败:', error)
|
||||||
})
|
}
|
||||||
.catch(error => {
|
}, 800) // 等待约800ms让动画播放完成
|
||||||
console.log('分享取消或失败:', error)
|
}
|
||||||
})
|
}
|
||||||
} else {
|
|
||||||
// 如果不支持Web Share API,可以复制到剪贴板
|
// 处理分享便签
|
||||||
navigator.clipboard
|
const handleShare = () => {
|
||||||
.writeText(plainText)
|
// 获取编辑器中的实际内容
|
||||||
.then(() => {
|
const editorContent = editorRef.value ? editorRef.value.getContent() : content.value
|
||||||
console.log('内容已复制到剪贴板')
|
|
||||||
// 在实际应用中,这里会显示一个提示消息
|
// 移除HTML标签,获取纯文本内容用于分享
|
||||||
})
|
const plainText = editorContent.replace(/<[^>]*>/g, '').trim()
|
||||||
.catch(error => {
|
|
||||||
console.error('复制失败:', error)
|
if (plainText) {
|
||||||
})
|
// 在实际应用中,这里会调用设备的分享功能
|
||||||
}
|
// 为了演示,我们使用Web Share API(如果支持)
|
||||||
}
|
if (navigator.share) {
|
||||||
}
|
navigator
|
||||||
|
.share({
|
||||||
// 在组件卸载前自动保存或删除
|
title: '分享便签',
|
||||||
onBeforeUnmount(async () => {
|
text: plainText,
|
||||||
await autoSaveNote()
|
})
|
||||||
})
|
.catch(error => {
|
||||||
</script>
|
console.log('分享取消或失败:', error)
|
||||||
|
})
|
||||||
<style lang="less" scoped>
|
} else {
|
||||||
.container {
|
// 如果不支持Web Share API,可以复制到剪贴板
|
||||||
display: flex;
|
navigator.clipboard
|
||||||
flex-direction: column;
|
.writeText(plainText)
|
||||||
height: 100vh;
|
.then(() => {
|
||||||
background-color: var(--background);
|
console.log('内容已复制到剪贴板')
|
||||||
section {
|
// 在实际应用中,这里会显示一个提示消息
|
||||||
width: 100%;
|
})
|
||||||
height: 100%;
|
.catch(error => {
|
||||||
}
|
console.error('复制失败:', error)
|
||||||
}
|
})
|
||||||
|
}
|
||||||
.editor-container {
|
}
|
||||||
flex: 1;
|
}
|
||||||
min-height: 100%;
|
|
||||||
overflow-y: auto;
|
// 计算.note-container的高度
|
||||||
background-color: var(--background-card);
|
const calculateNoteContainerHeight = () => {
|
||||||
&.disabled {
|
nextTick(() => {
|
||||||
pointer-events: none;
|
let headerHeight = 0
|
||||||
}
|
let headerInfoHeight = 0
|
||||||
}
|
|
||||||
|
// 获取Header组件的高度
|
||||||
.rich-text-editor {
|
if (headerRef.value?.$el) {
|
||||||
min-height: 100%;
|
headerHeight = headerRef.value.$el.offsetHeight || 0
|
||||||
}
|
} else {
|
||||||
|
// 如果headerRef不可用,尝试查找Header组件的DOM元素
|
||||||
.header-info {
|
const headerElement = document.querySelector('.component')
|
||||||
display: flex;
|
headerHeight = headerElement ? headerElement.offsetHeight : 100 // 默认高度
|
||||||
justify-content: flex-start;
|
}
|
||||||
gap: 0.625rem;
|
|
||||||
padding: 1rem 1rem 0.7rem 1rem;
|
// 获取.header-info的高度
|
||||||
background-color: var(--background-card);
|
if (headerInfoRef.value) {
|
||||||
border-bottom: 1px solid var(--border);
|
headerInfoHeight = headerInfoRef.value.offsetHeight || 0
|
||||||
font-size: 0.7rem;
|
} else {
|
||||||
color: var(--text-tertiary);
|
// 如果headerInfoRef不可用,获取.header-info的默认高度
|
||||||
}
|
const headerInfoElement = document.querySelector('.header-info')
|
||||||
</style>
|
headerInfoHeight = headerInfoElement ? headerInfoElement.offsetHeight : 40 // 默认高度
|
||||||
|
}
|
||||||
|
|
||||||
|
// 计算剩余高度 (屏幕高度 - Header高度 - HeaderInfo高度)
|
||||||
|
const totalHeaderHeight = headerHeight + headerInfoHeight
|
||||||
|
const screenHeight = window.innerHeight
|
||||||
|
const newHeight = screenHeight - totalHeaderHeight
|
||||||
|
|
||||||
|
// 设置.note-container的高度
|
||||||
|
noteContainerHeight.value = `${newHeight}px`
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 在组件挂载后和内容变化时重新计算高度
|
||||||
|
onMounted(() => {
|
||||||
|
// 等待DOM更新后再计算高度
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听窗口大小变化事件
|
||||||
|
window.addEventListener('resize', calculateNoteContainerHeight)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听编辑器焦点变化,重新计算高度
|
||||||
|
watch(isEditorFocus, () => {
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 监听内容变化,重新计算高度(当内容变化可能导致header-info高度变化时)
|
||||||
|
watch(content, () => {
|
||||||
|
nextTick(() => {
|
||||||
|
calculateNoteContainerHeight()
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
// 在组件卸载前自动保存或删除
|
||||||
|
onBeforeUnmount(async () => {
|
||||||
|
await autoSaveNote()
|
||||||
|
// 移除窗口大小变化事件监听器
|
||||||
|
window.removeEventListener('resize', calculateNoteContainerHeight)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100vh;
|
||||||
|
background-color: var(--background);
|
||||||
|
section {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.note-container {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: scroll;
|
||||||
|
background-color: var(--background);
|
||||||
|
}
|
||||||
|
|
||||||
|
.rich-text-editor {
|
||||||
|
min-height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-info {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 0.625rem;
|
||||||
|
padding: 1rem 1rem 0.7rem 1rem;
|
||||||
|
background-color: var(--background-card);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-tertiary);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -101,23 +101,7 @@ export const useAppStore = defineStore('app', {
|
|||||||
|
|
||||||
// Mock folders - 使用固定的日期值
|
// Mock folders - 使用固定的日期值
|
||||||
// 预设的文件夹示例数据
|
// 预设的文件夹示例数据
|
||||||
const mockFolders = [
|
const mockFolders = []
|
||||||
{
|
|
||||||
id: 'folder1',
|
|
||||||
name: '工作',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'folder2',
|
|
||||||
name: '个人',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 'folder3',
|
|
||||||
name: '学习',
|
|
||||||
createdAt: '2025-10-12T10:00:00.000Z',
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
// Mock settings
|
// Mock settings
|
||||||
// 预设的设置示例数据
|
// 预设的设置示例数据
|
||||||
|
|||||||
Reference in New Issue
Block a user