mirror of
https://github.com/acepanel/panel.git
synced 2026-02-04 06:47:20 +08:00
feat: 阶段提交
This commit is contained in:
516
web/src/components/common/DraggableWindow.vue
Normal file
516
web/src/components/common/DraggableWindow.vue
Normal file
@@ -0,0 +1,516 @@
|
||||
<script setup lang="ts">
|
||||
import { useThemeVars } from 'naive-ui'
|
||||
import { useGettext } from 'vue3-gettext'
|
||||
|
||||
const { $gettext } = useGettext()
|
||||
const themeVars = useThemeVars()
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
title?: string
|
||||
minWidth?: number
|
||||
minHeight?: number
|
||||
defaultWidth?: number
|
||||
defaultHeight?: number
|
||||
}>(),
|
||||
{
|
||||
title: '',
|
||||
minWidth: 400,
|
||||
minHeight: 300,
|
||||
defaultWidth: 800,
|
||||
defaultHeight: 600
|
||||
}
|
||||
)
|
||||
|
||||
const show = defineModel<boolean>('show', { default: false })
|
||||
const minimized = defineModel<boolean>('minimized', { default: false })
|
||||
|
||||
// 窗口状态
|
||||
const isMaximized = ref(false)
|
||||
const windowRef = ref<HTMLDivElement>()
|
||||
|
||||
// 窗口位置和大小
|
||||
const position = ref({ x: 0, y: 0 })
|
||||
const size = ref({ width: props.defaultWidth, height: props.defaultHeight })
|
||||
|
||||
// 最大化前的状态(用于恢复)
|
||||
const beforeMaximize = ref({ x: 0, y: 0, width: 0, height: 0 })
|
||||
|
||||
// 拖拽状态
|
||||
const isDragging = ref(false)
|
||||
const dragStart = ref({ x: 0, y: 0 })
|
||||
|
||||
// 调整大小状态
|
||||
const isResizing = ref(false)
|
||||
const resizeDirection = ref('')
|
||||
const resizeStart = ref({ x: 0, y: 0, width: 0, height: 0, posX: 0, posY: 0 })
|
||||
|
||||
// 窗口样式
|
||||
const windowStyle = computed(() => ({
|
||||
left: position.value.x + 'px',
|
||||
top: position.value.y + 'px',
|
||||
width: size.value.width + 'px',
|
||||
height: size.value.height + 'px',
|
||||
background: themeVars.value.cardColor,
|
||||
'--border-color': themeVars.value.borderColor,
|
||||
'--text-color-1': themeVars.value.textColor1,
|
||||
'--text-color-2': themeVars.value.textColor2,
|
||||
'--text-color-3': themeVars.value.textColor3,
|
||||
'--hover-color': themeVars.value.buttonColor2Hover,
|
||||
'--primary-color': themeVars.value.primaryColor,
|
||||
'--border-radius': themeVars.value.borderRadius
|
||||
}))
|
||||
|
||||
// 初始化窗口位置(居中)
|
||||
function initPosition() {
|
||||
position.value = {
|
||||
x: (window.innerWidth - size.value.width) / 2,
|
||||
y: (window.innerHeight - size.value.height) / 2
|
||||
}
|
||||
}
|
||||
|
||||
// 开始拖拽
|
||||
function startDrag(e: MouseEvent) {
|
||||
if (isMaximized.value) return
|
||||
isDragging.value = true
|
||||
dragStart.value = {
|
||||
x: e.clientX - position.value.x,
|
||||
y: e.clientY - position.value.y
|
||||
}
|
||||
document.addEventListener('mousemove', onDrag)
|
||||
document.addEventListener('mouseup', stopDrag)
|
||||
}
|
||||
|
||||
function onDrag(e: MouseEvent) {
|
||||
if (!isDragging.value) return
|
||||
position.value = {
|
||||
x: Math.max(0, Math.min(window.innerWidth - size.value.width, e.clientX - dragStart.value.x)),
|
||||
y: Math.max(0, Math.min(window.innerHeight - size.value.height, e.clientY - dragStart.value.y))
|
||||
}
|
||||
}
|
||||
|
||||
function stopDrag() {
|
||||
isDragging.value = false
|
||||
document.removeEventListener('mousemove', onDrag)
|
||||
document.removeEventListener('mouseup', stopDrag)
|
||||
}
|
||||
|
||||
// 开始调整大小
|
||||
function startResize(e: MouseEvent, direction: string) {
|
||||
if (isMaximized.value) return
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
isResizing.value = true
|
||||
resizeDirection.value = direction
|
||||
resizeStart.value = {
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
width: size.value.width,
|
||||
height: size.value.height,
|
||||
posX: position.value.x,
|
||||
posY: position.value.y
|
||||
}
|
||||
document.addEventListener('mousemove', onResize)
|
||||
document.addEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
function onResize(e: MouseEvent) {
|
||||
if (!isResizing.value) return
|
||||
|
||||
const deltaX = e.clientX - resizeStart.value.x
|
||||
const deltaY = e.clientY - resizeStart.value.y
|
||||
const dir = resizeDirection.value
|
||||
|
||||
let newWidth = resizeStart.value.width
|
||||
let newHeight = resizeStart.value.height
|
||||
let newX = resizeStart.value.posX
|
||||
let newY = resizeStart.value.posY
|
||||
|
||||
// 右边
|
||||
if (dir.includes('e')) {
|
||||
newWidth = Math.max(props.minWidth, resizeStart.value.width + deltaX)
|
||||
}
|
||||
// 左边
|
||||
if (dir.includes('w')) {
|
||||
const maxDelta = resizeStart.value.width - props.minWidth
|
||||
const actualDelta = Math.min(deltaX, maxDelta)
|
||||
newWidth = resizeStart.value.width - actualDelta
|
||||
newX = resizeStart.value.posX + actualDelta
|
||||
}
|
||||
// 下边
|
||||
if (dir.includes('s')) {
|
||||
newHeight = Math.max(props.minHeight, resizeStart.value.height + deltaY)
|
||||
}
|
||||
// 上边
|
||||
if (dir.includes('n')) {
|
||||
const maxDelta = resizeStart.value.height - props.minHeight
|
||||
const actualDelta = Math.min(deltaY, maxDelta)
|
||||
newHeight = resizeStart.value.height - actualDelta
|
||||
newY = resizeStart.value.posY + actualDelta
|
||||
}
|
||||
|
||||
// 限制在窗口内
|
||||
newX = Math.max(0, newX)
|
||||
newY = Math.max(0, newY)
|
||||
newWidth = Math.min(newWidth, window.innerWidth - newX)
|
||||
newHeight = Math.min(newHeight, window.innerHeight - newY)
|
||||
|
||||
size.value = { width: newWidth, height: newHeight }
|
||||
position.value = { x: newX, y: newY }
|
||||
}
|
||||
|
||||
function stopResize() {
|
||||
isResizing.value = false
|
||||
document.removeEventListener('mousemove', onResize)
|
||||
document.removeEventListener('mouseup', stopResize)
|
||||
}
|
||||
|
||||
// 最大化/还原
|
||||
function toggleMaximize() {
|
||||
if (minimized.value) {
|
||||
minimized.value = false
|
||||
return
|
||||
}
|
||||
|
||||
if (isMaximized.value) {
|
||||
// 还原
|
||||
position.value = { x: beforeMaximize.value.x, y: beforeMaximize.value.y }
|
||||
size.value = { width: beforeMaximize.value.width, height: beforeMaximize.value.height }
|
||||
isMaximized.value = false
|
||||
} else {
|
||||
// 最大化
|
||||
beforeMaximize.value = {
|
||||
x: position.value.x,
|
||||
y: position.value.y,
|
||||
width: size.value.width,
|
||||
height: size.value.height
|
||||
}
|
||||
position.value = { x: 0, y: 0 }
|
||||
size.value = { width: window.innerWidth, height: window.innerHeight }
|
||||
isMaximized.value = true
|
||||
}
|
||||
}
|
||||
|
||||
// 最小化
|
||||
function minimize() {
|
||||
minimized.value = true
|
||||
}
|
||||
|
||||
// 从最小化恢复
|
||||
function restore() {
|
||||
minimized.value = false
|
||||
}
|
||||
|
||||
// 关闭
|
||||
function close() {
|
||||
show.value = false
|
||||
}
|
||||
|
||||
// 双击标题栏最大化/还原
|
||||
function onTitleDoubleClick() {
|
||||
toggleMaximize()
|
||||
}
|
||||
|
||||
// 监听显示状态
|
||||
watch(show, (newShow) => {
|
||||
if (newShow) {
|
||||
minimized.value = false
|
||||
isMaximized.value = false
|
||||
size.value = { width: props.defaultWidth, height: props.defaultHeight }
|
||||
initPosition()
|
||||
}
|
||||
})
|
||||
|
||||
// 监听窗口大小变化
|
||||
onMounted(() => {
|
||||
window.addEventListener('resize', () => {
|
||||
if (isMaximized.value) {
|
||||
size.value = { width: window.innerWidth, height: window.innerHeight }
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Teleport to="body">
|
||||
<!-- 遮罩层 -->
|
||||
<Transition name="fade">
|
||||
<div v-if="show && !minimized" class="draggable-window-overlay" @click="minimize" />
|
||||
</Transition>
|
||||
|
||||
<!-- 主窗口 -->
|
||||
<Transition name="window">
|
||||
<div
|
||||
v-if="show && !minimized"
|
||||
ref="windowRef"
|
||||
class="draggable-window"
|
||||
:class="{ maximized: isMaximized, dragging: isDragging, resizing: isResizing }"
|
||||
:style="windowStyle"
|
||||
>
|
||||
<!-- 标题栏 -->
|
||||
<div class="draggable-window-header" @mousedown="startDrag" @dblclick="onTitleDoubleClick">
|
||||
<span class="draggable-window-title">{{ title }}</span>
|
||||
<div class="draggable-window-controls">
|
||||
<button class="control-btn minimize" @click.stop="minimize" :title="$gettext('Minimize')">
|
||||
<i-mdi-window-minimize />
|
||||
</button>
|
||||
<button
|
||||
class="control-btn maximize"
|
||||
@click.stop="toggleMaximize"
|
||||
:title="isMaximized ? $gettext('Restore') : $gettext('Maximize')"
|
||||
>
|
||||
<i-mdi-window-restore v-if="isMaximized" />
|
||||
<i-mdi-window-maximize v-else />
|
||||
</button>
|
||||
<button class="control-btn close" @click.stop="close" :title="$gettext('Close')">
|
||||
<i-mdi-close />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 内容区域 -->
|
||||
<div class="draggable-window-content">
|
||||
<slot />
|
||||
</div>
|
||||
|
||||
<!-- 调整大小的边框 -->
|
||||
<template v-if="!isMaximized">
|
||||
<div class="resize-handle n" @mousedown="startResize($event, 'n')" />
|
||||
<div class="resize-handle s" @mousedown="startResize($event, 's')" />
|
||||
<div class="resize-handle e" @mousedown="startResize($event, 'e')" />
|
||||
<div class="resize-handle w" @mousedown="startResize($event, 'w')" />
|
||||
<div class="resize-handle ne" @mousedown="startResize($event, 'ne')" />
|
||||
<div class="resize-handle nw" @mousedown="startResize($event, 'nw')" />
|
||||
<div class="resize-handle se" @mousedown="startResize($event, 'se')" />
|
||||
<div class="resize-handle sw" @mousedown="startResize($event, 'sw')" />
|
||||
</template>
|
||||
</div>
|
||||
</Transition>
|
||||
|
||||
<!-- 最小化后的图标 -->
|
||||
<Transition name="minimize">
|
||||
<div
|
||||
v-if="show && minimized"
|
||||
class="draggable-window-minimized"
|
||||
:style="{
|
||||
background: themeVars.cardColor,
|
||||
color: themeVars.textColor1,
|
||||
'--border-radius': themeVars.borderRadius
|
||||
}"
|
||||
@click="restore"
|
||||
>
|
||||
<i-mdi-file-document-outline />
|
||||
<span>{{ title }}</span>
|
||||
</div>
|
||||
</Transition>
|
||||
</Teleport>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.draggable-window-overlay {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.4);
|
||||
z-index: 1999;
|
||||
}
|
||||
|
||||
.draggable-window {
|
||||
position: fixed;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
overflow: hidden;
|
||||
|
||||
&.maximized {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
&.dragging,
|
||||
&.resizing {
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
.draggable-window-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 40px;
|
||||
padding: 0 8px 0 16px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
cursor: move;
|
||||
flex-shrink: 0;
|
||||
user-select: none;
|
||||
|
||||
.maximized & {
|
||||
cursor: default;
|
||||
}
|
||||
}
|
||||
|
||||
.draggable-window-title {
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
color: var(--text-color-1);
|
||||
}
|
||||
|
||||
.draggable-window-controls {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.draggable-window .control-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 32px;
|
||||
height: 28px;
|
||||
border: none;
|
||||
background: transparent;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
color: var(--text-color-2);
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
background: var(--hover-color);
|
||||
}
|
||||
|
||||
&.close:hover {
|
||||
background: #e81123;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.draggable-window-content {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
// 调整大小的手柄
|
||||
.draggable-window .resize-handle {
|
||||
position: absolute;
|
||||
|
||||
&.n,
|
||||
&.s {
|
||||
left: 8px;
|
||||
right: 8px;
|
||||
height: 6px;
|
||||
cursor: ns-resize;
|
||||
}
|
||||
|
||||
&.e,
|
||||
&.w {
|
||||
top: 8px;
|
||||
bottom: 8px;
|
||||
width: 6px;
|
||||
cursor: ew-resize;
|
||||
}
|
||||
|
||||
&.n {
|
||||
top: -3px;
|
||||
}
|
||||
&.s {
|
||||
bottom: -3px;
|
||||
}
|
||||
&.e {
|
||||
right: -3px;
|
||||
}
|
||||
&.w {
|
||||
left: -3px;
|
||||
}
|
||||
|
||||
&.ne,
|
||||
&.nw,
|
||||
&.se,
|
||||
&.sw {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
}
|
||||
|
||||
&.ne {
|
||||
top: -3px;
|
||||
right: -3px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
&.nw {
|
||||
top: -3px;
|
||||
left: -3px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
&.se {
|
||||
bottom: -3px;
|
||||
right: -3px;
|
||||
cursor: nwse-resize;
|
||||
}
|
||||
&.sw {
|
||||
bottom: -3px;
|
||||
left: -3px;
|
||||
cursor: nesw-resize;
|
||||
}
|
||||
}
|
||||
|
||||
// 最小化后的图标
|
||||
.draggable-window-minimized {
|
||||
position: fixed;
|
||||
bottom: 16px;
|
||||
right: 16px;
|
||||
z-index: 2000;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 16px;
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2);
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
&:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
|
||||
// 动画
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.2s;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.window-enter-active,
|
||||
.window-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.window-enter-from,
|
||||
.window-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.95);
|
||||
}
|
||||
|
||||
.minimize-enter-active,
|
||||
.minimize-leave-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.minimize-enter-from,
|
||||
.minimize-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateY(20px);
|
||||
}
|
||||
</style>
|
||||
@@ -334,4 +334,14 @@ defineExpose({
|
||||
overflow: hidden;
|
||||
min-width: 0; /* 允许在 flex 布局中收缩 */
|
||||
}
|
||||
|
||||
.settings-form {
|
||||
:deep(.n-input-number) {
|
||||
width: 180px;
|
||||
}
|
||||
|
||||
:deep(.n-select) {
|
||||
width: 180px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -773,6 +773,7 @@ defineExpose({
|
||||
<!-- 普通模式:显示文件树 -->
|
||||
<n-spin v-else :show="loading" class="tree-spin">
|
||||
<n-tree
|
||||
v-if="treeData.length > 0"
|
||||
block-line
|
||||
:data="treeData"
|
||||
:expanded-keys="expandedKeys"
|
||||
@@ -788,6 +789,7 @@ defineExpose({
|
||||
class="file-tree-content"
|
||||
style="height: 100%"
|
||||
/>
|
||||
<n-empty v-else-if="!loading" :description="$gettext('No data')" class="tree-empty" />
|
||||
</n-spin>
|
||||
</div>
|
||||
|
||||
@@ -893,4 +895,12 @@ defineExpose({
|
||||
.search-empty {
|
||||
padding: 40px 20px;
|
||||
}
|
||||
|
||||
.tree-empty {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import file from '@/api/panel/file'
|
||||
import DraggableWindow from '@/components/common/DraggableWindow.vue'
|
||||
import { FileEditorView } from '@/components/file-editor'
|
||||
import { useEditorStore } from '@/store'
|
||||
import { decodeBase64 } from '@/utils'
|
||||
@@ -9,10 +10,15 @@ const { $gettext } = useGettext()
|
||||
const editorStore = useEditorStore()
|
||||
|
||||
const show = defineModel<boolean>('show', { type: Boolean, required: true })
|
||||
const minimized = defineModel<boolean>('minimized', { type: Boolean, default: false })
|
||||
const filePath = defineModel<string>('file', { type: String, required: true })
|
||||
|
||||
const editorRef = ref<InstanceType<typeof FileEditorView>>()
|
||||
|
||||
// 窗口默认尺寸
|
||||
const defaultWidth = Math.min(1400, window.innerWidth * 0.9)
|
||||
const defaultHeight = Math.min(900, window.innerHeight * 0.85)
|
||||
|
||||
// 获取文件所在目录作为初始路径
|
||||
const initialPath = computed(() => {
|
||||
if (!filePath.value) return '/'
|
||||
@@ -21,6 +27,34 @@ const initialPath = computed(() => {
|
||||
return parts.join('/') || '/'
|
||||
})
|
||||
|
||||
// 加载文件
|
||||
function loadFile(path: string) {
|
||||
if (!path) return
|
||||
|
||||
// 如果文件已经打开,直接切换到该标签页
|
||||
if (editorStore.tabs.some(f => f.path === path)) {
|
||||
editorStore.switchTab(path)
|
||||
return
|
||||
}
|
||||
|
||||
// 打开新文件
|
||||
editorStore.openFile(path, '', 'utf-8')
|
||||
editorStore.setLoading(path, true)
|
||||
|
||||
useRequest(file.content(encodeURIComponent(path)))
|
||||
.onSuccess(({ data }) => {
|
||||
const content = decodeBase64(data.content)
|
||||
editorStore.reloadFile(path, content)
|
||||
})
|
||||
.onError(() => {
|
||||
window.$message.error($gettext('Failed to load file'))
|
||||
editorStore.closeTab(path)
|
||||
})
|
||||
.onComplete(() => {
|
||||
editorStore.setLoading(path, false)
|
||||
})
|
||||
}
|
||||
|
||||
// 打开时自动加载文件
|
||||
watch(show, (newShow) => {
|
||||
if (newShow && filePath.value) {
|
||||
@@ -31,41 +65,43 @@ watch(show, (newShow) => {
|
||||
editorStore.closeAllTabs()
|
||||
// 设置根目录
|
||||
editorStore.setRootPath(initialPath.value)
|
||||
// 打开指定文件
|
||||
editorStore.openFile(filePath.value, '', 'utf-8')
|
||||
editorStore.setLoading(filePath.value, true)
|
||||
|
||||
useRequest(file.content(encodeURIComponent(filePath.value)))
|
||||
.onSuccess(({ data }) => {
|
||||
const content = decodeBase64(data.content)
|
||||
editorStore.reloadFile(filePath.value, content)
|
||||
})
|
||||
.onError(() => {
|
||||
window.$message.error($gettext('Failed to load file'))
|
||||
editorStore.closeTab(filePath.value)
|
||||
})
|
||||
.onComplete(() => {
|
||||
editorStore.setLoading(filePath.value, false)
|
||||
})
|
||||
// 加载文件
|
||||
loadFile(filePath.value)
|
||||
} else if (!newShow) {
|
||||
// 恢复文件管理的键盘快捷键
|
||||
window.$bus.emit('file:keyboard-resume')
|
||||
}
|
||||
})
|
||||
|
||||
// 监听文件路径变化(编辑器已打开时双击新文件)
|
||||
watch(filePath, (newPath) => {
|
||||
if (show.value && newPath) {
|
||||
loadFile(newPath)
|
||||
}
|
||||
})
|
||||
|
||||
// 监听最小化状态
|
||||
watch(minimized, (isMinimized) => {
|
||||
if (isMinimized) {
|
||||
window.$bus.emit('file:keyboard-resume')
|
||||
} else {
|
||||
window.$bus.emit('file:keyboard-pause')
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<n-modal
|
||||
<DraggableWindow
|
||||
v-model:show="show"
|
||||
preset="card"
|
||||
v-model:minimized="minimized"
|
||||
:title="$gettext('File Editor')"
|
||||
style="width: 90vw; height: 85vh"
|
||||
content-style="padding: 0; height: calc(85vh - 60px); display: flex; flex-direction: column;"
|
||||
:bordered="false"
|
||||
:segmented="false"
|
||||
:default-width="defaultWidth"
|
||||
:default-height="defaultHeight"
|
||||
:min-width="600"
|
||||
:min-height="400"
|
||||
>
|
||||
<FileEditorView ref="editorRef" :initial-path="initialPath" />
|
||||
</n-modal>
|
||||
</DraggableWindow>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
|
||||
@@ -52,6 +52,7 @@ const permissionFileInfoList = defineModel<FileInfo[]>('permissionFileInfoList',
|
||||
})
|
||||
|
||||
const editorModal = ref(false)
|
||||
const editorMinimized = ref(false)
|
||||
const previewModal = ref(false)
|
||||
const currentFile = ref('')
|
||||
const propertyModal = ref(false)
|
||||
@@ -276,6 +277,7 @@ const openFile = (row: any) => {
|
||||
} else {
|
||||
currentFile.value = row.full
|
||||
editorModal.value = true
|
||||
editorMinimized.value = false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1418,7 +1420,7 @@ onUnmounted(() => {
|
||||
/>
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<edit-modal v-model:show="editorModal" v-model:file="currentFile" />
|
||||
<edit-modal v-model:show="editorModal" v-model:minimized="editorMinimized" v-model:file="currentFile" />
|
||||
<!-- 预览弹窗 -->
|
||||
<preview-modal v-model:show="previewModal" v-model:path="currentFile" />
|
||||
<!-- 解压弹窗 -->
|
||||
|
||||
Reference in New Issue
Block a user