2f5a0367 by 任超

style:上下布局完成

1 parent c2ff905e
1 <template>
2 <section class="app-main">
3 <transition name="fade-transform" mode="out-in">
4 <router-view />
5 </transition>
6 </section>
7 </template>
8 <script>
9 export default {
10 name: 'AppMain',
11 computed: {
12 key () {
13 return this.$route.path
14 }
15 }
16 }
17 </script>
18 <style lang="scss" scoped>
19 .hasTagsView {
20 .app-main {
21 height: calc(100% - 41px);
22 overflow-x: auto;
23 padding: 5px;
24 box-sizing: border-box;
25 background-color: #EDF1F7;
26 box-sizing: border-box;
27
28 }
29 }
30 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div class="navbar-con">
3 <div class="navbar">
4 <div class="logo">
5 {{ title }}
6 </div>
7 <div class="backdrop">
8 <sidebar />
9 </div>
10 <div class="right-menu">
11 <div class="dataView pointer" @click="handleDataView">大屏展示</div>
12 <svg-icon class="function" icon-class='function' />
13 <el-dropdown class="avatar-container right-menu-item hover-effect" trigger="hover" @command="handleCommand">
14 <div class="avatar-wrapper">
15 <span style="padding-right:10px">{{ name }}</span>
16 <img :src="avatar + '?imageView2/1/w/80/h/80'" class="user-avatar" />
17 </div>
18 <el-dropdown-menu slot="dropdown">
19 <el-dropdown-item command="a">个人中心</el-dropdown-item>
20 <el-dropdown-item command="b">退出</el-dropdown-item>
21 </el-dropdown-menu>
22 </el-dropdown>
23 </div>
24 </div>
25 </div>
26 </template>
27 <script>
28 import defaultSettings from '@/settings'
29 import Sidebar from './Sidebar'
30 import { mapGetters } from 'vuex'
31 export default {
32 components: {
33 Sidebar,
34 },
35 computed: {
36 ...mapGetters(['sidebar', 'avatar', 'name'])
37 },
38 data () {
39 return {
40 title: defaultSettings.title
41 }
42 },
43 methods: {
44 handleDataView () {
45 const { href } = this.$router.resolve('/dataView');
46 window.open(href, '_blank');
47 },
48 themeChange (val) {
49 this.$store.dispatch('app/updateTheme', val)
50 },
51 handleCommand (command) {
52 if (command == 'a') {
53 } else {
54
55 }
56 }
57 }
58 }
59 </script>
60 <style lang="scss" scoped>
61 .navbar-con {
62 position: relative;
63
64 .logo {
65 color: #fff;
66 font-size: 26px;
67 font-weight: 700;
68 }
69 }
70
71 .dataView {
72 color: #fff;
73 }
74
75 .NoticeBar {
76 position: absolute;
77 bottom: 0;
78 }
79
80 .el-dropdown-menu {
81 padding: 0 !important;
82 border: 1px solid #EBEEF5;
83 box-shadow: 0 2px 10px 0 rgba(0, 0, 0, 0.12);
84 border-radius: 4px 0 0 4px 4px;
85
86 .el-dropdown-menu__item {
87 text-align: center;
88 margin-top: 0 !important;
89 font-size: 14px;
90 font-family: PingFangSC-Regular, PingFang SC;
91 font-weight: 400;
92 color: #4A4A4A;
93 width: 140px;
94 height: 36px;
95 line-height: 36px;
96 }
97
98 .el-dropdown-menu__item:nth-child(6) {
99 border-top: 1px solid #EBEEF5;
100 }
101
102 .popper__arrow {
103 top: -11px !important;
104 left: 110px !important;
105 transform: rotate(0deg) scale(2);
106 }
107
108 .el-dropdown-menu__item:not(.is-disabled):hover,
109 .el-dropdown-menu__item:focus {
110 background: #F6F7F9;
111 color: #4A4A4A;
112 }
113 }
114
115 .navbar {
116 height: $headerHeight;
117 overflow: hidden;
118 position: relative;
119 background: linear-gradient(270deg, #148CEE 0%, #1870E3 100%); //默认颜色
120 box-shadow: 0 1px 0px rgba(0, 21, 41, 0.08);
121 display: flex;
122 align-items: center;
123 padding: 0 20px;
124 justify-content: space-between;
125
126 .header-logo {
127 width: 300px;
128 }
129
130 .backdrop {
131 flex: 1;
132 width: 60%;
133 background: url('../../image/backdrop.png');
134 background-size: 100% 100%;
135 height: $headerHeight;
136 display: flex;
137 align-items: center;
138 padding-left: 20px;
139 }
140
141 .hamburger-container {
142 line-height: 43px;
143 height: 100%;
144 float: left;
145 cursor: pointer;
146 transition: background 0.3s;
147 -webkit-tap-highlight-color: transparent;
148
149 &:hover {
150 background: rgba(0, 0, 0, 0.025);
151 }
152 }
153
154 .breadcrumb-container {
155 float: left;
156 }
157
158 .right-menu {
159 float: right;
160 height: 100%;
161 line-height: 50px;
162 display: flex;
163 align-items: center;
164
165 .function {
166 margin: 0 15px;
167 cursor: pointer;
168 }
169
170 .shutdown {
171 font-size: 20px;
172 margin-left: 15px;
173 cursor: pointer;
174 }
175
176 .organization-item {
177 margin-right: 40px;
178 margin-top: -40px !important;
179 }
180
181 .item {
182 margin-right: 40px;
183 margin-top: -20px;
184 line-height: 18.4px;
185 cursor: pointer;
186 position: relative;
187
188 .item-box {
189 position: absolute;
190 top: -5px;
191 left: 3px;
192 width: 100%;
193 min-width: 25px;
194 height: 25px;
195 cursor: pointer;
196 z-index: 100;
197 }
198 }
199
200 &:focus {
201 outline: none;
202 }
203
204 .right-menu-item {
205 display: inline-block;
206 height: 100%;
207 font-size: 18px;
208 color: #fff;
209 vertical-align: text-bottom;
210
211 &.hover-effect {
212 cursor: pointer;
213 transition: background 0.3s;
214 display: flex;
215 align-items: center;
216
217 &:hover {
218 background: rgba(0, 0, 0, 0.025);
219 }
220 }
221 }
222
223 .avatar-wrapper {
224 position: relative;
225 display: flex;
226 height: 40px;
227 align-items: center;
228
229 .user-avatar {
230 cursor: pointer;
231 width: 35px;
232 height: 35px;
233 border-radius: 50%;
234 }
235
236 .el-icon-caret-bottom {
237 cursor: pointer;
238 position: absolute;
239 right: -15px;
240 top: 17px;
241 font-size: 12px;
242 }
243 }
244 }
245 }
246 </style>
1 export default {
2 computed: {
3 device() {
4 return this.$store.state.app.device
5 }
6 },
7 mounted() {
8 // In order to fix the click on menu on the ios device will trigger the mouseleave bug
9 // https://github.com/PanJiaChen/vue-element-admin/issues/1135
10 this.fixBugIniOS()
11 },
12 methods: {
13 fixBugIniOS() {
14 const $subMenu = this.$refs.subMenu
15 if ($subMenu) {
16 const handleMouseleave = $subMenu.handleMouseleave
17 $subMenu.handleMouseleave = (e) => {
18 if (this.device === 'mobile') {
19 return
20 }
21 handleMouseleave(e)
22 }
23 }
24 }
25 }
26 }
1 <script>
2 export default {
3 name: 'MenuItem',
4 functional: true,
5 props: {
6 icon: {
7 type: String,
8 default: ''
9 },
10 title: {
11 type: String,
12 default: ''
13 }
14 },
15 render (h, context) {
16 const { icon, title } = context.props
17 const vnodes = []
18
19 if (icon) {
20 if (icon.includes('el-icon')) {
21 vnodes.push(<i class={[icon, 'sub-el-icon']} />)
22 } else {
23 vnodes.push(<svg-icon icon-class={icon} />)
24 }
25 }
26
27 if (title) {
28 vnodes.push(<span slot='title'>{(title)}</span>)
29 }
30 return vnodes
31 }
32 }
33 </script>
34
35 <style scoped>
36 .sub-el-icon {
37 color: currentColor;
38 width: 1em;
39 height: 1em;
40 }
41 </style>
1 <template>
2 <component :is="type" v-bind="linkProps(to)">
3 <slot />
4 </component>
5 </template>
6
7 <script>
8 import { isExternal } from '@/utils/validate'
9
10 export default {
11 props: {
12 to: {
13 type: String,
14 required: true
15 }
16 },
17 computed: {
18 isExternal() {
19 return isExternal(this.to)
20 },
21 type() {
22 if (this.isExternal) {
23 return 'a'
24 }
25 return 'router-link'
26 }
27 },
28 methods: {
29 linkProps(to) {
30 if (this.isExternal) {
31 return {
32 href: to,
33 target: '_blank',
34 rel: 'noopener'
35 }
36 }
37 return {
38 to: to
39 }
40 }
41 }
42 }
43 </script>
1 <template>
2 <div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
3 <transition name="sidebarLogoFade">
4 <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
5 <img v-if="logo" :src="logo" class="sidebar-logo">
6 <h1 v-else class="sidebar-title">
7 {{ title }}
8 </h1>
9 </router-link>
10 <router-link v-else key="expand" class="sidebar-logo-link" to="/">
11 <h1 class="sidebar-title">
12 {{ title }}
13 </h1>
14 </router-link>
15 </transition>
16 </div>
17 </template>
18
19 <script>
20 import defaultSettings from '@/settings'
21 const { title } = defaultSettings
22 export default {
23 name: 'SidebarLogo',
24 props: {
25 collapse: {
26 type: Boolean,
27 required: true
28 }
29 },
30 data () {
31 return {
32 title: title,
33 }
34 }
35 }
36 </script>
37
38 <style lang="scss" scoped>
39 .sidebarLogoFade-enter-active {
40 transition: opacity 1.5s;
41 }
42
43 .sidebarLogoFade-enter,
44 .sidebarLogoFade-leave-to {
45 opacity: 0;
46 }
47
48 .sidebar-logo-container {
49 position: relative;
50 width: 100%;
51 text-align: center;
52 overflow: hidden;
53 height: 100px;
54
55 & .sidebar-logo-link {
56 height: 100%;
57 width: 100%;
58
59 & .sidebar-logo {
60 width: 41px;
61 height: 39px;
62 vertical-align: middle;
63 // margin-left: 47px;
64 // margin-right: 48px;
65 margin-top: 22px;
66 }
67
68 & .sidebar-title {
69 margin: 0;
70 margin-top: 10px;
71 margin-bottom: 20px;
72 color: #fff;
73 font-weight: 600;
74 line-height: 25px;
75 font-size: 16px;
76 font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
77 vertical-align: middle;
78 }
79 }
80
81 &.collapse {
82 .sidebar-logo {
83 margin-right: 0px;
84 width: 32.8px;
85 height: 31.2px;
86 }
87 }
88 }
89 </style>
1 <template>
2 <div v-if="!item.hidden">
3 <template
4 v-if="hasOneShowingChild(item.children, item) && (!onlyOneChild.children || onlyOneChild.noShowingChildren)">
5 <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)">
6 <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{ 'submenu-title-noDropdown': !isNest }">
7 <item :icon="onlyOneChild.meta.icon || (item.meta && item.meta.icon)" :title="onlyOneChild.meta.title"
8 class="menu-icon" />
9 </el-menu-item>
10 </app-link>
11 </template>
12
13 <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body>
14 <template slot="title">
15 <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" />
16 </template>
17 <sidebar-item v-for="child in item.children" :key="child.path" :is-nest="true" :item="child"
18 :base-path="resolvePath(child.path)" class="nest-menu" />
19 </el-submenu>
20 </div>
21 </template>
22
23 <script>
24 import path from 'path'
25 import { isExternal } from '@/utils/validate'
26 import Item from './Item'
27 import AppLink from './Link'
28 import FixiOSBug from './FixiOSBug'
29
30 export default {
31 name: 'SidebarItem',
32 components: { Item, AppLink },
33 mixins: [FixiOSBug],
34 props: {
35 // route object
36 item: {
37 type: Object,
38 required: true
39 },
40 isNest: {
41 type: Boolean,
42 default: false
43 },
44 basePath: {
45 type: String,
46 default: ''
47 }
48 },
49 data () {
50 // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237
51 // TODO: refactor with render function
52 this.onlyOneChild = null
53 return {}
54 },
55 methods: {
56 hasOneShowingChild (children = [], parent) {
57 const showingChildren = children.filter(item => {
58 if (item.hidden) {
59 return false
60 } else {
61 // Temp set(will be used if only has one showing child)
62 this.onlyOneChild = item
63 return true
64 }
65 })
66
67 // When there is only one child router, the child router is displayed by default
68 if (showingChildren.length === 1) {
69 return true
70 }
71
72 // Show parent if there are no child router to display
73 if (showingChildren.length === 0) {
74 this.onlyOneChild = { ...parent, path: '', noShowingChildren: true }
75 return true
76 }
77
78 return false
79 },
80 resolvePath (routePath) {
81 if (isExternal(routePath)) {
82 return routePath
83 }
84 if (isExternal(this.basePath)) {
85 return this.basePath
86 }
87 return path.resolve(this.basePath, routePath)
88 }
89 }
90 }
91 </script>
92
93 <style scoped>
94 /deep/.el-menu-item {
95 padding-left: 30px !important;
96 }
97 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <div>
3 <el-menu router :default-active="activeMenu" mode="horizontal">
4 <!-- 权限菜单 -->
5 <!-- <sidebar-item v-for="route in permission_routes" :key="route.path" :item="route" :base-path="route.path" /> -->
6 <!-- 菜单全部展示 -->
7 <sidebar-item v-for="route in asyncRoutes" :key="route.path" :item="route" :base-path="route.path" />
8 </el-menu>
9 </div>
10 </template>
11
12 <script>
13 import { mapGetters } from 'vuex'
14 import Logo from './Logo'
15 import SidebarItem from './SidebarItem'
16 import variables from '@/styles/variables.scss'
17 import { asyncRoutes1 } from '@/router'
18 export default {
19 components: { SidebarItem, Logo },
20 computed: {
21 ...mapGetters(['permission_routes', 'sidebar']),
22 activeMenu () {
23 const route = this.$route
24 const { meta, path } = route
25 if (meta.activeMenu) {
26 return meta.activeMenu
27 }
28 return path
29 },
30 variables () {
31 return variables
32 },
33 asyncRoutes () {
34 return asyncRoutes1
35 }
36 }
37 }
38 </script>
39 <style scoped lang="scss">
40 .el-menu--horizontal {
41 display: flex;
42 background: none !important;
43 }
44
45 /deep/.el-menu-item {
46 color: #fff;
47 font-size: 18px;
48 }
49
50 /deep/.el-menu-item:hover {
51 background: none;
52 font-weight: 700;
53 color: #fff !important;
54 }
55
56 /deep/.el-submenu__title {
57 color: #fff;
58 font-size: 18px;
59 }
60
61 /deep/.el-submenu__title:hover {
62 background: none;
63 font-weight: 700;
64 }
65
66 /deep/.el-menu--horizontal .el-menu-item:not(.is-disabled):focus {
67 background: none;
68 color: #fff;
69 font-weight: 700 !important;
70 }
71 </style>
...\ No newline at end of file ...\ No newline at end of file
1 <template>
2 <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll">
3 <slot />
4 </el-scrollbar>
5 </template>
6
7 <script>
8 const tagAndTagSpacing = 4 // tagAndTagSpacing
9
10 export default {
11 name: 'ScrollPane',
12 data () {
13 return {
14 left: 0
15 }
16 },
17 computed: {
18 scrollWrapper () {
19 return this.$refs.scrollContainer.$refs.wrap
20 }
21 },
22 mounted () {
23 this.scrollWrapper.addEventListener('scroll', this.emitScroll, true)
24 },
25 beforeDestroy () {
26 this.scrollWrapper.removeEventListener('scroll', this.emitScroll)
27 },
28 methods: {
29 handleScroll (e) {
30 const eventDelta = e.wheelDelta || -e.deltaY * 40
31 const $scrollWrapper = this.scrollWrapper
32 $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4
33 },
34 emitScroll () {
35 this.$emit('scroll')
36 },
37 moveToTarget (currentTag) {
38 const $container = this.$refs.scrollContainer.$el
39 const $containerWidth = $container.offsetWidth
40 const $scrollWrapper = this.scrollWrapper
41 const tagList = this.$parent.$refs.tag
42
43 let firstTag = null
44 let lastTag = null
45
46 // find first tag and last tag
47 if (tagList.length > 0) {
48 firstTag = tagList[0]
49 lastTag = tagList[tagList.length - 1]
50 }
51
52 if (firstTag === currentTag) {
53 $scrollWrapper.scrollLeft = 0
54 } else if (lastTag === currentTag) {
55 $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth
56 } else {
57 // find preTag and nextTag
58 const currentIndex = tagList.findIndex(item => item === currentTag)
59 const prevTag = tagList[currentIndex - 1]
60 const nextTag = tagList[currentIndex + 1]
61
62 // the tag's offsetLeft after of nextTag
63 const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing
64
65 // the tag's offsetLeft before of prevTag
66 const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing
67
68 if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) {
69 $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth
70 } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) {
71 $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft
72 }
73 }
74 }
75 }
76 }
77 </script>
78
79 <style lang="scss" scoped>
80 .scroll-container {
81 white-space: nowrap;
82 position: relative;
83 overflow: hidden;
84 width: 100%;
85 height: 100%;
86 }
87
88 /deep/ .el-scrollbar__view {
89 display: inline-block !important;
90 }
91
92 /deep/ .el-scrollbar__wrap {
93 overflow-x: hidden !important;
94 }
95 </style>
1 <template>
2 <div id="tags-view-container" class="tags-view-container">
3 <scroll-pane ref="scrollPane" class="tags-view-wrapper" @scroll="handleScroll">
4 <router-link v-for="tag in visitedViews" ref="tag" :key="tag.path" :class="isActive(tag)?'active':''"
5 :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" tag="span" class="tags-view-item"
6 @click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
7 @contextmenu.prevent.native="openMenu(tag,$event)">
8 {{ tag.title }}
9 <span v-if="!isAffix(tag)" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" />
10 </router-link>
11 </scroll-pane>
12 <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
13 <li @click="refreshSelectedTag(selectedTag)">Refresh</li>
14 <li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
15 <li @click="closeOthersTags">Close Others</li>
16 <li @click="closeAllTags(selectedTag)">Close All</li>
17 </ul>
18 </div>
19 </template>
20
21 <script>
22 import ScrollPane from './ScrollPane'
23 import path from 'path'
24
25 export default {
26 components: { ScrollPane },
27 data () {
28 return {
29 visible: false,
30 top: 0,
31 left: 0,
32 selectedTag: {},
33 affixTags: []
34 }
35 },
36 computed: {
37 visitedViews () {
38 return this.$store.state.tagsView.visitedViews
39 },
40 routes () {
41 return this.$store.state.permission.routes
42 }
43 },
44 watch: {
45 $route () {
46 this.addTags()
47 this.moveToCurrentTag()
48 },
49 visible (value) {
50 if (value) {
51 document.body.addEventListener('click', this.closeMenu)
52 } else {
53 document.body.removeEventListener('click', this.closeMenu)
54 }
55 }
56 },
57 mounted () {
58 this.initTags()
59 this.addTags()
60 },
61 methods: {
62 isActive (route) {
63 return route.path === this.$route.path
64 },
65 isAffix (tag) {
66 return tag.meta && tag.meta.affix
67 },
68 filterAffixTags (routes, basePath = '/') {
69 let tags = []
70 routes.forEach(route => {
71 if (route.meta && route.meta.affix) {
72 const tagPath = path.resolve(basePath, route.path)
73 tags.push({
74 fullPath: tagPath,
75 path: tagPath,
76 name: route.name,
77 meta: { ...route.meta }
78 })
79 }
80 if (route.children) {
81 const tempTags = this.filterAffixTags(route.children, route.path)
82 if (tempTags.length >= 1) {
83 tags = [...tags, ...tempTags]
84 }
85 }
86 })
87 return tags
88 },
89 initTags () {
90 const affixTags = this.affixTags = this.filterAffixTags(this.routes)
91 for (const tag of affixTags) {
92 // Must have tag name
93 if (tag.name) {
94 this.$store.dispatch('tagsView/addVisitedView', tag)
95 }
96 }
97 },
98 addTags () {
99 const { name } = this.$route
100 if (name) {
101 this.$store.dispatch('tagsView/addView', this.$route)
102 }
103 return false
104 },
105 moveToCurrentTag () {
106 const tags = this.$refs.tag
107 this.$nextTick(() => {
108 for (const tag of tags) {
109 if (tag.to.path === this.$route.path) {
110 this.$refs.scrollPane.moveToTarget(tag)
111 // when query is different then update
112 if (tag.to.fullPath !== this.$route.fullPath) {
113 this.$store.dispatch('tagsView/updateVisitedView', this.$route)
114 }
115 break
116 }
117 }
118 })
119 },
120 refreshSelectedTag (view) {
121 this.$store.dispatch('tagsView/delCachedView', view).then(() => {
122 const { fullPath } = view
123 this.$nextTick(() => {
124 this.$router.replace({
125 path: '/redirect' + fullPath
126 })
127 })
128 })
129 },
130 closeSelectedTag (view) {
131 this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => {
132 if (this.isActive(view)) {
133 this.toLastView(visitedViews, view)
134 }
135 })
136 },
137 closeOthersTags () {
138 this.$router.push(this.selectedTag)
139 this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => {
140 this.moveToCurrentTag()
141 })
142 },
143 closeAllTags (view) {
144 this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
145 if (this.affixTags.some(tag => tag.path === view.path)) {
146 return
147 }
148 this.toLastView(visitedViews, view)
149 })
150 },
151 toLastView (visitedViews, view) {
152 const latestView = visitedViews.slice(-1)[0]
153 if (latestView) {
154 this.$router.push(latestView.fullPath)
155 } else {
156 // now the default is to redirect to the home page if there is no tags-view,
157 // you can adjust it according to your needs.
158 if (view.name === 'Dashboard') {
159 // to reload home page
160 this.$router.replace({ path: '/redirect' + view.fullPath })
161 } else {
162 this.$router.push('/')
163 }
164 }
165 },
166 openMenu (tag, e) {
167 const menuMinWidth = 105
168 const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
169 const offsetWidth = this.$el.offsetWidth // container width
170 const maxLeft = offsetWidth - menuMinWidth // left boundary
171 const left = e.clientX - offsetLeft + 15 // 15: margin right
172
173 if (left > maxLeft) {
174 this.left = maxLeft
175 } else {
176 this.left = left
177 }
178
179 this.top = e.clientY
180 this.visible = true
181 this.selectedTag = tag
182 },
183 closeMenu () {
184 this.visible = false
185 },
186 handleScroll () {
187 this.closeMenu()
188 }
189 }
190 }
191 </script>
192
193 <style lang="scss" scoped>
194 .tags-view-container {
195 height: 40px;
196 width: 100%;
197 background: #fff;
198 border-bottom: 1px solid #d8dce5;
199 box-sizing: border-box;
200 padding-top: 3px;
201 box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04);
202
203 .tags-view-wrapper {
204 .tags-view-item {
205 display: inline-block;
206 position: relative;
207 cursor: pointer;
208 height: 26px;
209 line-height: 26px;
210 border: 1px solid #d8dce5;
211 color: #495060;
212 background: #fff;
213 padding: 0 8px;
214 font-size: 12px;
215 margin-left: 5px;
216 margin-top: 4px;
217
218 &:first-of-type {
219 margin-left: 15px;
220 }
221
222 &:last-of-type {
223 margin-right: 15px;
224 }
225
226 &.active {
227 background-color: #0794FF;
228 color: #fff;
229 border-color: #0794FF;
230
231 &::before {
232 content: '';
233 background: #fff;
234 display: inline-block;
235 width: 8px;
236 height: 8px;
237 border-radius: 50%;
238 position: relative;
239 margin-right: 2px;
240 }
241 }
242 }
243 }
244
245 .contextmenu {
246 margin: 0;
247 background: #fff;
248 z-index: 3000;
249 position: absolute;
250 list-style-type: none;
251 padding: 5px 0;
252 border-radius: 4px;
253 font-size: 12px;
254 font-weight: 400;
255 color: #333;
256 box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3);
257
258 li {
259 margin: 0;
260 padding: 7px 16px;
261 cursor: pointer;
262
263 &:hover {
264 background: #eee;
265 }
266 }
267 }
268 }
269 </style>
270
271 <style lang="scss">
272 //reset element css of el-icon-close
273 .tags-view-wrapper {
274 .tags-view-item {
275 .el-icon-close {
276 width: 16px;
277 height: 16px;
278 vertical-align: 2px;
279 border-radius: 50%;
280 text-align: center;
281 transition: all .3s cubic-bezier(.645, .045, .355, 1);
282 transform-origin: 100% 50%;
283
284 &:before {
285 transform: scale(.6);
286 display: inline-block;
287 vertical-align: -3px;
288 }
289
290 &:hover {
291 background-color: #b4bccc;
292 color: #fff;
293 }
294 }
295 }
296 }
297 </style>
1 export { default as AppMain } from './AppMain'
2 export { default as Navbar } from './Navbar'
3 export { default as Sidebar } from './Sidebar/index.vue'
4 export { default as TagsView } from './TagsView/index.vue'
1 <template>
2 <div class="app-wrapper">
3 <navbar />
4 <div class="appMain">
5 <app-main />
6 </div>
7 </div>
8 </template>
9 <script>
10 import { AppMain, Navbar, Sidebar, TagsView } from './components'
11 import ResizeMixin from './mixin/ResizeHandler'
12 import { mapState } from 'vuex'
13 export default {
14 name: 'Layout',
15 components: {
16 AppMain,
17 Navbar,
18 Sidebar,
19 TagsView
20 },
21 mixins: [ResizeMixin],
22 computed: {
23 ...mapState({
24 sidebar: state => state.app.sidebar,
25 needTagsView: state => state.settings.tagsView,
26 fixedHeader: state => state.settings.fixedHeader
27 })
28 }
29 }
30 </script>
31 <style lang="scss" scoped>
32 @import "~@/styles/mixin.scss";
33
34 .app-wrapper {
35 @include clearfix;
36 position: relative;
37 height: 100%;
38 width: 100%;
39 background-color: #F2F6FC;
40
41 &.mobile.openSidebar {
42 position: fixed;
43 top: 0;
44 }
45 }
46
47 .appMain {
48 height: calc(100vh - 80px);
49 width: 80%;
50 margin: 0 auto;
51 background-color: #fff;
52
53 .app-main {
54 height: 100%;
55 }
56 }
57 </style>
1 import store from '@/store'
2
3 const { body } = document
4 const WIDTH = 992 // refer to Bootstrap's responsive design
5
6 export default {
7 watch: {
8 $route(route) {
9 if (this.device === 'mobile' && this.sidebar.opened) {
10 store.dispatch('app/closeSideBar', { withoutAnimation: false })
11 }
12 }
13 },
14 beforeMount() {
15 window.addEventListener('resize', this.$_resizeHandler)
16 },
17 beforeDestroy() {
18 window.removeEventListener('resize', this.$_resizeHandler)
19 },
20 mounted() {
21 const isMobile = this.$_isMobile()
22 if (isMobile) {
23 store.dispatch('app/toggleDevice', 'mobile')
24 store.dispatch('app/closeSideBar', { withoutAnimation: true })
25 }
26 },
27 methods: {
28 // use $_ for mixins properties
29 // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential
30 $_isMobile() {
31 const rect = body.getBoundingClientRect()
32 return rect.width - 1 < WIDTH
33 },
34 $_resizeHandler() {
35 if (!document.hidden) {
36 const isMobile = this.$_isMobile()
37 store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop')
38
39 if (isMobile) {
40 store.dispatch('app/closeSideBar', { withoutAnimation: true })
41 }
42 }
43 }
44 }
45 }
...@@ -3,6 +3,7 @@ import Router from 'vue-router' ...@@ -3,6 +3,7 @@ import Router from 'vue-router'
3 Vue.use(Router) 3 Vue.use(Router)
4 /* Layout */ 4 /* Layout */
5 import Layout from '@/layout' 5 import Layout from '@/layout'
6 import Layout1 from '@/layout1'
6 7
7 /* Router Modules */ 8 /* Router Modules */
8 // import componentsRouter from './modules/components' 9 // import componentsRouter from './modules/components'
...@@ -47,21 +48,6 @@ export const asyncRoutes = [ ...@@ -47,21 +48,6 @@ export const asyncRoutes = [
47 } 48 }
48 ] 49 ]
49 }, 50 },
50 // 监管首页
51 {
52 path: '/jgHome',
53 component: Layout,
54 redirect: '/jgHome',
55 meta: { title: '首页' },
56 children: [
57 {
58 path: 'jgHome',
59 component: () => import('@/views/jgHome/index'),
60 name: 'jgHome',
61 meta: { title: '首页', icon: 'workbench', affix: true }
62 }
63 ]
64 },
65 // 接收报文查询 51 // 接收报文查询
66 { 52 {
67 path: '/jsbwcx', 53 path: '/jsbwcx',
...@@ -311,10 +297,96 @@ export const asyncRoutes = [ ...@@ -311,10 +297,96 @@ export const asyncRoutes = [
311 } 297 }
312 ] 298 ]
313 299
300 export const asyncRoutes1 = [
301 // 监管首页
302 {
303 path: '/',
304 component: Layout1,
305 redirect: '/jgHome',
306 meta: { title: '首页' },
307 children: [
308 {
309 path: 'jgHome',
310 component: () => import('@/views/jgHome/index'),
311 name: 'jgHome',
312 meta: { title: '首页', icon: 'workbench', affix: true }
313 }
314 ]
315 },
316 // 接收报文查询
317 {
318 path: '/jsbwcx1',
319 component: Layout1,
320 children: [
321 {
322 path: 'index',
323 component: () => import('@/views/jsbwcx/index'),
324 name: 'jsbwcx',
325 meta: { title: '接收报文查询1', icon: 'zsgl' }
326 }
327 ]
328 },
329 // 上报报文查询
330 {
331 path: '/sbbwcx1',
332 component: Layout1,
333 children: [
334 {
335 path: 'index',
336 component: () => import('@/views/sbbwcx/index'),
337 name: 'sbbwcx',
338 meta: { title: '上报报文查询', icon: 'zsgl' }
339 }
340 ]
341 },
342 // 登簿日志
343 {
344 path: '/dbrzcx1',
345 component: Layout1,
346 children: [
347 {
348 path: 'index',
349 component: () => import('@/views/dbrzcx/index'),
350 name: 'dbrzcx',
351 meta: { title: '登簿日志查询', icon: 'zhcx' }
352 }
353 ]
354 },
355 // 系统管理
356 {
357 path: '/system1',
358 component: Layout1,
359 meta: { title: '系统管理', icon: 'sqcx', breadcrumb: false },
360 redirect: '/system/dictionaries',
361 alwaysShow: true,
362 name: 'system',
363 children: [
364 {
365 path: 'dictionaries',
366 component: () => import('@/views/system/dictionaries/dictionaries.vue'),
367 name: 'dictionaries',
368 meta: { title: '字典管理' }
369 },
370 {
371 path: 'validationRule',
372 component: () => import('@/views/system/validationRule'),
373 name: 'validationRule',
374 meta: { title: '上报效验规则配置' }
375 },
376 {
377 path: 'timedTask',
378 component: () => import('@/views/system/timedTask'),
379 name: 'timedTask',
380 meta: { title: '定时任务' }
381 }
382 ]
383 }
384 ]
385
314 const createRouter = () => 386 const createRouter = () =>
315 new Router({ 387 new Router({
316 scrollBehavior: () => ({ y: 0 }), 388 scrollBehavior: () => ({ y: 0 }),
317 routes: [...constantRoutes, ...asyncRoutes] 389 routes: [...constantRoutes, ...asyncRoutes, ...asyncRoutes1]
318 }) 390 })
319 391
320 const router = createRouter() 392 const router = createRouter()
......
...@@ -134,6 +134,6 @@ export default { ...@@ -134,6 +134,6 @@ export default {
134 .map-box { 134 .map-box {
135 display: inline-block; 135 display: inline-block;
136 width: 100%; 136 width: 100%;
137 height: 79.6vh; 137 height: calc(100vh - 133px);
138 } 138 }
139 </style> 139 </style>
......
1 .jgHome { 1 .jgHome {
2 display: flex; 2 display: flex;
3 justify-content: space-between; 3 justify-content: space-between;
4 height: 100%;
4 5
5 .bottom10 { 6 .bottom10 {
6 margin-bottom: 10px; 7 margin-bottom: 10px;
...@@ -51,7 +52,7 @@ ...@@ -51,7 +52,7 @@
51 .barChart { 52 .barChart {
52 flex: 1; 53 flex: 1;
53 margin-top: 10px; 54 margin-top: 10px;
54 height: calc(100vh - 355px); 55 height: calc(100vh - 310px);
55 } 56 }
56 57
57 &-center { 58 &-center {
......
...@@ -42,7 +42,7 @@ ...@@ -42,7 +42,7 @@
42 <span>违规总计</span> 42 <span>违规总计</span>
43 <el-button style="float: right;" type="text">更多</el-button> 43 <el-button style="float: right;" type="text">更多</el-button>
44 </div> 44 </div>
45 <lb-table ref="table" :pagination="false" :heightNum="595" :column="tableData.columns" :data="tableData.data"> 45 <lb-table ref="table" :pagination="false" :heightNum="546" :column="tableData.columns" :data="tableData.data">
46 </lb-table> 46 </lb-table>
47 </el-card> 47 </el-card>
48 </div> 48 </div>
......