diff --git a/apps/web/src/components/HomeworkEditDialog.vue b/apps/web/src/components/HomeworkEditDialog.vue index ba5fc677..ad3040ae 100644 --- a/apps/web/src/components/HomeworkEditDialog.vue +++ b/apps/web/src/components/HomeworkEditDialog.vue @@ -30,6 +30,26 @@ @keyup="updateCurrentLine" /> +
+ + 粘贴 + + + 粘贴并完成 + +
+
@@ -318,6 +338,9 @@ export default { showQuickTools() { return getSetting('display.showQuickTools') }, + showPasteButtons() { + return getSetting('display.showPasteButtons') + }, autoSavePromptText() { return getSetting('edit.autoSavePromptText') }, @@ -328,8 +351,9 @@ export default { watch: { async modelValue(newValue) { if (newValue) { - // 当对话框打开时,重置内容为初始内容 - this.content = this.initialContent + // 当对话框打开时,重置内容为初始内容;最后一行不是空行时,在文末加一个空行 + const initial = this.initialContent || '' + this.content = initial === '' || initial.endsWith('\n') ? initial : initial + '\n' // 加载模板数据 try { this.templateData = await dataProvider.loadData('classworks-config-homework-template') @@ -526,6 +550,47 @@ export default { }) } }, + // 从剪贴板读取内容并按光标位置组装新正文:有选中则替换,否则在光标处插入 + // 剪贴板完全为空时返回 null,空格或换行视为有效内容原样粘贴 + async buildContentWithPaste() { + const text = await navigator.clipboard.readText() + if (text == null || text === '') return null + const textarea = this.$refs.inputRef.$el.querySelector('textarea') + const start = textarea.selectionStart + const end = textarea.selectionEnd + return { + content: this.content.slice(0, start) + text + this.content.slice(end), + cursorPosition: start + text.length, + } + }, + // 从剪贴板粘贴,保留在对话框继续编辑 + async pasteFromClipboard() { + try { + const result = await this.buildContentWithPaste() + if (!result) return + this.content = result.content + this.$nextTick(() => { + const textarea = this.$refs.inputRef.$el.querySelector('textarea') + textarea.focus() + textarea.setSelectionRange(result.cursorPosition, result.cursorPosition) + this.updateCurrentLine() + }) + } catch (error) { + console.error('Failed to read clipboard:', error) + } + }, + // 从剪贴板粘贴并直接保存关闭,插入逻辑与粘贴一致 + async pasteAndComplete() { + try { + const result = await this.buildContentWithPaste() + if (!result) return + this.content = result.content + this.$emit('save', this.content.trim()) + this.dialogVisible = false + } catch (error) { + console.error('Failed to read clipboard:', error) + } + }, }, } diff --git a/apps/web/src/components/settings/cards/DisplaySettingsCard.vue b/apps/web/src/components/settings/cards/DisplaySettingsCard.vue index 6f1618fa..91724cb3 100644 --- a/apps/web/src/components/settings/cards/DisplaySettingsCard.vue +++ b/apps/web/src/components/settings/cards/DisplaySettingsCard.vue @@ -45,6 +45,9 @@ + + + diff --git a/apps/web/src/utils/settings.js b/apps/web/src/utils/settings.js index 53b539f4..ae136734 100644 --- a/apps/web/src/utils/settings.js +++ b/apps/web/src/utils/settings.js @@ -212,6 +212,13 @@ const settingsDefinitions = { description: '是否显示快捷键盘', icon: 'mdi-dialpad', }, + 'display.showPasteButtons': { + type: 'boolean', + default: true, + description: '是否显示作业编辑粘贴按钮', + icon: 'mdi-content-paste', + // 控制作业编辑对话框中的「粘贴」与「粘贴并完成」按钮,关闭后仅保留手动编辑 + }, 'display.forceDesktopMode': { type: 'boolean', default: false,