修复Area组件最大化功能,确保填充满父容器
This commit is contained in:
@@ -1,6 +1,10 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="vs-area-wrapper">
|
<div class="vs-area-wrapper">
|
||||||
<div class="vs-area select-none" :class="{ 'is-maximized': isMaximized }" :style="areaStyle">
|
<div
|
||||||
|
class="vs-area select-none"
|
||||||
|
:class="{ 'is-maximized': isMaximized, 'is-normal': !isMaximized }"
|
||||||
|
:style="areaStyle"
|
||||||
|
>
|
||||||
<!-- 顶部标题栏 -->
|
<!-- 顶部标题栏 -->
|
||||||
<div v-if="showTitleBar" class="vs-title-bar">
|
<div v-if="showTitleBar" class="vs-title-bar">
|
||||||
<div class="vs-title-left">
|
<div class="vs-title-left">
|
||||||
@@ -32,7 +36,7 @@
|
|||||||
clip-rule="evenodd"
|
clip-rule="evenodd"
|
||||||
d="M1 4 L4 4 L4 1 L11 1 L11 8 L8 8 L8 11 L1 11 Z
|
d="M1 4 L4 4 L4 1 L11 1 L11 8 L8 8 L8 11 L1 11 Z
|
||||||
M5 4 L5 3 L10 3 L10 7 L8 7 L8 4 Z
|
M5 4 L5 3 L10 3 L10 7 L8 7 L8 4 Z
|
||||||
M2 6 L12.6 5 L7 6 L7 10 L2 10 Z" />
|
M2 6 L12.6 5 L7 6 L7 10 L2 10 Z" />
|
||||||
</svg>
|
</svg>
|
||||||
</button>
|
</button>
|
||||||
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80" aria-label="关闭" @click="onClose">
|
<button class="button-icon p-[2px] rounded hover:opacity-100 opacity-80" aria-label="关闭" @click="onClose">
|
||||||
@@ -44,9 +48,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 内容区域(空) -->
|
<!-- 内容区域 -->
|
||||||
<div class="vs-content">
|
<div class="vs-content">
|
||||||
<!-- 内容区域已清空 -->
|
<!-- 内容区域 -->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -59,35 +63,68 @@ const props = defineProps({
|
|||||||
id: { type: String, required: true },
|
id: { type: String, required: true },
|
||||||
title: { type: String, default: '面板区' },
|
title: { type: String, default: '面板区' },
|
||||||
resizable: { type: Boolean, default: true },
|
resizable: { type: Boolean, default: true },
|
||||||
// 新增:初始状态(支持中文值)
|
// 初始状态(支持中文值)
|
||||||
WindowState: { type: String, default: '正常' },
|
WindowState: { type: String, default: '正常' },
|
||||||
// 新增:默认尺寸
|
// 默认尺寸
|
||||||
width: { type: Number, default: 300 },
|
width: { type: Number, default: 300 },
|
||||||
height: { type: Number, default: 250 },
|
height: { type: Number, default: 250 },
|
||||||
// 新增:控制标题栏显示
|
// 控制标题栏显示
|
||||||
showTitleBar: { type: Boolean, default: true }
|
showTitleBar: { type: Boolean, default: true },
|
||||||
|
// 位置属性,可选
|
||||||
|
left: { type: Number, default: undefined },
|
||||||
|
top: { type: Number, default: undefined }
|
||||||
})
|
})
|
||||||
|
|
||||||
// 本地状态:不再向父组件发事件,内部维护最大化/还原
|
// 本地状态
|
||||||
const localState = ref(props.WindowState)
|
const localState = ref(props.WindowState)
|
||||||
|
// 保存原始位置和大小信息
|
||||||
|
const originalPosition = ref({
|
||||||
|
width: props.width,
|
||||||
|
height: props.height,
|
||||||
|
left: props.left,
|
||||||
|
top: props.top
|
||||||
|
})
|
||||||
|
|
||||||
// 根据本地状态计算是否最大化
|
// 根据本地状态计算是否最大化
|
||||||
const isMaximized = computed(() => localState.value === '最大化' || localState.value === 'maximized')
|
const isMaximized = computed(() => localState.value === '最大化' || localState.value === 'maximized')
|
||||||
|
|
||||||
// 新增:根据状态计算尺寸样式
|
// 根据状态计算尺寸和位置样式
|
||||||
const areaStyle = computed(() => {
|
const areaStyle = computed(() => {
|
||||||
if (isMaximized.value) {
|
if (isMaximized.value) {
|
||||||
return { width: '100%', height: '100%' }
|
// 最大化时填充满父容器
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
position: 'absolute',
|
||||||
|
top: 0,
|
||||||
|
left: 0,
|
||||||
|
zIndex: 100
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return { width: `${props.width}px`, height: `${props.height}px` }
|
|
||||||
|
// 非最大化状态:使用原始位置或默认居中
|
||||||
|
const style = {
|
||||||
|
width: `${originalPosition.value.width}px`,
|
||||||
|
height: `${originalPosition.value.height}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有明确的位置,则使用指定位置
|
||||||
|
if (originalPosition.value.left !== undefined) {
|
||||||
|
style.left = `${originalPosition.value.left}px`
|
||||||
|
}
|
||||||
|
if (originalPosition.value.top !== undefined) {
|
||||||
|
style.top = `${originalPosition.value.top}px`
|
||||||
|
}
|
||||||
|
|
||||||
|
return style
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits(['close'])
|
const emit = defineEmits(['close', 'update:WindowState'])
|
||||||
|
|
||||||
const onToggleMaximize = () => {
|
const onToggleMaximize = () => {
|
||||||
const next = isMaximized.value ? '正常' : '最大化'
|
const next = isMaximized.value ? '正常' : '最大化'
|
||||||
// 直接更新本地状态,不再 emit 到父组件
|
|
||||||
localState.value = next
|
localState.value = next
|
||||||
|
emit('update:WindowState', next)
|
||||||
}
|
}
|
||||||
|
|
||||||
const onClose = () => emit('close')
|
const onClose = () => emit('close')
|
||||||
@@ -110,6 +147,15 @@ const onClose = () => emit('close')
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* 容器 */
|
/* 容器 */
|
||||||
|
.vs-area-wrapper {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
.vs-area {
|
.vs-area {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
@@ -119,6 +165,23 @@ const onClose = () => emit('close')
|
|||||||
min-height: 250px;
|
min-height: 250px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 正常状态样式 */
|
||||||
|
.vs-area.is-normal {
|
||||||
|
position: static;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 最大化状态样式 */
|
||||||
|
.vs-area.is-maximized {
|
||||||
|
width: 100% !important;
|
||||||
|
height: 100% !important;
|
||||||
|
position: absolute !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
z-index: 100 !important;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* 标题栏 */
|
/* 标题栏 */
|
||||||
.vs-title-bar {
|
.vs-title-bar {
|
||||||
height: 28px;
|
height: 28px;
|
||||||
@@ -195,19 +258,27 @@ const onClose = () => emit('close')
|
|||||||
:deep(::-webkit-scrollbar-thumb:hover) { background: linear-gradient(to bottom, #c1c7e2, #b2b8d9); }
|
:deep(::-webkit-scrollbar-thumb:hover) { background: linear-gradient(to bottom, #c1c7e2, #b2b8d9); }
|
||||||
|
|
||||||
:deep(*) { box-sizing: border-box; }
|
:deep(*) { box-sizing: border-box; }
|
||||||
.vs-area.is-maximized { width: 100%; height: 100%; }
|
.vs-area.is-maximized {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
z-index: 100;
|
||||||
|
}
|
||||||
.vs-icon-stage { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: transparent; overflow: auto; }
|
.vs-icon-stage { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; background: transparent; overflow: auto; }
|
||||||
.vs-app-icon--x200 { width: 2800px; height: 2800px; }
|
.vs-app-icon--x200 { width: 2800px; height: 2800px; }
|
||||||
.vs-app-icon { width: 14px; height: 14px; display: inline-block; background: transparent; opacity: 0.95; }
|
.vs-app-icon { width: 14px; height: 14px; display: inline-block; background: transparent; opacity: 0.95; }
|
||||||
.vs-icon { width: 100%; height: 100%; shape-rendering: crispEdges; }
|
.vs-icon { width: 100%; height: 100%; shape-rendering: crispEdges; }
|
||||||
.vs-app-icon svg { display: block; }
|
.vs-app-icon svg { display: block; }
|
||||||
|
|
||||||
/* 外层包裹,居中内容 */
|
/* 外层包裹,确保最大化时填充父容器,非最大化时居中 */
|
||||||
.vs-area-wrapper {
|
.vs-area-wrapper {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
display: flex;
|
position: relative;
|
||||||
align-items: center;
|
display: flex;
|
||||||
justify-content: center;
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,26 +1,33 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="dock-layout">
|
<div class="dock-layout">
|
||||||
<!-- 主区域 -->
|
|
||||||
<Area :WindowState="windowState" :showTitleBar="false" title="主区域" />
|
|
||||||
|
|
||||||
<!-- 浮动区域列表 -->
|
<!-- 浮动区域列表 -->
|
||||||
<Area
|
<Area
|
||||||
v-for="area in floatingAreas"
|
v-for="area in floatingAreas"
|
||||||
:key="area.id"
|
:key="area.id"
|
||||||
:id="area.id"
|
:id="area.id"
|
||||||
:title="area.title"
|
:title="area.title"
|
||||||
:WindowState="area.WindowState"
|
v-model:WindowState="area.WindowState"
|
||||||
:showTitleBar="area.showTitleBar"
|
:showTitleBar="area.showTitleBar"
|
||||||
:width="area.width"
|
:width="area.width"
|
||||||
:height="area.height"
|
:height="area.height"
|
||||||
:style="{
|
:left="area.WindowState !== '最大化' ? area.x : undefined"
|
||||||
|
:top="area.WindowState !== '最大化' ? area.y : undefined"
|
||||||
|
:style="area.WindowState !== '最大化' ? {
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
left: area.x + 'px',
|
|
||||||
top: area.y + 'px',
|
|
||||||
zIndex: 10
|
zIndex: 10
|
||||||
|
} : {
|
||||||
|
zIndex: 100
|
||||||
}"
|
}"
|
||||||
@close="onCloseFloatingArea(area.id)"
|
@close="onCloseFloatingArea(area.id)"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<!-- 主区域 -->
|
||||||
|
<Area
|
||||||
|
:WindowState="windowState"
|
||||||
|
:showTitleBar="false"
|
||||||
|
title="主区域"
|
||||||
|
:style="{ position: 'relative', zIndex: 1 }"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -100,7 +107,7 @@ defineExpose({
|
|||||||
position: relative;
|
position: relative;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
overflow: hidden;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 浮动区域样式已直接应用到Area组件 */
|
/* 浮动区域样式已直接应用到Area组件 */
|
||||||
|
|||||||
4
AutoRobot/Windows/Robot/Web/src/DockLayout/ToDoList.md
Normal file
4
AutoRobot/Windows/Robot/Web/src/DockLayout/ToDoList.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
### Area
|
||||||
|
1. 初始添加时,默认宽300px,高250px。位置相对父容器水平居中,垂直居中。
|
||||||
|
2. 最大化时,填充满父容器。
|
||||||
|
3. 还原时,恢复到最大化前的位置和大小。
|
||||||
Reference in New Issue
Block a user