This commit is contained in:
张成
2025-11-06 13:56:53 +08:00
parent b02853d5f7
commit c734e698de
12 changed files with 742 additions and 36 deletions

View File

@@ -0,0 +1,277 @@
<template>
<transition name="float-panel">
<div v-if="showPanel" class="float-panel-wrapper" @click.self="handleBackdropClick">
<div class="float-panel" :class="panelClass" :style="panelStyle">
<div class="float-panel-header">
<div class="header-left">
<Button v-if="showBack" type="text" icon="ios-arrow-back" @click="handleBack">
{{ backText }}
</Button>
<span v-if="title" class="panel-title">{{ title }}</span>
</div>
<div class="header-right">
<slot name="header-right"></slot>
<Button v-if="showClose" type="text" icon="ios-close" @click="hide"></Button>
</div>
</div>
<div class="float-panel-body">
<slot></slot>
</div>
</div>
</div>
</transition>
</template>
<script>
export default {
name: 'FloatPanel',
props: {
title: {
type: String,
default: ''
},
width: {
type: [String, Number],
default: '100%'
},
height: {
type: [String, Number],
default: '100%'
},
position: {
type: String,
default: 'right', // left, right, top, bottom, center
validator: (value) => ['left', 'right', 'top', 'bottom', 'center'].includes(value)
},
showBack: {
type: Boolean,
default: true
},
showClose: {
type: Boolean,
default: false
},
backText: {
type: String,
default: '返回'
},
closeOnClickBackdrop: {
type: Boolean,
default: false
},
mask: {
type: Boolean,
default: false
},
zIndex: {
type: Number,
default: 1000
}
},
data() {
return {
showPanel: false,
callback: null
}
},
computed: {
panelClass() {
return {
[`float-panel-${this.position}`]: true
}
},
panelStyle() {
const style = {
zIndex: this.zIndex,
width: typeof this.width === 'number' ? `${this.width}px` : this.width,
height: typeof this.height === 'number' ? `${this.height}px` : this.height
}
return style
}
},
methods: {
show(callback) {
this.showPanel = true
this.callback = callback
},
hide() {
this.showPanel = false
this.callback = null
},
handleBack() {
this.$emit('back')
this.hide()
},
handleBackdropClick() {
if (this.closeOnClickBackdrop) {
this.hide()
}
}
},
watch: {
showPanel(newVal) {
if (newVal) {
document.body.style.overflow = 'hidden'
} else {
document.body.style.overflow = ''
}
},
// 监听路由变化
'$route'(to, from) {
if (this.showPanel) {
this.hide()
}
}
},
beforeDestroy() {
// 组件销毁时关闭面板并清理
if (this.showPanel) {
this.hide()
}
document.body.style.overflow = ''
},
destroyed() {
// 确保清理
document.body.style.overflow = ''
}
}
</script>
<style lang="less" scoped>
.float-panel-wrapper {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 1000;
width: 100%;
height: 100%;
}
.float-panel {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: #fff;
display: flex;
flex-direction: column;
overflow: hidden;
&.float-panel-right {
animation: slideInRight 0.3s ease-out;
}
&.float-panel-left {
animation: slideInLeft 0.3s ease-out;
}
&.float-panel-top {
animation: slideInTop 0.3s ease-out;
}
&.float-panel-bottom {
animation: slideInBottom 0.3s ease-out;
}
&.float-panel-center {
animation: fadeInScale 0.3s ease-out;
}
}
.float-panel-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
flex-shrink: 0;
.header-left {
display: flex;
align-items: center;
flex: 1;
.panel-title {
font-size: 16px;
font-weight: 500;
color: #17233d;
margin-left: 8px;
}
}
.header-right {
display: flex;
align-items: center;
gap: 8px;
}
}
.float-panel-body {
flex: 1;
padding: 20px;
overflow-y: auto;
overflow-x: hidden;
}
// 动画效果
.float-panel-enter-active,
.float-panel-leave-active {
transition: opacity 0.3s;
}
.float-panel-enter,
.float-panel-leave-to {
opacity: 0;
}
@keyframes slideInRight {
from {
transform: translateX(100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideInLeft {
from {
transform: translateX(-100%);
}
to {
transform: translateX(0);
}
}
@keyframes slideInTop {
from {
transform: translateY(-100%);
}
to {
transform: translateY(0);
}
}
@keyframes slideInBottom {
from {
transform: translateY(100%);
}
to {
transform: translateY(0);
}
}
@keyframes fadeInScale {
from {
opacity: 0;
transform: scale(0.9);
}
to {
opacity: 1;
transform: scale(1);
}
}
</style>

View File

@@ -19,6 +19,7 @@ import Editor from './editor/index.vue'
import editModal from './tables/editModal.vue'
import fieldItem from './tables/fieldItem.vue'
import FieldRenderer from './tables/fieldRenderer.vue'
import FloatPanel from './FloatPanel/index.vue'
@@ -48,6 +49,7 @@ const registerGlobalComponents = (Vue) => {
Vue.component('editModal', editModal)
Vue.component('fieldItem', fieldItem)
Vue.component('FieldRenderer', FieldRenderer)
Vue.component('FloatPanel', FloatPanel)
}
// 注册自定义组件的方法
@@ -80,7 +82,7 @@ export default {
editModal,
fieldItem,
FieldRenderer,
FloatPanel
}

View File

@@ -72,7 +72,7 @@ export default {
.table-scroll-container {
width: 100%;
overflow-x: auto;
overflow: auto;
overflow-y: visible;
/* 横向滚动条始终可见,不需要滚动到底 */
}