diff options
| author | unknown <[email protected]> | 2022-06-24 17:11:23 +0800 |
|---|---|---|
| committer | unknown <[email protected]> | 2022-06-24 17:11:23 +0800 |
| commit | 8565e1bb597b481447d33bac6d8c48c2c45215de (patch) | |
| tree | a4f10c8f7f85a1a8b5c947f7d0d2f967d808a9c4 /UI source code/dns_mapping_ui-master/src/layout | |
| parent | 8165dfcc7bdb0b2e6f1c05f8e7c93553c0e7911e (diff) | |
Diffstat (limited to 'UI source code/dns_mapping_ui-master/src/layout')
20 files changed, 1609 insertions, 0 deletions
diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/AppMain.vue b/UI source code/dns_mapping_ui-master/src/layout/components/AppMain.vue new file mode 100644 index 0000000..1a737b2 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/AppMain.vue @@ -0,0 +1,63 @@ +<template> + <section class="app-main"> + <transition name="fade-transform" mode="out-in"> + <keep-alive :include="cachedViews"> + <router-view :key="key" /> + </keep-alive> + </transition> + <!-- <div v-if="$store.state.settings.showFooter" id="el-main-footer"> + <span v-html="$store.state.settings.footerTxt" /> + <span> ⋅ </span> + <a href="https://beian.miit.gov.cn/#/Integrated/index" target="_blank">{{ $store.state.settings.caseNumber }}</a> + </div> --> + </section> +</template> + +<script> +export default { + name: 'AppMain', + computed: { + cachedViews() { + return this.$store.state.tagsView.cachedViews + }, + key() { + return this.$route.path + } + } +} +</script> + +<style lang="scss" scoped> +.app-main { + /* 50= navbar 50 */ + min-height: calc(100vh - 50px); + width: 100%; + position: relative; + overflow: hidden; + background: #f7f9fc; +} + +.fixed-header+.app-main { + padding-top: 50px; +} + +.hasTagsView { + .app-main { + /* 84 = navbar + tags-view = 50 + 34 */ + min-height: calc(100vh - 84px); + } + + .fixed-header+.app-main { + padding-top: 84px; + } +} +</style> + +<style lang="scss"> +// fix css style bug in open el-dialog +.el-popup-parent--hidden { + .fixed-header { + padding-right: 15px; + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/FixiOSBug.js b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/FixiOSBug.js new file mode 100644 index 0000000..bc14856 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/FixiOSBug.js @@ -0,0 +1,26 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + // https://github.com/PanJiaChen/vue-element-admin/issues/1135 + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Item.vue b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Item.vue new file mode 100644 index 0000000..b515f61 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Item.vue @@ -0,0 +1,29 @@ +<script> +export default { + name: 'MenuItem', + functional: true, + props: { + icon: { + type: String, + default: '' + }, + title: { + type: String, + default: '' + } + }, + render(h, context) { + const { icon, title } = context.props + const vnodes = [] + + if (icon) { + vnodes.push(<svg-icon icon-class={icon}/>) + } + + if (title) { + vnodes.push(<span slot='title'>{(title)}</span>) + } + return vnodes + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Link.vue b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Link.vue new file mode 100644 index 0000000..eb4dd10 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Link.vue @@ -0,0 +1,36 @@ + +<template> + <!-- eslint-disable vue/require-component-is --> + <component v-bind="linkProps(to)"> + <slot /> + </component> +</template> + +<script> +import { isExternal } from '@/utils/validate' + +export default { + props: { + to: { + type: String, + required: true + } + }, + methods: { + linkProps(url) { + if (isExternal(url)) { + return { + is: 'a', + href: url, + target: '_blank', + rel: 'noopener' + } + } + return { + is: 'router-link', + to: url + } + } + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Logo.vue b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Logo.vue new file mode 100644 index 0000000..e50a338 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/Logo.vue @@ -0,0 +1,44 @@ +<template> + <div class="log"> + <img :src="logo" alt=""> + <p>DiamondV</p> + </div> +</template> + +<script> +import Logo from '@/assets/images/newlogo.png' +export default { + data() { + return { + logo: Logo + } + } +} +</script> + +<style lang="scss" scoped> + +.log{ + width: 13%; + height: 56px; + background-color: #4608ad; + color: white; + text-align: center; + display: flex; + justify-content:center; + align-items: center; + font-size: 25px; + font-weight: 600; + p{ + font-family:"仿宋"; + font-style:italic; + font-size: 20px; + } + img{ + margin-right: 18px; + width: 50px; + height: 55px; + } +} + +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/SidebarItem.vue b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/SidebarItem.vue new file mode 100644 index 0000000..b4d2911 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/SidebarItem.vue @@ -0,0 +1,105 @@ +<template> + <!-- <div v-if="!item.hidden">--> + <!-- todo 增加了style样式 style="display:inline-block;"--> + <div v-if="!item.hidden" style="display:inline-block;"> + <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> + <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> + <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> + <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> + </el-menu-item> + </app-link> + </template> + + <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> + <template slot="title"> + <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> + <!--todo 增加div固定宽度,用于菜单上的小箭头显示--> + <div style="display: inline-block; width:20px;" /> + </template> + <sidebar-item + v-for="child in item.children" + :key="child.path" + :is-nest="true" + :item="child" + :base-path="resolvePath(child.path)" + class="nest-menu" + /> + </el-submenu> + </div> +</template> + +<script> +import path from 'path' +import { isExternal } from '@/utils/validate' +import Item from './Item' +import AppLink from './Link' +import FixiOSBug from './FixiOSBug' + +export default { + name: 'SidebarItem', + components: { Item, AppLink }, + mixins: [FixiOSBug], + props: { + // route object + item: { + type: Object, + required: true + }, + isNest: { + type: Boolean, + default: false + }, + basePath: { + type: String, + default: '' + } + }, + data() { + // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 + // TODO: refactor with render function + this.onlyOneChild = null + return {} + }, + methods: { + hasOneShowingChild(children = [], parent) { + const showingChildren = children.filter(item => { + if (item.hidden) { + return false + } else { + // Temp set(will be used if only has one showing child) + this.onlyOneChild = item + return true + } + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return true + } + + // Show parent if there are no child router to display + if (showingChildren.length === 0) { + this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } + return true + } + + return false + }, + resolvePath(routePath) { + if (isExternal(routePath)) { + return routePath + } + if (isExternal(this.basePath)) { + return this.basePath + } + return path.resolve(this.basePath, routePath) + } + } +} +</script> +<style scoped> +.el-menu-item { + font-size: 17px; + color: white !important; +} +</style>
\ No newline at end of file diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/index.vue b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/index.vue new file mode 100644 index 0000000..1076b36 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/HeadNavbar/index.vue @@ -0,0 +1,90 @@ +<template> + <!-- <div :class="{'has-logo':showLogo}" > --> + <div class="menu"> + <!--TODO 文章中删除了logo,有验证了一次,可以不删除--> + <!-- <logo v-if="showLogo" :collapse="isCollapse"/> --> + <Logo /> + <el-scrollbar id="navlist" wrap-class="scrollbar-wrapper"> + <!--TODO 将el-menu标签下的mode属性更改为horizontal--> + <!-- <span>1111</span> --> + <el-menu + :default-active="activeMenu" + :collapse="isCollapse" + :background-color="variables.menuBg" + :text-color="variables.menuText" + :unique-opened="false" + :active-text-color="variables.menuActiveText" + :collapse-transition="false" + mode="horizontal" + > + <sidebar-item v-for="route in sidebarRouters" :key="route.path" :item="route" :base-path="route.path" /> + </el-menu> + </el-scrollbar> + <navbar /> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +import SidebarItem from './SidebarItem' +import variables from '@/assets/styles/variables.scss' +import Navbar from '../Navbar.vue' +import Logo from './Logo.vue' + +export default { + // components: {Logo }, + components: { SidebarItem, Navbar, Logo }, + computed: { + ...mapGetters([ + 'sidebarRouters', + 'sidebar' + ]), + activeMenu() { + const route = this.$route + const { meta, path } = route + // if set path, the sidebar will highlight the path you set + if (meta.activeMenu) { + return meta.activeMenu + } + return path + }, + showLogo() { + return this.$store.state.settings.sidebarLogo + }, + variables() { + return variables + }, + isCollapse() { + return !this.sidebar.opened + } + } +} +</script> +<style lang="scss" scoped> +.menu{ + display: flex; +} +#navlist { +width: 80% !important; +float: left; +} + + #navlist >>> .el-submenu__title { + font-weight: 100; + font-size:18px !important; + color: white !important; + } + #navlist >>> .is-active{ + font-size: 18px !important; + font-weight: 600 !important; + background-color: rgb(50, 4, 122) !important; + } + #navlist >>> .el-scrollbar__wrap{ + margin-bottom: 0px; + overflow: hidden; + } +/* .el-scrollbar__view{ + display: flex; +} */ + +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Navbar.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Navbar.vue new file mode 100644 index 0000000..e763c91 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Navbar.vue @@ -0,0 +1,210 @@ +<template> + <div class="navbar"> + <!-- <hamburger + v-if="menuInLeft" + id="hamburger-container" + :is-active="sidebar.opened" + class="hamburger-container" + @toggleClick="toggleSideBar" + /> --> + + <!-- <breadcrumb id="breadcrumb-container" class="breadcrumb-container" /> --> + + <div class="right-menu"> + <!-- <template v-if="device !== 'mobile'"> + <search id="header-search" class="right-menu-item" /> + + <el-tooltip content="项目文档" effect="dark" placement="bottom"> + <Doc class="right-menu-item hover-effect" /> + </el-tooltip> + + <el-tooltip content="全屏缩放" effect="dark" placement="bottom"> + <screenfull id="screenfull" class="right-menu-item hover-effect" /> + </el-tooltip> + + <el-tooltip content="布局设置" effect="dark" placement="bottom"> + <size-select id="size-select" class="right-menu-item hover-effect" /> + </el-tooltip> + </template> --> + + <el-dropdown + class="avatar-container right-menu-item hover-effect" + trigger="click" + > + <div class="avatar-wrapper"> + <!-- <img + :src=" + user.avatarName ? baseApi + '/avatar/' + user.avatarName : Avatar + " + class="user-avatar" + /> --> + <i class="el-icon-user" /> + <!-- <span>{{user.content.createBy}}</span> --> + <span>{{ user.username }}</span> + <i class="el-icon-caret-bottom" /> + </div> + <el-dropdown-menu slot="dropdown"> + <!-- <span style="display: block" @click="show = true"> + <el-dropdown-item> 布局设置 </el-dropdown-item> + </span> + <router-link to="/user/center"> + <el-dropdown-item> 个人中心 </el-dropdown-item> + </router-link> --> + <span style="display: block" @click="open"> + <el-dropdown-item divided> 退出登录 </el-dropdown-item> + </span> + </el-dropdown-menu> + </el-dropdown> + </div> + </div> +</template> + +<script> +import { mapGetters, mapState } from 'vuex' +// import Breadcrumb from "@/components/Breadcrumb"; +// import Hamburger from "@/components/Hamburger"; +// import Doc from "@/components/Doc"; +// import Screenfull from "@/components/Screenfull"; +// import SizeSelect from "@/components/SizeSelect"; +// import Search from "@/components/HeaderSearch"; +// import Avatar from "@/assets/images/avatar.png"; + +export default { + components: { + // Breadcrumb, + // Hamburger, + // Screenfull, + // SizeSelect, + // Search, + // Doc, + }, + data() { + return { + // Avatar: Avatar, + dialogVisible: false + } + }, + computed: { + ...mapGetters(['sidebar', 'device', 'user', 'baseApi']), + ...mapState({ + menuInLeft: state => state.settings.menuInLeft + }), + show: { + get() { + return this.$store.state.settings.showSettings + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'showSettings', + value: val + }) + } + } + }, + methods: { + toggleSideBar() { + this.$store.dispatch('app/toggleSideBar') + }, + open() { + this.$confirm('确定注销并退出系统吗?', '提示', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning' + }).then(() => { + this.logout() + }) + }, + logout() { + this.$store.dispatch('LogOut').then(() => { + location.reload() + }) + } + } +} +</script> + +<style lang="scss" scoped> +.navbar { + width: 10%; + height: 56px; + overflow: hidden; + position: relative; + background: #4608ad; + // box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08); + + // .hamburger-container { + // line-height: 46px; + // height: 100%; + // float: left; + // cursor: pointer; + // transition: background 0.3s; + // -webkit-tap-highlight-color: transparent; + + // &:hover { + // background: rgba(0, 0, 0, 0.025); + // } + // } + + // .breadcrumb-container { + // float: left; + // } + + // .errLog-container { + // display: inline-block; + // vertical-align: top; + // } + + .right-menu { + // float: right; + height: 100%; + line-height: 50px; + + &:focus { + outline: none; + } + + .right-menu-item { + display: inline-block; + padding: 0 8px; + height: 100%; + font-size: 18px; + color: #5a5e66; + vertical-align: text-bottom; + + &.hover-effect { + cursor: pointer; + transition: background 0.3s; + + &:hover { + background: rgba(0, 0, 0, 0.025); + } + } + } + + .avatar-container { + margin-right: 30px; + + .avatar-wrapper { + margin-top: 5px; + position: relative; + color: white; + + // .user-avatar { + // cursor: pointer; + // width: 40px; + // height: 40px; + // border-radius: 10px; + // } + + .el-icon-caret-bottom { + cursor: pointer; + position: absolute; + right: -20px; + top: 25px; + font-size: 12px; + } + } + } + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Settings/index.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Settings/index.vue new file mode 100644 index 0000000..3ed8092 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Settings/index.vue @@ -0,0 +1,141 @@ +<template> + <div class="drawer-container"> + <div> + <h3 class="drawer-title">系统布局设置</h3> + + <div class="drawer-item"> + <span>主题颜色</span> + <theme-picker style="float: right;height: 26px;margin: -3px 8px 0 0;" @change="themeChange" /> + </div> + + <div class="drawer-item"> + <span>显示标签</span> + <el-switch v-model="tagsView" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>固定头部</span> + <el-switch v-model="fixedHeader" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>显示LOGO</span> + <el-switch v-model="sidebarLogo" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>菜单UniqueOpened</span> + <el-switch v-model="uniqueOpened" class="drawer-switch" /> + </div> + + <div class="drawer-item"> + <span>切换菜单位置</span> + <el-switch v-model="menuInLeft" class="drawer-switch" /> + </div> + + </div> + </div> +</template> + +<script> +import ThemePicker from '@/components/ThemePicker' + +export default { + components: { ThemePicker }, + data() { + return {} + }, + computed: { + fixedHeader: { + get() { + return this.$store.state.settings.fixedHeader + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'fixedHeader', + value: val + }) + } + }, + tagsView: { + get() { + return this.$store.state.settings.tagsView + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'tagsView', + value: val + }) + } + }, + sidebarLogo: { + get() { + return this.$store.state.settings.sidebarLogo + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'sidebarLogo', + value: val + }) + } + }, + uniqueOpened: { + get() { + return this.$store.state.settings.uniqueOpened + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'uniqueOpened', + value: val + }) + } + }, + // 增加绑定的menuInLeft值 + menuInLeft: { + get() { + return this.$store.state.settings.menuInLeft + }, + set(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'menuInLeft', + value: val + }) + } + } + }, + methods: { + themeChange(val) { + this.$store.dispatch('settings/changeSetting', { + key: 'theme', + value: val + }) + } + } +} +</script> + +<style lang="scss" scoped> +.drawer-container { + padding: 24px; + font-size: 14px; + line-height: 1.5; + word-wrap: break-word; + + .drawer-title { + margin-bottom: 12px; + color: rgba(0, 0, 0, .85); + font-size: 14px; + line-height: 22px; + } + + .drawer-item { + color: rgba(0, 0, 0, .65); + font-size: 14px; + padding: 12px 0; + } + + .drawer-switch { + float: right + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/FixiOSBug.js b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/FixiOSBug.js new file mode 100644 index 0000000..bc14856 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/FixiOSBug.js @@ -0,0 +1,26 @@ +export default { + computed: { + device() { + return this.$store.state.app.device + } + }, + mounted() { + // In order to fix the click on menu on the ios device will trigger the mouseleave bug + // https://github.com/PanJiaChen/vue-element-admin/issues/1135 + this.fixBugIniOS() + }, + methods: { + fixBugIniOS() { + const $subMenu = this.$refs.subMenu + if ($subMenu) { + const handleMouseleave = $subMenu.handleMouseleave + $subMenu.handleMouseleave = (e) => { + if (this.device === 'mobile') { + return + } + handleMouseleave(e) + } + } + } + } +} diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Item.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Item.vue new file mode 100644 index 0000000..b515f61 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Item.vue @@ -0,0 +1,29 @@ +<script> +export default { + name: 'MenuItem', + functional: true, + props: { + icon: { + type: String, + default: '' + }, + title: { + type: String, + default: '' + } + }, + render(h, context) { + const { icon, title } = context.props + const vnodes = [] + + if (icon) { + vnodes.push(<svg-icon icon-class={icon}/>) + } + + if (title) { + vnodes.push(<span slot='title'>{(title)}</span>) + } + return vnodes + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Link.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Link.vue new file mode 100644 index 0000000..eb4dd10 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Link.vue @@ -0,0 +1,36 @@ + +<template> + <!-- eslint-disable vue/require-component-is --> + <component v-bind="linkProps(to)"> + <slot /> + </component> +</template> + +<script> +import { isExternal } from '@/utils/validate' + +export default { + props: { + to: { + type: String, + required: true + } + }, + methods: { + linkProps(url) { + if (isExternal(url)) { + return { + is: 'a', + href: url, + target: '_blank', + rel: 'noopener' + } + } + return { + is: 'router-link', + to: url + } + } + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Logo.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Logo.vue new file mode 100644 index 0000000..1233dcd --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/Logo.vue @@ -0,0 +1,82 @@ +<template> + <div class="sidebar-logo-container" :class="{'collapse':collapse}"> + <transition name="sidebarLogoFade"> + <router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo"> + <h1 v-else class="sidebar-title">{{ title }} </h1> + </router-link> + <router-link v-else key="expand" class="sidebar-logo-link" to="/"> + <img v-if="logo" :src="logo" class="sidebar-logo"> + <h1 class="sidebar-title">{{ title }} </h1> + </router-link> + </transition> + </div> +</template> + +<script> +import Logo from '@/assets/images/logo.png' +export default { + name: 'SidebarLogo', + props: { + collapse: { + type: Boolean, + required: true + } + }, + data() { + return { + title: 'DiamondV后台管理', + logo: Logo + } + } +} +</script> + +<style lang="scss" scoped> +.sidebarLogoFade-enter-active { + transition: opacity 1.5s; +} + +.sidebarLogoFade-enter, +.sidebarLogoFade-leave-to { + opacity: 0; +} + +.sidebar-logo-container { + position: relative; + width: 100%; + height: 50px; + line-height: 50px; + text-align: center; + overflow: hidden; + + & .sidebar-logo-link { + height: 100%; + width: 100%; + + & .sidebar-logo { + width: 32px; + height: 32px; + vertical-align: middle; + margin-right: 6px; + } + + & .sidebar-title { + display: inline-block; + margin: 0; + color: #fff; + font-weight: 600; + line-height: 50px; + font-size: 14px; + font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif; + vertical-align: middle; + } + } + + &.collapse { + .sidebar-logo { + margin-right: 0px; + } + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/SidebarItem.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/SidebarItem.vue new file mode 100644 index 0000000..2d49dd8 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/SidebarItem.vue @@ -0,0 +1,95 @@ +<template> + <div v-if="!item.hidden" class="menu-wrapper"> + <template v-if="hasOneShowingChild(item.children,item) && (!onlyOneChild.children||onlyOneChild.noShowingChildren)&&!item.alwaysShow"> + <app-link v-if="onlyOneChild.meta" :to="resolvePath(onlyOneChild.path)"> + <el-menu-item :index="resolvePath(onlyOneChild.path)" :class="{'submenu-title-noDropdown':!isNest}"> + <item :icon="onlyOneChild.meta.icon||(item.meta&&item.meta.icon)" :title="onlyOneChild.meta.title" /> + </el-menu-item> + </app-link> + </template> + + <el-submenu v-else ref="subMenu" :index="resolvePath(item.path)" popper-append-to-body> + <template slot="title"> + <item v-if="item.meta" :icon="item.meta && item.meta.icon" :title="item.meta.title" /> + </template> + <sidebar-item + v-for="child in item.children" + :key="child.path" + :is-nest="true" + :item="child" + :base-path="resolvePath(child.path)" + class="nest-menu" + /> + </el-submenu> + </div> +</template> + +<script> +import path from 'path' +import { isExternal } from '@/utils/validate' +import Item from './Item' +import AppLink from './Link' +import FixiOSBug from './FixiOSBug' + +export default { + name: 'SidebarItem', + components: { Item, AppLink }, + mixins: [FixiOSBug], + props: { + // route object + item: { + type: Object, + required: true + }, + isNest: { + type: Boolean, + default: false + }, + basePath: { + type: String, + default: '' + } + }, + data() { + // To fix https://github.com/PanJiaChen/vue-admin-template/issues/237 + // TODO: refactor with render function + this.onlyOneChild = null + return {} + }, + methods: { + hasOneShowingChild(children = [], parent) { + const showingChildren = children.filter(item => { + if (item.hidden) { + return false + } else { + // Temp set(will be used if only has one showing child) + this.onlyOneChild = item + return true + } + }) + + // When there is only one child router, the child router is displayed by default + if (showingChildren.length === 1) { + return true + } + + // Show parent if there are no child router to display + if (showingChildren.length === 0) { + this.onlyOneChild = { ... parent, path: '', noShowingChildren: true } + return true + } + + return false + }, + resolvePath(routePath) { + if (isExternal(routePath)) { + return routePath + } + if (isExternal(this.basePath)) { + return this.basePath + } + return path.resolve(this.basePath, routePath) + } + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/index.vue b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/index.vue new file mode 100644 index 0000000..b192bd2 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/Sidebar/index.vue @@ -0,0 +1,56 @@ +<template> + <div :class="{ 'has-logo': showLogo }"> + <logo v-if="showLogo" :collapse="isCollapse" /> + <el-scrollbar wrap-class="scrollbar-wrapper"> + <el-menu + :default-active="activeMenu" + :collapse="isCollapse" + :background-color="variables.menuBg" + :text-color="variables.menuText" + :unique-opened="$store.state.settings.uniqueOpened" + :active-text-color="variables.menuActiveText" + :collapse-transition="false" + mode="vertical" + > + <sidebar-item + v-for="route in sidebarRouters" + :key="route.path" + :item="route" + :base-path="route.path" + /> + </el-menu> + </el-scrollbar> + </div> +</template> + +<script> +import { mapGetters } from 'vuex' +import Logo from './Logo' +import SidebarItem from './SidebarItem' +import variables from '@/assets/styles/variables.scss' + +export default { + components: { SidebarItem, Logo }, + computed: { + ...mapGetters(['sidebarRouters', 'sidebar']), + activeMenu() { + const route = this.$route + const { meta, path } = route + // if set path, the sidebar will highlight the path you set + if (meta.activeMenu) { + return meta.activeMenu + } + return path + }, + showLogo() { + return this.$store.state.settings.sidebarLogo + }, + variables() { + return variables + }, + isCollapse() { + return !this.sidebar.opened + } + } +} +</script> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/ScrollPane.vue b/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/ScrollPane.vue new file mode 100644 index 0000000..018f1aa --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/ScrollPane.vue @@ -0,0 +1,86 @@ +<template> + <el-scrollbar ref="scrollContainer" :vertical="false" class="scroll-container" @wheel.native.prevent="handleScroll"> + <slot /> + </el-scrollbar> +</template> + +<script> +const tagAndTagSpacing = 4 // tagAndTagSpacing + +export default { + name: 'ScrollPane', + data() { + return { + left: 0 + } + }, + computed: { + scrollWrapper() { + return this.$refs.scrollContainer.$refs.wrap + } + }, + methods: { + handleScroll(e) { + const eventDelta = e.wheelDelta || -e.deltaY * 40 + const $scrollWrapper = this.scrollWrapper + $scrollWrapper.scrollLeft = $scrollWrapper.scrollLeft + eventDelta / 4 + }, + moveToTarget(currentTag) { + const $container = this.$refs.scrollContainer.$el + const $containerWidth = $container.offsetWidth + const $scrollWrapper = this.scrollWrapper + const tagList = this.$parent.$refs.tag + + let firstTag = null + let lastTag = null + + // find first tag and last tag + if (tagList.length > 0) { + firstTag = tagList[0] + lastTag = tagList[tagList.length - 1] + } + + if (firstTag === currentTag) { + $scrollWrapper.scrollLeft = 0 + } else if (lastTag === currentTag) { + $scrollWrapper.scrollLeft = $scrollWrapper.scrollWidth - $containerWidth + } else { + // find preTag and nextTag + const currentIndex = tagList.findIndex(item => item === currentTag) + const prevTag = tagList[currentIndex - 1] + const nextTag = tagList[currentIndex + 1] + + // the tag's offsetLeft after of nextTag + const afterNextTagOffsetLeft = nextTag.$el.offsetLeft + nextTag.$el.offsetWidth + tagAndTagSpacing + + // the tag's offsetLeft before of prevTag + const beforePrevTagOffsetLeft = prevTag.$el.offsetLeft - tagAndTagSpacing + + if (afterNextTagOffsetLeft > $scrollWrapper.scrollLeft + $containerWidth) { + $scrollWrapper.scrollLeft = afterNextTagOffsetLeft - $containerWidth + } else if (beforePrevTagOffsetLeft < $scrollWrapper.scrollLeft) { + $scrollWrapper.scrollLeft = beforePrevTagOffsetLeft + } + } + } + } +} +</script> + +<style lang="scss" scoped> +.scroll-container { + white-space: nowrap; + position: relative; + overflow: hidden; + width: 100%; + ::v-deep { + .el-scrollbar__bar { + bottom: 0px; + } + .el-scrollbar__wrap { + height: 49px; + // width: 80% !important; + } + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/index.vue b/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/index.vue new file mode 100644 index 0000000..9dd84f9 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/TagsView/index.vue @@ -0,0 +1,286 @@ +<template> + <div id="tags-view-container" class="tags-view-container"> + <scroll-pane ref="scrollPane" class="tags-view-wrapper"> + <router-link + v-for="tag in visitedViews" + ref="tag" + :key="tag.path" + :class="isActive(tag)?'active':''" + :to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }" + tag="span" + class="tags-view-item" + @click.middle.native="closeSelectedTag(tag)" + @contextmenu.prevent.native="openMenu(tag,$event)" + > + {{ tag.title }} + <span v-if="!tag.meta.affix" class="el-icon-close" @click.prevent.stop="closeSelectedTag(tag)" /> + </router-link> + </scroll-pane> + <ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu"> + <li @click="refreshSelectedTag(selectedTag)">刷新</li> + <li v-if="!(selectedTag.meta&&selectedTag.meta.affix)" @click="closeSelectedTag(selectedTag)">关闭</li> + <li @click="closeOthersTags">关闭其他</li> + <li @click="closeAllTags(selectedTag)">关闭全部</li> + </ul> + </div> +</template> + +<script> +import ScrollPane from './ScrollPane' +import path from 'path' + +export default { + components: { ScrollPane }, + data() { + return { + visible: false, + top: 0, + left: 0, + selectedTag: {}, + affixTags: [] + } + }, + computed: { + visitedViews() { + return this.$store.state.tagsView.visitedViews + }, + routes() { + return this.$store.state.permission.routers + } + }, + watch: { + $route() { + this.addTags() + this.moveToCurrentTag() + }, + visible(value) { + if (value) { + document.body.addEventListener('click', this.closeMenu) + } else { + document.body.removeEventListener('click', this.closeMenu) + } + } + }, + mounted() { + this.initTags() + this.addTags() + }, + methods: { + isActive(route) { + return route.path === this.$route.path + }, + filterAffixTags(routes, basePath = '/') { + let tags = [] + routes.forEach(route => { + if (route.meta && route.meta.affix) { + const tagPath = path.resolve(basePath, route.path) + tags.push({ + fullPath: tagPath, + path: tagPath, + name: route.name, + meta: { ...route.meta } + }) + } + if (route.children) { + const tempTags = this.filterAffixTags(route.children, route.path) + if (tempTags.length >= 1) { + tags = [...tags, ...tempTags] + } + } + }) + return tags + }, + initTags() { + const affixTags = this.affixTags = this.filterAffixTags(this.routes) + for (const tag of affixTags) { + // Must have tag name + if (tag.name) { + this.$store.dispatch('tagsView/addVisitedView', tag) + } + } + }, + addTags() { + const { name } = this.$route + if (name) { + this.$store.dispatch('tagsView/addView', this.$route) + } + return false + }, + moveToCurrentTag() { + const tags = this.$refs.tag + this.$nextTick(() => { + for (const tag of tags) { + if (tag.to.path === this.$route.path) { + this.$refs.scrollPane.moveToTarget(tag) + // when query is different then update + if (tag.to.fullPath !== this.$route.fullPath) { + this.$store.dispatch('tagsView/updateVisitedView', this.$route) + } + break + } + } + }) + }, + refreshSelectedTag(view) { + this.$store.dispatch('tagsView/delCachedView', view).then(() => { + const { fullPath } = view + this.$nextTick(() => { + this.$router.replace({ + path: '/redirect' + fullPath + }) + }) + }) + }, + closeSelectedTag(view) { + this.$store.dispatch('tagsView/delView', view).then(({ visitedViews }) => { + if (this.isActive(view)) { + this.toLastView(visitedViews, view) + } + }) + }, + closeOthersTags() { + this.$router.push(this.selectedTag) + this.$store.dispatch('tagsView/delOthersViews', this.selectedTag).then(() => { + this.moveToCurrentTag() + }) + }, + closeAllTags(view) { + this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => { + if (this.affixTags.some(tag => tag.path === view.path)) { + return + } + this.toLastView(visitedViews, view) + }) + }, + toLastView(visitedViews, view) { + const latestView = visitedViews.slice(-1)[0] + if (latestView) { + this.$router.push(latestView) + } else { + // now the default is to redirect to the home page if there is no tags-view, + // you can adjust it according to your needs. + if (view.name === 'Dashboard') { + // to reload home page + this.$router.replace({ path: '/redirect' + view.fullPath }) + } else { + this.$router.push('/') + } + } + }, + openMenu(tag, e) { + const menuMinWidth = 105 + const offsetLeft = this.$el.getBoundingClientRect().left // container margin left + const offsetWidth = this.$el.offsetWidth // container width + const maxLeft = offsetWidth - menuMinWidth // left boundary + const left = e.clientX - offsetLeft + 15 // 15: margin right + + if (left > maxLeft) { + this.left = maxLeft + } else { + this.left = left + } + + this.top = e.clientY + this.visible = true + this.selectedTag = tag + }, + closeMenu() { + this.visible = false + } + } +} +</script> + +<style lang="scss" scoped> +.tags-view-container { + height: 34px; + width: 100%; + background: #fff; + border-bottom: 1px solid #d8dce5; + box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .12), 0 0 3px 0 rgba(0, 0, 0, .04); + .tags-view-wrapper { + .tags-view-item { + display: inline-block; + position: relative; + cursor: pointer; + height: 26px; + line-height: 26px; + border: 1px solid #d8dce5; + color: #495060; + background: #fff; + padding: 0 8px; + font-size: 12px; + margin-left: 5px; + margin-top: 4px; + &:first-of-type { + margin-left: 15px; + } + &:last-of-type { + margin-right: 15px; + } + &.active { + background-color: #42b983; + color: #fff; + border-color: #42b983; + &::before { + content: ''; + background: #fff; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + position: relative; + margin-right: 2px; + } + } + } + } + .contextmenu { + margin: 0; + background: #fff; + z-index: 3000; + position: absolute; + list-style-type: none; + padding: 5px 0; + border-radius: 4px; + font-size: 12px; + font-weight: 400; + color: #333; + box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, .3); + li { + margin: 0; + padding: 7px 16px; + cursor: pointer; + &:hover { + background: #eee; + } + } + } +} +</style> + +<style lang="scss"> +//reset element css of el-icon-close +.tags-view-wrapper { + .tags-view-item { + .el-icon-close { + width: 16px; + height: 16px; + vertical-align: 2px; + border-radius: 50%; + text-align: center; + transition: all .3s cubic-bezier(.645, .045, .355, 1); + transform-origin: 100% 50%; + &:before { + transform: scale(.6); + display: inline-block; + vertical-align: -3px; + } + &:hover { + background-color: #b4bccc; + color: #fff; + } + } + } +} +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/components/index.js b/UI source code/dns_mapping_ui-master/src/layout/components/index.js new file mode 100644 index 0000000..61ef939 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/components/index.js @@ -0,0 +1,6 @@ +export { default as AppMain } from './AppMain' +export { default as Navbar } from './Navbar' +export { default as HeadNavbar } from './HeadNavbar/index.vue' +export { default as Settings } from './Settings' +export { default as Sidebar } from './Sidebar/index.vue' +export { default as TagsView } from './TagsView/index.vue' diff --git a/UI source code/dns_mapping_ui-master/src/layout/index.vue b/UI source code/dns_mapping_ui-master/src/layout/index.vue new file mode 100644 index 0000000..6e83e5c --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/index.vue @@ -0,0 +1,118 @@ +<template> + <div :class="classObj" class="app-wrapper"> + <div v-if="device==='mobile'&&sidebar.opened" class="drawer-bg" @click="handleClickOutside" /> + <sidebar v-if="menuInLeft" class="sidebar-container" /> + <div :class="{hasTagsView:needTagsView,'main-container':menuInLeft}" class="main-container"> + <div :class="{'fixed-header':fixedHeader}"> + <!-- <navbar /> --> + <HeadNavbar v-if="!menuInLeft" /> + <!-- <tags-view v-if="needTagsView" /> --> + </div> + <app-main /> + <right-panel v-if="showSettings"> + <settings /> + </right-panel> + </div> + <!-- 防止刷新后主题丢失 --> + <Theme v-show="false" ref="theme" /> + </div> +</template> + +<script> +import RightPanel from '@/components/RightPanel' +import { AppMain, Navbar, Settings, Sidebar, TagsView, HeadNavbar } from './components' +import ResizeMixin from './mixin/ResizeHandler' +import { mapState } from 'vuex' +import Theme from '@/components/ThemePicker' +import Cookies from 'js-cookie' +export default { + name: 'Layout', + components: { + AppMain, + RightPanel, + HeadNavbar, + Settings, + Sidebar, + TagsView, + Theme + }, + mixins: [ResizeMixin], + computed: { + ...mapState({ + sidebar: state => state.app.sidebar, + device: state => state.app.device, + showSettings: state => state.settings.showSettings, + needTagsView: state => state.settings.tagsView, + fixedHeader: state => state.settings.fixedHeader, + menuInLeft: state => state.settings.menuInLeft + }), + classObj() { + return { + hideSidebar: !this.sidebar.opened, + openSidebar: this.sidebar.opened, + withoutAnimation: this.sidebar.withoutAnimation, + mobile: this.device === 'mobile' + } + } + }, + mounted() { + if (Cookies.get('theme')) { + this.$refs.theme.theme = Cookies.get('theme') + this.$store.dispatch('settings/changeSetting', { + key: 'theme', + value: Cookies.get('theme') + }) + } + }, + methods: { + handleClickOutside() { + this.$store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } +} +</script> + +<style lang="scss" scoped> + @import "~@/assets/styles/mixin.scss"; + @import "~@/assets/styles/variables.scss"; + + .rapper { + @include clearfix; + position: relative; + height: 100%; + // width: 100%; + + &.mobile.openSidebar { + position: fixed; + top: 0; + } + } + + .drawer-bg { + background: #000; + opacity: 0.3; + width: 100%; + top: 0; + height: 100%; + position: absolute; + z-index: 999; + } + + .fixed-header { + position: fixed; + top: 0; + right: 0; + z-index: 9; + width: calc(100% - #{$sideBarWidth}); + transition: width 0.28s; + padding: 0; + } + + .hideSidebar .fixed-header { + width: calc(100% - 54px) + } + + .mobile .fixed-header { + width: 100%; + } +</style> diff --git a/UI source code/dns_mapping_ui-master/src/layout/mixin/ResizeHandler.js b/UI source code/dns_mapping_ui-master/src/layout/mixin/ResizeHandler.js new file mode 100644 index 0000000..e8d0df8 --- /dev/null +++ b/UI source code/dns_mapping_ui-master/src/layout/mixin/ResizeHandler.js @@ -0,0 +1,45 @@ +import store from '@/store' + +const { body } = document +const WIDTH = 992 // refer to Bootstrap's responsive design + +export default { + watch: { + $route(route) { + if (this.device === 'mobile' && this.sidebar.opened) { + store.dispatch('app/closeSideBar', { withoutAnimation: false }) + } + } + }, + beforeMount() { + window.addEventListener('resize', this.$_resizeHandler) + }, + beforeDestroy() { + window.removeEventListener('resize', this.$_resizeHandler) + }, + mounted() { + const isMobile = this.$_isMobile() + if (isMobile) { + store.dispatch('app/toggleDevice', 'mobile') + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + }, + methods: { + // use $_ for mixins properties + // https://vuejs.org/v2/style-guide/index.html#Private-property-names-essential + $_isMobile() { + const rect = body.getBoundingClientRect() + return rect.width - 1 < WIDTH + }, + $_resizeHandler() { + if (!document.hidden) { + const isMobile = this.$_isMobile() + store.dispatch('app/toggleDevice', isMobile ? 'mobile' : 'desktop') + + if (isMobile) { + store.dispatch('app/closeSideBar', { withoutAnimation: true }) + } + } + } + } +} |
