chore: adjust zip upload logical #90

This commit is contained in:
duorameng
2026-04-29 16:49:48 +08:00
parent ba6d92ec72
commit b4709ef873
2 changed files with 135 additions and 15 deletions
+42 -1
View File
@@ -13,6 +13,8 @@ import { PATHS, FILE_RUNNERS } from '@/constants'
import FileSidebar from './components/FileSidebar.vue'
import RunConfigDialog from './components/RunConfigDialog.vue'
import FileActionDialogs from './components/FileActionDialogs.vue'
import BaihuDialog from '@/components/ui/BaihuDialog.vue'
import { AlertCircle } from 'lucide-vue-next'
const route = useRoute()
const router = useRouter()
@@ -31,6 +33,12 @@ const isRefreshing = ref(false)
const isEditMode = ref(false)
const hasChanges = computed(() => fileContent.value !== originalContent.value)
const confirmLeave = ref({
show: false,
path: '',
onConfirm: () => {}
})
// Component Refs
const dialogsRef = ref<InstanceType<typeof FileActionDialogs> | null>(null)
const terminalRef = ref<InstanceType<typeof XTerminal> | null>(null)
@@ -166,7 +174,17 @@ async function handleSelect(node: FileNode) {
expandedDirs.value = new Set(expandedDirs.value)
selectedFile.value = null
} else {
if (hasChanges.value && !confirm('当前文件有未保存的更改,是否放弃?')) return
if (hasChanges.value) {
confirmLeave.value = {
show: true,
path: node.path,
onConfirm: () => {
originalContent.value = fileContent.value // 假装保存或标记为已确认放弃
loadFile(node.path)
}
}
return
}
await loadFile(node.path)
}
}
@@ -528,5 +546,28 @@ onUnmounted(() => {
</div>
</DialogContent>
</Dialog>
<BaihuDialog v-model:open="confirmLeave.show" title="未保存的更改" icon="AlertCircle" size="sm">
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-4 p-1">
<div class="h-12 w-12 rounded-full bg-yellow-500/10 flex items-center justify-center shrink-0">
<AlertCircle class="h-6 w-6 text-yellow-500" />
</div>
<div class="flex-1 text-center sm:text-left">
<p class="text-sm text-foreground/90 leading-relaxed font-medium">文件尚未保存</p>
<p class="text-[13px] text-muted-foreground mt-1">
当前文件有未保存的更改如果现在离开您的更改将会丢失
</p>
<div class="mt-3 px-2 py-1.5 bg-muted/50 rounded text-[11px] font-mono text-muted-foreground break-all border border-muted">
{{ confirmLeave.path }}
</div>
</div>
</div>
<template #footer>
<div class="flex flex-col-reverse sm:flex-row gap-2 w-full sm:w-auto mt-2 sm:mt-0">
<Button variant="outline" @click="confirmLeave.show = false" class="w-full sm:w-24">继续编辑</Button>
<Button variant="destructive" @click="confirmLeave.show = false; confirmLeave.onConfirm()" class="w-full sm:w-auto px-6">放弃更改</Button>
</div>
</template>
</BaihuDialog>
</div>
</template>
+93 -14
View File
@@ -1,11 +1,12 @@
<script setup lang="ts">
import { ref } from 'vue'
import { ref, computed } from 'vue'
import { Button } from '@/components/ui/button'
import { RefreshCw, FolderUp, Upload, Plus } from 'lucide-vue-next'
import { RefreshCw, FolderUp, Upload, Plus, AlertCircle } from 'lucide-vue-next'
import FileTreeNode from '@/components/FileTreeNode.vue'
import BaihuDialog from '@/components/ui/BaihuDialog.vue'
import { type FileNode } from '@/api'
defineProps<{
const props = defineProps<{
fileTree: FileNode[]
expandedDirs: Set<string>
selectedPath: string | null
@@ -29,6 +30,45 @@ const archiveInputRef = ref<HTMLInputElement | null>(null)
const filesInputRef = ref<HTMLInputElement | null>(null)
const uploadTargetDir = ref('')
// 确认框状态
const confirmUpload = ref<{
show: boolean
title: string
message: string
onConfirm: () => void
}>({
show: false,
title: '',
message: '',
onConfirm: () => {}
})
const currentTargetDir = computed(() => {
if (!props.selectedPath) return ''
// 深度优先搜索以确定选中的是目录还是文件
const findNode = (nodes: FileNode[], path: string): FileNode | null => {
for (const n of nodes) {
if (n.path === path) return n
if (n.children) {
const found = findNode(n.children, path)
if (found) return found
}
}
return null
}
const node = findNode(props.fileTree, props.selectedPath)
if (node && node.isDir) {
return node.path
}
// 如果选中的是文件,则返回它所在的目录
const parts = props.selectedPath.split('/')
parts.pop() // 移除文件名
return parts.join('/')
})
function triggerArchiveUpload(targetDir = '') {
uploadTargetDir.value = targetDir
if (archiveInputRef.value) archiveInputRef.value.click()
@@ -42,7 +82,17 @@ function triggerFilesUpload(targetDir = '') {
function handleArchiveUpload(e: Event) {
const input = e.target as HTMLInputElement
const file = input.files?.[0]
if (file) emit('uploadArchive', file, uploadTargetDir.value)
if (file) {
const dirName = uploadTargetDir.value || '根目录'
confirmUpload.value = {
show: true,
title: '确认导入压缩包',
message: `即将把压缩包 [${file.name}] 导入到目录 [ ${dirName} ]。确认导入吗?`,
onConfirm: () => {
emit('uploadArchive', file, uploadTargetDir.value)
}
}
}
input.value = ''
}
@@ -50,14 +100,22 @@ function handleFilesUpload(e: Event) {
const input = e.target as HTMLInputElement
const files = input.files
if (files && files.length > 0) {
const paths: string[] = []
for (let i = 0; i < files.length; i++) {
const f = files.item(i)
if (f) {
paths.push((f as any).webkitRelativePath || f.name)
}
const dirName = uploadTargetDir.value || '根目录'
confirmUpload.value = {
show: true,
title: '确认上传文件',
message: `即将把 ${files.length} 个文件/文件夹上传到目录 [ ${dirName} ]。确认上传吗?`,
onConfirm: () => {
const paths: string[] = []
for (let i = 0; i < files.length; i++) {
const f = files.item(i)
if (f) {
paths.push((f as any).webkitRelativePath || f.name)
}
}
emit('uploadFiles', files, paths, uploadTargetDir.value)
}
}
emit('uploadFiles', files, paths, uploadTargetDir.value)
}
input.value = ''
}
@@ -71,13 +129,13 @@ function handleFilesUpload(e: Event) {
<Button variant="ghost" size="icon" class="h-6 w-6" @click="emit('refresh')" :disabled="isRefreshing" title="刷新">
<RefreshCw class="h-3 w-3" :class="{ 'animate-spin': isRefreshing }" />
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6" @click="triggerFilesUpload('')" title="上传文件/文件夹(放在根目录)">
<Button variant="ghost" size="icon" class="h-6 w-6" @click="triggerFilesUpload(currentTargetDir)" :title="currentTargetDir ? `上传到: ${currentTargetDir}` : '上传到根目录'">
<FolderUp class="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6" @click="triggerArchiveUpload('')" title="导入压缩包(放在根目录)">
<Button variant="ghost" size="icon" class="h-6 w-6" @click="triggerArchiveUpload(currentTargetDir)" :title="currentTargetDir ? `导入压缩包到: ${currentTargetDir}` : '导入压缩包到根目录'">
<Upload class="h-3 w-3" />
</Button>
<Button variant="ghost" size="icon" class="h-6 w-6" @click="emit('create', '')" title="新建">
<Button variant="ghost" size="icon" class="h-6 w-6" @click="emit('create', currentTargetDir)" :title="currentTargetDir ? `在 ${currentTargetDir} 中新建` : '在根目录新建'">
<Plus class="h-3 w-3" />
</Button>
</div>
@@ -98,5 +156,26 @@ function handleFilesUpload(e: Event) {
@rename="p => emit('rename', p)"
@duplicate="p => emit('duplicate', p)" />
</div>
<!-- 上传确认对话框 -->
<BaihuDialog v-model:open="confirmUpload.show" :title="confirmUpload.title" icon="Upload" size="sm">
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-4 p-1">
<div class="h-12 w-12 rounded-full bg-primary/10 flex items-center justify-center shrink-0">
<AlertCircle class="h-6 w-6 text-primary" />
</div>
<div class="flex-1 text-center sm:text-left">
<p class="text-sm text-foreground/90 leading-relaxed font-medium">确认操作?</p>
<p class="text-[13px] text-muted-foreground mt-1 break-all">
{{ confirmUpload.message }}
</p>
</div>
</div>
<template #footer>
<div class="flex flex-col-reverse sm:flex-row gap-2 w-full sm:w-auto mt-2 sm:mt-0">
<Button variant="outline" @click="confirmUpload.show = false" class="w-full sm:w-24">取消</Button>
<Button @click="confirmUpload.show = false; confirmUpload.onConfirm()" class="w-full sm:w-auto px-6">立即开始</Button>
</div>
</template>
</BaihuDialog>
</div>
</template>