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;
/* 横向滚动条始终可见,不需要滚动到底 */
}

View File

@@ -35,7 +35,7 @@ export default class uiTool {
*/
static downloadFile(res, fileName) {
// 开头和结尾去掉 中间不去掉
let tempFileName = fileName || `${new Date().getTime()}.csv`
let tempFileName = fileName || `${new Date().getTime()}.csv`
const blob = new Blob([res.data || res])
const downloadElement = document.createElement('a')
@@ -130,20 +130,20 @@ export default class uiTool {
}
static delConfirm(callback) {
const Modal = (window.framework && window.framework.ViewUI && window.framework.ViewUI.Modal) || window.$Modal
if (Modal) {
Modal.confirm({
title: '温馨提示',
content: '<p>你确定删除吗?</p>',
onOk: () => {
callback && callback()
}
})
}
const Modal = window.rootVue.$Modal
Modal.confirm({
title: '温馨提示',
content: '<p>你确定删除吗?</p>',
onOk: () => {
callback && callback()
}
})
}
static showConfirm({ title = '温馨提示', content = '内容' }, callback) {
const Modal = (window.framework && window.framework.ViewUI && window.framework.ViewUI.Modal) || window.$Modal
const Modal = window.rootVue.$Modal
if (Modal) {
Modal.confirm({
title,

View File

@@ -2,7 +2,7 @@
<div class="content-view ">
<div class="tree-box">
<div class="btn-top-box pa10">
<Button type="warning" size="small" @click="delAll">全部删除</Button>
<Button type="warning" size="small" @click="delAll">全部删除2</Button>
</div>
<Tree class="mt10" :data="treeData" :render="renderContent" @on-select-change="selectChange"></Tree>
</div>
@@ -83,25 +83,33 @@ export default {
}
}),
h('span', data.title),
h('Icon', {
props: {
type: 'ios-trash',
size: '18'
},
h('span', {
on: {
click: () => {
click: (e) => {
e.stopPropagation() // 阻止事件冒泡
e.preventDefault() // 阻止默认行为
this.deleteLog(data)
}
},
style: {
'margin-left': '8px'
'margin-left': '8px',
cursor: 'pointer',
display: 'inline-block'
}
})
}, [
h('Icon', {
props: {
type: 'ios-trash',
size: '18'
}
})
])
])
]
)
},
async selectChange(row) {
row[0].selected = true
this.selectRow = row[0].title
let res = await sys_log_serve.detail({ title: this.selectRow })
@@ -135,7 +143,7 @@ export default {
.content-view {
display: flex;
flex-direction: row;
.tree-box {
.log-box {
height: 90vh;
width: 300px;
border-right: solid 1px #ccc;

View File

@@ -4,9 +4,7 @@
<Button type="primary" @click="addWarp()">新增</Button>
</div>
<div class="table-body">
<card class="tree-box">
<TreeGrid :columns="gridOption.columns" :data="gridOption.data"></TreeGrid>
</card>
</div>
<editModal ref="editModal" :columns="gridOption.editColumns" :rules="gridOption.rules">
<div slot="bottom">
@@ -254,7 +252,7 @@ export default {
this.gridOption.menuData = this.mapTree(menuTree)
this.$store.dispatch('setAuthorityMenus')
},
async initCol() {
let res = await menuServer.modelAll()
@@ -394,9 +392,5 @@ export default {
</script>
<style>
.tree-box {
overflow: auto;
height: 100%;
max-height: calc(100vh - 200px);
}
</style>