Vue2顶部固定且高度可变,如何让页面内容自适应高度

需求描述

Vue2
页面顶部有个页头,正常情况下只显示页面标题,需要筛选item时可以展开筛选框。
无论展开还是折叠,整个页头都是固定在顶部的。

page-img1
page-img2

自定义组件PageContainer

header和content的插槽

实现Vue2自定义组件PageContainer,template中写了header和content的插槽。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<template>
<div>
// headerRef,在js/ts代码中获取该dom的高度
<div ref="headerRef" class="page-header">
<slot name="header" ref="headerSlot"></slot>
</div>
// 根据headerRef的高度修改state.contentHeight
<div class="page-content" :style="{height: state.contentHeight}">
<slot name="content"> </slot>
</div>
</div>
</template>

<style lang="scss">
@import '@/style/header-search.scss';

.page-header {
border-bottom: 1px solid #e8eef2;
background: #fff;
width: 100%;
}
.page-content {
padding: 16px;
overflow-y: auto;
}
</style>

使用MutationObserver监听高度

MutationObserver 接口提供了监视对 DOM 树所做更改的能力。

https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver

使用MutationObserver监听header的高度:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48

<script lang="ts">
import {Component, Vue} from 'vue-property-decorator'

// observer 监听配置
// https://developer.mozilla.org/zh-CN/docs/Web/API/MutationObserver/observe
const config = {
attributes: true,
childList: true,
subtree: true
}

class MutationObserver extends window.MutationObserver {}

@Component({
name: 'page-container'
})
export default class PageContainer extends Vue {
private state = {
// 初始默认高度
contentHeight: 'calc(100vh - 78px)'
}
private observer: MutationObserver = new MutationObserver(() => {})

mounted() {
// 获取header高度
const header = this.$refs.headerRef as HTMLDivElement
this.state.contentHeight = `calc(100vh - ${header.clientHeight}px)`

// 获取header中‘折叠/展开’的按钮(当点击该按钮时,header高度改变,此时需要改变content高度)
const element = document.getElementById('filter-button') as HTMLDivElement
if (!element) return

this.observer = new MutationObserver((mutationsList, observer) => {
const header = this.$refs.headerRef as HTMLDivElement
let headerHeight = header.clientHeight
this.state.contentHeight = `calc(100vh - ${headerHeight}px)`
})

// 这个‘折叠/展开’按钮(element)样式改变时,会调用observer更新页面内容的高度
this.observer.observe(element, config)
}
Destoryed() {
this.observer.disconnect()
this.observer.takeRecords()
}
}
</script>

页面中应用组件

在页面中引入自定义组件PageContainer:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
<template>
<PageContainer>
// slot 插入header内容
<template slot="header">
<div class="header">
<div class="header-left">
<h1 class="header-title">页面header</h1>
<div>
<button id="filter-button" @click="onChangeSearchVisible">
筛选
<i :class="state.formVisible == true ? `el-icon-caret-bottom` : `icon-arrow`" />
</button>
</div>
</div>
</div>
<div v-if="state.formVisible" class="header-search">
<el-form ref="formRef" label-width="100px">
<div class="search-row">
<el-form-item label="ID" prop="id">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
<el-form-item label="学校" prop="school">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
<el-form-item label="学院" prop="department">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
<el-form-item label="专业" prop="major">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
<el-form-item label="导师" prop="teacher">
<el-input @change="onSubmitSearch" placeholder="请输入" clearable size="mini"></el-input>
</el-form-item>
</div>
</el-form>
</div>
</template>

// slot content 页面内容
<template slot="content">
<div v-for="(item, index) in [0, 1, 2, 3, 4, 5, 6, 7, 8]" :key="index" style="background-color: green; height: 150px; margin: 20px">
<div style="font-size: 70px">{{ index }}</div>
</div>
</template>
</PageContainer>
</template>


<script lang="ts">
import {Component, Vue, Prop} from 'vue-property-decorator'
import {PageContainer} from '@/components'

@Component({
name: 'fix-header',
components: {
PageContainer
}
})
export default class BatteryRequirement extends Vue {
private state = {
// header搜索筛选框的可见性
formVisible: false,
// 筛选表格
form: {
id: '',
name: '',
school: '',
department: '',
teacher: '',
major: '',
}
}

// 展开或折叠筛选弹窗
private onChangeSearchVisible() {
this.state.formVisible = !this.state.formVisible
}

private onClearForm() {
this.state.form = {
type: '',
user: '',
id: '',
current_dept: ''
}
}
private onSubmitSearch() {
console.log('search筛选')
}
}
</script>

最终效果图

page-gif