Commit caa7ba030ad86973b7f846fe8539a9c3af38f066
1 parent
15c8e131
Exists in
master
and in
1 other branch
组件布局增加右侧锚点导航
Showing
8 changed files
with
119 additions
and
27 deletions
Show diff stats
examples/views/docs/detail.md
examples/views/docs/form.md
examples/views/docs/scheme.md
examples/views/docs/search.md
examples/views/docs/select.md
examples/views/docs/table.md
examples/views/layout/component.vue
| 1 | <template> | 1 | <template> |
| 2 | - <div id="app"> | ||
| 3 | - <el-container> | ||
| 4 | - <layout-header></layout-header> | ||
| 5 | - <el-container class="layout-container__component"> | ||
| 6 | - <el-aside class="layout-aside__component" width="200px"> | ||
| 7 | - <el-menu :default-active="activeMenu" class="layout-aside-menu__component" @select="handleSelect"> | ||
| 8 | - <h4 style="padding: 0 20px;">组件</h4> | ||
| 9 | - <el-menu-item-group v-for="(component, idx) in components" :key="idx"> | ||
| 10 | - <template slot="title">{{ component.group }}</template> | ||
| 11 | - <el-menu-item v-for="(data, index) in component.children" :key="index" :index="data.name">{{ data.meta.title }}</el-menu-item> | ||
| 12 | - </el-menu-item-group> | ||
| 13 | - </el-menu> | ||
| 14 | - </el-aside> | ||
| 15 | - <el-main class="layout-main__component"> | ||
| 16 | - <router-view></router-view> | ||
| 17 | - </el-main> | ||
| 18 | - </el-container> | 2 | + <el-container> |
| 3 | + <layout-header></layout-header> | ||
| 4 | + <el-container class="layout-container__component"> | ||
| 5 | + <el-aside class="layout-aside__component" width="200px"> | ||
| 6 | + <el-menu :default-active="activeMenu" class="layout-aside-menu__component" @select="handleSelect"> | ||
| 7 | + <h4 style="padding: 0 20px;">组件</h4> | ||
| 8 | + <el-menu-item-group v-for="(component, idx) in componentList" :key="idx"> | ||
| 9 | + <template slot="title">{{ component.group }}</template> | ||
| 10 | + <el-menu-item v-for="(data, index) in component.children" :key="index" :index="data.name">{{ data.meta.title }}</el-menu-item> | ||
| 11 | + </el-menu-item-group> | ||
| 12 | + </el-menu> | ||
| 13 | + </el-aside> | ||
| 14 | + <a class="target-fix"></a> | ||
| 15 | + <el-main class="layout-main__component"> | ||
| 16 | + <router-view></router-view> | ||
| 17 | + </el-main> | ||
| 18 | + <el-aside class="layout-aside__preview" width="200px"> | ||
| 19 | + <a class="anchor" :class="{ 'active': item.hash === currentAnchor }" v-for="(item, index) in anchorList" :key="index" :href="item.hash">{{ item.text }}</a> | ||
| 20 | + </el-aside> | ||
| 19 | </el-container> | 21 | </el-container> |
| 20 | - </div> | 22 | + </el-container> |
| 21 | </template> | 23 | </template> |
| 22 | 24 | ||
| 23 | <script> | 25 | <script> |
| @@ -30,14 +32,74 @@ export default { | @@ -30,14 +32,74 @@ export default { | ||
| 30 | data() { | 32 | data() { |
| 31 | return { | 33 | return { |
| 32 | activeMenu: 'select', | 34 | activeMenu: 'select', |
| 33 | - components | 35 | + componentList: components, |
| 36 | + anchorList: [], | ||
| 37 | + currentAnchor: '', | ||
| 34 | } | 38 | } |
| 35 | }, | 39 | }, |
| 36 | created() { | 40 | created() { |
| 37 | const { name } = this.$route || {}; | 41 | const { name } = this.$route || {}; |
| 38 | this.activeMenu = name; | 42 | this.activeMenu = name; |
| 39 | }, | 43 | }, |
| 44 | + mounted() { | ||
| 45 | + this.initAnchorList(); | ||
| 46 | + window.onscroll = ((e) => { | ||
| 47 | + if (!this.isBottom()) { | ||
| 48 | + for (let index in this.anchorList) { | ||
| 49 | + let anchor = this.anchorList[index] || {}; | ||
| 50 | + if (window.pageYOffset <= anchor.offsetTop - 65) { | ||
| 51 | + this.currentAnchor = (anchor || this.anchorList[0]).hash; | ||
| 52 | + break; | ||
| 53 | + } | ||
| 54 | + } | ||
| 55 | + } | ||
| 56 | + }); | ||
| 57 | + }, | ||
| 58 | + watch:{ | ||
| 59 | + $route(to, from) { | ||
| 60 | + this.currentAnchor = to.hash; | ||
| 61 | + if (to.path !== from.path) { | ||
| 62 | + this.$nextTick(this.initAnchorList); | ||
| 63 | + } | ||
| 64 | + if (!this.isBottom()) { | ||
| 65 | + setTimeout(() => { | ||
| 66 | + window.scrollBy(0, -65); | ||
| 67 | + }, 1); | ||
| 68 | + } | ||
| 69 | + } | ||
| 70 | + }, | ||
| 40 | methods: { | 71 | methods: { |
| 72 | + isBottom() { | ||
| 73 | + // 变量scrollTop是滚动条滚动时,距离顶部的距离 | ||
| 74 | + const scrollTop = document.documentElement.scrollTop || document.body.scrollTop; | ||
| 75 | + // 变量windowHeight是可视区的高度 | ||
| 76 | + const windowHeight = document.documentElement.clientHeight || document.body.clientHeight; | ||
| 77 | + // 变量scrollHeight是滚动条的总高度 | ||
| 78 | + const scrollHeight = document.documentElement.scrollHeight || document.body.scrollHeight; | ||
| 79 | + // 滚动条到底部的条件 | ||
| 80 | + return scrollTop + windowHeight === scrollHeight; | ||
| 81 | + }, | ||
| 82 | + initAnchorList() { | ||
| 83 | + this.anchorList = []; | ||
| 84 | + const domList = document.querySelectorAll('.header-anchor'); | ||
| 85 | + let anchorList = []; | ||
| 86 | + for (let index in domList) { | ||
| 87 | + let dom = domList[index] || {}; | ||
| 88 | + const text = `${dom.parentNode.innerHTML}`.replace(/<\/?.*[^>]*>/g, '').trim(); | ||
| 89 | + if (text === 'API') { | ||
| 90 | + break; | ||
| 91 | + } else { | ||
| 92 | + anchorList.push({ | ||
| 93 | + text: `${dom.parentNode.innerHTML}`.replace(/<\/?.*[^>]*>/g, '').trim(), | ||
| 94 | + href: dom.href, | ||
| 95 | + hash: dom.hash, | ||
| 96 | + offsetTop: dom.offsetTop, | ||
| 97 | + }); | ||
| 98 | + } | ||
| 99 | + } | ||
| 100 | + this.anchorList = anchorList; | ||
| 101 | + this.currentAnchor = anchorList[0].hash; | ||
| 102 | + }, | ||
| 41 | handleSelect(key) { | 103 | handleSelect(key) { |
| 42 | this.$router.push({ name: key }); | 104 | this.$router.push({ name: key }); |
| 43 | } | 105 | } |
| @@ -61,6 +123,7 @@ export default { | @@ -61,6 +123,7 @@ export default { | ||
| 61 | height: 90%; | 123 | height: 90%; |
| 62 | border-right: 1px solid #e6e6e6; | 124 | border-right: 1px solid #e6e6e6; |
| 63 | } | 125 | } |
| 126 | + left: 0; | ||
| 64 | position: fixed; | 127 | position: fixed; |
| 65 | height: calc(100vh - 60px); | 128 | height: calc(100vh - 60px); |
| 66 | background: #fff; | 129 | background: #fff; |
| @@ -80,8 +143,30 @@ export default { | @@ -80,8 +143,30 @@ export default { | ||
| 80 | border-right: 0; | 143 | border-right: 0; |
| 81 | } | 144 | } |
| 82 | } | 145 | } |
| 146 | + .layout-aside__preview { | ||
| 147 | + position: fixed; | ||
| 148 | + right: 0; | ||
| 149 | + height: calc(100vh - 60px); | ||
| 150 | + width: 150px !important; | ||
| 151 | + padding: 30px 0; | ||
| 152 | + .anchor { | ||
| 153 | + width: 100%; | ||
| 154 | + display: inline-block; | ||
| 155 | + font-size: 12px; | ||
| 156 | + text-decoration: none; | ||
| 157 | + padding: 5px 20px; | ||
| 158 | + color: $text; | ||
| 159 | + transition: all 300ms; | ||
| 160 | + border-left: 1px solid $border; | ||
| 161 | + } | ||
| 162 | + .active { | ||
| 163 | + color: $primary; | ||
| 164 | + border-left: 1px solid $primary; | ||
| 165 | + } | ||
| 166 | + } | ||
| 83 | .layout-main__component { | 167 | .layout-main__component { |
| 84 | margin-left: 200px; | 168 | margin-left: 200px; |
| 169 | + margin-right: 150px; | ||
| 85 | padding: 20px 40px; | 170 | padding: 20px 40px; |
| 86 | } | 171 | } |
| 87 | } | 172 | } |
examples/views/layout/default.vue
| 1 | <template> | 1 | <template> |
| 2 | - <div id="app"> | ||
| 3 | - <el-container> | ||
| 4 | - <layout-header></layout-header> | ||
| 5 | - <el-container class="layout-container__default"> | ||
| 6 | - <router-view></router-view> | ||
| 7 | - </el-container> | 2 | + <el-container> |
| 3 | + <layout-header></layout-header> | ||
| 4 | + <el-container class="layout-container__default"> | ||
| 5 | + <router-view></router-view> | ||
| 8 | </el-container> | 6 | </el-container> |
| 9 | - </div> | 7 | + </el-container> |
| 10 | </template> | 8 | </template> |
| 11 | 9 | ||
| 12 | <script> | 10 | <script> |