修复无法复制命令bug
This commit is contained in:
@@ -1,10 +1,23 @@
|
||||
<template>
|
||||
<div class="full-screen-wrapper" style="width: 100vw; height: 100vh;">
|
||||
<MonacoTreeEditor ref="monacoEditorRef" :font-size="14" :files="files" :sider-min-width="250" filelist-title="文件列表"
|
||||
language="zh-CN" @reload="handleReload" @new-file="handleNewFile" @new-folder="handleNewFolder"
|
||||
@save-file="handleSaveFile" @delete-file="handleDeleteFile" @delete-folder="handleDeleteFolder"
|
||||
@rename-file="handleRename" @rename-folder="handleRename" :file-menu="fileMenu"
|
||||
@contextmenu-select="handleContextMenuSelect"></MonacoTreeEditor>
|
||||
<div class="full-screen-wrapper" style="width: 100vw; height: 100vh">
|
||||
<MonacoTreeEditor
|
||||
ref="monacoEditorRef"
|
||||
:font-size="14"
|
||||
:files="files"
|
||||
:sider-min-width="250"
|
||||
filelist-title="文件列表"
|
||||
language="zh-CN"
|
||||
@reload="handleReload"
|
||||
@new-file="handleNewFile"
|
||||
@new-folder="handleNewFolder"
|
||||
@save-file="handleSaveFile"
|
||||
@delete-file="handleDeleteFile"
|
||||
@delete-folder="handleDeleteFolder"
|
||||
@rename-file="handleRename"
|
||||
@rename-folder="handleRename"
|
||||
:file-menu="fileMenu"
|
||||
@contextmenu-select="handleContextMenuSelect"
|
||||
></MonacoTreeEditor>
|
||||
|
||||
<!-- 隐藏的文件上传输入框 -->
|
||||
<input ref="fileInputRef" type="file" multiple style="display: none" @change="handleFileUpload" />
|
||||
@@ -14,12 +27,18 @@
|
||||
<style scoped>
|
||||
.full-screen-wrapper :deep(.url-info-text) {
|
||||
font-size: 13px;
|
||||
|
||||
background-color: var(--url-info-bg, rgba(0, 0, 0, 0.05));
|
||||
border-color: var(--url-info-border, rgba(0, 0, 0, 0.1));
|
||||
color: var(--url-info-color, inherit);
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.full-screen-wrapper :deep(.url-info-text:hover) {
|
||||
background-color: var(--url-info-bg-hover, rgba(0, 0, 0, 0.1));
|
||||
}
|
||||
|
||||
.full-screen-wrapper :deep(.message-container) {
|
||||
user-select: none;
|
||||
@@ -31,7 +50,6 @@
|
||||
/* 上传按钮样式 - 插入到文件列表标题栏 */
|
||||
.full-screen-wrapper :deep(.monaco-tree-editor-list-title) {
|
||||
position: relative;
|
||||
|
||||
user-select: none;
|
||||
padding-right: 80px;
|
||||
}
|
||||
@@ -49,7 +67,6 @@
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
|
||||
.full-screen-wrapper :deep(.upload-error-message) {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
@@ -64,6 +81,32 @@
|
||||
max-width: 300px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 命令选择菜单样式 */
|
||||
.full-screen-wrapper :deep(.command-selection-menu) {
|
||||
position: fixed;
|
||||
background: white;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||
z-index: 1000;
|
||||
min-width: 200px;
|
||||
}
|
||||
|
||||
.full-screen-wrapper :deep(.command-selection-menu div) {
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.full-screen-wrapper :deep(.command-selection-menu div:last-child) {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.full-screen-wrapper :deep(.command-selection-menu div:hover) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
</style>
|
||||
<script setup lang="ts">
|
||||
import { Editor as MonacoTreeEditor, useMonaco, type Files } from 'monaco-tree-editor'
|
||||
@@ -85,6 +128,8 @@ declare global {
|
||||
getWorker: (moduleId: any, label: string) => Worker
|
||||
globalAPI?: boolean
|
||||
}
|
||||
copyManualText?: () => void
|
||||
closeManualCopy?: () => void
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,11 +239,12 @@ const handleRename = (path: string, newPath: string, resolve: () => void, reject
|
||||
}
|
||||
|
||||
// ================ 自定义菜单 =================
|
||||
const fileMenu = ref([
|
||||
{ label: '下载文件', value: 'download' },
|
||||
])
|
||||
const fileMenu = ref([{ label: '下载文件', value: 'download' }])
|
||||
|
||||
const handleContextMenuSelect = (path: string, item: { label: string | import('vue').ComputedRef<string>; value: any }) => {
|
||||
const handleContextMenuSelect = (
|
||||
path: string,
|
||||
item: { label: string | import('vue').ComputedRef<string>; value: any }
|
||||
) => {
|
||||
console.log('选中菜单项:', item.value)
|
||||
|
||||
if (item.value === 'download') {
|
||||
@@ -280,7 +326,7 @@ const handleFileUpload = (event: Event) => {
|
||||
const oversizedFiles: string[] = []
|
||||
const validFiles: File[] = []
|
||||
|
||||
Array.from(files).forEach(file => {
|
||||
Array.from(files).forEach((file) => {
|
||||
if (file.size > MAX_FILE_SIZE) {
|
||||
oversizedFiles.push(file.name)
|
||||
} else {
|
||||
@@ -302,22 +348,25 @@ const handleFileUpload = (event: Event) => {
|
||||
}
|
||||
|
||||
// 处理有效的文件
|
||||
validFiles.forEach(file => {
|
||||
validFiles.forEach((file) => {
|
||||
const reader = new FileReader()
|
||||
reader.onload = (e) => {
|
||||
const content = e.target?.result as string
|
||||
|
||||
|
||||
const filePath = `/${file.name}`
|
||||
|
||||
// 模拟保存文件到服务器
|
||||
server.createOrSaveFile(filePath, content)
|
||||
server
|
||||
.createOrSaveFile(filePath, content)
|
||||
.then(() => {
|
||||
console.log('文件上传成功:', file.name)
|
||||
// 重新加载文件列表
|
||||
handleReload(() => { }, (msg) => console.error(msg))
|
||||
handleReload(
|
||||
() => {},
|
||||
(msg) => console.error(msg)
|
||||
)
|
||||
})
|
||||
.catch(error => {
|
||||
.catch((error) => {
|
||||
console.error('文件上传失败:', error)
|
||||
showErrorMessage(`文件上传失败: ${file.name}`)
|
||||
})
|
||||
@@ -383,7 +432,7 @@ const commandManager = {
|
||||
// 获取所有命令
|
||||
getAllCommands() {
|
||||
return Array.from(this.commands.entries())
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
// ================ URL参数提取和命令生成逻辑 ================
|
||||
@@ -411,59 +460,126 @@ const extractUrlParams = () => {
|
||||
}
|
||||
}
|
||||
|
||||
// ================ 安全的剪贴板复制函数 ================
|
||||
const copyToClipboard = (text: string) => {
|
||||
// 如果文本是错误信息,直接复制错误信息
|
||||
if (text.includes('URL解析错误') || text.includes('未找到')) {
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
console.log('文本已复制到剪贴板:', text)
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err)
|
||||
})
|
||||
// 检查 clipboard API 是否可用
|
||||
if (!navigator.clipboard) {
|
||||
// 使用降级方案
|
||||
fallbackCopyToClipboard(text)
|
||||
return
|
||||
}
|
||||
|
||||
// 否则复制命令
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
navigator.clipboard
|
||||
.writeText(text)
|
||||
.then(() => {
|
||||
console.log('命令已复制到剪贴板:', text)
|
||||
}).catch(err => {
|
||||
console.error('复制失败:', err)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('复制失败,使用降级方案:', err)
|
||||
fallbackCopyToClipboard(text)
|
||||
})
|
||||
}
|
||||
|
||||
// 降级复制方案
|
||||
const fallbackCopyToClipboard = (text: string) => {
|
||||
const textArea = document.createElement('textarea')
|
||||
textArea.value = text
|
||||
textArea.style.position = 'fixed'
|
||||
textArea.style.left = '-999999px'
|
||||
textArea.style.top = '-999999px'
|
||||
textArea.style.opacity = '0'
|
||||
document.body.appendChild(textArea)
|
||||
textArea.select()
|
||||
|
||||
try {
|
||||
document.execCommand('copy')
|
||||
textArea.select()
|
||||
textArea.setSelectionRange(0, 99999) // 移动设备支持
|
||||
|
||||
const successful = document.execCommand('copy')
|
||||
if (successful) {
|
||||
console.log('命令已复制到剪贴板(降级方案):', text)
|
||||
} else {
|
||||
console.error('复制失败(降级方案)')
|
||||
// 最终方案:提示用户手动复制
|
||||
promptManualCopy(text)
|
||||
}
|
||||
} catch (fallbackError) {
|
||||
console.error('复制失败(降级方案):', fallbackError)
|
||||
}
|
||||
promptManualCopy(text)
|
||||
} finally {
|
||||
document.body.removeChild(textArea)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 最终方案:提示用户手动复制
|
||||
const promptManualCopy = (text: string) => {
|
||||
// 创建提示框让用户手动复制
|
||||
const promptElement = document.createElement('div')
|
||||
promptElement.style.cssText = `
|
||||
position: fixed;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: white;
|
||||
border: 2px solid #ccc;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 10000;
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
overflow: auto;
|
||||
`
|
||||
|
||||
promptElement.innerHTML = `
|
||||
<h3 style="margin: 0 0 15px 0; font-size: 16px;">请手动复制以下内容:</h3>
|
||||
<textarea id="manual-copy-text" style="width: 100%; height: 100px; margin: 10px 0; padding: 8px; border: 1px solid #ddd; border-radius: 4px; font-family: monospace; resize: vertical;">${text}</textarea>
|
||||
<div style="display: flex; justify-content: flex-end; gap: 10px;">
|
||||
<button onclick="window.copyManualText && window.copyManualText()" style="padding: 8px 16px; background: #1890ff; color: white; border: none; border-radius: 4px; cursor: pointer;">复制</button>
|
||||
<button onclick="window.closeManualCopy && window.closeManualCopy()" style="padding: 8px 16px; background: #f5f5f5; border: 1px solid #ddd; border-radius: 4px; cursor: pointer;">关闭</button>
|
||||
</div>
|
||||
`
|
||||
|
||||
// 添加全局函数供按钮使用
|
||||
window.copyManualText = () => {
|
||||
const textarea = document.getElementById('manual-copy-text') as HTMLTextAreaElement
|
||||
if (textarea) {
|
||||
textarea.select()
|
||||
try {
|
||||
const successful = document.execCommand('copy')
|
||||
if (successful) {
|
||||
alert('复制成功!')
|
||||
} else {
|
||||
alert('复制失败,请手动选择文本后按 Ctrl+C 复制')
|
||||
}
|
||||
} catch (e) {
|
||||
alert('复制失败,请手动选择文本后按 Ctrl+C 复制')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.closeManualCopy = () => {
|
||||
promptElement.remove()
|
||||
delete window.copyManualText
|
||||
delete window.closeManualCopy
|
||||
}
|
||||
|
||||
document.body.appendChild(promptElement)
|
||||
}
|
||||
|
||||
// ================ DOM操作逻辑 ================
|
||||
let observer: MutationObserver | null = null
|
||||
|
||||
onMounted(() => {
|
||||
// 检查剪贴板API支持情况
|
||||
if (!navigator.clipboard) {
|
||||
console.warn('剪贴板API不可用,将使用降级方案')
|
||||
}
|
||||
|
||||
extractUrlParams()
|
||||
|
||||
|
||||
nextTick(() => {
|
||||
setTimeout(insertUrlInfoText, 100)
|
||||
setTimeout(insertUploadButton, 100)
|
||||
|
||||
// 添加命令选择菜单的CSS样式
|
||||
const style = document.createElement('style')
|
||||
style.textContent = `
|
||||
.command-selection-menu div:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
.command-selection-menu div:hover {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
`
|
||||
document.head.appendChild(style)
|
||||
})
|
||||
|
||||
observer = new MutationObserver((mutations) => {
|
||||
@@ -486,7 +602,7 @@ onMounted(() => {
|
||||
if (container) {
|
||||
observer.observe(container, {
|
||||
childList: true,
|
||||
subtree: true
|
||||
subtree: true,
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -495,6 +611,10 @@ onUnmounted(() => {
|
||||
if (observer) {
|
||||
observer.disconnect()
|
||||
}
|
||||
|
||||
// 清理全局函数
|
||||
delete window.copyManualText
|
||||
delete window.closeManualCopy
|
||||
})
|
||||
|
||||
const insertUrlInfoText = () => {
|
||||
@@ -563,6 +683,7 @@ const showCommandSelection = (event: MouseEvent, commands: [string, string][]) =
|
||||
padding: 8px 12px;
|
||||
cursor: pointer;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
transition: background-color 0.2s;
|
||||
`
|
||||
menuItem.textContent = label
|
||||
menuItem.title = command
|
||||
@@ -600,16 +721,23 @@ const showCommandSelection = (event: MouseEvent, commands: [string, string][]) =
|
||||
|
||||
// 复制命令并显示反馈
|
||||
const copyCommand = (command: string, element: HTMLElement) => {
|
||||
const originalText = element.textContent
|
||||
const originalColor = element.style.color
|
||||
|
||||
copyToClipboard(command)
|
||||
|
||||
const originalText = element.textContent
|
||||
// 显示反馈
|
||||
element.textContent = '已复制,运行后请刷新文件列表'
|
||||
element.style.color = '#52c41a'
|
||||
element.style.fontWeight = 'bold'
|
||||
|
||||
setTimeout(() => {
|
||||
if (element) {
|
||||
element.textContent = originalText
|
||||
element.style.color = ''
|
||||
}, 1000)
|
||||
element.style.color = originalColor
|
||||
element.style.fontWeight = ''
|
||||
}
|
||||
}, 1500)
|
||||
}
|
||||
|
||||
const insertUploadButton = () => {
|
||||
@@ -634,6 +762,4 @@ const insertUploadButton = () => {
|
||||
titleArea.appendChild(uploadBtn)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
Reference in New Issue
Block a user